📄 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)
* Copyright (C) 2004 Apple Computer, Inc.
*
* 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.
*/
#include "dom2_rangeimpl.h"
#include "dom/dom_exception.h"
#include "dom_textimpl.h"
#include "dom_xmlimpl.h"
#include "html/html_elementimpl.h"
#include "misc/htmltags.h"
#include "editing/markup.h"
#include "editing/visible_position.h"
#include "editing/visible_text.h"
#include "xml/dom_position.h"
#include "render_block.h"
using khtml::createMarkup;
using khtml::RenderBlock;
using khtml::RenderObject;
using khtml::VisiblePosition;
using khtml::UPSTREAM;
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 = 0;
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) const
{
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;
}
if (!parentStart && containerA->getDocument())
return containerA->getDocument()->documentElement();
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, const RangeImpl *sourceRange, int &exceptioncode ) const
{
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->getDocument() != sourceCont->getDocument()) {
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 = containerA->firstChild();
while (n != c && offsetC < offsetA) {
offsetC++;
n = n->nextSibling();
}
if( offsetA <= offsetC ) return -1; // A is before B
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -