📄 position.cpp
字号:
/* * Copyright (C) 2004, 2005, 2006, 2009 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 "Position.h"#include "CSSComputedStyleDeclaration.h"#include "CString.h"#include "CharacterNames.h"#include "Logging.h"#include "PositionIterator.h"#include "RenderBlock.h"#include "Text.h"#include "TextIterator.h"#include "VisiblePosition.h"#include "htmlediting.h"#include "visible_units.h"#include <stdio.h> namespace WebCore {using namespace HTMLNames;static Node* nextRenderedEditable(Node* node){ while (1) { node = node->nextEditable(); if (!node) return 0; RenderObject* renderer = node->renderer(); if (!renderer) continue; if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox())) return node; } return 0;}static Node* previousRenderedEditable(Node* node){ while (1) { node = node->previousEditable(); if (!node) return 0; RenderObject* renderer = node->renderer(); if (!renderer) continue; if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox())) return node; } return 0;}Element* Position::documentElement() const{ if (Node* n = node()) if (Element* e = n->document()->documentElement()) return e; return 0;}Element *Position::element() const{ Node *n; for (n = node(); n && !n->isElementNode(); n = n->parentNode()) ; // empty loop body return static_cast<Element *>(n);}PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const{ Element* elem = element(); if (!elem) return 0; return WebCore::computedStyle(elem);}Position Position::previous(PositionMoveType moveType) const{ Node* n = node(); if (!n) return *this; int o = offset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. ASSERT(o >= 0); if (o > 0) { Node* child = n->childNode(o - 1); if (child) return Position(child, maxDeepOffset(child)); // There are two reasons child might be 0: // 1) The node is node like a text node that is not an element, and therefore has no children. // Going backward one character at a time is correct. // 2) The old offset was a bogus offset like (<br>, 1), and there is no child. // Going from 1 to 0 is correct. switch (moveType) { case CodePoint: return Position(n, o - 1); case Character: return Position(n, uncheckedPreviousOffset(n, o)); case BackwardDeletion: return Position(n, uncheckedPreviousOffsetForBackwardDeletion(n, o)); } } Node* parent = n->parentNode(); if (!parent) return *this; return Position(parent, n->nodeIndex());}Position Position::next(PositionMoveType moveType) const{ ASSERT(moveType != BackwardDeletion); Node* n = node(); if (!n) return *this; int o = offset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. ASSERT(o >= 0); Node* child = n->childNode(o); if (child || !n->hasChildNodes() && o < maxDeepOffset(n)) { if (child) return Position(child, 0); // There are two reasons child might be 0: // 1) The node is node like a text node that is not an element, and therefore has no children. // Going forward one character at a time is correct. // 2) The new offset is a bogus offset like (<br>, 1), and there is no child. // Going from 0 to 1 is correct. return Position(n, (moveType == Character) ? uncheckedNextOffset(n, o) : o + 1); } Node* parent = n->parentNode(); if (!parent) return *this; return Position(parent, n->nodeIndex() + 1);}int Position::uncheckedPreviousOffset(const Node* n, int current){ return n->renderer() ? n->renderer()->previousOffset(current) : current - 1;}int Position::uncheckedPreviousOffsetForBackwardDeletion(const Node* n, int current){ return n->renderer() ? n->renderer()->previousOffsetForBackwardDeletion(current) : current - 1;}int Position::uncheckedNextOffset(const Node* n, int current){ return n->renderer() ? n->renderer()->nextOffset(current) : current + 1;}bool Position::atStart() const{ Node *n = node(); if (!n) return true; return offset() <= 0 && n->parent() == 0;}bool Position::atEnd() const{ Node *n = node(); if (!n) return true; return n->parent() == 0 && offset() >= maxDeepOffset(n);}int Position::renderedOffset() const{ if (!node()->isTextNode()) return offset(); if (!node()->renderer()) return offset(); int result = 0; RenderText *textRenderer = toRenderText(node()->renderer()); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { int start = box->start(); int end = box->start() + box->len(); if (offset() < start) return result; if (offset() <= end) { result += offset() - start; return result; } result += box->len(); } return result;}// return first preceding DOM position rendered at a different location, or "this"Position Position::previousCharacterPosition(EAffinity affinity) const{ if (isNull()) return Position(); Node *fromRootEditableElement = node()->rootEditableElement(); bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity)); bool rendered = isCandidate(); Position currentPos = *this; while (!currentPos.atStart()) { currentPos = currentPos.previous(); if (currentPos.node()->rootEditableElement() != fromRootEditableElement) return *this; if (atStartOfLine || !rendered) { if (currentPos.isCandidate()) return currentPos; } else if (rendersInDifferentPosition(currentPos)) return currentPos; } return *this;}// return first following position rendered at a different location, or "this"Position Position::nextCharacterPosition(EAffinity affinity) const{ if (isNull()) return Position(); Node *fromRootEditableElement = node()->rootEditableElement(); bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity)); bool rendered = isCandidate(); Position currentPos = *this; while (!currentPos.atEnd()) { currentPos = currentPos.next(); if (currentPos.node()->rootEditableElement() != fromRootEditableElement) return *this; if (atEndOfLine || !rendered) { if (currentPos.isCandidate()) return currentPos; } else if (rendersInDifferentPosition(currentPos)) return currentPos; } return *this;}// Whether or not [node, 0] and [node, maxDeepOffset(node)] are their own VisiblePositions.// If true, adjacent candidates are visually distinct.// FIXME: Disregard nodes with renderers that have no height, as we do in isCandidate.// FIXME: Share code with isCandidate, if possible.static bool endsOfNodeAreVisuallyDistinctPositions(Node* node){ if (!node || !node->renderer()) return false; if (!node->renderer()->isInline()) return true; // Don't include inline tables. if (node->hasTagName(tableTag)) return false; // There is a VisiblePosition inside an empty inline-block container. return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild();}static Node* enclosingVisualBoundary(Node* node){ while (node && !endsOfNodeAreVisuallyDistinctPositions(node)) node = node->parentNode(); return node;}// upstream() and downstream() want to return positions that are either in a// text node or at just before a non-text node. This method checks for that.static bool isStreamer(const PositionIterator& pos){ if (!pos.node()) return true; if (isAtomicNode(pos.node())) return true; return pos.atStartOfNode();}// This function and downstream() are used for moving back and forth between visually equivalent candidates.// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates // that map to the VisiblePosition between 'b' and the space. This function will return the left candidate // and downstream() will return the right one.// Also, upstream() will return [boundary, 0] for any of the positions from [boundary, 0] to the first candidate// in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true.Position Position::upstream() const{ Node* startNode = node(); if (!startNode) return Position(); // iterate backward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); PositionIterator lastVisible = *this; PositionIterator currentPos = lastVisible; bool startEditable = startNode->isContentEditable();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -