📄 dom_position.cpp
字号:
/* * Copyright (C) 2004 Apple Computer, 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 "dom_position.h"#include "helper.h"#include "htmltags.h"#include "qstring.h"#include "rendering/render_block.h"#include "rendering/render_line.h"#include "rendering/render_object.h"#include "rendering/render_style.h"#include "rendering/render_text.h"#include "xml/dom_positioniterator.h"#include "xml/dom_elementimpl.h"#include "xml/dom_nodeimpl.h"#if KWIQ#include <stdio.h>#endif#if APPLE_CHANGES && !KWIQ#include "KWQAssertions.h"#include "KWQLogging.h"#else#define ASSERT(assertion) assert(assertion)#define LOG(channel, formatAndArgs...) ((void)0)#endifusing khtml::InlineBox;using khtml::InlineFlowBox;using khtml::InlineTextBox;using khtml::RenderBlock;using khtml::RenderObject;using khtml::RenderText;using khtml::RootInlineBox;namespace DOM {static bool renderersOnDifferentLine(RenderObject *r1, long o1, RenderObject *r2, long o2){ InlineBox *b1 = r1 ? r1->inlineBox(o1) : 0; InlineBox *b2 = r2 ? r2->inlineBox(o2) : 0; return (b1 && b2 && b1->root() != b2->root());}static NodeImpl *nextRenderedEditable(NodeImpl *node){ while (1) { node = node->nextEditable(); if (!node) return 0; if (!node->renderer()) continue; if (node->renderer()->inlineBox(0)) return node; } return 0;}static NodeImpl *previousRenderedEditable(NodeImpl *node){ while (1) { node = node->previousEditable(); if (!node) return 0; if (!node->renderer()) continue; if (node->renderer()->inlineBox(0)) return node; } return 0;}Position::Position(NodeImpl *node, long offset) : m_node(0), m_offset(offset) { if (node) { m_node = node; m_node->ref(); }};Position::Position(const Position &o) : m_node(0), m_offset(o.offset()) { if (o.node()) { m_node = o.node(); m_node->ref(); }}Position::~Position() { if (m_node) { m_node->deref(); }}Position &Position::operator=(const Position &o){ if (m_node) { m_node->deref(); } m_node = o.node(); if (m_node) { m_node->ref(); } m_offset = o.offset(); return *this;}ElementImpl *Position::element() const{ if (isEmpty()) return 0; NodeImpl *n = node(); for (; n && !n->isElementNode(); n = n->parentNode()); //loop return static_cast<ElementImpl *>(n);}long Position::renderedOffset() const{ if (!node()->isTextNode()) return offset(); if (!node()->renderer()) return offset(); long result = 0; RenderText *textRenderer = static_cast<RenderText *>(node()->renderer()); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { long start = box->m_start; long end = box->m_start + box->m_len; if (offset() < start) return result; if (offset() <= end) { result += offset() - start; return result; } result += box->m_len; } return result;}Position Position::equivalentLeafPosition() const{ if (isEmpty()) return Position(); if (!node()->renderer() || !node()->renderer()->firstChild()) return *this; NodeImpl *n = node(); int count = 0; while (1) { n = n->nextLeafNode(); if (!n || !n->inSameContainingBlockFlowElement(node())) return *this; if (count + n->maxOffset() >= offset()) { count = offset() - count; break; } count += n->maxOffset(); } return Position(n, count);}Position Position::previousRenderedEditablePosition() const{ if (isEmpty()) return Position(); if (node()->isContentEditable() && node()->hasChildNodes() == false && inRenderedContent()) return *this; NodeImpl *n = node(); while (1) { n = n->previousEditable(); if (!n) return Position(); if (n->renderer() && n->renderer()->style()->visibility() == khtml::VISIBLE) break; } return Position(n, 0);}Position Position::nextRenderedEditablePosition() const{ if (isEmpty()) return Position(); if (node()->isContentEditable() && node()->hasChildNodes() == false && inRenderedContent()) return *this; NodeImpl *n = node(); while (1) { n = n->nextEditable(); if (!n) return Position(); if (n->renderer() && n->renderer()->style()->visibility() == khtml::VISIBLE) break; } return Position(n, 0);}Position Position::previousCharacterPosition() const{ if (isEmpty()) return Position(); NodeImpl *fromRootEditableElement = node()->rootEditableElement(); PositionIterator it(*this); bool atStartOfLine = isFirstRenderedPositionOnLine(); while (!it.atStart()) { Position pos = it.previous(); if (pos.node()->rootEditableElement() != fromRootEditableElement) return *this; if (atStartOfLine) { if (pos.inRenderedContent()) return pos; } else if (rendersInDifferentPosition(pos)) return pos; } return *this;}Position Position::nextCharacterPosition() const{ if (isEmpty()) return Position(); NodeImpl *fromRootEditableElement = node()->rootEditableElement(); PositionIterator it(*this); bool atEndOfLine = isLastRenderedPositionOnLine(); while (!it.atEnd()) { Position pos = it.next(); if (pos.node()->rootEditableElement() != fromRootEditableElement) return *this; if (atEndOfLine) { if (pos.inRenderedContent()) return pos; } else if (rendersInDifferentPosition(pos)) return pos; } return *this;}Position Position::previousWordPosition() const{ if (isEmpty()) return Position(); Position pos = *this; for (PositionIterator it(*this); !it.atStart(); it.previous()) { if (it.current().node()->nodeType() == Node::TEXT_NODE || it.current().node()->nodeType() == Node::CDATA_SECTION_NODE) { DOMString t = it.current().node()->nodeValue(); QChar *chars = t.unicode(); uint len = t.length(); int start, end; khtml::findWordBoundary(chars, len, it.current().offset(), &start, &end); pos = Position(it.current().node(), start); } else { pos = Position(it.current().node(), it.current().node()->caretMinOffset()); } if (pos != *this) return pos; it.setPosition(pos); } return *this;}Position Position::nextWordPosition() const{ if (isEmpty()) return Position(); Position pos = *this; for (PositionIterator it(*this); !it.atEnd(); it.next()) { if (it.current().node()->nodeType() == Node::TEXT_NODE || it.current().node()->nodeType() == Node::CDATA_SECTION_NODE) { DOMString t = it.current().node()->nodeValue(); QChar *chars = t.unicode(); uint len = t.length(); int start, end; khtml::findWordBoundary(chars, len, it.current().offset(), &start, &end); pos = Position(it.current().node(), end); } else { pos = Position(it.current().node(), it.current().node()->caretMaxOffset()); } if (pos != *this) return pos; it.setPosition(pos); } return *this;}Position Position::previousLinePosition(int x) const{ if (!node()) return Position(); if (!node()->renderer()) return *this; InlineBox *box = node()->renderer()->inlineBox(offset()); if (!box) return *this; RenderBlock *containingBlock = 0; RootInlineBox *root = box->root()->prevRootBox(); if (root) { containingBlock = node()->renderer()->containingBlock(); } else { // This containing editable block does not have a previous line. // Need to move back to previous containing editable block in this root editable // block and find the last root line box in that block. NodeImpl *startBlock = node()->enclosingBlockFlowElement(); NodeImpl *n = node()->previousEditable(); while (n && startBlock == n->enclosingBlockFlowElement()) n = n->previousEditable(); if (n) { while (n && !Position(n, n->caretMaxOffset()).inRenderedContent()) n = n->previousEditable(); if (n && n->inSameRootEditableElement(node())) { ASSERT(n->renderer()); box = n->renderer()->inlineBox(n->caretMaxOffset()); ASSERT(box); // previous root line box found root = box->root(); containingBlock = n->renderer()->containingBlock(); } } } if (root) { int absx, absy; containingBlock->absolutePosition(absx, absy); RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object(); return renderer->positionForCoordinates(x, absy + root->topOverflow()); } return *this;}Position Position::nextLinePosition(int x) const{ if (!node()) return Position(); if (!node()->renderer()) return *this; InlineBox *box = node()->renderer()->inlineBox(offset()); if (!box) return *this; RenderBlock *containingBlock = 0; RootInlineBox *root = box->root()->nextRootBox(); if (root) { containingBlock = node()->renderer()->containingBlock(); } else { // This containing editable block does not have a next line. // Need to move forward to next containing editable block in this root editable // block and find the first root line box in that block. NodeImpl *startBlock = node()->enclosingBlockFlowElement(); NodeImpl *n = node()->nextEditable(); while (n && startBlock == n->enclosingBlockFlowElement()) n = n->nextEditable(); if (n) { while (n && !Position(n, n->caretMinOffset()).inRenderedContent()) n = n->nextEditable(); if (n && n->inSameRootEditableElement(node())) { ASSERT(n->renderer()); box = n->renderer()->inlineBox(n->caretMinOffset()); ASSERT(box); // previous root line box found root = box->root(); containingBlock = n->renderer()->containingBlock(); } } } if (root) { int absx, absy; containingBlock->absolutePosition(absx, absy); RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object(); return renderer->positionForCoordinates(x, absy + root->topOverflow()); } return *this;}Position Position::equivalentUpstreamPosition() const{ if (!node()) return Position(); NodeImpl *block = node()->enclosingBlockFlowElement(); PositionIterator it(*this); for (; !it.atStart(); it.previous()) { NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement(); if (block != currentBlock)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -