summaryrefslogtreecommitdiffstats
path: root/tdehtml/xml/dom_textimpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdehtml/xml/dom_textimpl.cpp')
-rw-r--r--tdehtml/xml/dom_textimpl.cpp522
1 files changed, 522 insertions, 0 deletions
diff --git a/tdehtml/xml/dom_textimpl.cpp b/tdehtml/xml/dom_textimpl.cpp
new file mode 100644
index 000000000..aa478b922
--- /dev/null
+++ b/tdehtml/xml/dom_textimpl.cpp
@@ -0,0 +1,522 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2001-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "dom/dom_exception.h"
+#include "css/cssstyleselector.h"
+#include "xml/dom2_eventsimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom_docimpl.h"
+
+#include "misc/htmlhashes.h"
+#include "rendering/render_text.h"
+#include "rendering/render_flow.h"
+
+#include <kdebug.h>
+
+using namespace DOM;
+using namespace tdehtml;
+
+static DOMString escapeHTML( const DOMString& in )
+{
+ return in.implementation()->escapeHTML();
+}
+
+CharacterDataImpl::CharacterDataImpl(DocumentImpl *doc, DOMStringImpl* _text)
+ : NodeImpl(doc)
+{
+ str = _text ? _text : new DOMStringImpl(0, 0);
+ str->ref();
+}
+
+CharacterDataImpl::~CharacterDataImpl()
+{
+ if(str) str->deref();
+}
+
+void CharacterDataImpl::setData( const DOMString &_data, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ if(str == _data.impl) return; // ### fire DOMCharacterDataModified if modified?
+ DOMStringImpl *oldStr = str;
+ str = _data.impl;
+ if(str) str->ref();
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ if(oldStr) oldStr->deref();
+}
+
+unsigned long CharacterDataImpl::length() const
+{
+ return str->l;
+}
+
+DOMString CharacterDataImpl::substringData( const unsigned long offset, const unsigned long count, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return DOMString();
+
+ return str->substring(offset,count);
+}
+
+void CharacterDataImpl::appendData( const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->append(arg.impl);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::insertData( const unsigned long offset, const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->insert(arg.impl, offset);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::deleteData( const unsigned long offset, const unsigned long count, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->remove(offset,count);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::replaceData( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ unsigned long realCount;
+ if (offset + count > str->l)
+ realCount = str->l-offset;
+ else
+ realCount = count;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->remove(offset,realCount);
+ str->insert(arg.impl, offset);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+DOMString CharacterDataImpl::nodeValue() const
+{
+ return str;
+}
+
+bool CharacterDataImpl::containsOnlyWhitespace() const
+{
+ return str->containsOnlyWhitespace();
+}
+
+void CharacterDataImpl::setNodeValue( const DOMString &_nodeValue, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
+ setData(_nodeValue, exceptioncode);
+}
+
+void CharacterDataImpl::dispatchModifiedEvent(DOMStringImpl *prevValue)
+{
+ if (parentNode())
+ parentNode()->childrenChanged();
+ if (!getDocument()->hasListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER))
+ return;
+
+ DOMStringImpl *newValue = str->copy();
+ newValue->ref();
+ int exceptioncode = 0;
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMCHARACTERDATAMODIFIED_EVENT,true,false,0,prevValue,newValue,DOMString(),0);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode);
+ evt->deref();
+ newValue->deref();
+ dispatchSubtreeModifiedEvent();
+}
+
+void CharacterDataImpl::checkCharDataOperation( const unsigned long offset, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit
+ // units in data.
+ if (offset > str->l) {
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ return;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+}
+
+long CharacterDataImpl::minOffset() const
+{
+ RenderText *r = static_cast<RenderText *>(renderer());
+ if (!r || !r->isText()) return 0;
+
+ // take :first-letter into consideration
+#ifdef __GNUC__
+#warning FIXME
+#endif
+#if 0
+ if (r->forcedMinOffset()) {
+ RenderFlow *firstLetter = static_cast<RenderFlow *>(r->previousSibling());
+ if (firstLetter && firstLetter->isFlow() && firstLetter->isFirstLetter()) {
+ RenderText *letterText = static_cast<RenderText *>(firstLetter->firstChild());
+ return letterText->minOffset();
+ }
+ }
+#endif
+
+ return r->minOffset();
+}
+
+long CharacterDataImpl::maxOffset() const
+{
+ RenderText *r = static_cast<RenderText *>(renderer());
+ if (!r || !r->isText()) return (long)length();
+ return r->maxOffset();
+}
+
+DOMStringImpl* CharacterDataImpl::textContent() const
+{
+ return new DOMStringImpl(str->s, str->l);
+}
+
+void CharacterDataImpl::setTextContent( const DOMString &str, int& exceptioncode )
+{
+ setData(str, exceptioncode);
+}
+
+// ---------------------------------------------------------------------------
+
+DOMString CommentImpl::nodeName() const
+{
+ return "#comment";
+}
+
+unsigned short CommentImpl::nodeType() const
+{
+ return Node::COMMENT_NODE;
+}
+
+NodeImpl *CommentImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createComment( str );
+}
+
+NodeImpl::Id CommentImpl::id() const
+{
+ return ID_COMMENT;
+}
+
+// DOM Section 1.1.1
+bool CommentImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+DOMString CommentImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return DOMString("<!--") + escapeHTML( nodeValue() ) + "-->";
+}
+
+// ---------------------------------------------------------------------------
+
+TextImpl *TextImpl::splitText( const unsigned long offset, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
+ // the number of 16-bit units in data.
+
+ // ### we explicitly check for a negative long that has been cast to an unsigned long
+ // ... this can happen if JS code passes in -1 - we need to catch this earlier! (in the
+ // kjs bindings)
+ if (offset > str->l || (long)offset < 0) {
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ return 0;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+ }
+
+ DOMStringImpl *oldStr = str;
+ TextImpl *newText = createNew(str->substring(offset,str->l-offset));
+ str = str->copy();
+ str->ref();
+ str->remove(offset,str->l-offset);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+
+ if (parentNode())
+ parentNode()->insertBefore(newText,nextSibling(), exceptioncode );
+ if ( exceptioncode )
+ return 0;
+
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+ return newText;
+}
+
+DOMString TextImpl::nodeName() const
+{
+ return "#text";
+}
+
+unsigned short TextImpl::nodeType() const
+{
+ return Node::TEXT_NODE;
+}
+
+NodeImpl *TextImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createTextNode(str);
+}
+
+bool TextImpl::rendererIsNeeded(RenderStyle *style)
+{
+ if (!CharacterDataImpl::rendererIsNeeded(style)) {
+ return false;
+ }
+ bool onlyWS = containsOnlyWhitespace();
+ if (!onlyWS) {
+ return true;
+ }
+
+ RenderObject *par = parentNode()->renderer();
+
+ if (par->isTable() || par->isTableRow() || par->isTableSection()) {
+ return false;
+ }
+
+ if (style->preserveWS() || style->preserveLF()) {
+ return true;
+ }
+
+ RenderObject *prev = previousRenderer();
+ if (par->isInlineFlow()) {
+ // <span><div/> <div/></span>
+ if (prev && !prev->isInline()) {
+ return false;
+ }
+ } else {
+ if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) {
+ return false;
+ }
+
+ RenderObject *first = par->firstChild();
+ while (first && first->isFloatingOrPositioned())
+ first = first->nextSibling();
+ RenderObject *next = nextRenderer();
+ if (!first || next == first) {
+ // Whitespace at the start of a block just goes away. Don't even
+ // make a render object for this text.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+RenderObject *TextImpl::createRenderer(RenderArena *arena, RenderStyle *style)
+{
+ return new (arena) RenderText(this, str);
+}
+
+void TextImpl::attach()
+{
+ createRendererIfNeeded();
+ CharacterDataImpl::attach();
+}
+
+NodeImpl::Id TextImpl::id() const
+{
+ return ID_TEXT;
+}
+
+void TextImpl::recalcStyle( StyleChange change )
+{
+// tqDebug("textImpl::recalcStyle");
+ // Create renderer if now needed
+ if ( changed() && !m_render) {
+ createRendererIfNeeded();
+ }
+ if (change != NoChange && parentNode()) {
+// tqDebug("DomText::recalcStyle");
+ if(m_render)
+ m_render->setStyle(parentNode()->renderer()->style());
+ }
+ if ( changed() && m_render && m_render->isText() )
+ static_cast<RenderText*>(m_render)->setText(str);
+ setChanged( false );
+}
+
+// DOM Section 1.1.1
+bool TextImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+TextImpl *TextImpl::createNew(DOMStringImpl *_str)
+{
+ return new TextImpl(docPtr(),_str);
+}
+
+DOMStringImpl* TextImpl::renderString() const
+{
+ if (renderer())
+ return static_cast<RenderText*>(renderer())->string();
+ else
+ return string();
+}
+
+DOMString TextImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return escapeHTML( nodeValue() );
+}
+
+DOMString TextImpl::toString(long long startOffset, long long endOffset) const
+{
+ // FIXME: substitute entity references as needed!
+
+ DOMString str = nodeValue();
+ if(endOffset >=0 || startOffset >0)
+ str = str.copy(); //we are going to modify this, so make a copy. I hope I'm doing this right.
+ if(endOffset >= 0)
+ str.truncate(endOffset);
+ if(startOffset > 0) //note the order of these 2 'if' statements so that it works right when n==m_startContainer==m_endContainer
+ str.remove(0, startOffset);
+ return escapeHTML( str );
+}
+
+// ---------------------------------------------------------------------------
+
+DOMString CDATASectionImpl::nodeName() const
+{
+ return "#cdata-section";
+}
+
+unsigned short CDATASectionImpl::nodeType() const
+{
+ return Node::CDATA_SECTION_NODE;
+}
+
+NodeImpl *CDATASectionImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createCDATASection(str);
+}
+
+// DOM Section 1.1.1
+bool CDATASectionImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+TextImpl *CDATASectionImpl::createNew(DOMStringImpl *_str)
+{
+ return new CDATASectionImpl(docPtr(),_str);
+}
+
+DOMString CDATASectionImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return DOMString("<![CDATA[") + nodeValue() + "]]>";
+}
+
+
+