diff options
Diffstat (limited to 'WebCore/editing/Editor.cpp')
-rw-r--r-- | WebCore/editing/Editor.cpp | 286 |
1 files changed, 193 insertions, 93 deletions
diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp index c74d765..327aa5f 100644 --- a/WebCore/editing/Editor.cpp +++ b/WebCore/editing/Editor.cpp @@ -457,9 +457,53 @@ bool Editor::shouldShowDeleteInterface(HTMLElement* element) const void Editor::respondToChangedSelection(const VisibleSelection& oldSelection) { +#if SUPPORT_AUTOCORRECTION_PANEL + VisibleSelection currentSelection(frame()->selection()->selection()); + if (currentSelection != oldSelection) { + stopCorrectionPanelTimer(); + dismissCorrectionPanel(CorrectionWasNotRejected); + } +#endif // SUPPORT_AUTOCORRECTION_PANEL + if (client()) client()->respondToChangedSelection(); m_deleteButtonController->respondToChangedSelection(oldSelection); + +#if SUPPORT_AUTOCORRECTION_PANEL + // When user moves caret to the end of autocorrected word and pauses, we show the panel + // containing the original pre-correction word so that user can quickly revert the + // undesired autocorrection. Here, we start correction panel timer once we confirm that + // the new caret position is at the end of a word. + if (!currentSelection.isCaret() || currentSelection == oldSelection) + return; + + VisiblePosition selectionPosition = currentSelection.start(); + VisiblePosition endPositionOfWord = endOfWord(selectionPosition, LeftWordIfOnBoundary); + if (selectionPosition != endPositionOfWord) + return; + + Position position = endPositionOfWord.deepEquivalent(); + if (position.anchorType() != Position::PositionIsOffsetInAnchor) + return; + + Node* node = position.containerNode(); + int endOffset = position.offsetInContainerNode(); + Vector<DocumentMarker> markers = node->document()->markers()->markersForNode(node); + size_t markerCount = markers.size(); + for (size_t i = 0; i < markerCount; ++i) { + const DocumentMarker& marker = markers[i]; + if (marker.type == DocumentMarker::CorrectionIndicator && static_cast<int>(marker.endOffset) == endOffset) { + RefPtr<Range> wordRange = Range::create(frame()->document(), node, marker.startOffset, node, marker.endOffset); + String currentWord = plainText(wordRange.get()); + if (currentWord.length() > 0 && marker.description.length() > 0) { + m_correctionPanelInfo.m_rangeToBeReplaced = wordRange; + m_correctionPanelInfo.m_replacementString = marker.description; + startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeReversion); + } + break; + } + } +#endif // SUPPORT_AUTOCORRECTION_PANEL } void Editor::respondToChangedContents(const VisibleSelection& endingSelection) @@ -564,7 +608,8 @@ WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbe } if (m_frame->selection()->isCaret()) { - if (CSSMutableStyleDeclaration* typingStyle = m_frame->selection()->typingStyle()) { + RefPtr<CSSMutableStyleDeclaration> typingStyle = m_frame->selection()->typingStyle(); + if (typingStyle) { RefPtr<CSSValue> unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (unicodeBidi) { ASSERT(unicodeBidi->isPrimitiveValue()); @@ -998,10 +1043,13 @@ static void dispatchEditableContentChangedEvents(const EditCommand& command) void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) { + // We may start reversion panel timer in respondToChangedSelection(). + // So we stop the timer for current panel before calling changeSelectionAfterCommand() later in this method. + stopCorrectionPanelTimer(); m_frame->document()->updateLayout(); - + dispatchEditableContentChangedEvents(*cmd); - + VisibleSelection newSelection(cmd->endingSelection()); // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. changeSelectionAfterCommand(newSelection, false, false); @@ -1020,7 +1068,6 @@ void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) client()->registerCommandForUndo(m_lastEditCommand); } respondToChangedContents(newSelection); - stopCorrectionPanelTimer(); } void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) @@ -1068,9 +1115,8 @@ Editor::Editor(Frame* frame) Editor::~Editor() { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) - if (client()) - client()->dismissCorrectionPanel(true); +#if SUPPORT_AUTOCORRECTION_PANEL + dismissCorrectionPanel(CorrectionWasNotRejected); #endif } @@ -1671,7 +1717,6 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) // repeated "check spelling" commands work. VisibleSelection selection(frame()->selection()->selection()); RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document())); - TextCheckingHelper checker(client(), spellingSearchRange); bool startedWithSelection = false; if (selection.start().node()) { @@ -1738,7 +1783,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) bool isSpelling = true; int foundOffset = 0; GrammarDetail grammarDetail; - String foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); + String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); if (isSpelling) { misspelledWord = foundItem; misspellingOffset = foundOffset; @@ -1748,7 +1793,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) } #else RefPtr<Range> firstMisspellingRange; - String misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); + String misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); String badGrammarPhrase; #ifndef BUILDING_ON_TIGER @@ -1765,7 +1810,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) } if (isGrammarCheckingEnabled()) - badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); + badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); #endif #endif @@ -1778,7 +1823,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) grammarSearchRange = spellingSearchRange->cloneRange(ec); - foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); + foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); if (isSpelling) { misspelledWord = foundItem; misspellingOffset = foundOffset; @@ -1787,7 +1832,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) grammarPhraseOffset = foundOffset; } #else - misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); + misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); #ifndef BUILDING_ON_TIGER grammarSearchRange = spellingSearchRange->cloneRange(ec); @@ -1797,8 +1842,9 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) chars.advance(misspellingOffset); grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec); } + if (isGrammarCheckingEnabled()) - badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); + badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); #endif #endif } @@ -1967,47 +2013,10 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelecti void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p) { #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) -#if !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL // Apply pending autocorrection before next round of spell checking. - bool didApplyCorrection = false; - if (m_rangeToBeReplacedByCorrection) { - ExceptionCode ec = 0; - RefPtr<Range> paragraphRangeContainingCorrection = m_rangeToBeReplacedByCorrection->cloneRange(ec); - if (!ec) { - setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_rangeToBeReplacedByCorrection->startPosition())); - setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_rangeToBeReplacedByCorrection->endPosition())); - // After we replace the word at range m_rangeToBeReplacedByCorrection, we need to add - // autocorrection underline at that range. However, once the replacement took place, the - // value of m_rangeToBeReplacedByCorrection is not valid anymore. So before we carry out - // the replacement, we need to store the start position of m_rangeToBeReplacedByCorrection - // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph - // to store this value. In order to obtain this offset, we need to first create a range - // which spans from the start of paragraph to the start position of m_rangeToBeReplacedByCorrection. - RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition()); - if (!ec) { - Position startPositionOfRangeToBeReplaced = m_rangeToBeReplacedByCorrection->startPosition(); - correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec); - if (!ec) { - // Take note of the location of autocorrection so that we can add marker after the replacement took place. - int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get()); - Position caretPosition = m_frame->selection()->selection().end(); - RefPtr<Range> rangeToBeReplaced = m_rangeToBeReplacedByCorrection->cloneRange(ec); - VisibleSelection selectionToReplace(rangeToBeReplaced.get(), DOWNSTREAM); - if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) { - m_frame->selection()->setSelection(selectionToReplace); - replaceSelectionWithText(m_correctionReplacementString, false, false); - caretPosition.moveToOffset(caretPosition.offsetInContainerNode() + m_correctionReplacementString.length() - m_stringToBeReplacedByCorrection.length()); - RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionReplacementString.length()); - replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::Replacement, m_correctionReplacementString); - replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::CorrectionIndicator); - m_frame->selection()->moveTo(caretPosition, false); - didApplyCorrection = true; - } - } - } - } - m_rangeToBeReplacedByCorrection.clear(); - } + applyCorrectionPanelInfo(true); + m_correctionPanelInfo.m_rangeToBeReplaced.clear(); #endif TextCheckingOptions textCheckingOptions = 0; @@ -2095,7 +2104,7 @@ void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, boo if (!editableNode || !editableNode->isContentEditable()) return; - if (!isSpellCheckingEnabledInFocusedNode()) + if (!isSpellCheckingEnabledFor(editableNode)) return; // Get the spell checker if it is available @@ -2115,11 +2124,8 @@ void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, boo } } -bool Editor::isSpellCheckingEnabledInFocusedNode() const +bool Editor::isSpellCheckingEnabledFor(Node* node) const { - // Ascend the DOM tree to find a "spellcheck" attribute. - // When we find a "spellcheck" attribute, retrieve its value and return false if its value is "false". - const Node* node = frame()->document()->focusedNode(); if (!node) return false; const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement(); @@ -2128,6 +2134,11 @@ bool Editor::isSpellCheckingEnabledInFocusedNode() const return focusedElement->isSpellCheckingEnabled(); } +bool Editor::isSpellCheckingEnabledInFocusedNode() const +{ + return isSpellCheckingEnabledFor(m_frame->selection()->start().node()); +} + void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange) { markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange); @@ -2170,7 +2181,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh if (!editableNode || !editableNode->isContentEditable()) return; - if (!isSpellCheckingEnabledInFocusedNode()) + if (!isSpellCheckingEnabledFor(editableNode)) return; // Expand the range to encompass entire paragraphs, since text checking needs that much context. @@ -2187,16 +2198,15 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh bool adjustSelectionForParagraphBoundaries = false; String paragraphString; RefPtr<Range> paragraphRange; - TextCheckingHelper checker(client(), grammarRange); if (shouldMarkGrammar) { // The spelling range should be contained in the paragraph-aligned extension of the grammar range. - paragraphRange = checker.paragraphAlignedRange(grammarRangeStartOffset, paragraphString); + paragraphRange = TextCheckingHelper(client(), grammarRange).paragraphAlignedRange(grammarRangeStartOffset, paragraphString); RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition()); spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange); } else { - paragraphRange = checker.paragraphAlignedRange(spellingRangeStartOffset, paragraphString); + paragraphRange = TextCheckingHelper(client(), spellingRange).paragraphAlignedRange(spellingRangeStartOffset, paragraphString); } spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange); paragraphLength = paragraphString.length(); @@ -2242,7 +2252,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh } client()->checkTextOfParagraph(paragraphString.characters(), paragraphLength, checkingTypes, results); -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL // If this checking is only for showing correction panel, we shouldn't bother to mark misspellings. if (shouldShowCorrectionPanel) shouldMarkSpelling = false; @@ -2327,7 +2337,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) { if (result->type == TextCheckingTypeCorrection) replacedString = plainText(rangeToReplace.get()); -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL if (shouldShowCorrectionPanel && resultLocation + resultLength == spellingRangeEndOffset && result->type == TextCheckingTypeCorrection) { // We only show the correction panel on the last word. Vector<FloatQuad> textQuads; @@ -2336,10 +2346,11 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh FloatRect totalBoundingBox; for (Vector<FloatQuad>::const_iterator it = textQuads.begin(); it < end; ++it) totalBoundingBox.unite(it->boundingBox()); - m_rangeToBeReplacedByCorrection = rangeToReplace; - m_stringToBeReplacedByCorrection = replacedString; - m_correctionReplacementString = result->replacement; - client()->showCorrectionPanel(totalBoundingBox, m_stringToBeReplacedByCorrection, result->replacement, this); + m_correctionPanelInfo.m_rangeToBeReplaced = rangeToReplace; + m_correctionPanelInfo.m_replacedString = replacedString; + m_correctionPanelInfo.m_replacementString = result->replacement; + m_correctionPanelInfo.m_isActive = true; + client()->showCorrectionPanel(m_correctionPanelInfo.m_panelType, totalBoundingBox, m_correctionPanelInfo.m_replacedString, result->replacement, this); doReplacement = false; } #endif @@ -2353,7 +2364,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh // 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()->markers()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString); - replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator); + replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator, replacedString); } } } @@ -2416,60 +2427,99 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelec void Editor::correctionPanelTimerFired(Timer<Editor>*) { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - VisibleSelection selection(frame()->selection()->selection()); - VisiblePosition start(selection.start(), selection.affinity()); - VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary); - VisibleSelection adjacentWords = VisibleSelection(p, start); - markAllMisspellingsAndBadGrammarInRanges(MarkSpelling | ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0); +#if SUPPORT_AUTOCORRECTION_PANEL + if (m_correctionPanelInfo.m_panelType == CorrectionPanelInfo::PanelTypeCorrection) { + VisibleSelection selection(frame()->selection()->selection()); + VisiblePosition start(selection.start(), selection.affinity()); + VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary); + VisibleSelection adjacentWords = VisibleSelection(p, start); + markAllMisspellingsAndBadGrammarInRanges(MarkSpelling | ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0); + } else { + String currentWord = plainText(m_correctionPanelInfo.m_rangeToBeReplaced.get()); + Vector<FloatQuad> textQuads; + m_correctionPanelInfo.m_rangeToBeReplaced->getBorderAndTextQuads(textQuads); + Vector<FloatQuad>::const_iterator end = textQuads.end(); + FloatRect totalBoundingBox; + for (Vector<FloatQuad>::const_iterator it = textQuads.begin(); it < end; ++it) + totalBoundingBox.unite(it->boundingBox()); + m_correctionPanelInfo.m_isActive = true; + m_correctionPanelInfo.m_replacedString = currentWord; + client()->showCorrectionPanel(m_correctionPanelInfo.m_panelType, totalBoundingBox, m_correctionPanelInfo.m_replacedString, m_correctionPanelInfo.m_replacementString, this); + } #endif } void Editor::handleRejectedCorrection() { - Range* replacedRange = m_rangeToBeReplacedByCorrection.get(); + Range* replacedRange = m_correctionPanelInfo.m_rangeToBeReplaced.get(); if (!replacedRange || m_frame->document() != replacedRange->ownerDocument()) return; - replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_stringToBeReplacedByCorrection); - m_rangeToBeReplacedByCorrection.clear(); + if (m_correctionPanelInfo.m_panelType == CorrectionPanelInfo::PanelTypeCorrection) + replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_correctionPanelInfo.m_replacedString); + else { + m_correctionPanelInfo.m_isActive = false; + applyCorrectionPanelInfo(false); + } + m_correctionPanelInfo.m_rangeToBeReplaced.clear(); } -void Editor::startCorrectionPanelTimer() +void Editor::startCorrectionPanelTimer(CorrectionPanelInfo::PanelType type) { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) - static const double correctionPanelTimerInterval = 0.3; +#if SUPPORT_AUTOCORRECTION_PANEL + const double correctionPanelTimerInterval = 0.3; if (isAutomaticSpellingCorrectionEnabled()) { - m_rangeToBeReplacedByCorrection.clear(); + if (type == CorrectionPanelInfo::PanelTypeCorrection) + // If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it. + m_correctionPanelInfo.m_rangeToBeReplaced.clear(); + m_correctionPanelInfo.m_panelType = type; m_correctionPanelTimer.startOneShot(correctionPanelTimerInterval); } +#else + UNUSED_PARAM(type); #endif } void Editor::stopCorrectionPanelTimer() { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL m_correctionPanelTimer.stop(); #endif } void Editor::handleCancelOperation() { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL + if (!m_correctionPanelInfo.m_isActive) + return; + m_correctionPanelInfo.m_isActive = false; if (client()) - client()->dismissCorrectionPanel(false); + client()->dismissCorrectionPanel(CorrectionWasRejected); #endif } bool Editor::isShowingCorrectionPanel() { -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL if (client()) return client()->isShowingCorrectionPanel(); #endif return false; } +void Editor::dismissCorrectionPanel(CorrectionWasRejectedOrNot correctionWasRejectedOrNot) +{ +#if SUPPORT_AUTOCORRECTION_PANEL + if (!m_correctionPanelInfo.m_isActive) + return; + m_correctionPanelInfo.m_isActive = false; + m_correctionPanelInfo.m_rangeToBeReplaced.clear(); + if (client()) + client()->dismissCorrectionPanel(correctionWasRejectedOrNot); +#else + UNUSED_PARAM(correctionWasRejectedOrNot); +#endif +} void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemoveIfSelectionAtWordBoundary) { // We want to remove the markers from a word if an editing command will change the word. This can happen in one of @@ -2536,6 +2586,8 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove Vector<RangeMarkerPair, 16> markersToRemove; for (TextIterator textIterator(wordRange.get()); !textIterator.atEnd(); textIterator.advance()) { Node* node = textIterator.node(); + if (!node) + continue; if (node == startOfFirstWord.deepEquivalent().containerNode() || node == endOfLastWord.deepEquivalent().containerNode()) { // First word and last word can belong to the same node bool processFirstWord = node == startOfFirstWord.deepEquivalent().containerNode() && document->markers()->hasMarkers(rangeOfFirstWord.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator); @@ -2565,6 +2617,54 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove document->markers()->removeMarkers(pairIterator->first.get(), pairIterator->second); } +void Editor::applyCorrectionPanelInfo(bool addCorrectionIndicatorMarker) +{ + if (!m_correctionPanelInfo.m_rangeToBeReplaced) + return; + + ExceptionCode ec = 0; + RefPtr<Range> paragraphRangeContainingCorrection = m_correctionPanelInfo.m_rangeToBeReplaced->cloneRange(ec); + if (ec) + return; + + setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_correctionPanelInfo.m_rangeToBeReplaced->startPosition())); + setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_correctionPanelInfo.m_rangeToBeReplaced->endPosition())); + + // After we replace the word at range m_rangeToBeReplaced, we need to add markers to that range. + // However, once the replacement took place, the value of m_rangeToBeReplaced is not valid anymore. + // So before we carry out the replacement, we need to store the start position of m_rangeToBeReplaced + // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph + // to store this value. In order to obtain this offset, we need to first create a range + // which spans from the start of paragraph to the start position of m_rangeToBeReplaced. + RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition()); + if (ec) + return; + + Position startPositionOfRangeToBeReplaced = m_correctionPanelInfo.m_rangeToBeReplaced->startPosition(); + correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec); + if (ec) + return; + + // Take note of the location of autocorrection so that we can add marker after the replacement took place. + int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get()); + Position caretPosition = m_frame->selection()->selection().end(); + + // Clone the range, since the caller of this method may want to keep the original range around. + RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.m_rangeToBeReplaced->cloneRange(ec); + VisibleSelection selectionToReplace(rangeToBeReplaced.get(), DOWNSTREAM); + if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) { + m_frame->selection()->setSelection(selectionToReplace); + replaceSelectionWithText(m_correctionPanelInfo.m_replacementString, false, false); + caretPosition.moveToOffset(caretPosition.offsetInContainerNode() + m_correctionPanelInfo.m_replacementString.length() - m_correctionPanelInfo.m_replacedString.length()); + setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(caretPosition)); + RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.m_replacementString.length()); + replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::Replacement, m_correctionPanelInfo.m_replacementString); + if (addCorrectionIndicatorMarker) + replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::CorrectionIndicator, m_correctionPanelInfo.m_replacedString); + m_frame->selection()->moveTo(caretPosition, false); + } +} + PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) { Document* document = m_frame->documentAtPoint(windowPoint); @@ -2831,7 +2931,7 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, b if (newSelection.start().isOrphan() || newSelection.end().isOrphan()) return; -#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#if SUPPORT_AUTOCORRECTION_PANEL // Check to see if the command introduced paragraph separator. If it did, we remove existing autocorrection underlines. // This is in consistency with the behavior in AppKit if (!inSameParagraph(m_frame->selection()->selection().visibleStart(), newSelection.visibleEnd())) @@ -2954,7 +3054,7 @@ void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction edi applyCommand(ApplyStyleCommand::create(m_frame->document(), blockStyle.get(), editingAction)); // Set the remaining style as the typing style. - m_frame->selection()->setTypingStyle(mutableStyle.release()); + m_frame->selection()->setTypingStyle(EditingStyle::create(mutableStyle.get())); } PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shouldUseFixedFontDefaultSize) const @@ -2985,10 +3085,10 @@ PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shou if (!m_frame->selection()->typingStyle()) return mutableStyle; - RefPtr<CSSMutableStyleDeclaration> typingStyle = m_frame->selection()->typingStyle()->copy(); - ApplyStyleCommand::removeNonEditingProperties(typingStyle.get()); - prepareEditingStyleToApplyAt(typingStyle.get(), position); - mutableStyle->merge(typingStyle.get()); + RefPtr<EditingStyle> typingStyle = EditingStyle::create(m_frame->selection()->typingStyle()); + typingStyle->removeNonEditingProperties(); + typingStyle->prepareToApplyAt(position); + mutableStyle->merge(typingStyle->style()); return mutableStyle; } |