VisiblePosition SelectionModifier::modifyMovingForward( TextGranularity granularity) { VisiblePosition pos; // FIXME: Stay in editable content for the less common granularities. switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) pos = createVisiblePosition(m_selection.end(), m_selection.affinity()); else pos = nextPositionOf( createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary); break; case WordGranularity: pos = nextWordPositionForPlatform( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case SentenceGranularity: pos = nextSentencePosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case LineGranularity: { // down-arrowing from a range selection that ends at the start of a line // needs to leave the selection at that line start (no need to call // nextLinePosition!) pos = endForPlatform(); if (!m_selection.isRange() || !isStartOfLine(pos)) pos = nextLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(START)); break; } case ParagraphGranularity: pos = nextParagraphPosition( endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case SentenceBoundary: pos = endOfSentence(endForPlatform()); break; case LineBoundary: pos = logicalEndOfLine(endForPlatform()); break; case ParagraphBoundary: pos = endOfParagraph(endForPlatform()); break; case DocumentBoundary: pos = endForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = endOfEditableContent(pos); else pos = endOfDocument(pos); break; } return pos; }
VisiblePosition SelectionModifier::modifyMovingBackward( TextGranularity granularity) { VisiblePosition pos; switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) pos = createVisiblePosition(m_selection.start(), m_selection.affinity()); else pos = previousPositionOf( createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case SentenceGranularity: pos = previousSentencePosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case LineGranularity: pos = previousLinePosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case ParagraphGranularity: pos = previousParagraphPosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } return pos; }
VisiblePosition SelectionModifier::modifyExtendingBackward( TextGranularity granularity) { VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); // Extending a selection backward by word or character from just after a table // selects the table. This "makes sense" from the user perspective, esp. when // deleting. It was done here instead of in VisiblePosition because we want // VPs to iterate over everything. switch (granularity) { case CharacterGranularity: pos = previousPositionOf(pos, CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition(pos); break; case SentenceGranularity: pos = previousSentencePosition(pos); break; case LineGranularity: pos = previousLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case ParagraphGranularity: pos = previousParagraphPosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); return pos; }
VisiblePosition SelectionModifier::modifyExtendingForward( TextGranularity granularity) { VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); switch (granularity) { case CharacterGranularity: pos = nextPositionOf(pos, CanSkipOverEditingBoundary); break; case WordGranularity: pos = nextWordPositionForPlatform(pos); break; case SentenceGranularity: pos = nextSentencePosition(pos); break; case LineGranularity: pos = nextLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case ParagraphGranularity: pos = nextParagraphPosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case SentenceBoundary: pos = endOfSentence(endForPlatform()); break; case LineBoundary: pos = logicalEndOfLine(endForPlatform()); break; case ParagraphBoundary: pos = endOfParagraph(endForPlatform()); break; case DocumentBoundary: pos = endForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = endOfEditableContent(pos); else pos = endOfDocument(pos); break; } adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); return pos; }
bool SelectionModifier::modifyWithPageGranularity(EAlteration alter, unsigned verticalDistance, VerticalDirection direction) { if (!verticalDistance) return false; DCHECK(!frame()->document()->needsLayoutTreeUpdate()); DocumentLifecycle::DisallowTransitionScope disallowTransition( frame()->document()->lifecycle()); willBeModified(alter, direction == FrameSelection::DirectionUp ? DirectionBackward : DirectionForward); VisiblePosition pos; LayoutUnit xPos; switch (alter) { case FrameSelection::AlterationMove: pos = createVisiblePosition(direction == FrameSelection::DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity()); xPos = lineDirectionPointForBlockDirectionNavigation( direction == FrameSelection::DirectionUp ? START : END); m_selection.setAffinity(direction == FrameSelection::DirectionUp ? TextAffinity::Upstream : TextAffinity::Downstream); break; case FrameSelection::AlterationExtend: pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT); m_selection.setAffinity(TextAffinity::Downstream); break; } int startY; if (!absoluteCaretY(pos, startY)) return false; if (direction == FrameSelection::DirectionUp) startY = -startY; int lastY = startY; VisiblePosition result; VisiblePosition next; for (VisiblePosition p = pos;; p = next) { if (direction == FrameSelection::DirectionUp) next = previousLinePosition(p, xPos); else next = nextLinePosition(p, xPos); if (next.isNull() || next.deepEquivalent() == p.deepEquivalent()) break; int nextY; if (!absoluteCaretY(next, nextY)) break; if (direction == FrameSelection::DirectionUp) nextY = -nextY; if (nextY - startY > static_cast<int>(verticalDistance)) break; if (nextY >= lastY) { lastY = nextY; result = next; } } if (result.isNull()) return false; switch (alter) { case FrameSelection::AlterationMove: m_selection = createVisibleSelection( SelectionInDOMTree::Builder() .collapse(result.toPositionWithAffinity()) .setIsDirectional(m_selection.isDirectional()) .build()); break; case FrameSelection::AlterationExtend: m_selection.setExtent(result); break; } m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend); return true; }
bool SelectionModifier::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity) { DCHECK(!frame()->document()->needsLayoutTreeUpdate()); DocumentLifecycle::DisallowTransitionScope disallowTransition( frame()->document()->lifecycle()); willBeModified(alter, direction); bool wasRange = m_selection.isRange(); VisiblePosition originalStartPosition = m_selection.visibleStart(); VisiblePosition position; switch (direction) { case DirectionRight: if (alter == FrameSelection::AlterationMove) position = modifyMovingRight(granularity); else position = modifyExtendingRight(granularity); break; case DirectionForward: if (alter == FrameSelection::AlterationExtend) position = modifyExtendingForward(granularity); else position = modifyMovingForward(granularity); break; case DirectionLeft: if (alter == FrameSelection::AlterationMove) position = modifyMovingLeft(granularity); else position = modifyExtendingLeft(granularity); break; case DirectionBackward: if (alter == FrameSelection::AlterationExtend) position = modifyExtendingBackward(granularity); else position = modifyMovingBackward(granularity); break; } if (position.isNull()) return false; if (isSpatialNavigationEnabled(frame())) { if (!wasRange && alter == FrameSelection::AlterationMove && position.deepEquivalent() == originalStartPosition.deepEquivalent()) return false; } // Some of the above operations set an xPosForVerticalArrowNavigation. // Setting a selection will clear it, so save it to possibly restore later. // Note: the START position type is arbitrary because it is unused, it would // be the requested position type if there were no // xPosForVerticalArrowNavigation set. LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START); m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend); switch (alter) { case FrameSelection::AlterationMove: m_selection = createVisibleSelection( SelectionInDOMTree::Builder() .collapse(position.toPositionWithAffinity()) .setIsDirectional(m_selection.isDirectional()) .build()); break; case FrameSelection::AlterationExtend: if (!m_selection.isCaret() && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity) && frame() && !frame() ->editor() .behavior() .shouldExtendSelectionByWordOrLineAcrossCaret()) { // Don't let the selection go across the base position directly. Needed // to match mac behavior when, for instance, word-selecting backwards // starting with the caret in the middle of a word and then // word-selecting forward, leaving the caret in the same place where it // was, instead of directly selecting to the end of the word. VisibleSelection newSelection = m_selection; newSelection.setExtent(position); if (m_selection.isBaseFirst() != newSelection.isBaseFirst()) position = m_selection.visibleBase(); } // Standard Mac behavior when extending to a boundary is grow the // selection rather than leaving the base in place and moving the // extent. Matches NSTextView. if (!frame() || !frame() ->editor() .behavior() .shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) { m_selection.setExtent(position); } else { TextDirection textDirection = directionOfEnclosingBlock(); if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) setSelectionEnd(&m_selection, position); else setSelectionStart(&m_selection, position); } break; } if (granularity == LineGranularity || granularity == ParagraphGranularity) m_xPosForVerticalArrowNavigation = x; return true; }
bool SelectionEditor::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align) { if (!verticalDistance) return false; if (userTriggered == UserTriggered) { OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create(); trialFrameSelection->setSelection(m_selection); trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered); } willBeModified(alter, direction == FrameSelection::DirectionUp ? DirectionBackward : DirectionForward); VisiblePosition pos; LayoutUnit xPos = 0; switch (alter) { case FrameSelection::AlterationMove: pos = createVisiblePosition(direction == FrameSelection::DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity()); xPos = lineDirectionPointForBlockDirectionNavigation(direction == FrameSelection::DirectionUp ? START : END); m_selection.setAffinity(direction == FrameSelection::DirectionUp ? TextAffinity::Upstream : TextAffinity::Downstream); break; case FrameSelection::AlterationExtend: pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT); m_selection.setAffinity(TextAffinity::Downstream); break; } int startY; if (!absoluteCaretY(pos, startY)) return false; if (direction == FrameSelection::DirectionUp) startY = -startY; int lastY = startY; VisiblePosition result; VisiblePosition next; for (VisiblePosition p = pos; ; p = next) { if (direction == FrameSelection::DirectionUp) next = previousLinePosition(p, xPos); else next = nextLinePosition(p, xPos); if (next.isNull() || next.deepEquivalent() == p.deepEquivalent()) break; int nextY; if (!absoluteCaretY(next, nextY)) break; if (direction == FrameSelection::DirectionUp) nextY = -nextY; if (nextY - startY > static_cast<int>(verticalDistance)) break; if (nextY >= lastY) { lastY = nextY; result = next; } } if (result.isNull()) return false; switch (alter) { case FrameSelection::AlterationMove: m_frameSelection->moveTo(result, userTriggered, align); break; case FrameSelection::AlterationExtend: m_frameSelection->setExtent(result, userTriggered); break; } m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend); return true; }
bool SelectionEditor::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered) { if (userTriggered == UserTriggered) { OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create(); trialFrameSelection->setSelection(m_selection); trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered); if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart()) return false; } willBeModified(alter, direction); bool wasRange = m_selection.isRange(); VisiblePosition originalStartPosition = m_selection.visibleStart(); VisiblePosition position; switch (direction) { case DirectionRight: if (alter == FrameSelection::AlterationMove) position = modifyMovingRight(granularity); else position = modifyExtendingRight(granularity); break; case DirectionForward: if (alter == FrameSelection::AlterationExtend) position = modifyExtendingForward(granularity); else position = modifyMovingForward(granularity); break; case DirectionLeft: if (alter == FrameSelection::AlterationMove) position = modifyMovingLeft(granularity); else position = modifyExtendingLeft(granularity); break; case DirectionBackward: if (alter == FrameSelection::AlterationExtend) position = modifyExtendingBackward(granularity); else position = modifyMovingBackward(granularity); break; } if (position.isNull()) return false; if (isSpatialNavigationEnabled(frame())) { if (!wasRange && alter == FrameSelection::AlterationMove && position.deepEquivalent() == originalStartPosition.deepEquivalent()) return false; } // Some of the above operations set an xPosForVerticalArrowNavigation. // Setting a selection will clear it, so save it to possibly restore later. // Note: the START position type is arbitrary because it is unused, it would be // the requested position type if there were no xPosForVerticalArrowNavigation set. LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START); m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend); switch (alter) { case FrameSelection::AlterationMove: m_frameSelection->moveTo(position, userTriggered); break; case FrameSelection::AlterationExtend: if (!m_selection.isCaret() && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity) && frame() && !frame()->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) { // Don't let the selection go across the base position directly. Needed to match mac // behavior when, for instance, word-selecting backwards starting with the caret in // the middle of a word and then word-selecting forward, leaving the caret in the // same place where it was, instead of directly selecting to the end of the word. VisibleSelection newSelection = m_selection; newSelection.setExtent(position); if (m_selection.isBaseFirst() != newSelection.isBaseFirst()) position = m_selection.visibleBase(); } // Standard Mac behavior when extending to a boundary is grow the // selection rather than leaving the base in place and moving the // extent. Matches NSTextView. if (!frame() || !frame()->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) { m_frameSelection->setExtent(position, userTriggered); } else { TextDirection textDirection = directionOfEnclosingBlock(); if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) m_frameSelection->setEnd(position, userTriggered); else m_frameSelection->setStart(position, userTriggered); } break; } if (granularity == LineGranularity || granularity == ParagraphGranularity) m_xPosForVerticalArrowNavigation = x; return true; }