📄 selectioncontroller.cpp
字号:
/* * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h"#include "SelectionController.h"#include "CString.h"#include "DeleteSelectionCommand.h"#include "Document.h"#include "Editor.h"#include "Element.h"#include "EventHandler.h"#include "EventNames.h"#include "ExceptionCode.h"#include "FocusController.h"#include "FloatQuad.h"#include "Frame.h"#include "FrameTree.h"#include "FrameView.h"#include "GraphicsContext.h"#include "HTMLInputElement.h"#include "HTMLNames.h"#include "HitTestRequest.h"#include "HitTestResult.h"#include "Page.h"#include "Range.h"#include "RenderTheme.h"#include "RenderView.h"#include "TextIterator.h"#include "TypingCommand.h"#include "htmlediting.h"#include "visible_units.h"#include <stdio.h>#define EDIT_DEBUG 0namespace WebCore {using namespace HTMLNames;const int NoXPosForVerticalArrowNavigation = INT_MIN;SelectionController::SelectionController(Frame* frame, bool isDragCaretController) : m_frame(frame) , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation) , m_needsLayout(true) , m_absCaretBoundsDirty(true) , m_lastChangeWasHorizontalExtension(false) , m_isDragCaretController(isDragCaretController) , m_isCaretBlinkingSuspended(false) , m_focused(false){}void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered){ setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);}void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered){ setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered);}void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered){ setSelection(VisibleSelection(pos, affinity), true, true, userTriggered);}void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered){ setSelection(VisibleSelection(startPosition(r), endPosition(r), affinity), true, true, userTriggered);}void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered){ setSelection(VisibleSelection(base, extent, affinity), true, true, userTriggered);}void SelectionController::setSelection(const VisibleSelection& s, bool closeTyping, bool clearTypingStyle, bool userTriggered){ if (m_isDragCaretController) { invalidateCaretRect(); m_sel = s; m_needsLayout = true; invalidateCaretRect(); return; } if (!m_frame) { m_sel = s; return; } Node* baseNode = s.base().node(); Document* document = 0; if (baseNode) document = baseNode->document(); // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection // if document->frame() == m_frame we can get into an infinite loop if (document && document->frame() != m_frame && document != m_frame->document()) { document->frame()->selection()->setSelection(s, closeTyping, clearTypingStyle, userTriggered); return; } if (closeTyping) TypingCommand::closeTyping(m_frame->editor()->lastEditCommand()); if (clearTypingStyle) m_frame->clearTypingStyle(); if (m_sel == s) return; VisibleSelection oldSelection = m_sel; m_sel = s; m_needsLayout = true; if (!s.isNone()) m_frame->setFocusedNodeIfNeeded(); m_frame->selectionLayoutChanged(); // Always clear the x position used for vertical arrow navigation. // It will be restored by the vertical arrow navigation code if necessary. m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation; selectFrameElementInParentIfFullySelected(); m_frame->notifyRendererOfSelectionChange(userTriggered); m_frame->respondToChangedSelection(oldSelection, closeTyping); if (userTriggered) m_frame->revealCaret(ScrollAlignment::alignToEdgeIfNeeded); notifyAccessibilityForSelectionChange();}static bool removingNodeRemovesPosition(Node* node, const Position& position){ if (!position.node()) return false; if (position.node() == node) return true; if (!node->isElementNode()) return false; Element* element = static_cast<Element*>(node); return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode());}void SelectionController::nodeWillBeRemoved(Node *node){ if (isNone()) return; // There can't be a selection inside a fragment, so if a fragment's node is being removed, // the selection in the document that created the fragment needs no adjustment. if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) return; bool baseRemoved = removingNodeRemovesPosition(node, m_sel.base()); bool extentRemoved = removingNodeRemovesPosition(node, m_sel.extent()); bool startRemoved = removingNodeRemovesPosition(node, m_sel.start()); bool endRemoved = removingNodeRemovesPosition(node, m_sel.end()); bool clearRenderTreeSelection = false; bool clearDOMTreeSelection = false; if (startRemoved || endRemoved) { // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away. clearRenderTreeSelection = true; clearDOMTreeSelection = true; } else if (baseRemoved || extentRemoved) { // The base and/or extent are about to be removed, but the start and end aren't. // Change the base and extent to the start and end, but don't re-validate the // selection, since doing so could move the start and end into the node // that is about to be removed. if (m_sel.isBaseFirst()) m_sel.setWithoutValidation(m_sel.start(), m_sel.end()); else m_sel.setWithoutValidation(m_sel.end(), m_sel.start()); // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges. } else if (Range::compareBoundaryPoints(m_sel.start(), Position(node, 0)) == -1 && Range::compareBoundaryPoints(m_sel.end(), Position(node, 0)) == 1) { // If we did nothing here, when this node's renderer was destroyed, the rect that it // occupied would be invalidated, but, selection gaps that change as a result of // the removal wouldn't be invalidated. // FIXME: Don't do so much unnecessary invalidation. clearRenderTreeSelection = true; } if (clearRenderTreeSelection) { RefPtr<Document> document = m_sel.start().node()->document(); document->updateRendering(); if (RenderView* view = toRenderView(document->renderer())) view->clearSelection(); } if (clearDOMTreeSelection) setSelection(VisibleSelection(), false, false);}void SelectionController::willBeModified(EAlteration alter, EDirection direction){ switch (alter) { case MOVE: m_lastChangeWasHorizontalExtension = false; break; case EXTEND: if (!m_lastChangeWasHorizontalExtension) { m_lastChangeWasHorizontalExtension = true; Position start = m_sel.start(); Position end = m_sel.end(); switch (direction) { // FIXME: right for bidi? case RIGHT: case FORWARD: m_sel.setBase(start); m_sel.setExtent(end); break; case LEFT: case BACKWARD: m_sel.setBase(end); m_sel.setExtent(start); break; } } break; }}VisiblePosition SelectionController::modifyExtendingRightForward(TextGranularity granularity){ VisiblePosition pos(m_sel.extent(), m_sel.affinity()); switch (granularity) { case CharacterGranularity: pos = pos.next(true); break; case WordGranularity: pos = nextWordPosition(pos); break; case SentenceGranularity: pos = nextSentencePosition(pos); break; case LineGranularity: pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); break; case ParagraphGranularity: pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); break; case SentenceBoundary: pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case LineBoundary: pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case ParagraphBoundary: pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case DocumentBoundary: pos = VisiblePosition(m_sel.end(), m_sel.affinity()); if (isEditablePosition(pos.deepEquivalent())) pos = endOfEditableContent(pos); else pos = endOfDocument(pos); break; } return pos;}VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity){ VisiblePosition pos; switch (granularity) { case CharacterGranularity: if (isRange()) pos = VisiblePosition(m_sel.end(), m_sel.affinity()); else pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).right(true); break; case WordGranularity: case SentenceGranularity: case LineGranularity: case ParagraphGranularity: case SentenceBoundary: case LineBoundary: case ParagraphBoundary: case DocumentBoundary: // FIXME: Implement all of the above. pos = modifyMovingForward(granularity); break; } return pos;}VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity){ VisiblePosition pos; // FIXME: Stay in editable content for the less common granularities. switch (granularity) { case CharacterGranularity: if (isRange()) pos = VisiblePosition(m_sel.end(), m_sel.affinity()); else pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).next(true); break; case WordGranularity: pos = nextWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity())); break; case SentenceGranularity: pos = nextSentencePosition(VisiblePosition(m_sel.extent(), m_sel.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 = VisiblePosition(m_sel.end(), m_sel.affinity()); if (!isRange() || !isStartOfLine(pos)) pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START)); break; } case ParagraphGranularity: pos = nextParagraphPosition(VisiblePosition(m_sel.end(), m_sel.affinity()), xPosForVerticalArrowNavigation(START)); break; case SentenceBoundary: pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case LineBoundary: pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case ParagraphBoundary: pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity())); break; case DocumentBoundary: pos = VisiblePosition(m_sel.end(), m_sel.affinity()); if (isEditablePosition(pos.deepEquivalent())) pos = endOfEditableContent(pos); else pos = endOfDocument(pos); break; } return pos;}VisiblePosition SelectionController::modifyExtendingLeftBackward(TextGranularity granularity){ VisiblePosition pos(m_sel.extent(), m_sel.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 = pos.previous(true); break; case WordGranularity: pos = previousWordPosition(pos); break; case SentenceGranularity: pos = previousSentencePosition(pos); break; case LineGranularity: pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); break; case ParagraphGranularity: pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); break; case SentenceBoundary: pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity())); break; case LineBoundary: pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity())); break; case ParagraphBoundary: pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity())); break; case DocumentBoundary: pos = VisiblePosition(m_sel.start(), m_sel.affinity()); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } return pos;}VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity){ VisiblePosition pos;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -