void TextCheckingParagraph::expandRangeToNextEnd() { ASSERT(m_checkingRange); setEnd(paragraphRange().get(), endOfParagraph(startOfNextParagraph(paragraphRange()->startPosition()))); invalidateParagraphRangeValues(); }
String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail) { if (!unifiedTextCheckerEnabled()) return ""; String firstFoundItem; String misspelledWord; String badGrammarPhrase; // Initialize out parameters; these will be updated if we find something to return. outIsSpelling = true; outFirstFoundOffset = 0; outGrammarDetail.location = -1; outGrammarDetail.length = 0; outGrammarDetail.guesses.clear(); outGrammarDetail.userDescription = ""; // Expand the search range to encompass entire paragraphs, since text checking needs that much context. // Determine the character offset from the start of the paragraph to the start of the original search range, // since we will want to ignore results in this area. RefPtr<Range> paragraphRange = m_range->cloneRange(IGNORE_EXCEPTION); setStart(paragraphRange.get(), startOfParagraph(m_range->startPosition())); int totalRangeLength = TextIterator::rangeLength(paragraphRange.get()); setEnd(paragraphRange.get(), endOfParagraph(m_range->startPosition())); RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->startPosition()); int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); int totalLengthProcessed = 0; bool firstIteration = true; bool lastIteration = false; while (totalLengthProcessed < totalRangeLength) { // Iterate through the search range by paragraphs, checking each one for spelling and grammar. int currentLength = TextIterator::rangeLength(paragraphRange.get()); int currentStartOffset = firstIteration ? rangeStartOffset : 0; int currentEndOffset = currentLength; if (inSameParagraph(paragraphRange->startPosition(), m_range->endPosition())) { // Determine the character offset from the end of the original search range to the end of the paragraph, // since we will want to ignore results in this area. RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->endPosition()); currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get()); lastIteration = true; } if (currentStartOffset < currentEndOffset) { String paragraphString = plainText(paragraphRange.get()); if (paragraphString.length() > 0) { bool foundGrammar = false; int spellingLocation = 0; int grammarPhraseLocation = 0; int grammarDetailLocation = 0; unsigned grammarDetailIndex = 0; Vector<TextCheckingResult> results; TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; checkTextOfParagraph(m_client->textChecker(), paragraphString.characters(), paragraphString.length(), checkingTypes, results); for (unsigned i = 0; i < results.size(); i++) { const TextCheckingResult* result = &results[i]; if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) { ASSERT(result->length > 0 && result->location >= 0); spellingLocation = result->location; misspelledWord = paragraphString.substring(result->location, result->length); ASSERT(misspelledWord.length()); break; } if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) { ASSERT(result->length > 0 && result->location >= 0); // We can't stop after the first grammar result, since there might still be a spelling result after // it begins but before the first detail in it, but we can stop if we find a second grammar result. if (foundGrammar) break; for (unsigned j = 0; j < result->details.size(); j++) { const GrammarDetail* detail = &result->details[j]; ASSERT(detail->length > 0 && detail->location >= 0); if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) { grammarDetailIndex = j; grammarDetailLocation = result->location + detail->location; foundGrammar = true; } } if (foundGrammar) { grammarPhraseLocation = result->location; outGrammarDetail = result->details[grammarDetailIndex]; badGrammarPhrase = paragraphString.substring(result->location, result->length); ASSERT(badGrammarPhrase.length()); } } } if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) { int spellingOffset = spellingLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = true; outFirstFoundOffset = spellingOffset; firstFoundItem = misspelledWord; break; } if (checkGrammar && !badGrammarPhrase.isEmpty()) { int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = false; outFirstFoundOffset = grammarPhraseOffset; firstFoundItem = badGrammarPhrase; break; } } } if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength) break; VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition()); setStart(paragraphRange.get(), newParagraphStart); setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart)); firstIteration = false; totalLengthProcessed += currentLength; } return firstFoundItem; }
void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity) { if (m_baseIsFirst) { m_start = m_base; m_end = m_extent; } else { m_start = m_extent; m_end = m_base; } switch (granularity) { case CharacterGranularity: // Don't do any expansion. break; case WordGranularity: { // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in // the document, select that last word (LeftWordIfOnBoundary). // Edge case: If the caret is after the last word in a paragraph, select from the the end of the // last word to the line break (also RightWordIfOnBoundary); VisiblePosition start = VisiblePosition(m_start, m_affinity); VisiblePosition originalEnd(m_end, m_affinity); EWordSide side = RightWordIfOnBoundary; if (isEndOfEditableOrNonEditableContent(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) side = LeftWordIfOnBoundary; m_start = startOfWord(start, side).deepEquivalent(); side = RightWordIfOnBoundary; if (isEndOfEditableOrNonEditableContent(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) side = LeftWordIfOnBoundary; VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) { // Select the paragraph break (the space from the end of a paragraph to the start of // the next one) to match TextEdit. end = wordEnd.next(); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); else end = wordEnd; } if (end.isNull()) end = wordEnd; } m_end = end.deepEquivalent(); break; } case SentenceGranularity: { m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } case LineGranularity: { m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); // If the end of this line is at the end of a paragraph, include the space // after the end of the line in the selection. if (isEndOfParagraph(end)) { VisiblePosition next = end.next(); if (next.isNotNull()) end = next; } m_end = end.deepEquivalent(); break; } case LineBoundary: m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphGranularity: { VisiblePosition pos(m_start, m_affinity); if (isStartOfLine(pos) && isEndOfEditableOrNonEditableContent(pos)) pos = pos.previous(); m_start = startOfParagraph(pos).deepEquivalent(); VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); // Include the "paragraph break" (the space from the end of this paragraph to the start // of the next one) in the selection. VisiblePosition end(visibleParagraphEnd.next()); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table, not at the position just after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); // There is no parargraph break after the last paragraph in the last cell of an inline table. else end = visibleParagraphEnd; } if (end.isNull()) end = visibleParagraphEnd; m_end = end.deepEquivalent(); break; } case DocumentBoundary: m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphBoundary: m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case SentenceBoundary: m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } // Make sure we do not have a dangling start or end. if (m_start.isNull()) m_start = m_end; if (m_end.isNull()) m_end = m_start; }
void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity) { if (m_baseIsFirst) { m_start = m_base; m_end = m_extent; } else { m_start = m_extent; m_end = m_base; } switch (granularity) { case CharacterGranularity: // Don't do any expansion. break; case WordGranularity: { // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in // the document, select that last word (LeftWordIfOnBoundary). // Edge case: If the caret is after the last word in a paragraph, select from the the end of the // last word to the line break (also RightWordIfOnBoundary); VisiblePosition start = VisiblePosition(m_start, m_affinity); VisiblePosition originalEnd(m_end, m_affinity); EWordSide side = RightWordIfOnBoundary; //SAMSUNG - Text Selection >> Document *doc = NULL; if (m_start.anchorNode()) doc = m_start.anchorNode()->document(); if (doc && doc->settings() && doc->settings()->advancedSelectionEnabled()) { bool endOfDocument; if ( (endOfDocument = isEndOfDocument(start)) || ( (isEndOfLine(start) || isStartOfSpace(start)) && !isStartOfLine(start))) { side = LeftWordIfOnBoundary; if (!endOfDocument && isEndOfParagraph(start)) { originalEnd = start; } } m_start = startOfWord(start, side).deepEquivalent(); side = RightWordIfOnBoundary; if (isStartOfSpace(start)) { side = LeftWordIfOnBoundary; } if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd))) side = LeftWordIfOnBoundary; VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); m_end = end.deepEquivalent(); } else { //SAMSUNG - Text Selection << if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) side = LeftWordIfOnBoundary; m_start = startOfWord(start, side).deepEquivalent(); side = RightWordIfOnBoundary; if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) side = LeftWordIfOnBoundary; VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) { // Select the paragraph break (the space from the end of a paragraph to the start of // the next one) to match TextEdit. end = wordEnd.next(); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); else end = wordEnd; } if (end.isNull()) end = wordEnd; } m_end = end.deepEquivalent(); //SAMSUNG - Text Selection >> } //SAMSUNG - Text Selection << break; } case SentenceGranularity: { m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } case LineGranularity: { m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); // If the end of this line is at the end of a paragraph, include the space // after the end of the line in the selection. if (isEndOfParagraph(end)) { VisiblePosition next = end.next(); if (next.isNotNull()) end = next; } m_end = end.deepEquivalent(); break; } case LineBoundary: m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphGranularity: { VisiblePosition pos(m_start, m_affinity); if (isStartOfLine(pos) && isEndOfDocument(pos)) pos = pos.previous(); m_start = startOfParagraph(pos).deepEquivalent(); VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); // Include the "paragraph break" (the space from the end of this paragraph to the start // of the next one) in the selection. //SAMSUNG - Text Selection >> // Here it tries to extend the end point from current paragraph to beginning of next paragraph. So somehow it is // selecting the next paragraph also and it looks wrong when we do backward paragraph selection. // Even though user is trying to extend selection in backward direction, because of this condition it extends selection // in both directions. So commented the below code to avoid wrong backward text selection // WAS: VisiblePosition end(visibleParagraphEnd.next()); VisiblePosition end(visibleParagraphEnd); //SAMSUNG - Text Selection << if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table, not at the position just after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); // There is no parargraph break after the last paragraph in the last cell of an inline table. else end = visibleParagraphEnd; } if (end.isNull()) end = visibleParagraphEnd; m_end = end.deepEquivalent(); break; } case DocumentBoundary: m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphBoundary: m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case SentenceBoundary: m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case WebKitVisualWordGranularity: break; } // Make sure we do not have a dangling start or end. if (m_start.isNull()) m_start = m_end; if (m_end.isNull()) m_end = m_start; }
void Selection::validate() { // Move the selection to rendered positions, if possible. bool baseAndExtentEqual = m_base == m_extent; if (m_base.isNotNull()) { m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); if (baseAndExtentEqual) m_extent = m_base; } if (m_extent.isNotNull() && !baseAndExtentEqual) m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); // Make sure we do not have a dangling base or extent. if (m_base.isNull() && m_extent.isNull()) m_baseIsFirst = true; else if (m_base.isNull()) { m_base = m_extent; m_baseIsFirst = true; } else if (m_extent.isNull()) { m_extent = m_base; m_baseIsFirst = true; } else { m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; } if (m_baseIsFirst) { m_start = m_base; m_end = m_extent; } else { m_start = m_extent; m_end = m_base; } // Expand the selection if requested. switch (m_granularity) { case CharacterGranularity: // Don't do any expansion. break; case WordGranularity: { // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in // the document, select that last word (LeftWordIfOnBoundary). // Edge case: If the caret is after the last word in a paragraph, select from the the end of the // last word to the line break (also RightWordIfOnBoundary); VisiblePosition start = VisiblePosition(m_start, m_affinity); VisiblePosition originalEnd(m_end, m_affinity); EWordSide side = RightWordIfOnBoundary; if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) side = LeftWordIfOnBoundary; m_start = startOfWord(start, side).deepEquivalent(); side = RightWordIfOnBoundary; if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) side = LeftWordIfOnBoundary; VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); if (isEndOfParagraph(originalEnd)) { // Select the paragraph break (the space from the end of a paragraph to the start of // the next one) to match TextEdit. end = wordEnd.next(); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table. if (isBlock(table)) end = end.next(true); else end = wordEnd; } if (end.isNull()) end = wordEnd; } m_end = end.deepEquivalent(); break; } case SentenceGranularity: { m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } case LineGranularity: { m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); // If the end of this line is at the end of a paragraph, include the space // after the end of the line in the selection. if (isEndOfParagraph(end)) { VisiblePosition next = end.next(); if (next.isNotNull()) end = next; } m_end = end.deepEquivalent(); break; } case LineBoundary: m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphGranularity: { VisiblePosition pos(m_start, m_affinity); if (isStartOfLine(pos) && isEndOfDocument(pos)) pos = pos.previous(); m_start = startOfParagraph(pos).deepEquivalent(); VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); // Include the "paragraph break" (the space from the end of this paragraph to the start // of the next one) in the selection. VisiblePosition end(visibleParagraphEnd.next()); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table, not at the position just after the table. if (isBlock(table)) end = end.next(true); // There is no parargraph break after the last paragraph in the last cell of an inline table. else end = visibleParagraphEnd; } if (end.isNull()) end = visibleParagraphEnd; m_end = end.deepEquivalent(); break; } case DocumentBoundary: m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphBoundary: m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case SentenceBoundary: m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } // Make sure we do not have a dangling start or end. if (m_start.isNull()) m_start = m_end; if (m_end.isNull()) m_end = m_start; adjustForEditableContent(); // adjust the state if (m_start.isNull()) { ASSERT(m_end.isNull()); m_state = NONE; // enforce downstream affinity if not caret, as affinity only // makes sense for caret m_affinity = DOWNSTREAM; } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { m_state = CARET; } else { m_state = RANGE; // enforce downstream affinity if not caret, as affinity only // makes sense for caret m_affinity = DOWNSTREAM; // "Constrain" the selection to be the smallest equivalent range of nodes. // This is a somewhat arbitrary choice, but experience shows that it is // useful to make to make the selection "canonical" (if only for // purposes of comparing selections). This is an ideal point of the code // to do this operation, since all selection changes that result in a RANGE // come through here before anyone uses it. m_start = m_start.downstream(); m_end = m_end.upstream(); } }
void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity) { Selection selectionToDelete; Selection selectionAfterUndo; switch (endingSelection().state()) { case Selection::RANGE: selectionToDelete = endingSelection(); selectionAfterUndo = selectionToDelete; break; case Selection::CARET: { m_smartDelete = false; // Handle delete at beginning-of-block case. // Do nothing in the case that the caret is at the start of a // root editable element or at the start of a document. SelectionController selection; selection.setSelection(endingSelection()); selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, granularity); Position downstreamEnd = endingSelection().end().downstream(); VisiblePosition visibleEnd = endingSelection().visibleEnd(); if (visibleEnd == endOfParagraph(visibleEnd)) downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream(); // When deleting tables: Select the table first, then perform the deletion if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.offset() == 0) { setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM)); typingAddedToOpenCommand(); return; } // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any) if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd())) selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity); selectionToDelete = selection.selection(); if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) selectionAfterUndo = selectionToDelete; else { // It's a little tricky to compute what the starting selection would have been in the original document. // We can't let the Selection class's validation kick in or it'll adjust for us based on // the current state of the document and we'll get the wrong result. Position extent = startingSelection().end(); if (extent.node() != selectionToDelete.end().node()) extent = selectionToDelete.extent(); else { int extraCharacters; if (selectionToDelete.start().node() == selectionToDelete.end().node()) extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset(); else extraCharacters = selectionToDelete.end().offset(); extent = Position(extent.node(), extent.offset() + extraCharacters); } selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent); } break; } case Selection::NONE: ASSERT_NOT_REACHED(); break; } if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { // make undo select what was deleted setStartingSelection(selectionAfterUndo); CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); setSmartDelete(false); typingAddedToOpenCommand(); } }
void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity) { if (m_baseIsFirst) { m_start = m_base; m_end = m_extent; } else { m_start = m_extent; m_end = m_base; } switch (granularity) { case CharacterGranularity: // Don't do any expansion. break; case WordGranularity: { // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in // the document, select that last word (LeftWordIfOnBoundary). // Edge case: If the caret is after the last word in a paragraph, select from the the end of the // last word to the line break (also RightWordIfOnBoundary); VisiblePosition start = VisiblePosition(m_start, m_affinity); VisiblePosition originalEnd(m_end, m_affinity); EWordSide side = RightWordIfOnBoundary; if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) side = LeftWordIfOnBoundary; m_start = startOfWord(start, side).deepEquivalent(); side = RightWordIfOnBoundary; if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) side = LeftWordIfOnBoundary; VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) { // Select the paragraph break (the space from the end of a paragraph to the start of // the next one) to match TextEdit. end = wordEnd.next(); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); else end = wordEnd; } if (end.isNull()) end = wordEnd; } m_end = end.deepEquivalent(); //added this to select only letters for dictionary viewing if( m_selectOnlyLetters && m_start.anchorNode()->isTextNode() && m_end.anchorNode()->isTextNode() ) { CharacterData* startText = static_cast<CharacterData*>(m_start.anchorNode()); String startStr = startText->data(); CharacterData* endText = static_cast<CharacterData*>(m_end.anchorNode()); String endStr = endText->data(); int n1 = m_start.offsetInContainerNode(); int n2 = m_end.offsetInContainerNode() - 1; //unhandled corner case: at beginning of sentence, m_start may refer //to the Node of the last sentence, this causes the space at the beginning //of a sentence to be part of the selection. Assigning m_end to m_start and //adjusting the offset does not fix this. QChar c1 = startStr[n1]; QChar c2 = endStr[n2]; while(!c1.isLetter() && n1 <= n2) { c1 = startStr[++n1]; m_start = m_start.next(Character); } while(!c2.isLetter() && n2 >= n1) { c2 = endStr[--n2]; m_end = m_end.previous(Character); } } break; } case SentenceGranularity: { m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } case LineGranularity: { m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); // If the end of this line is at the end of a paragraph, include the space // after the end of the line in the selection. if (isEndOfParagraph(end)) { VisiblePosition next = end.next(); if (next.isNotNull()) end = next; } m_end = end.deepEquivalent(); break; } case LineBoundary: m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphGranularity: { VisiblePosition pos(m_start, m_affinity); if (isStartOfLine(pos) && isEndOfDocument(pos)) pos = pos.previous(); m_start = startOfParagraph(pos).deepEquivalent(); VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); // Include the "paragraph break" (the space from the end of this paragraph to the start // of the next one) in the selection. VisiblePosition end(visibleParagraphEnd.next()); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table, not at the position just after the table. if (isBlock(table)) end = end.next(CannotCrossEditingBoundary); // There is no parargraph break after the last paragraph in the last cell of an inline table. else end = visibleParagraphEnd; } if (end.isNull()) end = visibleParagraphEnd; m_end = end.deepEquivalent(); break; } case DocumentBoundary: m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphBoundary: m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case SentenceBoundary: m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case WebKitVisualWordGranularity: break; } // Make sure we do not have a dangling start or end. if (m_start.isNull()) m_start = m_end; if (m_end.isNull()) m_end = m_start; }