/***************************************************************************
                               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)
    {
        delete 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)
    {
        delete 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)
    {
    delete 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
        {
            delete 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)
        delete 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;
    }
}


