📄 dom2_rangeimpl.cpp
字号:
/** * This file is part of the DOM implementation for KDE. * * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) * (C) 2001 Peter Kelly (pmk@post.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id: dom2_rangeimpl.cpp,v 1.23 2001/07/15 19:55:01 mueller Exp $ */#include "dom/dom2_traversal.h"#include "dom/dom_node.h"#include "dom/dom_doc.h"#include "dom/dom_string.h"#include "dom/dom_text.h"#include "dom/dom_exception.h"#include "dom_docimpl.h"#include "dom2_rangeimpl.h"#include "dom2_traversalimpl.h"#include "dom_textimpl.h"#include "dom_xmlimpl.h"using namespace DOM;RangeImpl::RangeImpl(DocumentPtr *_ownerDocument){ m_ownerDocument = _ownerDocument; m_ownerDocument->ref(); m_startContainer = _ownerDocument->document(); m_startContainer->ref(); m_endContainer = _ownerDocument->document(); m_endContainer->ref(); m_startOffset = 0; m_endOffset = 0; m_detached = false;}RangeImpl::RangeImpl(DocumentPtr *_ownerDocument, NodeImpl *_startContainer, long _startOffset, NodeImpl *_endContainer, long _endOffset){ m_ownerDocument = _ownerDocument; m_ownerDocument->ref(); m_startContainer = _startContainer; m_startContainer->ref(); m_startOffset = _startOffset; m_endContainer = _endContainer; m_endContainer->ref(); m_endOffset = _endOffset; m_detached = false;}RangeImpl::~RangeImpl(){ m_ownerDocument->deref(); int exceptioncode; if (!m_detached) detach(exceptioncode);}NodeImpl *RangeImpl::startContainer(int &exceptioncode) const{ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } return m_startContainer;}long RangeImpl::startOffset(int &exceptioncode) const{ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } return m_startOffset;}NodeImpl *RangeImpl::endContainer(int &exceptioncode) const{ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } return m_endContainer;}long RangeImpl::endOffset(int &exceptioncode) const{ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } return m_endOffset;}NodeImpl *RangeImpl::commonAncestorContainer(int &exceptioncode){ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } NodeImpl *com = commonAncestorContainer(m_startContainer,m_endContainer); if (!com) // should never happen exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return com;}NodeImpl *RangeImpl::commonAncestorContainer(NodeImpl *containerA, NodeImpl *containerB){ NodeImpl *parentStart; for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) { NodeImpl *parentEnd = containerB; while( parentEnd && (parentStart != parentEnd) ) parentEnd = parentEnd->parentNode(); if(parentStart == parentEnd) break; } return parentStart;}bool RangeImpl::collapsed(int &exceptioncode) const{ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } return (m_startContainer == m_endContainer && m_startOffset == m_endOffset);}void RangeImpl::setStart( NodeImpl *refNode, long offset, int &exceptioncode ){ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return; } if (!refNode) { exceptioncode = DOMException::NOT_FOUND_ERR; return; } if (refNode->getDocument() != m_ownerDocument->document()) { exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return; } checkNodeWOffset( refNode, offset, exceptioncode ); if (exceptioncode) return; setStartContainer(refNode); m_startOffset = offset; // check if different root container NodeImpl *endRootContainer = m_endContainer; while (endRootContainer->parentNode()) endRootContainer = endRootContainer->parentNode(); NodeImpl *startRootContainer = m_startContainer; while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); if (startRootContainer != endRootContainer) collapse(true,exceptioncode); // check if new start after end else if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) collapse(true,exceptioncode);}void RangeImpl::setEnd( NodeImpl *refNode, long offset, int &exceptioncode ){ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return; } if (!refNode) { exceptioncode = DOMException::NOT_FOUND_ERR; return; } if (refNode->getDocument() != m_ownerDocument->document()) { exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return; } checkNodeWOffset( refNode, offset, exceptioncode ); if (exceptioncode) return; setEndContainer(refNode); m_endOffset = offset; // check if different root container NodeImpl *endRootContainer = m_endContainer; while (endRootContainer->parentNode()) endRootContainer = endRootContainer->parentNode(); NodeImpl *startRootContainer = m_startContainer; while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); if (startRootContainer != endRootContainer) collapse(false,exceptioncode); // check if new end before start if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) collapse(false,exceptioncode);}void RangeImpl::collapse( bool toStart, int &exceptioncode ){ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return; } if( toStart ) // collapse to start { setEndContainer(m_startContainer); m_endOffset = m_startOffset; } else // collapse to end { setStartContainer(m_endContainer); m_startOffset = m_endOffset; }}short RangeImpl::compareBoundaryPoints( Range::CompareHow how, RangeImpl *sourceRange, int &exceptioncode ){ if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return 0; } if (!sourceRange) { exceptioncode = DOMException::NOT_FOUND_ERR; return 0; } NodeImpl *thisCont = commonAncestorContainer(exceptioncode); NodeImpl *sourceCont = sourceRange->commonAncestorContainer(exceptioncode); if (exceptioncode) return 0; if (thisCont->ownerDocument() != sourceCont->ownerDocument()) { exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return 0; } NodeImpl *thisTop = thisCont; NodeImpl *sourceTop = sourceCont; while (thisTop->parentNode()) thisTop = thisTop->parentNode(); while (sourceTop->parentNode()) sourceTop = sourceTop->parentNode(); if (thisTop != sourceTop) { // in different DocumentFragments exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return 0; } switch(how) { case Range::START_TO_START: return compareBoundaryPoints( m_startContainer, m_startOffset, sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); break; case Range::START_TO_END: return compareBoundaryPoints( m_startContainer, m_startOffset, sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); break; case Range::END_TO_END: return compareBoundaryPoints( m_endContainer, m_endOffset, sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); break; case Range::END_TO_START: return compareBoundaryPoints( m_endContainer, m_endOffset, sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); break; default: exceptioncode = DOMException::SYNTAX_ERR; return 0; }}short RangeImpl::compareBoundaryPoints( NodeImpl *containerA, long offsetA, NodeImpl *containerB, long offsetB ){ // see DOM2 traversal & range section 2.5 // case 1: both points have the same container if( containerA == containerB ) { if( offsetA == offsetB ) return 0; // A is equal to B if( offsetA < offsetB ) return -1; // A is before B else return 1; // A is after B } // case 2: node C (container B or an ancestor) is a child node of A NodeImpl *c = containerB; while (c && c->parentNode() != containerA) c = c->parentNode(); if (c) { int offsetC = 0; NodeImpl *n = n = containerA->firstChild(); while (n != c) { offsetC++; n = n->nextSibling(); } if( offsetA <= offsetC ) return -1; // A is before B else return 1; // A is after B } // case 3: node C (container A or an ancestor) is a child node of B c = containerA; while (c && c->parentNode() != containerB) c = c->parentNode(); if (c) { int offsetC = 0; NodeImpl *n = n = containerB->firstChild(); while (n != c) { offsetC++; n = n->nextSibling(); } if( offsetC < offsetB ) return -1; // A is before B else return 1; // A is after B } // case 4: containers A & B are siblings, or children of siblings // ### we need to do a traversal here instead NodeImpl *cmnRoot = commonAncestorContainer(containerA,containerB); NodeImpl *childA = containerA; while (childA->parentNode() != cmnRoot) childA = childA->parentNode(); NodeImpl *childB = containerB; while (childB->parentNode() != cmnRoot) childB = childB->parentNode(); NodeImpl *n = cmnRoot->firstChild(); int i = 0; int childAOffset = -1; int childBOffset = -1; while (childAOffset < 0 || childBOffset < 0) { if (n == childA) childAOffset = i; if (n == childB) childBOffset = i; n = n->nextSibling(); i++; } if( childAOffset == childBOffset ) return 0; // A is equal to B if( childAOffset < childBOffset ) return -1; // A is before B else return 1; // A is after B}bool RangeImpl::boundaryPointsValid( ){ short valid = compareBoundaryPoints( m_startContainer, m_startOffset, m_endContainer, m_endOffset ); if( valid == 1 ) return false; else return true;}void RangeImpl::deleteContents( int &exceptioncode ) { if (m_detached) { exceptioncode = DOMException::INVALID_STATE_ERR; return; } checkDeleteExtract(exceptioncode); if (exceptioncode) return; processContents(DELETE_CONTENTS,exceptioncode);}DocumentFragmentImpl *RangeImpl::processContents ( ActionType action, int &exceptioncode ){ // ### when mutation events are implemented, we will have to take into account // situations where the tree is being transformed while we delete - ugh! // ### perhaps disable node deletion notification for this range while we do this? if (collapsed(exceptioncode)) return 0; if (exceptioncode) return 0; NodeImpl *cmnRoot = commonAncestorContainer(exceptioncode); if (exceptioncode) return 0; // what is the highest node that partially selects the start of the range? NodeImpl *partialStart = 0; if (m_startContainer != cmnRoot) { partialStart = m_startContainer; while (partialStart->parentNode() != cmnRoot) partialStart = partialStart->parentNode(); } // what is the highest node that partially selects the end of the range? NodeImpl *partialEnd = 0; if (m_endContainer != cmnRoot) { partialEnd = m_endContainer; while (partialEnd->parentNode() != cmnRoot) partialEnd = partialEnd->parentNode(); } DocumentFragmentImpl *fragment = 0; if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) fragment = new DocumentFragmentImpl(m_ownerDocument); // Simple case: the start and end containers are the same. We just grab // everything >= start offset and < end offset if (m_startContainer == m_endContainer) { if(m_startContainer->nodeType() == Node::TEXT_NODE ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -