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 (auto* 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(); // End must not be before start. if (m_start.deprecatedNode() == m_end.deprecatedNode() && m_start.deprecatedEditingOffset() > m_end.deprecatedEditingOffset()) { Position swap(m_start); m_start = m_end; m_end = swap; } 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; case DocumentGranularity: ASSERT_NOT_REACHED(); 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. // FIXME: Canonicalizing is good, but haven't we already done it (when we // set these two positions to VisiblePosition deepEquivalent()s above)? m_start = m_start.downstream(); m_end = m_end.upstream(); } }
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; }
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 CHANGE + 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 CHANGE - 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 CHANGE 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 CHANGE + // 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 //VisiblePosition end(visibleParagraphEnd.next()); VisiblePosition end(visibleParagraphEnd); // SAMSUNG CHANGE - 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; }