summaryrefslogtreecommitdiffstats
path: root/quanta/parts/kafka/kafkacommon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'quanta/parts/kafka/kafkacommon.cpp')
-rw-r--r--quanta/parts/kafka/kafkacommon.cpp4257
1 files changed, 4257 insertions, 0 deletions
diff --git a/quanta/parts/kafka/kafkacommon.cpp b/quanta/parts/kafka/kafkacommon.cpp
new file mode 100644
index 00000000..fe147bc7
--- /dev/null
+++ b/quanta/parts/kafka/kafkacommon.cpp
@@ -0,0 +1,4257 @@
+/***************************************************************************
+ kafkacommon.cpp
+ -------------------
+
+ copyright : (C) 2003, 2004 - Nicolas Deschildre
+ email : ndeschildre@kdewebdev.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qptrdict.h>
+
+#include <kdebug.h>
+#include <dom/dom_exception.h>
+#include <dom/dom_doc.h>
+#include <dom/dom_element.h>
+#include <dom/dom_text.h>
+
+#include "node.h"
+#include "tag.h"
+#include "document.h"
+#include "resource.h"
+#include "quantacommon.h"
+
+#include "kafkacommon.h"
+#include "wkafkapart.h"
+#include "undoredo.h"
+#include "cursors.h"
+
+#include <cassert>
+
+Node *kafkaCommon::getNextNode(Node *node, bool &goUp, Node *endNode)
+{
+ //goto next node, my favorite part :)
+ if(!node || node == endNode)
+ return 0L;
+ if(goUp)
+ {
+ if(node->next)
+ {
+ goUp = false;
+ if(node->next == endNode)
+ return 0L;
+ return node->next;
+ }
+ else
+ {
+ if(node->parent == endNode)
+ return 0L;
+ return getNextNode(node->parent, goUp);
+ }
+ }
+ else
+ {
+ if(node->child)
+ {
+ if(node->child == endNode)
+ return 0L;
+ return node->child;
+ }
+ else if(node->next)
+ {
+ if(node->next == endNode)
+ return 0L;
+ return node->next;
+ }
+ else
+ {
+ goUp = true;
+ if(node->parent == endNode)
+ return 0L;
+ return getNextNode(node->parent, goUp);
+ }
+ }
+}
+
+Node* kafkaCommon::getNextNodeNE(Node *node, bool &goUp, Node *endNode)
+{
+ Node *n = node;
+ n = getNextNode(n, goUp, endNode);
+ while(n && n->tag->type == Tag::Empty)
+ n = getNextNode(n, goUp, endNode);
+ return n;
+}
+
+Node* kafkaCommon::getPrevNode(Node *node, Node *endNode)
+{
+ Node *n = node;
+
+ if(!node)
+ return 0L;
+
+ if(n->prev && n->prev->child)
+ {
+ n = n->prev;
+ if(n == endNode)
+ return 0L;
+ while(n->child)
+ {
+ n = n->child;
+ while(n && n->next)
+ n = n->next;
+ if(n == endNode)
+ return 0L;
+ }
+ }
+ else if(n->prev)
+ {
+ n = n->prev;
+ if(n == endNode)
+ return 0L;
+ }
+ else
+ {
+ n = n->parent;
+ if(n == endNode)
+ return 0L;
+ }
+ return n;
+}
+
+Node* kafkaCommon::getPrevNodeNE(Node *node, Node *endNode)
+{
+ Node *n = node;
+ n = getPrevNode(node, endNode);
+ while(n && n->tag->type == Tag::Empty)
+ n = getPrevNode(n, endNode);
+ return n;
+}
+
+Node* kafkaCommon::DTDGetCommonParent(Node* startNode, Node* endNode,
+ QValueList<int>& commonParentStartChildLocation,
+ QValueList<int>& commonParentEndChildLocation, Node* nodeSubtree)
+{
+ // look for commonParent
+ Node* commonParent = 0;
+ Node* commonParentStartChild = 0, *commonParentEndChild = 0;
+ int locOffset = 1;
+ QValueList<int> startNodeLocation = getLocation(startNode);
+ QValueList<int> endNodeLocation = getLocation(endNode);
+ QValueList<int>::iterator itStart = startNodeLocation.begin();
+ QValueList<int>::iterator itEnd = endNodeLocation.begin();
+ while(itStart != startNodeLocation.end() && itEnd != endNodeLocation.end() &&
+ (*itStart) == (*itEnd))
+ {
+ commonParent = getNodeFromSubLocation(startNodeLocation, locOffset, nodeSubtree);
+ itStart++;
+ itEnd++;
+ locOffset++;
+ }
+
+ //look for commonParentStartChild and commonParentEndChild
+ if(itStart != startNodeLocation.end())
+ commonParentStartChild = getNodeFromSubLocation(startNodeLocation, locOffset, nodeSubtree);
+ else
+ commonParentStartChild = commonParent;
+
+ if(itEnd != endNodeLocation.end())
+ commonParentEndChild = getNodeFromSubLocation(endNodeLocation, locOffset, nodeSubtree);
+ else
+ commonParentEndChild = commonParent;
+
+ //If commonParent isn't inline, move commonParent to the closest non inline node
+ if(commonParent && (commonParent->tag->type == Tag::Text || commonParent->tag->type == Tag::Empty))
+ {
+ Node* oldCommonParent = commonParent;
+ commonParent = commonParent->parent;
+ commonParentStartChild = oldCommonParent;
+ commonParentEndChild = oldCommonParent;
+ }
+ //startNode or endNode can't be the commonParent.
+ else if(commonParent && (itStart == startNodeLocation.end() || itEnd == endNodeLocation.end()))
+ commonParent = commonParent->parent;
+
+ commonParentStartChildLocation = getLocation(commonParentStartChild);
+ commonParentEndChildLocation = getLocation(commonParentEndChild);
+
+ return commonParent;
+}
+
+Node* kafkaCommon::DTDGetNonInlineCommonParent(Node* startNode, Node* endNode,
+ QValueList<int>& commonParentStartChildLocation,
+ QValueList<int>& commonParentEndChildLocation, Node* nodeSubtree)
+{
+ // look for commonParent
+ Node* commonParent = 0;
+ Node* commonParentStartChild = 0, *commonParentEndChild = 0;
+ int locOffset = 1;
+ QValueList<int> startNodeLocation = getLocation(startNode);
+ QValueList<int> endNodeLocation = getLocation(endNode);
+ QValueList<int>::iterator itStart = startNodeLocation.begin();
+ QValueList<int>::iterator itEnd = endNodeLocation.begin();
+ while(itStart != startNodeLocation.end() && itEnd != endNodeLocation.end() &&
+ (*itStart) == (*itEnd))
+ {
+ commonParent = getNodeFromSubLocation(startNodeLocation, locOffset, nodeSubtree);
+ itStart++;
+ itEnd++;
+ locOffset++;
+ }
+
+ //look for commonParentStartChild and commonParentEndChild
+ if(itStart != startNodeLocation.end())
+ commonParentStartChild = getNodeFromSubLocation(startNodeLocation, locOffset, nodeSubtree);
+ else
+ commonParentStartChild = commonParent;
+
+ if(itEnd != endNodeLocation.end())
+ commonParentEndChild = getNodeFromSubLocation(endNodeLocation, locOffset, nodeSubtree);
+ else
+ commonParentEndChild = commonParent;
+
+ //If commonParent isn't inline, move commonParent to the closest non inline node
+ if(commonParent && (isInline(commonParent->tag->name) ||
+ commonParent->tag->type == Tag::Text || commonParent->tag->type == Tag::Empty))
+ {
+ Node* oldCommonParent = commonParent;
+ commonParent = commonParent->parent;
+ while(commonParent && isInline(commonParent->tag->name))
+ {
+ oldCommonParent = commonParent;
+ commonParent = commonParent->parent;
+ }
+ commonParentStartChild = oldCommonParent;
+ commonParentEndChild = oldCommonParent;
+ }
+ //startNode or endNode can't be the commonParent.
+ else if(commonParent && (itStart == startNodeLocation.end() || itEnd == endNodeLocation.end()))
+ commonParent = commonParent->parent;
+
+ commonParentStartChildLocation = getLocation(commonParentStartChild);
+ commonParentEndChildLocation = getLocation(commonParentEndChild);
+
+ return commonParent;
+}
+
+DOM::Node kafkaCommon::getNextDomNode(DOM::Node node, bool &goUp, bool returnParentNode, DOM::Node endNode)
+{
+ if(node.isNull())
+ return 0L;
+ if(node.hasChildNodes() && !goUp)
+ {
+ if(endNode == node.firstChild())
+ return 0L;
+ else
+ return node.firstChild();
+ }
+ else if(!node.nextSibling().isNull())
+ {
+ goUp = false;
+ if(endNode == node.nextSibling())
+ return 0L;
+ else
+ return node.nextSibling();
+ }
+ else
+ {
+ goUp = true;
+ if(node.parentNode().isNull() || endNode == node.parentNode())
+ return 0L;
+ if(returnParentNode)
+ return node.parentNode();
+ else
+ return getNextDomNode(node.parentNode(), goUp, returnParentNode, endNode);
+ }
+}
+
+DOM::Node kafkaCommon::getPrevDomNode(DOM::Node node, DOM::Node endNode)
+{
+ DOM::Node n = node;
+
+ if(node.isNull())
+ return 0L;
+
+ if(!n.previousSibling().isNull() && !n.previousSibling().firstChild().isNull())
+ {
+ n = n.previousSibling();
+ if(n == endNode)
+ return 0L;
+ while(!n.firstChild().isNull())
+ {
+ n = n.firstChild();
+ while(!n.isNull() && !n.nextSibling().isNull())
+ n = n.nextSibling();
+ if(n == endNode)
+ return 0L;
+ }
+ }
+ else if(!n.previousSibling().isNull())
+ {
+ n = n.previousSibling();
+ if(n == endNode)
+ return 0L;
+ }
+ else
+ {
+ n = n.parentNode();
+ if(n == endNode)
+ return 0L;
+ }
+ return n;
+}
+
+Node* kafkaCommon::getCorrectStartNode(Node* startNode, int& start_offset)
+{
+ Node* start_node = startNode;
+
+ while(start_node && (start_node->tag->type != Tag::Text || (uint)start_offset == start_node->tag->tagStr().length()))
+ {
+ start_node = start_node->nextSibling();
+ if(start_node->tag->type == Tag::Text || start_node->tag->type == Tag::Empty)
+ {
+ start_offset = 0;
+ break;
+ }
+ }
+
+ return start_node;
+}
+
+Node* kafkaCommon::getCorrectEndNode(Node* endNode, int& end_offset)
+{
+ Node* end_node = endNode;
+
+ while(end_node && (end_node->tag->type != Tag::Text || end_offset == 0))
+ {
+ end_node = end_node->previousSibling();
+ if(end_node && end_node->tag->type == Tag::Text)
+ {
+ end_offset = end_node->tag->tagStr().length();
+ break;
+ }
+ }
+
+ return end_node;
+}
+
+Node* kafkaCommon::getCommonParentChild(Node* node, Node* commonParent)
+{
+ assert(node && commonParent);
+
+ Node* aux = commonParent->child;
+ assert(aux);
+
+ while(aux && aux != node)
+ {
+ if(aux->hasForChild(node))
+ return aux;
+ aux = aux->nextSibling();
+ }
+ return aux;
+}
+
+
+void kafkaCommon::applyIndentation(Node *node, int nbOfSpaces, int nbOfTabs, NodeModifsSet* modifs, bool inlineNodeIndentation)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::applyIndentation()" << endl;
+#endif
+
+ Node *parent, *nextNE, *prevNE, *realPrevNE, *realNextNE, *realPrev, *realNext, *prev, *next;
+ int nonInlineDepth = 0, nonInlineDepth2 = 0, i;
+ bool b = false;
+ QString indentation1, indentation2, text;
+
+ if(!node)
+ return;
+
+ prev = node->previousSibling();
+ next = node->nextSibling();
+ prevNE = getPrevNodeNE(node);
+ nextNE = getNextNodeNE(node, b);
+ realPrevNE = node->prevNE();
+ realNextNE = node->nextNE();
+ realPrev = node->prev;
+ realNext = node->next;
+
+ if(inlineNodeIndentation &&
+ !node->prev && getNodeDisplay(node->parent, true) == kafkaCommon::blockDisplay)
+ {
+ AreaStruct node_area = node->tag->area();
+ AreaStruct parent_area = node->parent->tag->area();
+
+ if(node_area.bLine == parent_area.bLine)
+ {
+ node->tag->setIndentationDone(true);
+ return;
+ }
+ }
+
+ //First remove all the indentation
+ if(node->tag->type == Tag::Text)
+ setTagString(node, removeUnnecessaryWhitespaces(node->tag->tagStr()), modifs);
+
+ //compute the "non-inline depth" of the Node and of the next NE (not Empty) Node
+ // i.e. we count how many non-inline parent they have.
+ parent = node->parent;
+ while(parent)
+ {
+ if(getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ ++nonInlineDepth;
+ parent = parent->parent;
+ }
+
+ //compute the "non-inline depth" of the next non-empty Node.
+ if (nextNE)
+ parent = nextNE->parent;
+ else
+ parent = 0L;
+ while(parent)
+ {
+ if(getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ ++nonInlineDepth2;
+ parent = parent->parent;
+ }
+
+ parent = node->parent;
+
+ if(!parent || getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ {
+ //prepare the indentation
+ indentation1 = "\n";
+ indentation2 = "\n";
+
+ if(nbOfSpaces == 0) // tabs are used
+ {
+ indentation1 += QString().fill('\t', nbOfTabs * nonInlineDepth);
+ indentation2 += QString().fill('\t', nbOfTabs * nonInlineDepth2);
+ }
+ else // spaces are used
+ {
+ indentation1 += QString().fill(' ', nbOfSpaces * nonInlineDepth);
+ indentation2 += QString().fill(' ', nbOfSpaces * nonInlineDepth2);
+ }
+
+ //test and add indentations if necessary
+ if(!prevNE || (prevNE && getNodeDisplay(node, true) ==
+ kafkaCommon::blockDisplay) ||
+ (prevNE && getNodeDisplay(node, true) == kafkaCommon::inlineDisplay &&
+ getNodeDisplay(prevNE, true) == kafkaCommon::blockDisplay))
+ {
+ if(node->tag->type == Tag::Text && !hasParent(node, "pre"))
+ {
+ setTagStringAndFitsNodes(node, indentation1 + node->tag->tagStr(), modifs);
+ }
+ else if(prev && prev->tag->type == Tag::Empty)
+ {
+ setTagStringAndFitsNodes(prev, indentation1, modifs);
+ }
+ //The indentation is always done at the left because we apply this function "from left to right"
+ else if(prev && prev->tag->type == Tag::Text /** && prev->tag->indentationDone() */)
+ {
+ //Remove the indentation at the right of the text Node
+ text = prev->tag->tagStr();
+ for(i = 0; (unsigned)i < text.length(); ++i)
+ {
+ if(!text[i].isSpace())
+ break;
+ }
+ if(i == 0)
+ prev->tag->setStr(removeUnnecessaryWhitespaces(text));
+ else
+ prev->tag->setStr(text.mid(0, i) + removeUnnecessaryWhitespaces(text, true));
+ setTagStringAndFitsNodes(prev, prev->tag->tagStr() + indentation1, modifs);
+ }
+ }
+
+ if(!nextNE || (nextNE && getNodeDisplay(node, true) ==
+ kafkaCommon::blockDisplay) ||
+ (nextNE && getNodeDisplay(node, true) == kafkaCommon::inlineDisplay &&
+ getNodeDisplay(nextNE, true) == kafkaCommon::blockDisplay))
+ {
+ if(node->tag->type == Tag::Text && !hasParent(node, "pre"))
+ {
+ setTagStringAndFitsNodes(node, node->tag->tagStr() + indentation2, modifs);
+ }
+ else if(next && next->tag->type == Tag::Empty)
+ {
+ setTagStringAndFitsNodes(next, indentation2, modifs);
+ }
+ //If next's cleanStrBuilt is not true, the next node to be processed will be this
+ //one and the indentation spaces will be handled as real spaces.
+ else if(next && next->tag->type == Tag::Text && next->tag->indentationDone())
+ {
+ //Remove the indentation at the left of the text Node
+ text = next->tag->tagStr();
+ for(i = text.length() - 1; i <= 0; i--)
+ {
+ if(!text[i].isSpace())
+ break;
+ }
+ if((unsigned)i == text.length() - 1)
+ next->tag->setStr(removeUnnecessaryWhitespaces(text));
+ else
+ next->tag->setStr(removeUnnecessaryWhitespaces(text, false, true) +
+ text.mid(i + 1));
+ setTagStringAndFitsNodes(next, indentation2 + next->tag->tagStr(), modifs);
+ }
+ }
+ }
+ else
+ {
+ //The parent is inline, so no indentation.
+ //Nothing to do.
+ }
+ node->tag->setIndentationDone(true);
+}
+
+void kafkaCommon::fitIndentationNodes(Node *n1, Node *n2, NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::fitIndentationNodes()" << endl;
+#endif
+
+ Node *parent, *child, *node, *emptyNode = 0L, *emptyNode2 = 0L;
+ int nbEmptyNodes = 0, n1Depth, n2Depth;
+ bool lastChild = false, firstChild = false;
+
+ if(!n1 || !n2 || n1 == n2 || n1->tag->type == Tag::Empty || n2->tag->type == Tag::Empty)
+ return;
+
+ n1Depth = nodeDepth(n1);
+ n2Depth = nodeDepth(n2);
+
+ if(n1Depth != n2Depth)
+ {
+ if(n1Depth > n2Depth)
+ {
+ child = n1;
+ parent = n2;
+ }
+ else
+ {
+ child = n2;
+ parent = n1;
+ }
+ if(child->parent->firstChildNE() == child)
+ firstChild = true;
+ if(child->parent->lastChildNE() == child)
+ lastChild = true;
+
+ //counting the Empty Nodes and deleting them to have only one empty node.
+ if(firstChild)
+ {
+ node = child->prev;
+ while(node)
+ {
+ if(node->tag->type == Tag::Empty)
+ nbEmptyNodes++;
+ node = node->prev;
+ }
+ node = child->prev;
+ while(nbEmptyNodes > 1)
+ {
+ extractAndDeleteNode(node, modifs, false, false, false);
+ nbEmptyNodes--;
+ node = child->prev;
+ }
+ if(nbEmptyNodes == 1)
+ emptyNode = child->prev;
+ }
+
+ nbEmptyNodes = 0;
+ if(lastChild)
+ {
+ node = child->next;
+ while(node)
+ {
+ if(node->tag->type == Tag::Empty)
+ nbEmptyNodes++;
+ node = node->next;
+ }
+ node = child->next;
+ while(nbEmptyNodes > 1)
+ {
+ extractAndDeleteNode(node, modifs, false, false, false);
+ nbEmptyNodes--;
+ node = child->next;
+ }
+ if(nbEmptyNodes == 1)
+ emptyNode2 = child->next;
+ }
+
+ //adding/deleting a empty node if necessary
+ if(firstChild)
+ {
+ if(getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ {
+ if(child->tag->type != Tag::Text && !emptyNode)
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), child->parent,
+ child, child, modifs);
+ }
+ }
+ else
+ {
+ if(child->tag->type == Tag::Text && emptyNode)
+ {
+ extractAndDeleteNode(emptyNode, modifs, false, false, false);
+ }
+ }
+ }
+
+ if(lastChild)
+ {
+ if(getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ {
+ if(child->tag->type != Tag::Text && !emptyNode2)
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), child->parent,
+ 0L, 0L, modifs);
+ }
+ }
+ else
+ {
+ if(child->tag->type == Tag::Text && emptyNode2)
+ {
+ extractAndDeleteNode(emptyNode2, modifs, false, false, false);
+ }
+ }
+ }
+ }
+ else
+ {
+ if(n1->next != n2)
+ {
+ //counting the Empty Nodes and deleting them to have only one empty node.
+ node = n1->next;
+ while(node && node != n2)
+ {
+ if(node->tag->type == Tag::Empty)
+ nbEmptyNodes++;
+ node = node->next;
+ }
+ node = n1->next;
+ while(nbEmptyNodes > 1 || (nbEmptyNodes > 0 && n1->getClosingNode() == n2))
+ {
+ extractAndDeleteNode(node, modifs, false, false, false);
+ nbEmptyNodes--;
+ node = n1->next;
+ }
+ if(nbEmptyNodes == 1)
+ emptyNode = n1->next;
+
+ if(n1->getClosingNode() == n2 && n1->child && n1->child->tag->type == Tag::Empty)
+ emptyNode = n1->child;
+ }
+
+ //adding/deleting a empty node if necessary
+ parent = n1->parent;
+ if(!parent || getNodeDisplay(parent, true) == kafkaCommon::blockDisplay)
+ {
+ if(getNodeDisplay(n1, true) == kafkaCommon::blockDisplay &&
+ n1->tag->type != Tag::Text)
+ {
+ if(n2->tag->type == Tag::Text && emptyNode)
+ {
+ extractAndDeleteNode(emptyNode, modifs, false, false, false);
+ }
+ else if(n2->tag->type != Tag::Text && !emptyNode)
+ {
+ if(n1->getClosingNode() == n2)
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), n1, 0L, 0L, modifs);
+ }
+ else
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), parent, n2, n2, modifs);
+ }
+ }
+ }
+ else
+ {
+ if((n2->tag->type == Tag::Text ||
+ getNodeDisplay(n2, true) == kafkaCommon::inlineDisplay) &&
+ emptyNode)
+ {
+ extractAndDeleteNode(emptyNode, modifs, false, false, false);
+ }
+ else if(n2->tag->type != Tag::Text &&
+ getNodeDisplay(n2, true) == kafkaCommon::blockDisplay &&
+ n1->tag->type != Tag::Text && !emptyNode)
+ {
+ if(n1->getClosingNode() == n2)
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), n1, 0L, 0L, modifs);
+ }
+ else
+ {
+ createAndInsertNode("", "", Tag::Empty, n2->tag->write(), parent, n2, n2, modifs);
+ }
+ }
+ }
+ }
+ else
+ {
+ if(emptyNode)
+ extractAndDeleteNode(emptyNode, modifs, false, false, false);
+ }
+ }
+}
+
+void kafkaCommon::fitsNodesPosition(Node* startNode, int colMovement, int lineMovement, int colEnd, int lineEnd)
+{
+ bool b = false;
+ int j, SNbeginLine, SNbeginCol/**, SNlastLine, SNlastCol*/;
+ int beginLine, beginCol, lastLine, lastCol;
+ Node *node = startNode;
+
+ if(!node)
+ return;
+
+ node->tag->beginPos(SNbeginLine, SNbeginCol);
+ //node->tag->endPos(SNlastLine, SNlastCol);
+
+ while(node)
+ {
+ node->tag->beginPos(beginLine, beginCol);
+ node->tag->endPos(lastLine, lastCol);
+ if(beginLine >= lineEnd && beginCol >= colEnd &&
+ colEnd != -2 && lineEnd != -2)
+ return;
+ if(beginLine == SNbeginLine && lastLine == SNbeginLine)
+ node->tag->setTagPosition(beginLine + lineMovement,
+ beginCol + colMovement, lastLine + lineMovement,
+ lastCol + colMovement);
+ else if(beginLine == SNbeginLine)//&&lastLine != SNbeginLine
+ node->tag->setTagPosition(beginLine + lineMovement,
+ beginCol + colMovement, lastLine + lineMovement,
+ lastCol);
+ else
+ node->tag->setTagPosition(beginLine + lineMovement,
+ beginCol, lastLine + lineMovement, lastCol);
+ for(j = 0; j < node->tag->attrCount(); ++j)
+ {
+ if(node->tag->getAttribute(j).nameLine == SNbeginLine)
+ {
+ node->tag->getAttribute(j).nameLine += lineMovement;
+ node->tag->getAttribute(j).nameCol += colMovement;
+ node->tag->getAttribute(j).valueLine += lineMovement;
+ node->tag->getAttribute(j).valueCol += colMovement;
+ }
+ else
+ {
+ node->tag->getAttribute(j).nameLine += lineMovement;
+ node->tag->getAttribute(j).valueLine += lineMovement;
+ }
+ }
+ node = getNextNode(node, b);
+ }
+}
+
+int kafkaCommon::getNodeDisplay(Node *node, bool closingNodeToo)
+{
+ QString nodeName;
+
+ if(!node)
+ return kafkaCommon::errorDisplay;
+
+ if(node->tag->type == Tag::Text)
+ return kafkaCommon::inlineDisplay;
+ else if(node->tag->type == Tag::XmlTag || (node->tag->type == Tag::XmlTagEnd &&
+ closingNodeToo))
+ {
+ //If we areusing a non (X)HTML DTD, make everything blockDisplay by default
+ if(node->tag->dtd() && node->tag->dtd()->name.contains("HTML", false) == 0)
+ return kafkaCommon::blockDisplay;
+
+ nodeName = node->tag->name.lower();
+ if(closingNodeToo && nodeName.startsWith("/"))
+ nodeName = nodeName.mid(1);
+ if(nodeName == "html" || nodeName == "head" || nodeName == "meta" ||
+ nodeName == "link" || nodeName == "style" || nodeName == "option" ||
+ nodeName == "optgroup" || nodeName == "area" || nodeName == "param" ||
+ nodeName == "thead" || nodeName == "tbody" || nodeName == "dt" ||
+ nodeName == "tfoot" || nodeName == "col" || nodeName == "colgroup" ||
+ nodeName == "tr" || nodeName == "td" || nodeName == "th" || nodeName == "caption" ||
+ nodeName == "ins" || nodeName == "legend")
+ //Ok right, but this is only for indentation...
+ //return kafkaCommon::noneDisplay;
+ return kafkaCommon::blockDisplay;
+ else if(nodeName == "body" || nodeName == "p" || nodeName == "div" ||
+ nodeName == "blockquote" || nodeName == "isindex" ||
+ nodeName == "center" || nodeName == "hr" || nodeName == "h1" ||
+ nodeName == "h2" || nodeName == "h3" || nodeName == "h4" || nodeName == "h5" ||
+ nodeName == "h6" || nodeName == "table" ||
+ nodeName == "ul" || nodeName == "menu" || nodeName == "dir" || nodeName == "ol" ||
+ nodeName == "li" || nodeName == "ul" || nodeName == "dd" || nodeName == "dl" ||
+ nodeName == "form" || nodeName == "fieldset" ||
+ nodeName == "pre" || nodeName == "noscript" || nodeName == "noframes" ||
+ nodeName == "frameset" || nodeName == "frame" ||
+ nodeName == "address" || nodeName == "del" || nodeName == "br")
+ return kafkaCommon::blockDisplay;
+ else if(nodeName == "q" || nodeName == "u" || nodeName == "i" || nodeName == "b" ||
+ nodeName == "cite" || nodeName == "em" || nodeName == "var" || nodeName == "em" ||
+ nodeName == "tt" || nodeName == "code" || nodeName == "kbd" || nodeName == "samp" ||
+ nodeName == "big" || nodeName == "small" || nodeName == "s" || nodeName == "strike" ||
+ nodeName == "sub" || nodeName == "sup" || nodeName == "abbr" || nodeName == "title" ||
+ nodeName == "acronym" || nodeName == "a" || nodeName == "bdo" ||
+ nodeName == "font" || nodeName == "#text" || nodeName == "strong" || nodeName == "dfn" ||
+ nodeName == "img" || nodeName == "applet" || nodeName == "object" || nodeName == "basefont" || nodeName == "script" || nodeName == "map" || nodeName == "span" ||
+ nodeName == "iframe" || nodeName == "input" || nodeName == "select" || nodeName == "textarea" ||
+ nodeName == "label" || nodeName == "button" )
+ return kafkaCommon::inlineDisplay;
+ else
+ {
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::getNodeType() - ERROR " << nodeName <<
+ " not found" << endl;
+#endif
+
+ return kafkaCommon::noneDisplay;
+ }
+ }
+ return kafkaCommon::errorDisplay;
+}
+
+QString kafkaCommon::removeUnnecessaryWhitespaces(const QString &string,
+ bool removeAllSpacesAtTheLeft, bool removeAllSpacesAtTheRight)
+{
+ /**QString newString;
+ int i;
+
+ if(string.length() == 0)
+ return "";
+
+ newString = string[0];
+ for(i = 1; (unsigned)i < string.length(); ++i)
+ {
+ if(!string[i - 1].isSpace() || !string[i].isSpace())
+ newString += string[i];
+ }
+
+ if(removeAllSpacesAtTheLeft && newString.length() > 0 && newString[0].isSpace())
+ newString = newString.mid(1);
+ if(removeAllSpacesAtTheRight && newString.length() > 0 &&
+ newString[newString.length() - 1].isSpace())
+ newString = newString.mid(0, newString.length() - 1);
+
+ return newString;*/
+ QString newString;
+ bool hasLeftWhiteSpaces, hasRightWhiteSpaces;
+
+ if(string.length() == 0)
+ return QString();
+
+ hasLeftWhiteSpaces = (string[0].isSpace());
+ hasRightWhiteSpaces = (string[string.length() - 1].isSpace());
+
+ newString = string.stripWhiteSpace();
+ if(hasLeftWhiteSpaces && !removeAllSpacesAtTheLeft)
+ newString.insert(0, " ");
+ if(hasRightWhiteSpaces && !removeAllSpacesAtTheRight)
+ newString.insert(newString.length(), " ");
+
+ return newString;
+}
+
+Node* kafkaCommon::createNode(const QString &nodeName, const QString &tagString, int nodeType, Document *doc)
+{
+ Node *node;
+
+ //create the Node.
+ node = new Node(0L);
+
+ //Create the corresponding Tag.
+ node->tag = new Tag();
+ if(doc)
+ node->tag->setDtd(doc->defaultDTD());
+ else
+ node->tag->setDtd(0L);
+ node->tag->setWrite(doc);
+ node->tag->type = nodeType;
+ node->tag->name = QuantaCommon::tagCase(nodeName);
+ if(doc)
+ node->tag->single = QuantaCommon::isSingleTag(doc->defaultDTD()->name, nodeName);
+ else
+ node->tag->single = false;
+ node->tag->setStr(tagString);
+ node->tag->setCleanStrBuilt(false);
+ node->tag->setIndentationDone(false);
+ return node;
+}
+
+void kafkaCommon::restorePastedNode(Node* node, Document* doc)
+{
+ if(doc)
+ node->tag->setDtd(doc->defaultDTD());
+ else
+ node->tag->setDtd(0L);
+
+ node->tag->setWrite(doc);
+
+}
+
+Node *kafkaCommon::createDoctypeNode(Document *doc)
+{
+ Node *node, *child, *closingNode;
+
+ if(!doc)
+ return 0L;
+
+ //Build the script Tag
+ node = kafkaCommon::createNode("DTD block", "", Tag::ScriptTag, doc);
+ closingNode = kafkaCommon::createNode("", "", Tag::XmlTagEnd, doc);
+ node->next = closingNode;
+ closingNode->prev = node;
+
+ //Then build the Script tag which will be child of the above node.
+ child = kafkaCommon::createNode("#text", "DOCTYPE" + doc->defaultDTD()->doctypeStr, Tag::Text, doc);
+ child->tag->setCleanStrBuilt(true);
+ child->insideSpecial = true;
+ insertNode(child, node, 0L, 0L, false);
+
+ return node;
+}
+
+Node *kafkaCommon::createXmlDeclarationNode(Document *doc, const QString &encoding)
+{
+ Node *node, *child, *closingNode;
+ QString text;
+
+ if(!doc)
+ return 0L;
+
+ //build the script Tag
+ node = kafkaCommon::createNode("XML PI block" ,"", Tag::ScriptTag, doc);
+ closingNode = kafkaCommon::createNode("", "", Tag::XmlTagEnd, doc);
+ node->next = closingNode;
+ closingNode->prev = node;
+
+ //Then build the Text tag which will be child of the above node.
+ text = " encoding=\"" + encoding + "\" version=\"1.0\"";
+ child = kafkaCommon::createNode("#text", text, Tag::Text, doc);
+ child->tag->setCleanStrBuilt(true);
+ child->insideSpecial = true;
+ insertNode(child, node, 0L, 0L, false);
+
+ return node;
+}
+
+Node* kafkaCommon::createMandatoryNodeSubtree(Node *node, Document *doc)
+{
+ QTag *nodeQTag, *oldNodeQTag;
+ bool searchForMandatoryNode;
+ Node *currentParent;
+ QMap<QString, bool>::iterator it;
+
+ if(!node)
+ return 0L;
+
+ nodeQTag = QuantaCommon::tagFromDTD(node);
+ if(!nodeQTag)
+ return false;
+
+ searchForMandatoryNode = true;
+ currentParent = node;
+ while(searchForMandatoryNode)
+ {
+ oldNodeQTag = nodeQTag;
+ for(it = nodeQTag->childTags.begin(); it != nodeQTag->childTags.end(); ++it)
+ {
+ if(it.data())
+ {
+ nodeQTag = QuantaCommon::tagFromDTD(nodeQTag->parentDTD, it.key());
+ if(!nodeQTag)
+ return node;
+ currentParent = createAndInsertNode(nodeQTag->name(), "", Tag::XmlTag, doc,
+ currentParent, 0L, 0L, (NodeModifsSet*)0L);
+ break;
+ }
+ }
+ if(oldNodeQTag == nodeQTag)
+ searchForMandatoryNode = false;
+ }
+
+ return currentParent;
+}
+
+Node* kafkaCommon::insertNode(Node *node, Node* parentNode, Node* nextSibling,
+ NodeModifsSet *modifs, bool merge)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::insertNode()" << endl;
+#endif
+
+ NodeModif* modif;
+ Node *n, *closingNode;
+ bool nodeIsFirstChild = false, b;
+
+ if(!node)
+ return 0L;
+
+ //Reset the listviews items pointers for node and its children
+ n = node;
+ b = false;
+ while(n)
+ {
+ /**node->mainListItem = 0L;
+ node->listItems.clear();
+ node->groupElementLists.clear();*/
+ n = getNextNode(n, b);
+ }
+
+ //place the new Node.
+ if(parentNode)
+ n = parentNode->child;
+ else
+ n = baseNode;
+ while(n && n->next)
+ n = n->next;
+
+ if(!parentNode && (!baseNode || (nextSibling && !nextSibling->prev)))
+ {
+ nodeIsFirstChild = true;
+ baseNode = node;
+ parser->setRootNode(baseNode);
+ }
+ if(parentNode && (!parentNode->child || nextSibling == parentNode->child))
+ {
+ nodeIsFirstChild = true;
+ parentNode->child = node;
+ }
+ node->parent = parentNode;
+
+ if(nextSibling && nextSibling->prev)
+ {
+ nextSibling->prev->next = node;
+ node->prev = nextSibling->prev;
+ }
+ else if(n && !nodeIsFirstChild)
+ {
+ n->next = node;
+ node->prev = n;
+ }
+
+ if(nextSibling)
+ nextSibling->prev = node;
+ node->next = nextSibling;
+
+ //log this.
+ if(modifs)
+ {
+ modif = new NodeModif();
+ if(node->child)
+ modif->setType(NodeModif::NodeAndChildsAdded);
+ else
+ modif->setType(NodeModif::NodeAdded);
+ modif->setLocation(getLocation(node));
+ modifs->addNodeModif(modif);
+ }
+
+ //Then try to merge with the siblings
+ if(merge)
+ {
+ if(node->prev)
+ {
+ n = node->prev;
+ if(mergeNodes(node->prev, node, modifs))
+ node = n;
+ }
+ if(node->next)
+ {
+ mergeNodes(node, node->next, modifs);
+ }
+ }
+
+ //update the closesPrevious switch
+ closingNode = node->getClosingNode();
+ if(closingNode)
+ closingNode->closesPrevious = true;
+
+#ifdef HEAVY_DEBUG
+
+ coutTree(baseNode, 2);
+#endif
+
+ return node;
+}
+
+Node* kafkaCommon::insertNode(Node *node, Node* parentNode, Node* nextSibling, NodeSelection& cursorHolder,
+ NodeModifsSet *modifs, bool merge)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::insertNode()" << endl;
+#endif
+
+ NodeModif* modif;
+ Node *n, *closingNode;
+ bool nodeIsFirstChild = false, b;
+
+ if(!node)
+ return 0L;
+
+ //Reset the listviews items pointers for node and its children
+ n = node;
+ b = false;
+ while(n)
+ {
+ /**node->mainListItem = 0L;
+ node->listItems.clear();
+ node->groupElementLists.clear();*/
+ n = getNextNode(n, b);
+ }
+
+ //place the new Node.
+ if(parentNode)
+ n = parentNode->child;
+ else
+ n = baseNode;
+ while(n && n->next)
+ n = n->next;
+
+ if(!parentNode && (!baseNode || (nextSibling && !nextSibling->prev)))
+ {
+ nodeIsFirstChild = true;
+ baseNode = node;
+ parser->setRootNode(baseNode);
+ }
+ if(parentNode && (!parentNode->child || nextSibling == parentNode->child))
+ {
+ nodeIsFirstChild = true;
+ parentNode->child = node;
+ }
+ node->parent = parentNode;
+
+ if(nextSibling && nextSibling->prev)
+ {
+ nextSibling->prev->next = node;
+ node->prev = nextSibling->prev;
+ }
+ else if(n && !nodeIsFirstChild)
+ {
+ n->next = node;
+ node->prev = n;
+ }
+
+ if(nextSibling)
+ nextSibling->prev = node;
+ node->next = nextSibling;
+
+ //log this.
+ if(modifs)
+ {
+ modif = new NodeModif();
+ if(node->child)
+ modif->setType(NodeModif::NodeAndChildsAdded);
+ else
+ modif->setType(NodeModif::NodeAdded);
+ modif->setLocation(getLocation(node));
+ modifs->addNodeModif(modif);
+ }
+
+ //Then try to merge with the siblings
+ if(merge)
+ {
+ if(node->prev)
+ {
+ n = node->prev;
+ if(mergeNodes(node->prev, node, cursorHolder, modifs))
+ node = n;
+ }
+ if(node->next)
+ {
+ mergeNodes(node, node->next, cursorHolder, modifs);
+ }
+ }
+
+ //update the closesPrevious switch
+ closingNode = node->getClosingNode();
+ if(closingNode)
+ closingNode->closesPrevious = true;
+
+#ifdef HEAVY_DEBUG
+
+ coutTree(baseNode, 2);
+#endif
+
+ return node;
+}
+
+Node *kafkaCommon::insertNode(Node *newNode, Node *parent, Node *nextSibling, Node *nextEndSibling,
+ NodeModifsSet *modifs, bool merge)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::insertNode()1" << endl;
+#endif
+
+ Node *n, *nodeEnd = 0;
+
+ if(!newNode)
+ return 0L;
+
+ //place the new Node.
+ newNode = insertNode(newNode, parent, nextSibling, modifs, merge);
+
+ if(!newNode->tag->single && newNode->tag->type == Tag::XmlTag)
+ {
+ //create the new closing Node.
+ nodeEnd = createNode("/" + newNode->tag->name, "", Tag::XmlTagEnd, newNode->tag->write());
+ nodeEnd->closesPrevious = true;
+
+ //place the new closing Node.
+ nodeEnd = insertNode(nodeEnd, parent, nextEndSibling, modifs, merge);
+ }
+
+ //If nextSibling != nextEndSibling, move all Nodes between node and nodeEnd as child of node
+ if(nextSibling != nextEndSibling)
+ {
+ n = newNode->next;
+ while(newNode->next && newNode->next != nodeEnd)
+ moveNode(newNode->next, newNode, 0L, modifs);
+ }
+
+ return newNode;
+}
+
+Node* kafkaCommon::insertNode(Node *newNode, Node *parent, Node *startNodeToSurround,
+ Node *endNodeToSurround, int startOffset, int endOffset, NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::insertNode()2" << endl;
+#endif
+
+ if(!newNode || !startNodeToSurround || !endNodeToSurround)
+ return 0L;
+
+ //first split the Nodes.
+ if(splitNode(startNodeToSurround, startOffset, modifs))
+ {
+ if(endNodeToSurround == startNodeToSurround)
+ {
+ endNodeToSurround = endNodeToSurround->next;
+ endOffset -= startOffset;
+ }
+ startNodeToSurround = startNodeToSurround->next;
+ }
+ if(splitNode(endNodeToSurround, endOffset, modifs))
+ endNodeToSurround = endNodeToSurround->next;
+
+ //Then create and insert the new Node.
+ return insertNode(newNode, parent, startNodeToSurround,
+ endNodeToSurround, modifs);
+}
+
+Node* kafkaCommon::insertNodeSubtree(Node *node, Node* parentNode, Node* nextSibling,
+ NodeModifsSet *modifs, bool merge)
+{
+ Node *nextNode, *currentNode;
+
+ if(!node || (node && node->prev))
+ return 0L;
+
+ //insert the node subtree
+ currentNode = node;
+ while(currentNode)
+ {
+ nextNode = currentNode->next;
+ if(currentNode == node)
+ node = insertNode(currentNode, parentNode, nextSibling, nextSibling, modifs, merge);
+ else
+ insertNode(currentNode, parentNode, nextSibling, nextSibling, modifs, merge);
+
+ currentNode = nextNode;
+ }
+
+ return node;
+}
+
+Node* kafkaCommon::insertNodeSubtree(Node *node, Node* parentNode, Node* nextSibling,
+ Node* nextEndSibling, NodeModifsSet *modifs, bool merge)
+{
+ Node *nextNode, *currentNode, *currentParent;
+
+ if(!node || (node && node->prev))
+ return 0L;
+
+ //insert the node subtree.
+ currentNode = node;
+ currentParent = parentNode;
+ while(currentNode)
+ {
+ nextNode = currentNode->child;
+ currentNode->child = 0L;
+
+ //If the closing tag of currentNode is present, let's delete it
+ if(currentNode->next && QuantaCommon::closesTag(currentNode->tag, currentNode->next->tag))
+ delete extractNode(currentNode->next, 0L);
+
+ //insert the node and its closing tag if necessary.
+ if(currentNode == node)
+ {
+ currentParent = insertNode(currentNode, currentParent, nextSibling,
+ nextEndSibling, modifs, merge);
+ node = currentParent;
+ }
+ else
+ currentParent = insertNode(currentNode, currentParent, nextSibling,
+ 0L, modifs, merge);
+
+ currentNode = nextNode;
+ }
+
+ return node;
+}
+
+Node* kafkaCommon::DTDInsertNodeSubtree(Node *newNode, NodeSelectionInd& selection,
+ Node **cursorNode, long& cursorOffset, NodeModifsSet *modifs)
+{
+ Q_ASSERT(!selection.hasSelection());
+
+ Node* startNode = 0;
+ if(!(*cursorNode)) // see KafkaDocument::slotPaste()
+ startNode = getNodeFromLocation(selection.cursorNode());
+ else
+ startNode = *cursorNode;
+
+ if(!startNode)
+ {
+ kdError() << "NULL startNode in kafkaCommon::DTDInsertNodeSubtree given by NodeSelectionInd::cursorNode()" << endl;
+ return 0;
+ }
+
+ Node* endNode = 0;
+ if(!cursorNode)
+ return 0;
+ //int startOffset = selection.cursorOffset();
+ int startOffset = cursorOffset;
+
+ /**
+ * TODO : Optionnal for the moment : move the cursor coordinates so that we have good locations.
+ * e.g. <b>boo|</b>baa should be translated to <b>boo</b>|baa
+ */
+
+ if(cursorOffset == (signed)startNode->tag->tagStr().length())
+ {
+ while(startNode && startNode->tag->type != Tag::Text)
+ startNode = startNode->nextSibling();
+ if(!startNode)
+ {
+ insertNodeSubtree(newNode, baseNode->child, 0, modifs, true);
+ return newNode;
+ }
+ else
+ cursorOffset = 0;
+ }
+
+ // look for commonParent
+ QValueList<int> commonParentStartChildLocation;
+ QValueList<int> commonParentEndChildLocation;
+
+ Node* commonParent = DTDGetNonInlineCommonParent(startNode, startNode,
+ commonParentStartChildLocation, commonParentEndChildLocation, 0);
+
+ Node* commonParentStartChild = getNodeFromLocation(commonParentStartChildLocation);
+
+ //OK now, we are sure the node can be inserted. Start the work by splitting
+ //startNode if necessary
+ if(cursorOffset != 0)
+ {
+ if(startNode->tag->type == Tag::Text || startNode->tag->type == Tag::Empty)
+ {
+ if(splitNode(startNode, startOffset, modifs))
+ {
+ //</TEMPORARY>
+ if(startNode == commonParentStartChild)
+ commonParentStartChild = commonParentStartChild->nextSibling();
+ endNode = startNode->nextSibling();
+ }
+ else if(startOffset == (signed)startNode->tag->tagStr().length())
+ {
+ //No need to update endNode. If endNode == startNode && startOffset == endOffset,
+ //we'll catch this later.
+ if(startNode == commonParentStartChild)
+ commonParentStartChild = commonParentStartChild->nextSibling();
+ startNode = startNode->nextSibling();
+ }
+ }
+ }
+
+ if(newNode->tag->type == Tag::Text || newNode->tag->type == Tag::Empty)
+ {
+ *cursorNode = newNode;
+ cursorOffset = newNode->tag->tagStr().length();
+ return insertNodeSubtree(newNode, startNode->parent, endNode, modifs);
+ }
+
+ //Then we "split" the lastValidStartParent - startNode subtree into two : the first part is untouched
+ // and the second will be surrounded by the new Node. Same thing for endNode.
+ Node* node = startNode;
+ Node* parentNode = startNode->parent;
+ Node* newParentNode = 0, *child = 0, *next = 0;
+ while(parentNode && commonParent && parentNode != commonParent)
+ {
+ if(true/*node != parentNode->firstChild()*/)
+ {
+ //node is not the first Child of parentNode, we have to duplicate parentNode, and put node and
+ //all its next sibling as child of the new parentNode.
+ /**newParentNode = insertNode(parentNode->tag->name, parentNode->tag->tagStr(),
+ parentNode->tag->type, parentNode->tag->write(), parentNode->parentNode(),
+ parentNode, parentNode, modifs);*/
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ child = parentNode->firstChild();
+ if(cursorOffset != 0)
+ {
+ while(child && (child != endNode) && !child->hasForChild(endNode))
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ child = next;
+ }
+ }
+ else
+ {
+ while(child)
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs, true, true);
+ if(child == startNode || child->hasForChild(startNode))
+ break;
+
+ child = next;
+ }
+ }
+ }
+ //commonParentStartChild = parentNode;
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+
+ if(endNode)
+ {
+ node = endNode;
+ parentNode = endNode->parent;
+ while(parentNode && commonParent && parentNode != commonParent)
+ {
+ if(true/*node != parentNode->firstChild()*/)
+ {
+ //node is not the first Child of parentNode, we have to duplicate parentNode, and put node and
+ //all its next sibling as child of the new parentNode.
+ /**newParentNode = insertNode(parentNode->tag->name, parentNode->tag->tagStr(),
+ parentNode->tag->type, parentNode->tag->write(), parentNode->parentNode(),
+ parentNode, parentNode, modifs);*/
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ child = parentNode->firstChild();
+ while(child /*&& child == endNode*/ &&
+ (child == endNode || child->hasForChild(endNode)/* ||
+ (child->prev && child->prev->hasForChild(endNode) && child->closesPrevious)*/))
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs, true, true);
+ child = next;
+ }
+ }
+ commonParentStartChild = newParentNode;
+ node = parentNode;
+ Node* aux = parentNode;
+ parentNode = parentNode->parent;
+ // Remove node subtree if empty
+ if(!aux->hasChildNodes())
+ extractAndDeleteNode(aux, modifs);
+ }
+ }
+ if(newNode->next && QuantaCommon::closesTag(newNode->tag, newNode->next->tag))
+ delete extractNode(newNode->next, 0L);
+
+ Node* nextSibling = commonParentStartChild;
+ /*
+ if(cursorOffset == 0)
+ nextSibling = nextSibling->SNext();
+ */
+ return insertNodeSubtree(newNode, commonParent, nextSibling/*, nextSibling*/, modifs);
+
+ //mergeInlineNode(commonParent, commonParent->next, cursorNode, cursorOffset, modifs);
+ //return newNode;
+}
+
+Node* kafkaCommon::DTDInsertNodeSubtree(Node* newNode, Node* parentNode, Node* nextSibling,
+ NodeSelection& /*cursorHolder*/, NodeModifsSet *modifs)
+{
+ QTag* nodeQTag = QuantaCommon::tagFromDTD(parentNode);
+ if(!nodeQTag || !nodeQTag->isChild(newNode))
+ return 0;
+ else
+ return insertNodeSubtree(newNode, parentNode, nextSibling, modifs);
+}
+
+bool kafkaCommon::DTDinsertNode(Node *newNode, Node *startNode, int startOffset, Node *endNode,
+ int endOffset, Document *doc, Node **cursorNode, long &cursorOffset, NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::DTDinsertNode()" << endl;
+#endif
+
+ QValueList<int> startNodeLocation, endNodeLocation;
+ QValueList<int>::iterator itStart, itEnd;
+ Node *commonParent = 0L, *commonParentStartChild, *commonParentEndChild, *parentNode, *node;
+ Node *lastValidStartParent = 0L, *lastValidEndParent = 0L, *newParentNode, *child, *next;
+ Node *oldCommonParent, *lastNewNode, *oldParentNode;
+ QTag *parentNodeQTag = 0, *newNodeQTag, *lastNewNodeQTag;
+ NodeModif modif;
+ int locOffset = 1;
+ bool newNodeIsInline, isAfter;
+
+ if(!startNode || !endNode || !newNode || !doc)
+ {
+ Node::deleteNode(newNode);
+ return false;
+ }
+
+ //FIrst get the mandatory Nodes if necessary, and get the qTag of the first and last Node.
+ lastNewNode = createMandatoryNodeSubtree(newNode, doc);
+ lastNewNodeQTag = QuantaCommon::tagFromDTD(lastNewNode);
+ newNodeQTag = QuantaCommon::tagFromDTD(newNode);
+ if(!newNodeQTag || !lastNewNodeQTag)
+ {
+ Node::deleteNode(newNode);
+ return false;
+ }
+
+ //Then search for the common parent of startNode and endNode (commonParent)
+ //and for the childs of commonParent which are parent of startNode and endNode
+ //(commonParentStartChild && commonParentEndChild)
+ //CommonParent will be the limit (startNode -- commonNode) where Nodes can
+ //be splitted in order to insert the newNode.
+ startNodeLocation = getLocation(startNode);
+ endNodeLocation = getLocation(endNode);
+ itStart = startNodeLocation.begin();
+ itEnd = endNodeLocation.begin();
+ while(itStart != startNodeLocation.end() && itEnd != endNodeLocation.end() &&
+ (*itStart) == (*itEnd))
+ {
+ commonParent = getNodeFromSubLocation(startNodeLocation, locOffset);
+ itStart++;
+ itEnd++;
+ locOffset++;
+ }
+ //look for commonParentStartChild and commonParentEndChild
+ if(itStart != startNodeLocation.end())
+ commonParentStartChild = getNodeFromSubLocation(startNodeLocation, locOffset);
+ else
+ commonParentStartChild = commonParent;
+ if(itEnd != endNodeLocation.end())
+ commonParentEndChild = getNodeFromSubLocation(endNodeLocation, locOffset);
+ else
+ commonParentEndChild = commonParent;
+ //If newNode isn't inline, move commonParent to the closest non inline node
+ newNodeIsInline = isInline(newNode->tag->name);
+ if(!newNodeIsInline && commonParent && (isInline(commonParent->tag->name) ||
+ commonParent->tag->type == Tag::Text || commonParent->tag->type == Tag::Empty))
+ {
+ oldCommonParent = commonParent;
+ commonParent = commonParent->parent;
+ while(commonParent && isInline(commonParent->tag->name))
+ {
+ oldCommonParent = commonParent;
+ commonParent = commonParent->parent;
+ }
+ commonParentStartChild = oldCommonParent;
+ commonParentEndChild = oldCommonParent;
+ }
+ //startNode or endNode can't be the commonParent.
+ else if(commonParent && (itStart == startNodeLocation.end() || itEnd == endNodeLocation.end()))
+ commonParent = commonParent->parent;
+
+ //Now look if at least one of the parent Nodes between startNode and commonParent
+ //can have nodeName as child. If so for startNode and endNode, let's find the last
+ //parent Nodes which can have nodeName as child.
+ parentNode = startNode->parent;
+ oldParentNode = startNode;
+ while(parentNode && commonParent && parentNode != commonParent->parent)
+ {
+ parentNodeQTag = QuantaCommon::tagFromDTD(parentNode);
+ if(parentNodeQTag && parentNodeQTag->isChild(newNode) &&
+ lastNewNodeQTag->isChild(oldParentNode))
+ lastValidStartParent = parentNode;
+ else if(newNodeIsInline || !isInline(parentNode->tag->name))
+ break;
+ //else if(!newNodeIsInline && isInline(parentNode)), we continue : BLOCK element can
+ //cut some inline tag in order to be inserted.
+ oldParentNode = parentNode;
+ parentNode = parentNode->parent;
+ }
+ parentNode = endNode->parent;
+ oldParentNode = endNode;
+ while(parentNode && commonParent && parentNode != commonParent->parent)
+ {
+ parentNodeQTag = QuantaCommon::tagFromDTD(parentNode);
+ if(parentNodeQTag && parentNodeQTag->isChild(newNode) &&
+ lastNewNodeQTag->isChild(oldParentNode))
+ lastValidEndParent = parentNode;
+ else if(newNodeIsInline || !isInline(parentNode->tag->name))
+ break;
+ //else if(!newNodeIsInline && isInline(parentNode)), we continue : BLOCK element can
+ //cut some inline tag in order to be inserted.
+ oldParentNode = parentNode;
+ parentNode = parentNode->parent;
+ }
+
+ /**if(!lastValidEndParent || !lastValidStartParent)
+ {
+ Node::deleteNode(newNode);
+ return false;
+ }*/
+
+ //OK now, we are sure the node can be inserted. Start the work by splitting
+ //startNode and endNode if necessary
+ if(startNode->tag->type == Tag::Text || startNode->tag->type == Tag::Empty)
+ {
+ if(splitNode(startNode, startOffset, modifs))
+ {
+ //<TEMPORARY>
+ if(startNode == (*cursorNode) && cursorOffset > startOffset)
+ {
+ (*cursorNode) = (*cursorNode)->nextSibling();
+ cursorOffset -= startOffset;
+ }
+ //</TEMPORARY>
+ if(startNode == commonParentStartChild)
+ commonParentStartChild = commonParentStartChild->nextSibling();
+ if(startNode == endNode)
+ {
+ endNode = endNode->nextSibling();
+ endOffset -= startOffset;
+ }
+ startNode = startNode->nextSibling();
+ startOffset = 0;
+ }
+ else if(startOffset == (signed)startNode->tag->tagStr().length())
+ {
+ //No need to update endNode. If endNode == startNode && startOffset == endOffset,
+ //we'll catch this later.
+ if(startNode == commonParentStartChild)
+ commonParentStartChild = commonParentStartChild->nextSibling();
+ startNode = startNode->nextSibling();
+ }
+ }
+ if(endNode->tag->type == Tag::Text || endNode->tag->type == Tag::Empty)
+ {
+ if(!splitNode(endNode, endOffset, modifs) && endOffset == 0)
+ {
+ //No need to update startNode. If startNode == endNode && startOffset == endOffset,
+ //we'll catch this later.
+ if(endNode == commonParentEndChild)
+ commonParentEndChild = commonParentEndChild->previousSibling();
+ if (endNode->previousSibling())
+ endNode = endNode->previousSibling();
+ }
+ }
+
+ //Then we "split" the lastValidStartParent - startNode subtree into two : the first part is untouched
+ // and the second will be surrounded by the new Node. Same thing for endNode.
+ node = startNode;
+ if (!startNode) //Andras: it can happen.
+ return false;
+ parentNode = startNode->parent;
+ while(lastValidStartParent && parentNode && parentNode != lastValidStartParent)
+ {
+ if(node != parentNode->firstChild())
+ {
+ //node is not the first Child of parentNode, we have to duplicate parentNode, and put node and
+ //all its next sibling as child of the new parentNode.
+ /**newParentNode = insertNode(parentNode->tag->name, parentNode->tag->tagStr(),
+ parentNode->tag->type, parentNode->tag->write(), parentNode->parentNode(),
+ parentNode, parentNode, modifs);*/
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ child = parentNode->firstChild();
+ while(child && child != startNode && !child->hasForChild(startNode))
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ child = next;
+ }
+ }
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+ node = endNode;
+ parentNode = endNode->parent;
+ while(lastValidEndParent && parentNode && parentNode != lastValidEndParent)
+ {
+ if(node != parentNode->lastChild())
+ {
+ //node is not the last Child of parentNode, we have to duplicate parentNode, and put all
+ //the next sibling of node as child of the new parentNode
+ /**newParentNode = insertNode(parentNode->tag->name, parentNode->tag->tagStr(),
+ parentNode->tag->type, parentNode->tag->write(), parentNode->parentNode(),
+ parentNode, parentNode, modifs);*/
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ if(parentNode == commonParentStartChild)
+ commonParentStartChild = newParentNode;
+ if(parentNode == commonParentEndChild)
+ commonParentEndChild = newParentNode;
+ child = parentNode->firstChild();
+ while(child)
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ if(child == endNode || child->hasForChild(endNode))
+ {
+ if(QuantaCommon::closesTag(child->tag, next->tag))
+ moveNode(next, newParentNode, 0L, modifs);
+ break;
+ }
+ child = next;
+ }
+ }
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+
+ //Now if startNode is after endNode, this means that a selectionless insertion is being done.
+ //(This is due to the text splitting)
+ //Let's insert it and return
+ isAfter = (compareNodePosition(startNode, endNode) == kafkaCommon::isAfter);
+ if(isAfter || (startNode == endNode && startOffset == endOffset &&
+ (signed)startNode->tag->tagStr().length() == startOffset))
+ {
+ if(isAfter)
+ parentNodeQTag = QuantaCommon::tagFromDTD(commonParent);
+ else if((signed)startNode->tag->tagStr().length() == startOffset && startNode->tag->type == Tag::XmlTag)
+ parentNodeQTag = QuantaCommon::tagFromDTD(startNode);
+ else if((signed)startNode->tag->tagStr().length() == startOffset && startNode->tag->type == Tag::XmlTagEnd)
+ parentNodeQTag = QuantaCommon::tagFromDTD(startNode->parent);
+ if(!parentNodeQTag || (parentNodeQTag && parentNodeQTag->isChild(newNode)))
+ {
+ if(isAfter)
+ insertNodeSubtree(newNode, commonParent, commonParentStartChild, modifs);
+ else if((signed)startNode->tag->tagStr().length() == startOffset && startNode->tag->type == Tag::XmlTag)
+ insertNodeSubtree(newNode, startNode, 0L, modifs);
+ else if((signed)startNode->tag->tagStr().length() == startOffset && startNode->tag->type == Tag::XmlTagEnd)
+ insertNodeSubtree(newNode, startNode->parent, startNode->next, modifs);
+ //<TEMPORARY>
+ (*cursorNode) = lastNewNode;
+ cursorOffset = 0;
+ //</TEMPORARY>
+ return true;
+ }
+ else
+ {
+ Node::deleteNode(newNode);
+ return false;
+ }
+ }
+ else
+ {
+ //Else we apply the recursive function to add the new Node when necessary/possible.
+ bool addingStarted = false;
+ bool examinationStarted = false;
+ bool nodeInserted = false;
+ int level = 0;
+ addNodeRecursively(newNode, lastNewNode,
+ (compareNodePosition(lastValidStartParent, commonParentStartChild) ==
+ kafkaCommon::isAfter)?lastValidStartParent:commonParentStartChild,
+ (compareNodePosition(lastValidEndParent, commonParentEndChild) ==
+ kafkaCommon::isAfter)?lastValidEndParent:commonParentEndChild,
+ startNode, endNode, commonParentStartChild, examinationStarted,
+ addingStarted, nodeInserted, level, modifs);
+
+ //And we merge if necessary some identical inline Nodes.
+ mergeInlineNode(startNode, endNode, cursorNode, cursorOffset, modifs);
+ return nodeInserted;
+ }
+}
+
+bool kafkaCommon::DTDinsertRemoveNode(Node *newNode, Node *startNode, int startOffset,
+ Node *endNode, int endOffset, Document *doc, Node **cursorNode, long &cursorOffset,
+ NodeModifsSet *modifs)
+{
+ int result;
+
+ if(!newNode || !startNode || !endNode || !doc)
+ return false;
+
+ //First try to remove the Nodes. If unsuccessfull, try to insert it.
+ result = DTDExtractNode(newNode->tag->name, doc, startNode, startOffset, endNode, endOffset,
+ cursorNode, cursorOffset, modifs);
+ if(result == kafkaCommon::nothingExtracted || result == kafkaCommon::extractionBadParameters)
+ {
+ return DTDinsertNode(newNode, startNode, startOffset, endNode, endOffset, doc, cursorNode,
+ cursorOffset, modifs);
+ }
+ else
+ return true;
+ //else if result == kafkaCommon::extractionStoppedDueToBadNodes,
+ //what should we do?
+}
+
+Node *kafkaCommon::createAndInsertNode(const QString &nodeName, const QString &tagString,
+ int nodeType, Document *doc, Node* parent, Node* nextSibling, NodeModifsSet *modifs,
+ bool merge)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::createAndInsertNode() - nodeName :" << nodeName <<
+ " - tagStr :" << tagString << " - nodeType :" << nodeType << endl;
+#endif
+
+ Node *node;
+
+ //create the new Node.
+ node = createNode(nodeName, tagString, nodeType, doc);
+
+ //insert the new Node.
+ insertNode(node, parent, nextSibling, modifs, merge);
+
+ return node;
+}
+
+Node *kafkaCommon::createAndInsertNode(const QString &nodeName, const QString &tagString,
+ int nodeType, Document *doc, Node *parent, Node *nextSibling, Node *nextEndSibling,
+ NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::createAndInsertNode()2- nodeName :" << nodeName <<
+ " - tagStr :" << tagString << " - nodeType :" << nodeType << endl;
+#endif
+
+ Node *node;
+
+ //create the new Node.
+ node = createNode(nodeName, tagString, nodeType, doc);
+
+ //insert the new Node.
+ insertNode(node, parent, nextSibling, nextEndSibling, modifs);
+
+ return node;
+}
+
+Node *kafkaCommon::createAndInsertNode(const QString &nodeName, const QString &tagString,
+ int nodeType, Document *doc, Node *parent, Node *startNodeToSurround,
+ Node *endNodeToSurround, int startOffset, int endOffset, NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::createAndInsertNode()3 - nodeName :" << nodeName <<
+ " - tagStr :" << tagString << " - nodeType :" << nodeType << endl;
+#endif
+
+ Node *node;
+
+ if(!startNodeToSurround || !endNodeToSurround)
+ return 0L;
+
+ //create the new Node.
+ node = createNode(nodeName, tagString, nodeType, doc);
+
+ //insert the new Node.
+ insertNode(node, parent, startNodeToSurround, endNodeToSurround, startOffset, endOffset,
+ modifs);
+
+ return node;
+
+}
+
+bool kafkaCommon::DTDcreateAndInsertNode(const QString &nodeName, const QString &tagString,
+ int nodeType, Document *doc, Node *startNode, int startOffset, Node *endNode, int endOffset,
+ Node **cursorNode, long &cursorOffset, NodeModifsSet *modifs)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001)<< "kafkaCommon::DTDcreateAndInsertNode()2 - nodeName : " << nodeName <<
+ " - tagStr" <<tagString << endl;
+#endif
+
+ Node *node;
+
+ if(!startNode || !endNode)
+ return false;
+
+ //create the new Node.
+ node = createNode(nodeName, tagString, nodeType, doc);
+
+ //insert the new Node.
+ return DTDinsertNode(node, startNode, startOffset, endNode, endOffset, doc, cursorNode,
+ cursorOffset, modifs);
+
+}
+
+bool kafkaCommon::addNodeRecursively(Node *newNode, Node *leafNode,
+ Node *startExaminationNode, Node *endExaminationNode, Node* startNode, Node *endNode,
+ Node* currentNode, bool &examinationStarted, bool &addingStarted, bool &nodeInserted, int level,
+ NodeModifsSet *modifs)
+{
+
+ QTag *leafNodeQTag, *currentNodeParentQTag;
+ Node *startSelection = 0L, *endSelection = 0L, *oldCurrentNode, *copyNewNode;
+ bool selectionInProgress = false, validCurNodeParent = false;
+
+ leafNodeQTag = QuantaCommon::tagFromDTD(leafNode);
+ if(!leafNodeQTag)
+ return false;
+
+ if(currentNode && currentNode->parent)
+ {
+ currentNodeParentQTag = QuantaCommon::tagFromDTD(currentNode->parent);
+ if(currentNodeParentQTag && currentNodeParentQTag->isChild(newNode))
+ validCurNodeParent = true;
+ }
+
+ while(currentNode)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level << "] - currentNode :" <<
+ currentNode->tag->name << "("<< currentNode->tag->type << ")(" << currentNode << ")" << endl;
+#endif
+ //If currentNode is the startExaminationNode, let's start to examine Nodes (=> search the startNode)
+ if(currentNode == startExaminationNode)
+ examinationStarted = true;
+
+ //If currentNode is the startNode, let's start to try to add Nodes.
+ if(currentNode == startNode)
+ addingStarted = true;
+
+ //If the currentNode is text or XmlTag, and if it is DTD valid to insert the node Subtree and
+ //if the examination has started and currentNode doesn't have endExaminationNode as
+ //child, let's start/extend the selection over this node.
+ if((currentNode->tag->type == Tag::XmlTag || currentNode->tag->type == Tag::Text) &&
+ leafNodeQTag->isChild(currentNode) && validCurNodeParent && examinationStarted &&
+ !currentNode->hasForChild(endExaminationNode))
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - Valid Child : " << currentNode->tag->name << endl;
+#endif
+ //extend the selection to this node.
+ if(currentNode->tag->type == Tag::XmlTag && currentNode->getClosingNode())
+ endSelection = currentNode->getClosingNode();
+ else
+ endSelection = currentNode;
+
+ //If this Node is, or has for child startNode, let's start to add newNode
+ if(currentNode->hasForChild(startNode) || currentNode == startNode)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - This Node has the startNode as Child : " << currentNode->tag->name << endl;
+#endif
+
+ addingStarted = true;
+ }
+
+ //If there isn't a previously started selection, let's start it now.
+ if(!selectionInProgress && addingStarted)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - selection started at Node " << currentNode->tag->name << endl;
+#endif
+
+ selectionInProgress = true;
+ startSelection = currentNode;
+ }
+ }
+ else if(currentNode->tag->type == Tag::XmlTag || currentNode->tag->type == Tag::Text)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - Invalid Child : " << currentNode->tag->name << endl;
+#endif
+ //the current Node can't handle newNode as a child, let's stop the selection
+ // here and surround the current selection with newNode
+ endSelection = currentNode->prev;
+ if(selectionInProgress)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - selection ended(2) at Node " << currentNode->tag->name << endl;
+#endif
+
+ selectionInProgress = false;
+ if(addingStarted)
+ {
+ while(startSelection && startSelection->tag->type == Tag::Empty)
+ startSelection = startSelection->next;
+ while(endSelection && endSelection->tag->type == Tag::Empty)
+ endSelection = endSelection->prev;
+ if (startSelection && endSelection)
+ {
+ /**copyNewNode = duplicateNode(newNode);
+ insertNode(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);*/
+ copyNewNode = duplicateNodeSubtree(newNode);
+ insertNodeSubtree(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);
+ nodeInserted = true;
+ }
+ }
+ }
+
+ //TESTING: If this Node is, or has for child startNode, let's start to add newNode
+ /**if(currentNode->hasForChild(startNode) || currentNode == startNode)
+ {
+ #ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - This Node has the startNode as Child : " << currentNode->tag->name << endl;
+ #endif
+
+ addingStarted = true;
+ }*/
+
+ //Let's try to surround some of the childs of currentNode.
+ if(currentNode->child)
+ {
+ addNodeRecursively(newNode, leafNode, startExaminationNode,
+ endExaminationNode, startNode, endNode, currentNode->child,
+ examinationStarted, addingStarted, nodeInserted, level + 1, modifs);
+ }
+ }
+ //If the currentNode is XmlTagEnd, Empty or whatever but not XmlTag and Text,
+ // we will surround them with newNode if a selection was started.
+ else
+ {
+ if(selectionInProgress)
+ {
+ if((currentNode->tag->type == Tag::XmlTag || currentNode->tag->type == Tag::ScriptTag) &&
+ currentNode->getClosingNode())
+ endSelection = currentNode->getClosingNode();
+ else
+ endSelection = currentNode;
+ }
+ //If this Node is, or has for child startNode, let's start to add newNode
+ if((currentNode->hasForChild(startNode) || currentNode == startNode) &&
+ examinationStarted)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - This Node has the startNode as Child : " << currentNode->tag->name << endl;
+#endif
+
+ addingStarted = true;
+ }
+ }
+
+ //If the current Node is, or has for child endNode, or if currentNode is
+ //endExaminationNode or if examination is stopped, let's stop the current selection.
+ if(currentNode->hasForChild(endNode) || currentNode == endNode ||
+ currentNode == endExaminationNode)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - This Node has the endNode as Child : " << currentNode->tag->name << endl;
+#endif
+
+ addingStarted = false;
+ examinationStarted = false;
+ if(selectionInProgress)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - selection ended at Node " << currentNode->tag->name << endl;
+#endif
+
+ selectionInProgress = false;
+ while(startSelection && startSelection->tag->type == Tag::Empty)
+ startSelection = startSelection->next;
+ while(endSelection && endSelection->tag->type == Tag::Empty)
+ endSelection = endSelection->prev;
+ if (startSelection && endSelection)
+ {
+ /**copyNewNode = duplicateNode(newNode);
+ insertNode(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);*/
+ copyNewNode = duplicateNodeSubtree(newNode);
+ insertNodeSubtree(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);
+ nodeInserted = true;
+ }
+ }
+ }
+
+ oldCurrentNode = currentNode;
+ currentNode = currentNode->next;
+ }
+
+ if(selectionInProgress)
+ {
+#ifdef HEAVY_DEBUG
+ kdDebug(25001)<< "kafkaCommon::addNodeRevursively() [" << level <<
+ "] - selection ended(3) at Node " << oldCurrentNode->tag->name << endl;
+#endif
+
+ selectionInProgress = false;
+ endSelection = oldCurrentNode;
+ if(addingStarted)
+ {
+ while(startSelection && startSelection->tag->type == Tag::Empty)
+ startSelection = startSelection->next;
+ while(endSelection && endSelection->tag->type == Tag::Empty)
+ endSelection = endSelection->prev;
+ /**copyNewNode = duplicateNode(newNode);
+ insertNode(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);*/
+ copyNewNode = duplicateNodeSubtree(newNode);
+ insertNodeSubtree(copyNewNode, startSelection->parentNode(), startSelection,
+ endSelection->next, modifs);
+ nodeInserted = true;
+ }
+ }
+
+ //The newNode was a template, let's delete it now.
+ if(level == 0)
+ Node::deleteNode(newNode);
+ return true;
+}
+
+Node *kafkaCommon::duplicateNode(Node *node)
+{
+ Node *newNode;
+
+ if(!node)
+ return 0L;
+
+ newNode = new Node(0L);
+ (*newNode) = node;
+ newNode->tag->setCleanStrBuilt(false);
+ newNode->tag->setIndentationDone(false);
+
+ return newNode;
+}
+
+typedef struct boo
+{
+ boo()
+ {
+ m_n1 = m_n2 = 0L;
+ }
+ boo(Node *n1, Node *n2)
+ {
+ m_n1 = n1;
+ m_n2 = n2;
+ }
+ Node *m_n1;
+ Node *m_n2;
+}
+NodeLink;
+
+Node* kafkaCommon::getLastChild(Node* node)
+{
+ assert(node);
+
+ Node* end_node = node->getClosingNode();
+ if(!end_node && node->hasChildNodes())
+ end_node = node->lastChildNE();
+ else if(!end_node)
+ end_node = node;
+
+ assert(end_node);
+
+ return end_node;
+}
+
+Node *kafkaCommon::duplicateNodeSubtree(Node *node, bool childAndClosingTagOnly)
+{
+ QPtrList<NodeLink> nodeLinkList;
+ bool goUp = false;
+ Node *currentNode, *currentNewNode, *newRootNode = 0, *newNext, *newParent, *newPrev;
+ NodeLink *link;
+ Node* endNode = 0;
+ if(!node)
+ return 0L;
+
+ if(childAndClosingTagOnly)
+ endNode = getLastChild(node);
+
+
+ nodeLinkList.setAutoDelete(true);
+ currentNode = node;
+ while(currentNode)
+ {
+ currentNewNode = duplicateNode(currentNode);
+ nodeLinkList.append(new NodeLink(currentNode, currentNewNode));
+
+ newNext = 0L;
+ newParent = 0L;
+ newPrev = 0L;
+ for(link = nodeLinkList.first(); link; link = nodeLinkList.next())
+ {
+ if(link->m_n1 == currentNode->parent)
+ newParent = link->m_n2;
+ else if(link->m_n1 == currentNode->next)
+ newNext = link->m_n2;
+ else if(link->m_n1 == currentNode->prev)
+ newPrev = link->m_n2;
+ }
+
+ if(!newParent && !newPrev)
+ newRootNode = currentNewNode;
+ else if(!newParent)
+ {
+ //Temporary, insertNode would rely on baseNode which can be dangerous
+ currentNewNode->prev = newPrev;
+ newPrev->next = currentNewNode;
+ }
+ else
+ insertNode(currentNewNode, newParent, newNext, 0L, false);
+
+ if(childAndClosingTagOnly)
+ currentNode = getNextNode(currentNode, goUp, endNode);
+ else
+ currentNode = getNextNode(currentNode, goUp, node);
+ }
+
+ return newRootNode;
+}
+
+Node* kafkaCommon::extractNode(Node *node, NodeModifsSet *modifs, bool extractChildren,
+ bool extractClosingTag)
+{
+ NodeModif *modif = 0, *modifChild;
+ Node *lastChild, *curNode;
+ Node *parent, *next, *child, *n;
+ //Node *prev;
+ bool isSingle;
+ int type;
+ QString namespaceName, nodeName, caseSensitive;
+ QString closingNamespaceName, closingNodeName, closingCaseSensitive;
+ QValueList<int> location;
+
+ if(!node)
+ return 0L;
+
+ if(!node->child)
+ extractChildren = true;
+
+ parent = node->parent;
+ next = node->next;
+ //prev = node->prev; //Should this be used at all?
+ child = node->child;
+ lastChild = node->lastChild();
+ isSingle = node->tag->single;
+ type = node->tag->type;
+ namespaceName = node->tag->nameSpace;
+ nodeName = node->tag->name;
+ caseSensitive = node->tag->dtd()->caseSensitive;
+
+ //logging
+ if(modifs)
+ {
+ modif = new NodeModif();
+ if(extractChildren)
+ modif->setType(NodeModif::NodeAndChildsRemoved);
+ else
+ modif->setType(NodeModif::NodeRemoved);
+ modif->setLocation(getLocation(node));
+
+ //log the children move if we don't extract the children
+ if(!extractChildren)
+ {
+ location = getLocation(node);
+ location.last()++;
+ n = lastChild;
+ while(n)
+ {
+ modifChild = new NodeModif();
+ modifChild->setType(NodeModif::NodeAndChildsMoved);
+ modifChild->setLocation(getLocation(n));
+ modifChild->setFinalLocation(location);
+ modifs->addNodeModif(modifChild);
+ n = n->prev;
+ }
+ }
+ }
+
+ //starting to extract.
+ if(node == baseNode)
+ {
+ if(extractChildren)
+ baseNode = 0L;
+ else
+ baseNode = node->child;
+ parser->setRootNode(baseNode);
+ }
+ if(!extractChildren)
+ {
+ curNode = node->child;
+ while(curNode)
+ {
+ curNode->parent = node->parent;
+ curNode = curNode->next;
+ }
+ }
+ if(node->parent && node->parent->child == node)
+ {
+ if(extractChildren)
+ node->parent->child = node->next;
+ else
+ node->parent->child = node->child;
+ }
+ node->parent = 0L;
+ if(node->prev)
+ {
+ if(extractChildren)
+ node->prev->next = node->next;
+ else
+ {
+ node->prev->next = node->child;
+ node->child->prev = node->prev;
+ }
+ }
+ if(node->next)
+ {
+ if(extractChildren)
+ node->next->prev = node->prev;
+ else
+ {
+ /**lastChild = node->child;
+ while(lastChild->next)
+ lastChild = lastChild->next;*/
+ node->next->prev = lastChild;
+ lastChild->next = node->next;
+ }
+ }
+ node->prev = 0L;
+ node->next = 0L;
+ if(!extractChildren)
+ node->child = 0L;
+
+ if(modifs)
+ {
+ modif->setNode(0/*node*/); // this deletes the node!!???
+ modifs->addNodeModif(modif);
+ }
+
+ //extract the closing Tag
+ if(extractClosingTag && type == Tag::XmlTag && !isSingle && next)
+ {
+ while(next && next->tag->type == Tag::Empty)
+ next = next->next;
+ if(next)
+ {
+ closingNamespaceName = next->tag->nameSpace;
+ closingNodeName = next->tag->name;
+ closingCaseSensitive = next->tag->dtd()->caseSensitive;
+ if(QuantaCommon::closesTag(namespaceName, nodeName, caseSensitive,
+ closingNamespaceName, closingNodeName, closingCaseSensitive))
+ extractNode(next, modifs, false, false);
+ }
+ }
+
+#ifdef HEAVY_DEBUG
+ coutTree(baseNode, 2);
+#endif
+
+ return node;
+}
+
+Node* kafkaCommon::DTDExtractNodeSubtree(Node *startNode, int startOffset, Node *endNode, int endOffset,
+ Node **cursorNode, long &cursorOffset, NodeModifsSet *modifs, bool extractInlineParentNodes)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001) << "kafkaCommon::extractNodeSubtree()" << endl;
+#endif
+
+ if(!startNode || !endNode)
+ return 0;
+
+ QValueList<int> commonParentStartChildLocation;
+ QValueList<int> commonParentEndChildLocation;
+
+ Node* commonParent = 0;
+
+ NodeSelection cursorHolder;
+ cursorHolder.setCursorNode(*cursorNode);
+ cursorHolder.setCursorOffset(cursorOffset);
+
+ splitStartAndEndNodeSubtree(startNode, startOffset, endNode, endOffset, commonParent,
+ commonParentStartChildLocation, commonParentEndChildLocation,
+ cursorHolder, 0, modifs, extractInlineParentNodes);
+
+ *cursorNode = cursorHolder.cursorNode();
+ cursorOffset = cursorHolder.cursorOffset();
+ Node* commonParentStartChild = getNodeFromLocation(commonParentStartChildLocation);
+ Node* commonParentEndChild = getNodeFromLocation(commonParentEndChildLocation);
+
+ if(startNode == endNode)
+ {
+ Q_ASSERT(startNode->tag->type == Tag::Text || startNode->tag->type == Tag::Empty);
+
+ Node* prev = startNode->prev;
+ Node* next = startNode->next;
+
+ Node* aux = extractNode(startNode, modifs);
+
+ mergeInlineNode(prev, next, cursorNode, cursorOffset, modifs);
+
+ return aux;
+ }
+
+ // now let us extract the subtree
+
+ if(!commonParentEndChild)
+ commonParentEndChild = endNode;
+ extractNodeSubtreeAux(commonParentStartChild, commonParentEndChild, modifs);
+
+ // merge identical nodes
+ Node* commonParentEndChild_next = commonParentEndChild->SNext();
+ mergeInlineNode(commonParent, commonParentEndChild_next, cursorNode, cursorOffset, modifs);
+ mergeInlineNode(commonParentStartChild, commonParentEndChild, cursorNode, cursorOffset, modifs);
+
+#ifdef LIGHT_DEBUG
+ coutTree(commonParentStartChild, 3);
+#endif
+
+ return commonParentStartChild;
+}
+
+Node* kafkaCommon::DTDExtractNodeSubtree(Node *startNode, int startOffset, Node *endNode, int endOffset,
+ Node* nodeSubtree, NodeModifsSet* modifs, bool extractInlineParentNodes)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001) << "kafkaCommon::extractNodeSubtree()" << endl;
+#endif
+
+ if(!startNode || !endNode)
+ return 0;
+
+ QValueList<int> commonParentStartChildLocation;
+ QValueList<int> commonParentEndChildLocation;
+
+ Node* commonParent = 0;
+ if(extractInlineParentNodes)
+ {
+ commonParent = DTDGetNonInlineCommonParent(startNode, endNode,
+ commonParentStartChildLocation, commonParentEndChildLocation, nodeSubtree);
+ }
+ else
+ {
+ commonParent = DTDGetCommonParent(startNode, endNode,
+ commonParentStartChildLocation, commonParentEndChildLocation, nodeSubtree);
+ }
+ assert(commonParent == nodeSubtree);
+
+ NodeSelection selection;
+ splitStartAndEndNodeSubtree(startNode, startOffset, endNode, endOffset, commonParent,
+ commonParentStartChildLocation, commonParentEndChildLocation,
+ selection, nodeSubtree, modifs);
+
+ Node* cursorNode = selection.cursorNode();
+ long cursorOffset = selection.cursorOffset();
+ Node* commonParentStartChild = getNodeFromLocation(commonParentStartChildLocation, nodeSubtree);
+ Node* commonParentEndChild = getNodeFromLocation(commonParentEndChildLocation, nodeSubtree);
+
+ if(startNode == endNode)
+ {
+ Q_ASSERT(startNode->tag->type == Tag::Text || startNode->tag->type == Tag::Empty);
+ return extractNode(startNode, modifs);
+ }
+
+ // now let us extract the subtree
+ commonParentStartChild = getNodeFromLocation(commonParentStartChildLocation, commonParent);
+ commonParentEndChild = getNodeFromLocation(commonParentEndChildLocation, commonParent);
+
+ if(!commonParentEndChild)
+ commonParentEndChild = endNode;
+ extractNodeSubtreeAux(commonParentStartChild, commonParentEndChild, modifs);
+
+ //merge identical nodes
+/* Node* cursorNode = 0;
+ int cursorOffset = 0;*/
+ Node* commonParentEndChild_next = commonParentEndChild->SNext();
+ mergeInlineNode(commonParent, commonParentEndChild_next, &cursorNode, cursorOffset, modifs);
+ mergeInlineNode(commonParentStartChild, commonParentEndChild, &cursorNode, cursorOffset, modifs);
+
+#ifdef LIGHT_DEBUG
+ coutTree(commonParentStartChild, 3);
+#endif
+
+ return commonParentStartChild;
+}
+
+Node* kafkaCommon::extractNodeSubtreeAux(Node* commonParentStartChild, Node* commonParentEndChild, NodeModifsSet* modifs)
+{
+ Node* node = commonParentStartChild;
+ Node* prev_node = 0;
+ Node* next_node = 0;
+ Node* significant_next_node = 0;
+ Node* node_extracted = 0;
+
+ Node* commonParentEndChild_next = commonParentEndChild->SNext();
+
+ while(node && node != commonParentEndChild_next)
+ {
+ next_node = node->next;
+ significant_next_node = node->SNext();
+ node_extracted = extractNode(node, modifs, true, true);
+ if(node_extracted)
+ {
+ node_extracted->prev = prev_node;
+ if(significant_next_node != commonParentEndChild_next || (next_node && next_node->closesPrevious))
+ node_extracted->next = next_node;
+ if(next_node && next_node->closesPrevious)
+ {
+ next_node->prev = node_extracted;
+ node_extracted->_closingNode = next_node;
+ }
+ }
+ prev_node = node_extracted;
+ node = significant_next_node;
+ }
+
+ return commonParentStartChild;
+}
+
+Node* kafkaCommon::getNodeSubtree(Node *startNode, int startOffset, Node *endNode, int endOffset, bool extractInlineParentNodes)
+{
+#ifdef LIGHT_DEBUG
+ kdDebug(25001) << "kafkaCommon::getNodeSubtree()" << endl;
+#endif
+
+ if(!startNode || !endNode)
+ return 0;
+
+ QValueList<int> commonParentStartChildLocation;
+ QValueList<int> commonParentEndChildLocation;
+
+ Node* commonParent = 0;
+ if(extractInlineParentNodes)
+ commonParent = DTDGetNonInlineCommonParent(startNode, endNode,
+ commonParentStartChildLocation, commonParentEndChildLocation, 0);
+ else
+ commonParent = DTDGetCommonParent(startNode, endNode,
+ commonParentStartChildLocation, commonParentEndChildLocation, 0);
+
+ // get the subtree to operate
+ Node* newStartNode = 0;
+ Node* newEndNode = 0;
+
+ Node* newCommonParent = duplicateNodeSubtree(commonParent, true);
+
+ QValueList<int> const startNodeLocation = getLocation(startNode);
+ QValueList<int> const commonParentLocation = getLocation(commonParent);
+ uint const commonParentDepth = commonParentLocation.size();
+ uint const newStartNodeDepth = startNodeLocation.size() - commonParentDepth + 1;
+ uint const newEndNodeDepth = startNodeLocation.size() - commonParentDepth + 1;
+
+ QValueList<int> newStartNodeLocation, newEndNodeLocation;
+ newStartNodeLocation.push_back(1);
+ newEndNodeLocation.push_back(1);
+
+ for(uint i = 1; i != newStartNodeDepth; ++i)
+ newStartNodeLocation.push_back(startNodeLocation[i + commonParentDepth - 1]);
+
+ QValueList<int> const endNodeLocation = getLocation(endNode);
+ for(uint i = 1; i != newEndNodeDepth; ++i)
+ newEndNodeLocation.push_back(endNodeLocation[i + commonParentDepth - 1]);
+
+ newStartNode = getNodeFromLocation(newStartNodeLocation, newCommonParent);
+ newEndNode = getNodeFromLocation(newEndNodeLocation, newCommonParent);
+
+ return DTDExtractNodeSubtree(newStartNode, startOffset, newEndNode, endOffset, newCommonParent, 0);
+}
+
+Node* kafkaCommon::DTDRemoveSelection(NodeSelectionInd& selection,
+ Node **cursorNode, long& cursorOffset, NodeModifsSet *modifs, bool extractInlineParentNodes)
+{
+ Q_ASSERT(selection.hasSelection());
+
+ int startOffset = selection.cursorOffset();
+ int endOffset = selection.cursorOffsetEndSel();
+ Node* startNode = getNodeFromLocation(selection.cursorNode());
+ Node* endNode = getNodeFromLocation(selection.cursorNodeEndSel());
+
+ return DTDExtractNodeSubtree(startNode, startOffset, endNode, endOffset, cursorNode, cursorOffset, modifs, extractInlineParentNodes);
+}
+
+void kafkaCommon::extractAndDeleteNode(Node *node, NodeModifsSet *modifs, bool deleteChildren,
+ bool deleteClosingTag, bool mergeAndFormat)
+{
+ NodeModif modif;
+ Node *curNode, *nodePrev, *nodeNext, *nodeNext2, *n, *n2;
+ QString nodeName, closingNodeName, namespaceName, namespaceName2;
+ bool isSingle, caseSensitive, caseSensitive2;
+
+ if(!node)
+ return;
+
+ isSingle = node->tag->single;
+ nodeName = node->tag->name;
+ namespaceName = node->tag->nameSpace;
+ caseSensitive = node->tag->dtd()->caseSensitive;
+ nodePrev = node->prev;
+ nodeNext = node->next;
+ if(!node->child)
+ deleteChildren = true;
+ node = extractNode(node, modifs, deleteChildren);
+
+ //delete the closing Tag
+ if(!isSingle && deleteClosingTag && nodeNext)
+ {
+ curNode = nodeNext;
+ while(curNode && curNode->tag->type == Tag::Empty)
+ curNode = curNode->next;
+ if(curNode)
+ {
+ closingNodeName = curNode->tag->name;
+ namespaceName2 = curNode->tag->nameSpace;
+ caseSensitive2 = curNode->tag->dtd()->caseSensitive;
+ if(QuantaCommon::closesTag(namespaceName, nodeName, caseSensitive,
+ namespaceName2, closingNodeName, caseSensitive2))
+ {
+ curNode = nodeNext;
+ while(curNode)
+ {
+ nodeNext2 = curNode->next;
+ closingNodeName = curNode->tag->name;
+ namespaceName2 = curNode->tag->nameSpace;
+ caseSensitive2 = curNode->tag->dtd()->caseSensitive;
+ curNode = extractNode(curNode, modifs, deleteChildren);
+ curNode = nodeNext2;
+ if(QuantaCommon::closesTag(namespaceName, nodeName, caseSensitive,
+ namespaceName2, closingNodeName, caseSensitive2))
+ break;
+ }
+ nodeNext = curNode;
+ }
+ }
+ }
+
+ //merge the next and prev Nodes if they are both of type Text or Empty
+ if(mergeAndFormat && nodePrev)
+ {
+ n = nodePrev;
+ n2 = nodePrev->next;
+ while(n && n2 && n2->prev != nodeNext)
+ {
+ if(!mergeNodes(n, n2, modifs))
+ break;
+ n2 = n->next;
+ }
+ }
+}
+
+int kafkaCommon::DTDExtractNode(const QString &nodeName, Document *doc, Node *startNode,
+ int startOffset, Node *endNode, int endOffset, Node **cursorNode, long &cursorOffset,
+ NodeModifsSet *modifs)
+{
+ QTag *nodeNameQTag, *parentQTag;
+ Node *node, *lastNodeNameStartNode, *lastNodeNameEndNode;
+ Node *parentNode, *newParentNode, *child, *next;
+ bool goUp, nodesRemoved = false, DTDError = false, result;
+ bool startNodeSplitted = false, endNodeSplitted = false;
+
+ if(!doc || !startNode || !endNode)
+ return kafkaCommon::extractionBadParameters;
+
+ //First check that nodeName is really inline and that an area is selected.
+ nodeNameQTag = QuantaCommon::tagFromDTD(doc->defaultDTD(), nodeName);
+ if(!nodeNameQTag)
+ return kafkaCommon::extractionBadParameters;
+ if(!isInline(nodeName))
+ return kafkaCommon::extractionBadParameters;
+ if(startNode->tag->type == Tag::Text && startOffset == (signed)startNode->tag->tagStr().length())
+ {
+ startOffset = 0;
+ while(startNode && startNode->nextSibling())
+ {
+ startNode = startNode->nextSibling();
+ if(startNode == endNode || startNode->tag->type == Tag::Text)
+ break;
+ }
+ }
+ if(startNode == endNode && startOffset == endOffset)
+ return kafkaCommon::extractionBadParameters;
+
+ //Then, process startNode and endNode : look if a nodeName parent is one of
+ //startNode/endNode's inline parents and if it is the case, split the necessary Nodes.
+ //The comparaison is made in lowercase, even in xml : it could be strange, for an user, to have
+ //its nodes not removed because there are in the wrong case.
+ node = startNode;
+ lastNodeNameStartNode = 0L;
+ while(node && (isInline(node->tag->name) || node->tag->type == Tag::Text))
+ {
+ if(node->tag->name.lower() == nodeName.lower())
+ lastNodeNameStartNode = node;
+ node = node->parent;
+ }
+ node = endNode;
+ lastNodeNameEndNode = 0L;
+ while(node && (isInline(node->tag->name) || node->tag->type == Tag::Text))
+ {
+ if(node->tag->name.lower() == nodeName.lower())
+ lastNodeNameEndNode = node;
+ node = node->parent;
+ }
+
+ if(startNode->tag->type == Tag::Text)
+ {
+ if(splitNode(startNode, startOffset, modifs))
+ {
+ startNodeSplitted = true;
+ //<TEMPORARY>
+ if(startNode == (*cursorNode) && cursorOffset > startOffset)
+ {
+ (*cursorNode) = (*cursorNode)->nextSibling();
+ cursorOffset -= startOffset;
+ }
+ //</TEMPORARY>
+ if(startNode == endNode)
+ {
+ endNode = endNode->nextSibling();
+ endOffset -= startOffset;
+ }
+ startNode = startNode->nextSibling();
+ }
+ }
+ if(endNode->tag->type == Tag::Text)
+ {
+ result = splitNode(endNode, endOffset, modifs);
+ if(result)
+ endNodeSplitted = true;
+ else if(!result && endOffset == 0)
+ endNode = endNode->previousSibling();
+ }
+
+ if(lastNodeNameStartNode)
+ {
+ node = startNode;
+ parentNode = startNode->parent;
+ while(parentNode && parentNode != lastNodeNameStartNode->parent)
+ {
+ if(node != parentNode->firstChild())
+ {
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ child = parentNode->firstChild();
+ while(child && child != startNode && !child->hasForChild(startNode))
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ child = next;
+ }
+ }
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+ }
+ if(lastNodeNameEndNode)
+ {
+ node = endNode;
+ parentNode = endNode->parent;
+ while(parentNode && parentNode != lastNodeNameEndNode->parent)
+ {
+ if(node != parentNode->SLastChild())
+ {
+ newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ if(parentNode == lastNodeNameStartNode)
+ lastNodeNameStartNode = newParentNode;
+ child = parentNode->firstChild();
+ while(child)
+ {
+ next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ if(child == endNode || child->hasForChild(endNode))
+ {
+ if(QuantaCommon::closesTag(child->tag, next->tag))
+ moveNode(next, newParentNode, 0L, modifs);
+ break;
+ }
+ child = next;
+ }
+ }
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+ }
+
+ //Now delete the nodeName Nodes when possible from lastNodeNameStartParent to endNode.
+ node = lastNodeNameStartNode?lastNodeNameStartNode:startNode;
+ goUp = false;
+ while(node && !DTDError)
+ {
+ next = getNextNode(node, goUp);
+ if(node->tag->type == Tag::XmlTag && node->tag->name.lower() == nodeName.lower())
+ {
+ parentQTag = QuantaCommon::tagFromDTD(node->parent);
+ if(parentQTag)
+ {
+ child = node->firstChild();
+ while(child)
+ {
+ if(!parentQTag->isChild(child))
+ DTDError = true;
+ child = child->next;
+ }
+ if(!DTDError)
+ {
+ extractNode(node, modifs, false, true);
+ nodesRemoved = true;
+ }
+ }
+ }
+ if(node == endNode)
+ break;
+ node = next;
+ }
+
+ //TODO: merge the unnecessary splitted Nodes.
+ if(endNode && endNodeSplitted)
+ mergeNodes(endNode, endNode->nextSibling(), modifs, true);
+ if(startNode && startNodeSplitted)
+ {
+ node = startNode->previousSibling();
+ result = mergeNodes(startNode->previousSibling(), startNode, modifs, true);
+ startNode = node;
+ //<TEMPORARY>
+ if(result)
+ {
+ (*cursorNode) = node;
+ cursorOffset += startOffset;
+ }
+ //</TEMPORARY>
+ }
+
+ if(DTDError)
+ return kafkaCommon::extractionStoppedDueToBadNodes;
+ else if(!nodesRemoved)
+ return kafkaCommon::nothingExtracted;
+ else
+ {
+ //merge when necessary some text/identical inlines.
+ mergeInlineNode(startNode, endNode, cursorNode, cursorOffset, modifs);
+
+ return kafkaCommon::extractionDone;
+ }
+}
+
+
+void kafkaCommon::moveNode(Node *nodeToMove, Node *newParent, Node *newNextSibling,
+ NodeModifsSet *modifs, bool merge, bool moveClosingNode)
+{
+ NodeModif *modif = 0;
+ Node *newNode, *closingNode;
+ closingNode = nodeToMove->getClosingNode();
+
+ //DON'T log the removal and addition of the same Node!! When spliting the undoRedo stack
+ //it will delete the remove NodeModif and thus the Node inside which is the Node inserted.
+ if(modifs)
+ {
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeAndChildsMoved);
+ modif->setLocation(getLocation(nodeToMove));
+ }
+
+ //extract the old Node.
+ newNode = extractNode(nodeToMove, 0L, true);
+
+ //insert the new Node.
+ insertNode(newNode, newParent, newNextSibling, 0L, merge);
+ if(modifs)
+ modif->setFinalLocation(getLocation(newNode));
+
+ if(moveClosingNode && closingNode)
+ moveNode(closingNode, newParent, newNextSibling,
+ modifs, merge, false);
+
+ if(modifs)
+ modifs->addNodeModif(modif);
+}
+
+void kafkaCommon::moveNode(Node *nodeToMove, Node *newParent, Node *newNextSibling, NodeSelection& cursorHolder,
+ NodeModifsSet *modifs, bool merge, bool moveClosingNode)
+{
+ NodeModif *modif = 0;
+ Node *newNode, *closingNode;
+ closingNode = nodeToMove->getClosingNode();
+
+ //DON'T log the removal and addition of the same Node!! When spliting the undoRedo stack
+ //it will delete the remove NodeModif and thus the Node inside which is the Node inserted.
+ if(modifs)
+ {
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeAndChildsMoved);
+ modif->setLocation(getLocation(nodeToMove));
+ }
+
+ //extract the old Node.
+ newNode = extractNode(nodeToMove, 0L, true);
+ cursorHolder.setCursorNode(newNode);
+
+ //insert the new Node.
+ insertNode(newNode, newParent, newNextSibling, cursorHolder, 0L, merge);
+ if(modifs)
+ modif->setFinalLocation(getLocation(newNode));
+
+ if(moveClosingNode && closingNode)
+ moveNode(closingNode, newParent, newNextSibling,
+ modifs, merge, false);
+
+ if(modifs)
+ modifs->addNodeModif(modif);
+}
+
+bool kafkaCommon::splitNode(Node *n, int offset, NodeModifsSet *modifs)
+{
+ NodeModif *modif;
+ Tag *tag;
+ QString tagStr;
+ Node *node;
+
+ if(!n || (n->tag->type != Tag::Text && n->tag->type != Tag::Empty) || offset <= 0 || offset >=
+ (signed)n->tag->tagStr().length())
+ return false;
+
+ //logging
+ if(modifs)
+ {
+ tag = new Tag(*(n->tag));
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeModified);
+ modif->setTag(tag);
+ modif->setLocation(getLocation(n));
+ modifs->addNodeModif(modif);
+ }
+
+ tagStr = n->tag->tagStr();
+ n->tag->setStr(tagStr.left(offset));
+
+ if(n->tag->type == Tag::Text)
+ node = createAndInsertNode("#text", tagStr.right(tagStr.length() - offset), Tag::Text, n->tag->write(),
+ n->parent, n->next, modifs, false);
+ else
+ node = createAndInsertNode("", tagStr.right(tagStr.length() - offset), Tag::Empty, n->tag->write(),
+ n->parent, n->next, modifs, false);
+
+ //Node's string is a part of n's clean string
+ node->tag->setCleanStrBuilt(true);
+ node->tag->setIndentationDone(true);
+ return true;
+}
+
+void kafkaCommon::splitStartNodeSubtree(Node* startNode, Node* commonParent,
+ QValueList<int>& commonParentStartChildLocation, NodeModifsSet* modifs)
+{
+ //Then we "split" the lastValidStartParent - startNode subtree into two : the first part is untouched
+ // and the second will be surrounded by the new Node. Same thing for endNode.
+ Node* node = startNode;
+ Node* parentNode = startNode->parent;
+ Node* commonParentStartChild = 0;
+ while(parentNode && commonParent && parentNode != commonParent)
+ {
+ if(node != parentNode->firstChild())
+ {
+ Node* newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ Node* child = parentNode->firstChild();
+ while(child && child != startNode && !child->hasForChild(startNode))
+ {
+ Node* next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ child = next;
+ }
+ }
+ commonParentStartChild = parentNode;
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+
+ if(commonParentStartChild)
+ commonParentStartChildLocation = getLocation(commonParentStartChild);
+}
+
+void kafkaCommon::splitEndNodeSubtree(Node* endNode, Node* commonParent,
+ QValueList<int>& commonParentStartChildLocation,
+ QValueList<int>& commonParentEndChildLocation,
+ bool subTree, NodeModifsSet* modifs)
+{
+ Node* node = endNode;
+ Node* parentNode = endNode->parent;
+
+ Node* aux = 0;
+ if(subTree)
+ aux = commonParent;
+ else
+ aux = baseNode;
+ Node* commonParentStartChild = getNodeFromLocation(commonParentStartChildLocation, aux);
+ Node* commonParentEndChild = getNodeFromLocation(commonParentEndChildLocation, aux);
+ while(parentNode && commonParent && parentNode != commonParent)
+ {
+ if(node != parentNode->lastChild())
+ {
+ Node* newParentNode = duplicateNode(parentNode);
+ insertNode(newParentNode, parentNode->parentNode(), parentNode, parentNode, modifs);
+ if(parentNode == commonParentStartChild)
+ commonParentStartChild = newParentNode;
+ if(parentNode == commonParentEndChild)
+ commonParentEndChild = newParentNode;
+ Node* child = parentNode->firstChild();
+ while(child)
+ {
+ Node* next = child->next;
+ moveNode(child, newParentNode, 0L, modifs);
+ if(child == endNode || child->hasForChild(endNode))
+ {
+ if(QuantaCommon::closesTag(child->tag, next->tag))
+ moveNode(next, newParentNode, 0L, modifs);
+ break;
+ }
+ child = next;
+ }
+ }
+ node = parentNode;
+ parentNode = parentNode->parent;
+ }
+ commonParentStartChildLocation = getLocation(commonParentStartChild);
+ commonParentEndChildLocation = getLocation(commonParentEndChild);
+}
+
+void kafkaCommon::splitStartAndEndNodeSubtree(Node*& startNode, int startOffset, Node*& endNode, int endOffset, Node*& commonParent,
+ QValueList<int>& commonParentStartChildLocation,
+ QValueList<int>& commonParentEndChildLocation,
+ NodeSelection& cursorHolder,
+ Node* subTree, NodeModifsSet* modifs, bool extractInlineParentNodes)
+{
+ assert(startNode && endNode);
+ assert(startOffset >= 0);
+ assert(endOffset >= 0);
+
+ // get correct start and end nodes and offsets
+ startNode = getCorrectStartNode(startNode, startOffset);
+ endNode = getCorrectEndNode(endNode, endOffset);
+
+ // look for common parent
+ if(!commonParent)
+ {
+ if(extractInlineParentNodes)
+ // get the non inline common parent
+ commonParent =
+ DTDGetNonInlineCommonParent(startNode, endNode, commonParentStartChildLocation, commonParentEndChildLocation, subTree);
+ else
+ commonParent =
+ DTDGetCommonParent(startNode, endNode, commonParentStartChildLocation, commonParentEndChildLocation, subTree);
+ }
+ else
+ {
+ assert(commonParent->hasForChild(startNode));
+ assert(commonParent->hasForChild(endNode));
+ assert(!commonParentStartChildLocation.empty());
+ assert(!commonParentEndChildLocation.empty());
+ }
+
+ Node* commonParentStartChild = kafkaCommon::getNodeFromLocation(commonParentStartChildLocation, subTree);
+ Node* commonParentEndChild = kafkaCommon::getNodeFromLocation(commonParentEndChildLocation, subTree);
+
+ Node* cursorNode = cursorHolder.cursorNode();
+ int cursorOffset = cursorHolder.cursorOffset();
+
+ // split start and end node
+ if(splitNode(startNode, startOffset, modifs))
+ {
+ if(startNode == cursorNode && cursorOffset > startOffset)
+ {
+ cursorNode = cursorNode->nextSibling();
+ cursorOffset -= startOffset;
+ }
+ if(startNode == commonParentStartChild)
+ commonParentStartChild = commonParentStartChild->nextSibling();
+
+ if(startNode == endNode)
+ {
+ endNode = endNode->nextSibling();
+ endOffset -= startOffset;
+ }
+ startNode = startNode->nextSibling();
+ startOffset = 0;
+ }
+ splitNode(endNode, endOffset, modifs);
+
+ // split start and end nodes subtree in function of common parent
+ commonParentStartChildLocation = kafkaCommon::getLocation(commonParentStartChild);
+ splitStartNodeSubtree(startNode, commonParent, commonParentStartChildLocation, modifs);
+
+ commonParentEndChildLocation = kafkaCommon::getLocation(commonParentEndChild);
+ splitEndNodeSubtree(endNode, commonParent, commonParentStartChildLocation, commonParentEndChildLocation, subTree, modifs);
+
+ cursorHolder.setCursorNode(cursorNode);
+ cursorHolder.setCursorOffset(cursorOffset);
+}
+
+bool kafkaCommon::mergeNodes(Node *n, Node *n2, NodeModifsSet *modifs, bool mergeTextOnly)
+{
+ NodeModif *modif;
+ Tag *tag;
+ if(!n || !n2)
+ return false;
+
+ if(((n->tag->type == Tag::Empty && !mergeTextOnly) || n->tag->type == Tag::Text) &&
+ ((n2->tag->type == Tag::Empty && !mergeTextOnly) || n2->tag->type == Tag::Text))
+ {
+ tag = new Tag(*(n->tag));
+
+ //logging
+ if(modifs)
+ {
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeModified);
+ modif->setTag(tag);
+ modif->setLocation(getLocation(n));
+ modifs->addNodeModif(modif);
+ }
+
+ // have in consideration two spaces in a row
+ QString nStr(n->tag->tagStr());
+ QString n2Str(n2->tag->tagStr());
+ if(nStr[nStr.length() - 1] == ' ' && n2Str[0] == ' ')
+ {
+ nStr = nStr.left(nStr.length() - 1);
+ nStr.append("&nbsp;");
+ n->tag->setStr(nStr);
+
+ n2Str = n2Str.right(n2Str.length() - 1);
+ n2Str.prepend("&nbsp;");
+ n2->tag->setStr(n2Str);
+ }
+
+ if((n->tag->type == Tag::Text && n2->tag->type == Tag::Text) ||
+ (n->tag->type == Tag::Empty && n2->tag->type == Tag::Empty))
+ n->tag->setStr(n->tag->tagStr() + n2->tag->tagStr());
+ else if(n->tag->type == Tag::Empty && n2->tag->type == Tag::Text)
+ n->tag->setStr(n2->tag->tagStr());
+ //else n's string is already in n
+
+ if(n->tag->type == Tag::Text || n2->tag->type == Tag::Text)
+ n->tag->type = Tag::Text;
+ if(!n->tag->cleanStrBuilt() || !n2->tag->cleanStrBuilt())
+ n->tag->setCleanStrBuilt(false);
+ if(!n->tag->indentationDone() || !n2->tag->indentationDone())
+ n->tag->setIndentationDone(false);
+ kafkaCommon::extractAndDeleteNode(n2, modifs, false, false, false);
+
+ return true;
+ }
+ return false;
+}
+
+bool kafkaCommon::mergeNodes(Node *n, Node *n2, NodeSelection& cursorHolder, NodeModifsSet *modifs, bool mergeTextOnly)
+{
+ NodeModif *modif;
+ Tag *tag;
+ if(!n || !n2)
+ return false;
+
+ if(((n->tag->type == Tag::Empty && !mergeTextOnly) || n->tag->type == Tag::Text) &&
+ ((n2->tag->type == Tag::Empty && !mergeTextOnly) || n2->tag->type == Tag::Text))
+ {
+ tag = new Tag(*(n->tag));
+
+ //logging
+ if(modifs)
+ {
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeModified);
+ modif->setTag(tag);
+ modif->setLocation(getLocation(n));
+ modifs->addNodeModif(modif);
+ }
+
+ // have in consideration two spaces in a row
+ QString nStr(n->tag->tagStr());
+ QString n2Str(n2->tag->tagStr());
+ if(nStr[nStr.length() - 1] == ' ' && n2Str[0] == ' ')
+ {
+ nStr = nStr.left(nStr.length() - 1);
+ nStr.append("&nbsp;");
+ n->tag->setStr(nStr);
+
+ n2Str = n2Str.right(n2Str.length() - 1);
+ n2Str.prepend("&nbsp;");
+ n2->tag->setStr(n2Str);
+ }
+
+ if((n->tag->type == Tag::Text && n2->tag->type == Tag::Text) ||
+ (n->tag->type == Tag::Empty && n2->tag->type == Tag::Empty))
+ {
+ if(cursorHolder.cursorNode() == n2)
+ cursorHolder.setCursorOffset(n->tag->tagStr().length() + cursorHolder.cursorOffset() - 1);
+
+ n->tag->setStr(n->tag->tagStr() + n2->tag->tagStr());
+ }
+ else if(n->tag->type == Tag::Empty && n2->tag->type == Tag::Text)
+ n->tag->setStr(n2->tag->tagStr());
+ //else n's string is already in n
+
+ if(n->tag->type == Tag::Text || n2->tag->type == Tag::Text)
+ n->tag->type = Tag::Text;
+ if(!n->tag->cleanStrBuilt() || !n2->tag->cleanStrBuilt())
+ n->tag->setCleanStrBuilt(false);
+ if(!n->tag->indentationDone() || !n2->tag->indentationDone())
+ n->tag->setIndentationDone(false);
+ kafkaCommon::extractAndDeleteNode(n2, modifs, false, false, false);
+
+ cursorHolder.setCursorNode(n);
+
+ return true;
+ }
+ return false;
+}
+
+void kafkaCommon::mergeInlineNode(Node *startNode, Node *endNode, Node **cursorNode,
+ long &cursorOffset, NodeModifsSet *modifs)
+{
+ Node *startNodeLastInlineParent, *parent, *node, *next;
+ bool goUp, success, isCursorNode, isEndNode;
+ int nodeLength;
+
+ if(!startNode || !endNode)
+ return;
+
+ //first search for the last inline parent of startNode, and then its last prev neighbour
+ // which is also inline : the merge will start from this Node.
+ startNodeLastInlineParent = startNode;
+ parent = startNode->parent;
+ while(parent && isInline(parent->tag->name))
+ {
+ startNodeLastInlineParent = parent;
+ parent = parent->parent;
+ }
+ if(startNodeLastInlineParent->prev)
+ {
+ if(startNodeLastInlineParent->prev->tag->type == Tag::Text)
+ startNodeLastInlineParent = startNodeLastInlineParent->prev;
+ else
+ {
+ node = startNodeLastInlineParent->prev;
+ while(node && (node->tag->type == Tag::Empty || node->tag->type == Tag::XmlTagEnd))
+ node = node->prev;
+ if(node && node->tag->type == Tag::XmlTag && isInline(node->tag->name))
+ startNodeLastInlineParent = node;
+ }
+ }
+
+
+ //Then navigate though the tree and merge.
+ node = startNodeLastInlineParent;
+ goUp = false;
+ while(node)
+ {
+ if(node->tag->type == Tag::XmlTag && isInline(node->tag->name))
+ {
+ next = node->next;
+ while(next && (next->tag->type == Tag::XmlTagEnd || next->tag->type == Tag::Empty))
+ next = next->next;
+ while(next && next != node && compareNodes(node, next))
+ {
+ while(next->firstChild())
+ moveNode(next->firstChild(), node, 0L, modifs, false);
+ if(next == endNode)
+ endNode = node;
+ else if((*cursorNode) == node->next)
+ {
+ //<TEMPORARY>
+ (*cursorNode) = node;
+ //</TEMPORARY>
+ }
+ extractNode(next, modifs, false, true);
+ next = node->next;
+ while(next && (next->tag->type == Tag::XmlTagEnd || next->tag->type == Tag::Empty))
+ next = next->next;
+ }
+ }
+ else if(node->tag->type == Tag::Text)
+ {
+ while(node->next && (node->next->tag->type == Tag::Text ||
+ node->next->tag->type == Tag::Empty))
+ {
+ nodeLength = (signed)node->tag->tagStr().length();
+ isCursorNode = ((*cursorNode) == node->next);
+ isEndNode = (endNode == node->next);
+ success = mergeNodes(node, node->next, modifs);
+ if(isCursorNode && success)
+ {
+ //<TEMPORARY>
+ (*cursorNode) = node;
+ cursorOffset += nodeLength;
+ //</TEMPORARY>
+ }
+ else if(isEndNode && success)
+ endNode = node;
+ }
+ }
+ if(node == endNode)
+ break;
+ node = getNextNode(node, goUp);
+ }
+}
+
+void kafkaCommon::getEndPosition(const QString &tagString, int bLine, int bCol, int &eLine, int &eCol)
+{
+ /**int result, oldResult;
+
+ result = tagString.find("\n", 0);
+ if(result == -1)
+ {
+ eLine = bLine;
+ eCol = bCol + tagString.length() - 1;
+ }
+ else
+ {
+ eLine = bLine;
+ while(result != -1)
+ {
+ eLine++;
+ oldResult = result;
+ result = tagString.find("\n", result + 1);
+ }
+ eCol = tagString.length() - oldResult - 2;
+ }*/
+ int i;
+
+ eLine = bLine;
+ eCol = bCol - 1;
+ for(i = 0; i < (signed)tagString.length(); ++i)
+ {
+ if(tagString[i] == "\n")
+ {
+ eLine++;
+ eCol = -1;
+ }
+ else
+ eCol++;
+ }
+}
+
+void kafkaCommon::getEndPosition(Node *node, int bLine, int bCol, int &eLine, int &eCol)
+{
+ if(!node)
+ {
+ eLine = 0;
+ eCol = 0;
+ return;
+ }
+
+ getEndPosition(node->tag->tagStr(), bLine, bCol, eLine, eCol);
+}
+
+void kafkaCommon::setTagString(Node *node, const QString &newTagString, NodeModifsSet* modifs)
+{
+ int eLine, eCol, bLine, bCol;
+ Tag *tag;
+ NodeModif* modif;
+
+ if(!node)
+ return;
+
+ //logging
+ if(modifs)
+ {
+ tag = new Tag(*(node->tag));
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeModified);
+ modif->setTag(tag);
+ modif->setLocation(getLocation(node));
+ modifs->addNodeModif(modif);
+ }
+
+ node->tag->beginPos(bLine, bCol);
+ node->tag->setStr(newTagString);
+ getEndPosition(node, bLine, bCol, eLine, eCol);
+ node->tag->setTagPosition(bLine, bCol, eLine, eCol);
+}
+
+void kafkaCommon::setTagStringAndFitsNodes(Node *node, const QString &newTagString, NodeModifsSet* modifs)
+{
+ int eLine, eCol, oldELine, oldECol;
+ bool b = false;
+
+ if(!node)
+ return;
+
+ node->tag->endPos(oldELine, oldECol);
+ setTagString(node, newTagString, modifs);
+ node->tag->endPos(eLine, eCol);
+
+ fitsNodesPosition(getNextNode(node, b), eCol - oldECol, eLine - oldELine);
+}
+
+void kafkaCommon::editNodeAttribute(Node* node, const QString& name, const QString& value, NodeModifsSet* modifs)
+{
+ NodeModif *modif = 0;
+
+ if(!node)
+ return;
+
+ if(modifs)
+ {
+ modif = new NodeModif();
+ modif->setType(NodeModif::NodeModified);
+ modif->setTag(new Tag(*(node->tag)));
+ modif->setLocation(getLocation(node));
+ }
+
+ if(node->tag->editAttribute(name, value))
+ {
+ node->tag->setCleanStrBuilt(false);
+ if(modifs)
+ modifs->addNodeModif(modif);
+ }
+}
+
+QValueList<int> kafkaCommon::getLocation(Node * node)
+{
+ QValueList<int> loc;
+ int i = 0;
+
+ while(node)
+ {
+ i = 1;
+ while(node->prev)
+ {
+ ++i;
+ node = node->prev;
+ }
+ loc.prepend(i);
+ node = node->parent;
+ }
+ return loc;
+}
+
+QValueList<int> kafkaCommon::getLocation(DOM::Node domNode)
+{
+ QValueList<int> loc;
+ int i = 0;
+
+ while(!domNode.isNull())
+ {
+ i = 1;
+ while(!domNode.previousSibling().isNull())
+ {
+ ++i;
+ domNode = domNode.previousSibling();
+ }
+ loc.prepend(i);
+ domNode = domNode.parentNode();
+ }
+ return loc;
+}
+
+Node* kafkaCommon::getNodeFromLocation(QValueList<int> loc)
+{
+ QValueList<int>::iterator it;
+ Node *node = baseNode;
+ Node *m = 0L;
+ int i;
+
+ if(!node)
+ return 0L;
+ for(it = loc.begin(); it != loc.end(); ++it)
+ {
+ if(!node)
+ return 0L;
+ for(i = 1; i < (*it); ++i)
+ {
+ if(!node->next)
+ return 0L;
+ node = node->next;
+ }
+ m = node;
+ node = node->child;
+ }
+ return m;
+}
+
+Node* kafkaCommon::getNodeFromLocation(QValueList<int> loc, Node* nodeTree)
+{
+ QValueList<int>::iterator it;
+ Node *node = nodeTree;
+ if(!node)
+ node = baseNode;
+ Node *m = 0L;
+ int i;
+
+ if(!node)
+ return 0L;
+ for(it = loc.begin(); it != loc.end(); ++it)
+ {
+ if(!node)
+ return 0L;
+ for(i = 1; i < (*it); ++i)
+ {
+ if(!node->next)
+ return 0L;
+ node = node->next;
+ }
+ m = node;
+ node = node->child;
+ }
+ return m;
+}
+
+DOM::Node kafkaCommon::getNodeFromLocation(QValueList<int> loc, DOM::Node rootNode)
+{
+ QValueList<int>::iterator it;
+ DOM::Node node = rootNode;
+ DOM::Node m = rootNode;
+ int i;
+
+ if(rootNode.isNull())
+ return DOM::Node();
+
+ for(it = loc.begin(); it != loc.end(); ++it)
+ {
+ if(node.isNull())
+ return DOM::Node();
+ for(i = 1; i < (*it); ++i)
+ {
+ if(node.nextSibling().isNull())
+ return DOM::Node();
+ node = node.nextSibling();
+ }
+ m = node;
+ node = node.firstChild();
+ }
+ return m;
+}
+
+Node* kafkaCommon::getNodeFromSubLocation(QValueList<int> loc, int locOffset)
+{
+ QValueList<int>::iterator it = loc.begin();
+ QValueList<int> list;
+ int i;
+
+ for(i = 0; i < locOffset; ++i)
+ {
+ list.append((*it));
+ ++it;
+ }
+
+ return getNodeFromLocation(list);
+}
+
+Node* kafkaCommon::getNodeFromSubLocation(QValueList<int> loc, int locOffset, Node* nodeTree)
+{
+ QValueList<int>::iterator it = loc.begin();
+ QValueList<int> list;
+ int i;
+
+ for(i = 0; i != locOffset; ++i)
+ {
+ list.append((*it));
+ ++it;
+ }
+
+ return getNodeFromLocation(list, nodeTree);
+}
+
+int kafkaCommon::compareNodePosition(QValueList<int> pos1, QValueList<int> pos2)
+{
+ QValueList<int>::iterator it1, it2;
+
+ it1 = pos1.begin();
+ it2 = pos2.begin();
+ while(it1 != pos1.end() && it2 != pos2.end() && (*it1) == (*it2))
+ {
+ it1++;
+ it2++;
+ }
+
+ if(it1 == pos1.end() && it2 == pos2.end())
+ return kafkaCommon::isAtTheSamePosition;
+ else if(it1 == pos1.end())
+ return kafkaCommon::isBefore;
+ else if(it2 == pos2.end() || (*it1) > (*it2))
+ return kafkaCommon::isAfter;
+ else if((*it1) < (*it2))
+ return kafkaCommon::isBefore;
+ else
+ return kafkaCommon::positionError;
+}
+
+int kafkaCommon::compareNodePosition(Node *n1, Node *n2)
+{
+ QValueList<int> pos1, pos2;
+
+ if(!n1 || !n2)
+ return kafkaCommon::positionError;
+
+ pos1 = getLocation(n1);
+ pos2 = getLocation(n2);
+
+ return compareNodePosition(pos1, pos2);
+}
+
+bool kafkaCommon::compareNodes(Node *n1, Node *n2)
+{
+ int i, j;
+
+ if(!n1 || !n2)
+ return false;
+
+ if(n1->tag->type != n2->tag->type)
+ return false;
+
+ if(n1->tag->type == Tag::XmlTag)
+ {
+ if(n1->tag->name.lower() != n2->tag->name.lower())
+ return false;
+
+ if(n1->tag->attrCount() != n2->tag->attrCount())
+ return false;
+
+ for(i = 0; i < n1->tag->attrCount(); ++i)
+ {
+ for(j = 0; j < n2->tag->attrCount(); ++j)
+ {
+ if(n1->tag->getAttribute(i).name.lower() == n2->tag->getAttribute(j).name.lower() &&
+ n1->tag->getAttribute(i).value.lower() == n2->tag->getAttribute(j).value.lower())
+ break;
+ }
+ if(j == n2->tag->attrCount())
+ return false;
+ }
+ }
+ else if(n1->tag->type == Tag::Text)
+ {
+ //TODO
+ }
+
+ return true;
+}
+
+int kafkaCommon::nodeDepth(Node *node)
+{
+ int depth = 0;
+
+ if(!node)
+ return -1;
+
+ node = node->parent;
+ while(node)
+ {
+ depth++;
+ node = node->parent;
+ }
+
+ return depth;
+}
+
+Node* kafkaCommon::hasParent(Node *node, const QString &name)
+{
+ node = node->parent;
+ while(node)
+ {
+ if(node->tag->name.lower() == name.lower())
+ return node;
+ node = node->parent;
+ }
+
+ return 0L;
+}
+
+Node* kafkaCommon::hasParent(Node* startNode, Node* endNode, const QString &name)
+{
+ Q_ASSERT(startNode && endNode);
+ //Andras: don't crash
+ if (!startNode || !endNode)
+ return 0;
+
+ QValueList<int> commonParentStartChildLocation;
+ QValueList<int> commonParentEndChildLocation;
+
+ Node* node = DTDGetCommonParent(startNode, endNode, commonParentStartChildLocation, commonParentEndChildLocation, 0);
+
+ while(node)
+ {
+ if(node->tag->name.lower() == name.lower())
+ return node;
+ node = node->parent;
+ }
+
+ return 0;
+}
+
+bool kafkaCommon::insertDomNode(DOM::Node node, DOM::Node parent, DOM::Node nextSibling,
+ DOM::Node rootNode)
+{
+ if(node.isNull())
+ return false;
+
+ if(parent.isNull())
+ {
+ if(rootNode.isNull())
+ return false;
+ parent = rootNode;
+ }
+ //Andras: avoid exceptions
+ if (!nextSibling.isNull() && nextSibling.parentNode() != parent)
+ {
+ kdDebug(25001)<< "kafkaCommon::insertDomNode() - invalid nextSibling!" << endl;
+ return false;
+ }
+ if (node.ownerDocument() != parent.ownerDocument())
+ {
+ kdDebug(25001)<< "kafkaCommon::insertDomNode() - ownerDocument is different!" << endl;
+ return false;
+ }
+
+ try
+ {
+ parent.insertBefore(node, nextSibling);
+ }
+ catch(DOM::DOMException e)
+ {
+ kdDebug(25001)<< "kafkaCommon::insertDomNode() - ERROR code :" << e.code << endl;
+ }
+ return true;
+}
+
+bool kafkaCommon::removeDomNode(DOM::Node node)
+{
+ DOM::Node parent = node.parentNode();
+
+ if(parent.isNull())
+ return false;
+
+ parent.removeChild(node);
+
+ return true;
+}
+
+DOM::Node kafkaCommon::createDomNode(const QString &nodeName, const DTDStruct* dtd,
+ DOM::Document rootNode)
+{
+ // FIXME
+ //this will change with the futur multi-DTDs support
+ //It does not use exceptions handling, so everything is checked via the DTEP definitions.
+ DOM::Node dn;
+ QTag *qTag = 0L;
+
+ qTag = QuantaCommon::tagFromDTD(dtd, nodeName);
+
+ if(qTag)
+ dn = rootNode.createElement(nodeName);
+#ifdef HEAVY_DEBUG
+
+ else
+ kdDebug(25001)<< "kafkaCommon::createDomNode() - ERROR bad nodeName :" <<
+ nodeName << endl;
+#endif
+
+ return dn;
+}
+
+DOM::Node kafkaCommon::createDomNode(Node *node, DOM::Document rootNode)
+{
+ if(!node)
+ return DOM::Node();
+
+ return createDomNode(node->tag->name, node->tag->dtd(), rootNode);
+}
+
+DOM::Node kafkaCommon::createTextDomNode(const QString &textString, DOM::Document rootNode)
+{
+ return rootNode.createTextNode(textString);
+}
+
+DOM::Node kafkaCommon::createDomNodeAttribute(const QString &nodeName, const DTDStruct* dtd,
+ const QString &attrName, const QString &attrValue, DOM::Document rootNode)
+{
+ DOM::Node attr;
+ QTag *qTag = 0L;
+
+ qTag = QuantaCommon::tagFromDTD(dtd, nodeName);
+ if(!qTag)
+ return DOM::Node();
+
+ if(qTag->isAttribute(attrName))
+ {
+ attr = rootNode.createAttribute(attrName);
+ attr.setNodeValue(attrValue);
+ }
+#ifdef HEAVY_DEBUG
+ else
+ kdDebug(25001)<< "kafkaCommon::createDomNodeAttribute() - ERROR bad attrName " <<
+ attrName << endl;
+#endif
+
+ return attr;
+}
+
+DOM::Node kafkaCommon::createDomNodeAttribute(Node* node, const QString &attrName,
+ DOM::Document rootNode)
+{
+ if(!node)
+ return DOM::Node();
+
+ return createDomNodeAttribute(node->tag->name, node->tag->dtd(), attrName, "", rootNode);
+}
+
+//DOM::node kafkaCommon::createDomNodeAttribute(DOM::Node node, const QString &attrName,
+// DOM::Document rootNode)
+//{
+/**if(node.isNull())
+ return DOM::node();
+
+return createDomNodeAttribute()*/
+//}
+
+bool kafkaCommon::insertDomNodeAttribute(DOM::Node node, DOM::Node attr)
+{
+ if(node.isNull())
+ return false;
+
+ //should we check if the attr is valid???
+ node.attributes().setNamedItem(attr);
+
+ return true;
+}
+
+bool kafkaCommon::editDomNodeAttribute(DOM::Node node, const QString &nodeName, const DTDStruct* dtd,
+ const QString &attrName, const QString &attrValue, DOM::Document rootNode)
+{
+ DOM::Node attr;
+
+ if(node.isNull())
+ return false;
+
+ attr = node.attributes().getNamedItem(attrName);
+ if(attr.isNull())
+ {
+ //let's create it
+ attr = createDomNodeAttribute(nodeName, dtd, attrName, attrValue, rootNode);
+ if(attr.isNull())
+ return false;
+ insertDomNodeAttribute(node, attr);
+ }
+
+ return true;
+}
+
+bool kafkaCommon::editDomNodeAttribute(DOM::Node domNode, Node* node,
+ const QString &attrName, const QString &attrValue, DOM::Document rootNode)
+{
+ if(!node)
+ return false;
+
+ return editDomNodeAttribute(domNode, node->tag->name, node->tag->dtd(),
+ attrName, attrValue, rootNode);
+}
+
+DOM::Node kafkaCommon::hasParent(DOM::Node domNode, const QString &name)
+{
+ while(!domNode.isNull())
+ {
+ if(domNode.nodeName().string().lower() == name.lower())
+ return domNode;
+ domNode = domNode.parentNode();
+ }
+
+ return DOM::Node();
+}
+
+int kafkaCommon::childPosition(DOM::Node domNode)
+{
+ DOM::Node parentNode, child;
+ int position = 1;
+
+ if(domNode.isNull())
+ return -1;
+
+ parentNode = domNode.parentNode();
+ child = parentNode.firstChild();
+ while(!child.isNull() && child != domNode)
+ {
+ position++;
+ child = child.nextSibling();
+ }
+
+ if(child == domNode)
+ return position;
+ else
+ return -1;
+}
+
+DOM::Node kafkaCommon::getChildNode(DOM::Node parentNode, int position, bool fallback)
+{
+ DOM::Node child;
+
+ if(parentNode.isNull())
+ return DOM::Node();
+
+ child = parentNode.firstChild();
+ while(!child.isNull() && position > 1 && ((fallback && !child.nextSibling().isNull()) || !fallback ))
+ {
+ child = child.nextSibling();
+ position--;
+ }
+
+ return child;
+}
+
+bool kafkaCommon::isInline(DOM::Node domNode)
+{
+ if(domNode.isNull())
+ return false;
+
+ if(domNode.nodeType() == DOM::Node::TEXT_NODE)
+ return true;
+
+ return isInline(domNode.nodeName().string());
+}
+
+bool kafkaCommon::parentSupports(DOM::Node parent, DOM::Node startNode, DOM::Node endNode,
+ const DTDStruct* dtd)
+{
+ QTag *parentQTag;
+ DOM::Node child;
+
+ if(!dtd || parent.isNull())
+ return false;
+
+ parentQTag = QuantaCommon::tagFromDTD(dtd, parent.nodeName().string());
+
+ if(!parentQTag)
+ return false;
+
+ child = startNode;
+ while(!child.isNull())
+ {
+ if(!parentQTag->isChild(child.nodeName().string()))
+ return false;
+ if(child == endNode)
+ return true;
+ child = child.nextSibling();
+ }
+
+ return true;
+}
+
+bool kafkaCommon::isInline(const QString &nodeNam)
+{
+ QString nodeName = nodeNam.lower();
+ if(nodeName == "q" || nodeName == "u" || nodeName == "i" || nodeName == "b" ||
+ nodeName == "cite" || nodeName == "em" || nodeName == "var" || nodeName == "em" ||
+ nodeName == "tt" || nodeName == "code" || nodeName == "kbd" || nodeName == "samp" ||
+ nodeName == "big" || nodeName == "small" || nodeName == "s" || nodeName == "strike" ||
+ nodeName == "sub" || nodeName == "sup" || nodeName == "abbr" ||
+ nodeName == "acronym" || nodeName == "a" || nodeName == "bdo" ||
+ nodeName == "font" || nodeName == "#text" || nodeName == "strong" || nodeName == "dfn" ||
+ nodeName == "img" || nodeName == "applet" || nodeName == "object" || nodeName == "basefont" ||
+ nodeName == "br" || nodeName == "script" || nodeName == "map" || nodeName == "span" ||
+ nodeName == "iframe" || nodeName == "input" || nodeName == "select" || nodeName == "textarea" ||
+ nodeName == "label" || nodeName == "button" )
+ return true;
+ else
+ return false;
+}
+
+#ifdef HEAVY_DEBUG
+void kafkaCommon::coutDomTree(DOM::Node rootNode, int indent)
+#else
+void kafkaCommon::coutDomTree(DOM::Node, int)
+#endif
+{
+#ifdef HEAVY_DEBUG
+ QString output, dots;
+ int j;
+ DOM::Node node;
+ if(rootNode.isNull())
+ kdDebug(25001)<< "kafkaCommon::coutDomTree() - bad node!" << endl;
+
+ node = rootNode;
+ while (!node.isNull())
+ {
+ dots = "";
+ dots.fill('_', indent);
+ output = dots;
+ if (node.nodeType() != DOM::Node::TEXT_NODE)
+ output += node.nodeName().string().replace('\n'," ");
+ else
+ {
+ output += "\"";
+ output+= node.nodeValue().string().replace('\n'," ");
+ output += "\"";
+ }
+ kdDebug(25001) << output <<" (" << node.nodeType() << ") "<<
+ node.handle() << endl;
+ kdDebug(25001)<< dots << " +++ prev " << node.previousSibling().handle() << " next " <<
+ node.nextSibling().handle() << " parent " <<
+ node.parentNode().handle() << " child " << node.firstChild().handle() << endl;
+ for(j = 0; j < (int)node.attributes().length(); ++j)
+ {
+ kdDebug(25001)<< dots << " *** attr" << j << " " <<
+ node.attributes().item(j).nodeName().string() << " - " <<
+ node.attributes().item(j).nodeValue().string() << endl;
+ }
+
+ if (node.hasChildNodes())
+ coutDomTree(node.firstChild(), indent + 4);
+ node = node.nextSibling();
+ }
+#endif
+}
+
+void kafkaCommon::coutTree(Node *node, int indent)
+{
+ QString output, dots;
+ int bLine, bCol, eLine, eCol, j;
+ if(!node)
+ kdDebug(25001)<< "kafkaCommon::coutTree() - bad node!" << endl;
+
+ while (node)
+ {
+ dots = "";
+ dots.fill('.', indent);
+ output = dots;
+ node->tag->beginPos(bLine, bCol);
+ node->tag->endPos(eLine, eCol);
+ if (node->tag->type == Tag::XmlTag || node->tag->type == Tag::XmlTagEnd ||
+ node->tag->type == Tag::ScriptTag)
+ output += node->tag->name.replace('\n',"<return>");
+ else
+ {
+ output += "\"";
+ output+= node->tag->tagStr().replace('\n',"<return>");
+ output += "\"";
+ }
+ kdDebug(25001) << output <<" (" << node->tag->type << ", " << node->tag->cleanStrBuilt() << ", " <<
+ node->tag->indentationDone() << ") "<< node << " at pos " << bLine << ":" << bCol << " - " <<
+ eLine << ":" << eCol << endl;
+ kdDebug(25001)<< dots << " +++ prev " << node->prev << " next " << node->next << " parent " <<
+ node->parent << " child " << node->child << endl;
+ for(j = 0; j < node->tag->attrCount(); ++j)
+ {
+ kdDebug(25001)<< dots << " *** attr" << j << " " <<
+ node->tag->getAttribute(j).nameLine << ":" <<
+ node->tag->getAttribute(j).nameCol << ":" <<
+ node->tag->getAttribute(j).name << " - " <<
+ node->tag->getAttribute(j).valueLine << ":" <<
+ node->tag->getAttribute(j).valueCol << ":" <<
+ node->tag->getAttribute(j).value << endl;
+ }
+
+ if (node->child)
+ coutTree(node->child, indent + 4);
+ if(node == node->next || (node->next && node == node->next->next) ||
+ (node->next && node->next->next && node == node->next->next->next) ||
+ (node->next && node->next->next && node->next->next->next &&
+ node == node->next->next->next->next) || (node->next && node->next->next &&
+ node->next->next->next && node->next->next->next->next && node ==
+ node->next->next->next->next->next))
+ {
+ //try to detect invalid pointers.
+ kdDebug(25001)<< "ERROR - node == node->[..]next" << endl;
+ return;
+ }
+ node = node->next;
+ }
+}
+
+int kafkaCommon::isInsideTag(Node* start_node, Node* end_node, QString const& tag_name)
+{
+ Q_ASSERT(start_node && end_node);
+ //Andras: don't crash
+ if (!start_node || !end_node)
+ return -1;
+
+ Node* tag_start = hasParent(start_node, end_node, tag_name);
+ if(tag_start)
+ return 1; // both start_node and end_node are surrounded by tag_name
+
+ tag_start = hasParent(start_node, tag_name);
+ if(tag_start)
+ return 0; // only start_node has tag_name as parent
+
+ tag_start = hasParent(end_node, tag_name);
+ if(tag_start)
+ return 0; // only end_node has tag_name as parent
+
+ return -1; // neither the nodes have tag_name as parent
+}
+
+int kafkaCommon::isInsideTag(Node* start_node, Node* end_node, QString const& tag_name,
+ QString const& attribute_name, QString const& attribute_value)
+{
+ Q_ASSERT(start_node && end_node);
+ //Andras: don't crash
+ if (!start_node || !end_node)
+ return -1;
+
+ Node* tag_start = hasParent(start_node, end_node, tag_name);
+ if(tag_start && tag_start->tag->hasAttribute(attribute_name) && tag_start->tag->attributeValue(attribute_name, true) == attribute_value)
+ return 1; // both start_node and end_node are surrounded by tag_name
+
+ tag_start = hasParent(start_node, tag_name);
+ if(tag_start && tag_start->tag->hasAttribute(attribute_name) && tag_start->tag->attributeValue(attribute_name, true) == attribute_value)
+ return 0; // only start_node has tag_name as parent
+
+ tag_start = hasParent(end_node, tag_name);
+ if(tag_start && tag_start->tag->hasAttribute(attribute_name) && tag_start->tag->attributeValue(attribute_name, true) == attribute_value)
+ return 0; // only end_node has tag_name as parent
+
+ return -1; // neither the nodes have tag_name as parent
+}
+
+bool kafkaCommon::isBetweenWords(Node* node, int offset)
+{
+ Q_ASSERT(node->tag->type == Tag::Text || node->tag->type == Tag::Empty);
+ Q_ASSERT(offset >= 0);
+ Q_ASSERT(node);
+ if (!node)
+ return false; //FIXME: Andras: don't crash
+
+ QString tag_str = node->tag->tagStr();
+
+ return !
+ (tag_str[offset].isSpace() || tag_str[offset].isPunct() ||
+ tag_str[offset - 1].isSpace() || tag_str[offset - 1].isPunct());/* ||
+ tag_str[offset + 1].isSpace() || tag_str[offset + 1].isPunct());*/
+}
+
+void kafkaCommon::getStartOfWord(Node*& node, int& offset)
+{
+ Q_ASSERT(node);
+// Q_ASSERT(isBetweenWords(node, offset)); recursive
+ Q_ASSERT(offset >= 0);
+ //Andras: don't crash
+ if (!node || offset < 0)
+ return;
+
+ kdDebug(23100) << "getStartOfWord node length: " << node->tag->tagStr().length() << endl;
+ kdDebug(23100) << "getStartOfWord offset BEGIN: " << offset << endl;
+
+ QString tag_str = node->tag->tagStr();
+ while(offset >= 0 && !tag_str[offset].isSpace() && !tag_str[offset].isPunct())
+ --offset;
+
+ if(offset == -1)
+ {
+ Node* aux = node->previousSibling();
+ while(aux && aux->tag->type != Tag::Text)
+ {
+ if(!isInline(aux->tag->name))
+ {
+ ++offset;
+ return;
+ }
+
+ aux = aux->previousSibling();
+ }
+ if(aux)
+ {
+ node = aux;
+ offset = aux->tag->tagStr().length() - 1;
+ kdDebug(23100) << "getStartOfWord node length: " << node->tag->tagStr().length() << endl;
+ kdDebug(23100) << "getStartOfWord offset RECURS: " << offset << endl;
+ getStartOfWord(node, offset);
+ return;
+ }
+ }
+ ++offset;
+ kdDebug(23100) << "getStartOfWord node length: " << node->tag->tagStr().length() << endl;
+ kdDebug(23100) << "getStartOfWord offset END: " << offset << endl;
+}
+
+void kafkaCommon::getEndOfWord(Node*& node, int& offset)
+{
+ Q_ASSERT(node);
+// assert(isBetweenWords(node, offset)); recursive
+ Q_ASSERT(isBetweenWords(node, offset));
+ Q_ASSERT(offset >= 0);
+
+ //Andras: if the following asserts are hit, don't do anything = don't crash
+ if (!node || !isBetweenWords(node, offset) || offset < 0)
+ return;
+
+
+ QString tag_str = node->tag->tagStr();
+ while((uint)offset != tag_str.length() && !tag_str[offset].isSpace() && !tag_str[offset].isPunct())
+ ++offset;
+
+ if((uint)offset == tag_str.length())
+ {
+ Node* aux = node->nextSibling();
+ while(aux && aux->tag->type != Tag::Text)
+ {
+ if(!isInline(aux->tag->name))
+ return;
+
+ aux = aux->nextSibling();
+ }
+ if(aux)
+ {
+ node = aux;
+ offset = 0;
+ getEndOfWord(node, offset);
+ }
+ }
+}
+
+void kafkaCommon::getStartOfParagraph(Node*& node, int& offset)
+{
+ Q_ASSERT(node);
+ //Andras: don't crash
+ if (!node)
+ {
+ offset = 0;
+ return;
+ }
+
+ Node* previous = node->previousSibling();
+ while(previous && (isInline(previous->tag->name) || previous->tag->name.lower() == "br" || previous->tag->type == Tag::Text))
+ previous = previous->previousSibling();
+
+ offset = 0;
+ if(previous)
+ {
+ node = previous->nextSibling();
+ return;
+ }
+ Q_ASSERT(node->tag->type == Tag::Text);
+}
+
+void kafkaCommon::getEndOfParagraph(Node*& node, int& offset)
+{
+ Q_ASSERT(node);
+ if (!node)
+ {
+ offset = 0;
+ return;
+ }
+
+ Node* begin_paragraph = node;
+ getStartOfParagraph(begin_paragraph, offset);
+
+ Node* next = begin_paragraph->nextSibling();
+ while(nodeDepth(next) > nodeDepth(begin_paragraph))
+ next = next->nextSibling();
+ while(next && (isInline(next->tag->name) || next->tag->name.lower() == "br" || next->tag->type == Tag::Text))
+ {
+ next = next->nextSibling();
+ while(nodeDepth(next) > nodeDepth(node))
+ next = next->nextSibling();
+ }
+ if(next)
+ {
+ node = next;
+ if(nodeDepth(next) < nodeDepth(begin_paragraph))
+ node = node->previousSibling();
+
+ if(node->tag->type == Tag::Text)
+ offset = node->tag->tagStr().length() - 1;
+ else
+ offset = 0;
+ return;
+ }
+}
+
+