Commit 6dc0e6d2 authored by darin@apple.com's avatar darin@apple.com

WebCore:

2009-05-13  Douglas R. Davidson  <ddavidso@apple.com>

        Reviewed by Darin Adler.

        <rdar://problem/6879145>
        Generate a contextual menu item allowing autocorrections to
        easily be changed back.  Refrain from re-correcting items
        that have already been autocorrected once.

        * dom/DocumentMarker.h:
        * editing/Editor.cpp:
        (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
        (WebCore::Editor::changeBackToReplacedString):
        * editing/Editor.h:
        * page/ContextMenuController.cpp:
        (WebCore::ContextMenuController::contextMenuItemSelected):
        * page/mac/WebCoreViewFactory.h:
        * platform/ContextMenu.cpp:
        (WebCore::ContextMenu::populate):
        (WebCore::ContextMenu::checkOrEnableIfNeeded):
        * platform/ContextMenuItem.h:
        * platform/LocalizedStrings.h:
        * platform/mac/LocalizedStringsMac.mm:
        (WebCore::contextMenuItemTagChangeBack):
        * rendering/HitTestResult.cpp:
        (WebCore::HitTestResult::replacedString):
        * rendering/HitTestResult.h:
        * rendering/InlineTextBox.cpp:
        (WebCore::InlineTextBox::computeRectForReplacementMarker):
        (WebCore::InlineTextBox::paintDocumentMarkers):
        * rendering/InlineTextBox.h:

WebKit/mac:

2009-05-13  Douglas R. Davidson  <ddavidso@apple.com>

        Reviewed by Darin Adler.

        <rdar://problem/6879145>
        Generate a contextual menu item allowing autocorrections to
        easily be changed back.  Refrain from re-correcting items
        that have already been autocorrected once.

        * WebCoreSupport/WebViewFactory.mm:
        (-[WebViewFactory contextMenuItemTagChangeBack:]):
        * WebView/WebUIDelegatePrivate.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@43639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 36f3fe57
2009-05-13 Douglas R. Davidson <ddavidso@apple.com>
Reviewed by Darin Adler.
<rdar://problem/6879145>
Generate a contextual menu item allowing autocorrections to
easily be changed back. Refrain from re-correcting items
that have already been autocorrected once.
* dom/DocumentMarker.h:
* editing/Editor.cpp:
(WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
(WebCore::Editor::changeBackToReplacedString):
* editing/Editor.h:
* page/ContextMenuController.cpp:
(WebCore::ContextMenuController::contextMenuItemSelected):
* page/mac/WebCoreViewFactory.h:
* platform/ContextMenu.cpp:
(WebCore::ContextMenu::populate):
(WebCore::ContextMenu::checkOrEnableIfNeeded):
* platform/ContextMenuItem.h:
* platform/LocalizedStrings.h:
* platform/mac/LocalizedStringsMac.mm:
(WebCore::contextMenuItemTagChangeBack):
* rendering/HitTestResult.cpp:
(WebCore::HitTestResult::replacedString):
* rendering/HitTestResult.h:
* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::computeRectForReplacementMarker):
(WebCore::InlineTextBox::paintDocumentMarkers):
* rendering/InlineTextBox.h:
2009-05-13 Holger Hans Peter Freyther <zecke@selfish.org>
Rubber Stamped by Oliver Hunt.
......@@ -38,7 +38,8 @@ struct DocumentMarker {
AllMarkers = -1,
Spelling,
Grammar,
TextMatch
TextMatch,
Replacement
};
MarkerType type;
......
......@@ -2401,6 +2401,23 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range*
if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1)
doReplacement = false;
// Don't correct spelling in an already-corrected word.
if (doReplacement && result->type == TextCheckingTypeCorrection) {
Node* node = rangeToReplace->startContainer();
int startOffset = rangeToReplace->startOffset();
int endOffset = startOffset + replacementLength;
Vector<DocumentMarker> markers = node->document()->markersForNode(node);
size_t markerCount = markers.size();
for (size_t i = 0; i < markerCount; ++i) {
const DocumentMarker& marker = markers[i];
if (marker.type == DocumentMarker::Replacement && static_cast<int>(marker.startOffset) < endOffset && static_cast<int>(marker.endOffset) > startOffset) {
doReplacement = false;
break;
}
if (static_cast<int>(marker.startOffset) >= endOffset)
break;
}
}
if (doReplacement && selectionToReplace != m_frame->selection()->selection()) {
if (m_frame->shouldChangeSelection(selectionToReplace)) {
m_frame->selection()->setSelection(selectionToReplace);
......@@ -2415,11 +2432,19 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range*
if (canEditRichly())
applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
} else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
String replacedString;
if (result->type == TextCheckingTypeCorrection)
replacedString = plainText(rangeToReplace.get());
replaceSelectionWithText(result->replacement, false, false);
spellingRangeEndOffset += replacementLength - resultLength;
offsetDueToReplacement += replacementLength - resultLength;
if (resultLocation < selectionOffset)
selectionOffset += replacementLength - resultLength;
if (result->type == TextCheckingTypeCorrection) {
// Add a marker so that corrections can easily be undone and won't be re-corrected.
RefPtr<Range> replacedRange = TextIterator::subrange(paragraphRange.get(), resultLocation, replacementLength);
replacedRange->startContainer()->document()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString);
}
}
}
}
......@@ -2442,6 +2467,23 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range*
}
}
void Editor::changeBackToReplacedString(const String& replacedString)
{
if (replacedString.isEmpty())
return;
RefPtr<Range> selection = selectedRange();
if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
return;
String paragraphString;
int selectionOffset;
RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(selection.get(), selectionOffset, paragraphString);
replaceSelectionWithText(replacedString, false, false);
RefPtr<Range> changedRange = TextIterator::subrange(paragraphRange.get(), selectionOffset, replacedString.length());
changedRange->startContainer()->document()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
}
#endif
void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
......
......@@ -218,6 +218,7 @@ public:
bool isAutomaticSpellingCorrectionEnabled();
void toggleAutomaticSpellingCorrection();
void markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range* spellingRange, bool markGrammar, Range* grammarRange, bool performTextCheckingReplacements);
void changeBackToReplacedString(const String& replacedString);
#endif
void advanceToNextMisspelling(bool startBeforeSelection = false);
void showSpellingGuessPanel();
......
......@@ -326,6 +326,9 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
case ContextMenuItemTagCorrectSpellingAutomatically:
frame->editor()->toggleAutomaticSpellingCorrection();
break;
case ContextMenuItemTagChangeBack:
frame->editor()->changeBackToReplacedString(result.replacedString());
break;
#endif
case ContextMenuItemTagInspectElement:
if (Page* page = frame->page())
......
......@@ -95,6 +95,7 @@
- (NSString *)contextMenuItemTagMakeUpperCase;
- (NSString *)contextMenuItemTagMakeLowerCase;
- (NSString *)contextMenuItemTagCapitalize;
- (NSString *)contextMenuItemTagChangeBack:(NSString *)replacedString;
- (NSString *)contextMenuItemTagInspectElement;
- (NSString *)searchMenuNoRecentSearchesText;
......
......@@ -406,6 +406,16 @@ void ContextMenu::populate()
} else
appendItem(IgnoreGrammarItem);
appendItem(*separatorItem());
#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
} else {
// If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
String replacedString = result.replacedString();
if (!replacedString.isEmpty()) {
ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
appendItem(item);
appendItem(*separatorItem());
}
#endif
}
}
......@@ -659,6 +669,7 @@ void ContextMenu::checkOrEnableIfNeeded(ContextMenuItem& item) const
case ContextMenuItemTagMakeUpperCase:
case ContextMenuItemTagMakeLowerCase:
case ContextMenuItemTagCapitalize:
case ContextMenuItemTagChangeBack:
shouldEnable = frame->editor()->canEdit();
break;
case ContextMenuItemTagCorrectSpellingAutomatically:
......
......@@ -137,6 +137,7 @@ namespace WebCore {
ContextMenuItemTagMakeUpperCase,
ContextMenuItemTagMakeLowerCase,
ContextMenuItemTagCapitalize,
ContextMenuItemTagChangeBack,
#endif
ContextMenuItemBaseApplicationTag = 10000
};
......
......@@ -100,6 +100,7 @@ namespace WebCore {
String contextMenuItemTagMakeUpperCase();
String contextMenuItemTagMakeLowerCase();
String contextMenuItemTagCapitalize();
String contextMenuItemTagChangeBack(const String& replacedString);
#endif
String contextMenuItemTagInspectElement();
......
......@@ -529,6 +529,14 @@ String contextMenuItemTagCapitalize()
return String();
}
String contextMenuItemTagChangeBack(const String& replacedString)
{
BEGIN_BLOCK_OBJC_EXCEPTIONS;
return [[WebCoreViewFactory sharedFactory] contextMenuItemTagChangeBack:replacedString];
END_BLOCK_OBJC_EXCEPTIONS;
return replacedString;
}
String contextMenuItemTagInspectElement()
{
BEGIN_BLOCK_OBJC_EXCEPTIONS;
......
......@@ -159,6 +159,20 @@ String HitTestResult::spellingToolTip() const
return marker->description;
}
String HitTestResult::replacedString() const
{
// Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected,
// and is used for generating a contextual menu item that allows it to easily be changed back if desired.
if (!m_innerNonSharedNode)
return String();
DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Replacement);
if (!marker)
return String();
return marker->description;
}
String HitTestResult::title() const
{
// Find the title in the nearest enclosing DOM node.
......
......@@ -65,6 +65,7 @@ public:
IntRect boundingBox() const;
bool isSelected() const;
String spellingToolTip() const;
String replacedString() const;
String title() const;
String altDisplayString() const;
String titleDisplayString() const;
......
......@@ -744,6 +744,22 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, Do
}
}
void InlineTextBox::computeRectForReplacementMarker(int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font& font)
{
// Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
int y = selectionTop();
int h = selectionHeight();
int sPos = max(marker.startOffset - m_start, (unsigned)0);
int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered());
IntPoint startPoint = IntPoint(m_x + tx, y + ty);
// Compute and store the rect associated with this marker.
IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
}
void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, RenderStyle* style, const Font& font, bool background)
{
if (!renderer()->node())
......@@ -761,6 +777,7 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, Re
switch (marker.type) {
case DocumentMarker::Grammar:
case DocumentMarker::Spelling:
case DocumentMarker::Replacement:
if (background)
continue;
break;
......@@ -794,6 +811,9 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, Re
case DocumentMarker::TextMatch:
paintTextMatchMarker(pt, tx, ty, marker, style, font);
break;
case DocumentMarker::Replacement:
computeRectForReplacementMarker(tx, ty, marker, style, font);
break;
default:
ASSERT_NOT_REACHED();
}
......
......@@ -133,6 +133,7 @@ private:
void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font&);
void paintSpellingOrGrammarMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font&, bool grammar);
void paintTextMatchMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font&);
void computeRectForReplacementMarker(int tx, int ty, DocumentMarker, RenderStyle*, const Font&);
};
inline RenderText* InlineTextBox::textRenderer() const
......
2009-05-13 Douglas R. Davidson <ddavidso@apple.com>
Reviewed by Darin Adler.
<rdar://problem/6879145>
Generate a contextual menu item allowing autocorrections to
easily be changed back. Refrain from re-correcting items
that have already been autocorrected once.
* WebCoreSupport/WebViewFactory.mm:
(-[WebViewFactory contextMenuItemTagChangeBack:]):
* WebView/WebUIDelegatePrivate.h:
2009-05-12 Anders Carlsson <andersca@apple.com>
Reviewed by Dan Bernstein.
......
......@@ -430,6 +430,19 @@
return UI_STRING("Capitalize", "Capitalize context menu item");
}
- (NSString *)contextMenuItemTagChangeBack:(NSString *)replacedString
{
static NSString *formatString = nil;
#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
static bool lookedUpString = false;
if (!lookedUpString) {
formatString = [[[NSBundle bundleForClass:[NSSpellChecker class]] localizedStringForKey:@"Change Back to \\U201C%@\\U201D" value:nil table:@"MenuCommands"] retain];
lookedUpString = true;
}
#endif
return formatString ? [NSString stringWithFormat:formatString, replacedString] : replacedString;
}
- (NSString *)contextMenuItemTagInspectElement
{
return UI_STRING("Inspect Element", "Inspect Element context menu item");
......
......@@ -84,6 +84,7 @@ enum {
WebMenuItemTagMakeUpperCase,
WebMenuItemTagMakeLowerCase,
WebMenuItemTagCapitalize,
WebMenuItemTagChangeBack,
WebMenuItemTagBaseApplication = 10000
};
@class WebGeolocation;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment