//
// C++ Implementation: doctreeviewimpl
//
// Description: 
//
//
// Author: Robert Vogl <voglrobe@lapislazuli>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include <stdlib.h> // getenv

// Qt includes
#include <qfile.h>
#include <qwidget.h>
#include <qtextstream.h>
#include <qcstring.h>
#include <qpixmap.h>
#include <qregexp.h>

//KDE includes
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kfiledialog.h>
#include <kmessagebox.h>

// App specific includes
#include "doctreeviewimpl.h"
#include "parasaxparser.h"
// #include "contextmenuhandler.h"

static const int initial_id = 100;

//////////////////////////////////////
// Interface
//////////////////////////////////////
ListViewInterface::ListViewInterface(ListViewInterface *parent, QString label)
    : QListViewItem( parent, label )
{
}
    
ListViewInterface::ListViewInterface(QListView *lv, QString label)
    : QListViewItem( lv, label )
{
}


//////////////////////////////////////
// Classes for Roberts elements
//////////////////////////////////////
RobDocument::RobDocument(QListView *lv, QString label)
    : ListViewInterface( lv, label )
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("contents", KIcon::Small);
    this->setPixmap(0, pixmap);   
}

RobDocument::~RobDocument()
{
}

QString RobDocument::getRTFHeader() const
{
    QString header = "<H1><center>" + this->text(0) + "</center></H1>";
    return header;
}

QString RobDocument::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString RobDocument::getRTFData() const
{
    return QString::null;
}

QString RobDocument::getData() const
{
    return QString::null;
}

QString RobDocument::getRawData() const
{
    return QString::null;
}

void RobDocument::setData(const QString &data)
{
    // structural element only
}


//////////////////////////////////////
// Classes for Docbook elements
//////////////////////////////////////
Overview::Overview(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("filenew", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "0"); // upmost element
}
       
Overview::~Overview()
{
}
    
QString Overview::getRTFHeader() const
{
    QString header = "<H1>" + this->text(0) + "</H1>";
    return header;
}

QString Overview::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Overview::getRTFData() const
{
    return QString::null;
}

QString Overview::getData() const
{
    return QString::null;
}

QString Overview::getRawData() const
{
    return QString::null;
}

void Overview::setData(const QString &data)
{
    // structural element only
}   


Date::Date(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("filenew", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "00000001");
    m_node = QDomNode();
}

Date::~Date()
{
}
    
void Date::setNode(QDomNode node)
{
    m_node = node;
}

QString Date::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + ": </B>";
    return header;
}

QString Date::getHeader() const
{
    QString header = this->text(0) + ": ";
    return header;
}

QString Date::getRTFData() const
{
    if ( !m_node.isNull() ){
        QString date = m_node.nodeValue() + ", "; 
        return date;
    } else
        return QString::null;
}

QString Date::getData() const
{
    if ( !m_node.isNull() ){
        return m_node.nodeValue();
    } else
        return QString::null;
}
    
QString Date::getRawData() const
{
    QString ret;
    
    // get content of element
    QTextStream in(ret, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    
    return ret;
}

void Date::setData(const QString &data)
{
    m_node.setNodeValue(data);
    this->setText(1, data); // update ListView
}   


ReleaseInfo::ReleaseInfo(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("filenew", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "00000002");
    m_node = QDomNode();
}

ReleaseInfo::~ReleaseInfo()
{
}
    
void ReleaseInfo::setNode(QDomNode node)
{
    m_node = node;
}

QString ReleaseInfo::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + ": </B>";
    return header;
}

QString ReleaseInfo::getHeader() const
{
    QString header = this->text(0) + ": ";
    return header;
}

QString ReleaseInfo::getRTFData() const
{
    if ( !m_node.isNull() ){
        QString releaseinfo = m_node.nodeValue(); 
        return releaseinfo;
    } else
        return QString::null;
}

QString ReleaseInfo::getData() const
{
    if ( !m_node.isNull() ){
        return m_node.nodeValue();
    } else
        return QString::null;
}
    
QString ReleaseInfo::getRawData() const
{
    QString ret;
    
    // get content of element
    QTextStream in(ret, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    
    return ret;
}

void ReleaseInfo::setData(const QString &data)
{
    m_node.setNodeValue(data);
    this->setText(1, data); // update ListView
}   


AuthorGroup::AuthorGroup(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("kdmconfig", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "00000003");
}

AuthorGroup::~AuthorGroup()
{
}
    
QString AuthorGroup::getRTFHeader() const
{
    QString header = "<H2>" + this->text(0) + "</H2>";
    return header;
}

QString AuthorGroup::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString AuthorGroup::getRTFData() const
{
    return QString::null;
}

QString AuthorGroup::getData() const
{
    return QString::null;
}
    
QString AuthorGroup::getRawData() const
{
    return QString::null;
}

void AuthorGroup::setData(const QString &data)
{
    // structural element only
}   


Author::Author(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("personal", KIcon::Small);
    this->setPixmap(0, pixmap);
    m_node1 = QDomNode();
    m_node2 = QDomNode();    
}

Author::~Author()
{
}
    
void Author::setNode(QDomNode node1, QDomNode node2)
{
    m_node1 = node1;
    m_node2 = node2;
}

QString Author::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + ": </B>";
    return header;
}

QString Author::getHeader() const
{
    QString author = this->text(0) + ": ";
    return author;
}

QString Author::getRTFData() const
{
    QString ret = QString::null;
    if ( !m_node1.isNull() ){
        ret = m_node1.nodeValue() + " ";
    }
    
    if ( !m_node2.isNull() ){
        ret += m_node2.nodeValue();    
    }    
    return ret;
}

QString Author::getData() const
{
    QString ret = QString::null;
    if ( !m_node1.isNull() ){
        ret = m_node1.nodeValue() + " ";
    }
    
    if ( !m_node2.isNull() ){
        ret += m_node2.nodeValue();    
    }    
    return ret;
}
    
QString Author::getRawData() const
{
    QString ret = QString::null;
    if ( !m_node1.isNull() ){
        ret = m_node1.nodeValue() + " ";
    }
    
    if ( !m_node2.isNull() ){
        ret += m_node2.nodeValue();    
    }    
    return ret;
}

void Author::setData(const QString &data)
{
    // canonify string
    QString m_data = data;
    m_data.replace( QRegExp("\n"), "" ); // remove Newlines
    m_data.replace( QRegExp(" {2,}"), " " ); // remove multiple spaces
    m_data.replace( QRegExp("[\t|\r]{1,}"), ""); // remove Tabs
    // split string "firstname surname"
    QString firstname = m_data.section(' ', 0, 0);
    QString surname = m_data.section(' ', 1, 1);
    // update node elements
    m_node1.setNodeValue(firstname);
    m_node2.setNodeValue(surname);
    // update Listview
    this->setText(1, firstname + " " + surname);
}   


KeywordSet::KeywordSet(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("txt", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "00000004");
}

KeywordSet::~KeywordSet()
{
}
    
QString KeywordSet::getRTFHeader() const
{
    QString header = "<H2>" + this->text(0) + "</H2>";
    return header;
}

QString KeywordSet::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString KeywordSet::getRTFData() const
{
    return QString::null;
}

QString KeywordSet::getData() const
{
    return QString::null;
}
    
QString KeywordSet::getRawData() const
{
    return QString::null;
}

void KeywordSet::setData(const QString &data)
{
    // structural element only
}   


Keyword::Keyword(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    m_node = QDomNode();
}

Keyword::~Keyword()
{
}
    
void Keyword::setNode(QDomNode node)
{
    m_node = node;
}

QString Keyword::getRTFHeader() const
{
    return QString::null;
}

QString Keyword::getHeader() const
{
    return QString::null;
}

QString Keyword::getRTFData() const
{
    if ( !m_node.isNull() ){
        QString keyword = m_node.nodeValue() + "<br>"; 
        return keyword;
    } else
        return QString::null;
}

QString Keyword::getData() const
{
    if ( !m_node.isNull() ){
        return m_node.nodeValue();
    } else
        return QString::null;
}
    
QString Keyword::getRawData() const
{
    QString ret;
    
    // get content of element
    QTextStream in(ret, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    
    return ret;
}

void Keyword::setData(const QString &data)
{
    m_node.setNodeValue(data);
    this->setText(0, data); // update ListView
}   


Abstract::Abstract(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("filenew", KIcon::Small);
    this->setPixmap(0, pixmap);
    this->setText(3, "00000005");
}

Abstract::~Abstract()
{
}
    
QString Abstract::getRTFHeader() const
{
    QString header = "<H2>" + this->text(0) + "</H2>";
    return header;
}

QString Abstract::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Abstract::getRTFData() const
{
    return QString::null;
}

QString Abstract::getData() const
{
    return QString::null;
}
    
QString Abstract::getRawData() const
{
    return QString::null;
}

void Abstract::setData(const QString &data)
{
    // structural element only
}   


Para::Para(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
    m_node = QDomNode();
}

Para::~Para()
{
}
    
void Para::setNode(QDomNode node)
{
    m_node = node;   
}

QString Para::getRTFHeader() const
{
    return QString::null;
}

QString Para::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Para::getRTFData() const
{
    // get content of element
    QString paragraph;
    QTextStream in(paragraph, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    
    // delegate job to SAX-Parser
    QString data;
    QString ret;
    
    ParaSaxParser *p = ParaSaxParser::Instance();
    p->setData( paragraph );
    p->getRTFText( data );
    ret = "<p>" + data + "</p>";
    
    return ret;
}

QString Para::getData() const
{
    // get content of element
    QString paragraph;
    QTextStream in(paragraph, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    
    // delegate job to SAX-Parser
    QString data;
    
    ParaSaxParser *p = ParaSaxParser::Instance();
    p->setData( paragraph );
    p->getText( data );
    
    return data;
}
    
QString Para::getRawData() const
{
    QString ret;
    
    // get content of element
    QTextStream in(ret, IO_WriteOnly);
    in.setEncoding(QTextStream::UnicodeUTF8);
    in << m_node;
    // remove opening and closing para-tags
    ret.replace( QRegExp("</?(para|Para|PARA)>"),"");
    ret.replace( QRegExp("<para/>"), "" ); // from empty doc
    ret.replace( QRegExp("^ "),"" );
    ret.replace( QRegExp("^\n"), "" );
    
    return ret;
}

void Para::setData(const QString &data)
{
    kdDebug(100200) << "Para::setData()" << endl;
    
    // add tags
    QString l_data = "<para>\n" + data + "</para>";
    
    // create a temporary document
    QString err;
    QDomDocument tempDoc;
    if ( !tempDoc.setContent(l_data) ){
        l_data = "<para><![CDATA[\n" + data + "\n]]></para>";
        // try again
        if ( !tempDoc.setContent(l_data) ){
            err = i18n("Unable to make document persistent.");
            throw err;
        }
        err = i18n("This paragraph contains mismatching tags. It will will be handled as CDATA.");
        KMessageBox::information(0, err, i18n("XML-Error"));
    }
    
    // get parent
    QDomNode parent = m_node.parentNode();
    if ( parent.isNull() ){
        err = i18n("Structural DocBook format error:\nNo parent element for this paragraph.");
        throw err;    
    }        
    
    // replace Node
    parent.replaceChild(tempDoc, m_node);
    m_node = tempDoc;
}   


Chapter::Chapter(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Chapter::~Chapter()
{
}
    
QString Chapter::getRTFHeader() const
{
    QString header = "<H1>" + this->text(0) + "</H1>";
    return header;
}

QString Chapter::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Chapter::getRTFData() const
{
    return QString::null;
}

QString Chapter::getData() const
{
    return QString::null;
}
    
QString Chapter::getRawData() const
{
    return QString::null;
}

void Chapter::setData(const QString &data)
{
    // structural element only
}   


Sect1::Sect1(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Sect1::~Sect1()
{
}
    
QString Sect1::getRTFHeader() const
{
    QString header = "<H2>" + this->text(0) + "</H2>";
    return header;
}

QString Sect1::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Sect1::getRTFData() const
{
    return QString::null;
}

QString Sect1::getData() const
{
    return QString::null;
}
    
QString Sect1::getRawData() const
{
    return QString::null;
}

void Sect1::setData(const QString &data)
{
    // structural element only
}   


Sect2::Sect2(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Sect2::~Sect2()
{
}
    
QString Sect2::getRTFHeader() const
{
    QString header = "<H3>" + this->text(0) + "</H3>";
    return header;
}

QString Sect2::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Sect2::getRTFData() const
{
    return QString::null;
}

QString Sect2::getData() const
{
    return QString::null;
}
    
QString Sect2::getRawData() const
{
    return QString::null;
}

void Sect2::setData(const QString &data)
{
    // structural element only
}   


Sect3::Sect3(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Sect3::~Sect3()
{
}
    
QString Sect3::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + "</B>";
    return header;
}

QString Sect3::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Sect3::getRTFData() const
{
    return QString::null;
}

QString Sect3::getData() const
{
    return QString::null;
}
    
QString Sect3::getRawData() const
{
    return QString::null;
}

void Sect3::setData(const QString &data)
{
    // structural element only
}   


Sect4::Sect4(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Sect4::~Sect4()
{
}
    
QString Sect4::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + "</B>";
    return header;
}

QString Sect4::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Sect4::getRTFData() const
{
    return QString::null;
}

QString Sect4::getData() const
{
    return QString::null;
}
    
QString Sect4::getRawData() const
{
    return QString::null;
}

void Sect4::setData(const QString &data)
{
    // structural element only
}   


Sect5::Sect5(ListViewInterface *parent, QString label)
    : ListViewInterface(parent, label)
{
    QPixmap pixmap = KGlobal::iconLoader()->loadIcon("leftjust", KIcon::Small);
    this->setPixmap(0, pixmap);
}

Sect5::~Sect5()
{
}
    
QString Sect5::getRTFHeader() const
{
    QString header = "<B>" + this->text(0) + "</B>";
    return header;
}

QString Sect5::getHeader() const
{
    QString header = this->text(0) + "\n";
    return header;
}

QString Sect5::getRTFData() const
{
    return QString::null;
}

QString Sect5::getData() const
{
    return QString::null;
}
    
QString Sect5::getRawData() const
{
    return QString::null;
}

void Sect5::setData(const QString &data)
{
    // structural element only
}   





//////////////////////////////////////
// TreeView Implementation
//////////////////////////////////////
DocTreeViewImpl::DocTreeViewImpl(QWidget* parent, const char* name, WFlags fl)
 : DocTreeView(parent, name, fl)
{
    m_rootItem = NULL;
    m_url = KURL();
    // the hidden formated id to set a sort order
    listView->setSorting(3, true);
    listView->setColumnWidthMode(3, QListView::Manual );
    listView->setColumnWidth(3, 0);
    m_idCounter = initial_id;
    m_stopped = false;
    m_currentItem = m_rootItem;
    m_parasaxparser = ParaSaxParser::Instance();
    m_editMode = false;
    m_changedContent = QString::null;
    
    // m_contextmenuhandler = new ContextMenuHandler(this, "contextmenuhandler");
    // m_contextmenu = NULL;
}


DocTreeViewImpl::~DocTreeViewImpl()
{
    if ( m_rootItem )
        delete m_rootItem;
    delete m_parasaxparser;
    // delete m_contextmenuhandler;
}


void DocTreeViewImpl::clear()
{
    kdDebug(100200) << "DocTreeViewImpl::clear()" << endl;
    m_url = KURL();
    m_domTree.clear();   
    
    if ( m_rootItem )
        delete m_rootItem;
    
    // inform BookmarkHandler
    // emit signalSetBookmarkFilename( QString::null );
    
    listView->clear();
    m_rootItem = new RobDocument( listView, i18n("unnamed") );
    m_idCounter = initial_id;
    m_stopped = false;
    m_currentItem = static_cast<ListViewInterface*>(m_rootItem);
}


void DocTreeViewImpl::createEmptyDocument()
{
    kdDebug(100200) << "DocTreeViewImpl::createEmptyDocument()" << endl;
    // create empty document
    QByteArray data;
    QTextStream wrapped(data, IO_ReadWrite);
    wrapped.setEncoding(QTextStream::UnicodeUTF8);

    wrapped << "<?xml version=\"1.0\" ?>" << endl;
    wrapped << "<!--" << endl;
    wrapped << "!DOCTYPE book PUBLIC \"-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN\" \"dtd/kdex.dtd\"" << endl;
    wrapped << "-->" << endl;
    
    QString header(data);
    m_parasaxparser->setProcessingInstruction( header );
    
    wrapped << "<book>" << endl;
    wrapped << "<bookinfo>" << endl;
    wrapped << "<title><![CDATA[" << i18n("Empty Document") << "]]></title>" << endl;
    wrapped << "<authorgroup>" << endl;
    wrapped << "<author>" << endl;
    wrapped << "<firstname>KSayIt</firstname>" << endl;
    wrapped << "<surname>Wrapper</surname>" << endl;
    wrapped << "</author>" << endl;
    wrapped << "</authorgroup>" << endl;
    wrapped << "<copyright>" << endl;
    wrapped << "<year>2005</year>" << endl;
    wrapped << "<holder>Robert Vogl</holder>" << endl;        
    wrapped << "</copyright>" << endl;
    wrapped << "</bookinfo>" << endl;       
    wrapped << "<chapter>" << endl;
    wrapped << "<title><![CDATA[" << i18n("Untitled") << "]]></title>" << endl;
    wrapped << "<para>" << endl;
    wrapped << "";
    wrapped << "</para>" << endl;
    wrapped << "</chapter>" << endl;
    wrapped << "</book>" << endl;
    
    if ( !m_domTree.setContent( data ) ){
        kdDebug(100200) << "Kein gueltiges Dokument!!" << endl;
    };
    // inform BookmarkHandler
    emit signalSetBookmarkFilename( i18n("Untitled") );

    QDomElement root = m_domTree.documentElement();
    QDomNode node;  
    if( root.tagName().lower() == "book" ){ // DocBook
        parseBook(root , m_rootItem); // we parse only ONE Book  
    }
    
    // Initial view
    m_currentItem = static_cast<ListViewInterface*>( listView->lastItem() );
    listView->setSelected( m_currentItem, true );
    listView->ensureItemVisible( m_currentItem );
    slotItemClicked( m_currentItem );
}


void DocTreeViewImpl::openFile(const KURL &url)
{
    kdDebug(100200) << "DocTreeViewImpl::openFile(" << url.path() << ")" << endl;

    m_url = url;
    // open file
    QString err;
    QFile file( url.path() );
    if( !file.open(IO_ReadOnly) ){
        err = i18n("Unable to open File.");
        throw(err);
    }
    // check document
    if( m_domTree.setContent(&file) ){
        // extract Header
        file.reset();
        QString header = QString::null;
        QString line;
        int offset;
        file.readLine( line, file.size() );
        while( !file.atEnd() && (offset = line.find("<book", 0, false)) < 0 ){
            header += line;    
            file.readLine( line, file.size() );  
        }
        file.close();
        header += line.left( offset );
        kdDebug(100200) << "### Header: " << endl << header << endl;       
        m_parasaxparser->setProcessingInstruction( header );
        // inform BookmarkHandler about the opened file
        emit signalSetBookmarkFilename( url.fileName() );
    } else {
        // File is not a valid XML-File. Wrap it
        file.reset();
        QByteArray data;
        QTextStream wrapped(data, IO_ReadWrite);
        wrapped.setEncoding(QTextStream::UnicodeUTF8);

        wrapped << "<?xml version=\"1.0\" ?>" << endl;
        wrapped << "<!--" << endl;
        wrapped << "!DOCTYPE book PUBLIC \"-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN\" \"dtd/kdex.dtd\"" << endl;
        wrapped << "-->" << endl;
        
        QString header(data);
        kdDebug(100200) << "Header (Wrapper): " << endl << header << endl;
        m_parasaxparser->setProcessingInstruction( header );
        
        wrapped << "<book>" << endl;
        wrapped << "<bookinfo>" << endl;
        wrapped << "<title><![CDATA[" << url.path() << "]]></title>" << endl;
        wrapped << "<authorgroup>" << endl;
        wrapped << "<author>" << endl;
        wrapped << "<firstname>KSayIt</firstname>" << endl;
        wrapped << "<surname>Wrapper</surname>" << endl;
        wrapped << "</author>" << endl;
        wrapped << "</authorgroup>" << endl;
        wrapped << "<copyright>" << endl;
        wrapped << "<year>2005</year>" << endl;
        wrapped << "<holder>Robert Vogl</holder>" << endl;        
        wrapped << "</copyright>" << endl;
        wrapped << "</bookinfo>" << endl;       
        wrapped << "<chapter>" << endl;
        wrapped << "<title><![CDATA[" << i18n("Plain File") << "]]></title>" << endl;
        wrapped << "<para><![CDATA[";
        wrapped << QString( file.readAll() );
        wrapped << "]]></para>" << endl;
        wrapped << "</chapter>" << endl;
        wrapped << "</book>" << endl;
        file.close();
        
        // Try again
        if (!m_domTree.setContent(data)){
            err = i18n("Failed wrapping the file into XML.");
            throw(err);
        }
        emit signalSetBookmarkFilename( i18n("Plain File") );
    }

    // check if correct document type
    QDomElement root = m_domTree.documentElement();
    if( root.tagName().lower() == "book" ){ // DocBook
            parseBook(root , m_rootItem); // we parse only ONE Book  
    } else {
        err = i18n("The file is of type %1, 'book' expected.").arg(root.tagName() );
        throw(err);
    }
    
    // Initial view
    listView->setSelected( m_rootItem, true );
    slotItemClicked( m_rootItem );
}


void DocTreeViewImpl::saveFile()
{
    kdDebug(100200) << "DocTreeViewImpl::saveFile()" << endl;
    
    makeCurrentNodePersistent();
    
    QString err;

    if ( m_url.isEmpty() ){ // file has no name, ask user
        QString usershome( getenv("HOME") );
        m_url = KFileDialog::getSaveURL(usershome, "*.docbook", this, i18n("Save File"));
    }
    if ( m_url.isEmpty() ) // dialog cancelled
        return;
    if ( !m_url.isValid() ){
        err = i18n("The given URL is invalid. Try 'File save as...' instead.");
        throw( err );
    }
    if ( m_url.isLocalFile() ){
        QFile file( m_url.path() );
        if ( !file.open(IO_WriteOnly) ){
            err = i18n("Unable open file to write.");
            throw( err );
        }
        QTextStream out(&file);
        out.setEncoding(QTextStream::UnicodeUTF8);
        m_domTree.save( out, 3 );
        m_rootItem->setText(0, m_url.path() );
        file.close();
    } else {
        err = i18n("Save operation currently works on local files only.");
        throw( err );
    }
    // inform BookmarkHandler
    emit signalSetBookmarkFilename( m_url.fileName() );
}


void DocTreeViewImpl::saveFileAs()
{
    kdDebug(100200) << "DocTreeViewImpl::saveFileAs()" << endl;
    QString err;

    makeCurrentNodePersistent();
    
    m_url = KFileDialog::getSaveURL(QString::null, "*.docbook", this, i18n("Save File As"));
    if ( m_url.isEmpty() ) // dialog cancelled
        return;
    if ( !m_url.isValid() ){
        err = i18n("The given URL is invalid.");
        throw( err );
    }
    if ( m_url.isLocalFile() ){
        QFile file( m_url.path() );
        if ( !file.open(IO_WriteOnly) ){
            err = i18n("Unable open file to write.");
            throw( err );
        }
        QTextStream out(&file);
        out.setEncoding(QTextStream::UnicodeUTF8);
        m_domTree.save( out, 3 );
        m_rootItem->setText(0, m_url.path() );
        file.close();
    } else {
        err = i18n("Save operation currently works on local files only.");
        throw( err );
    }
    // inform BookmarkHandler
    emit signalSetBookmarkFilename( m_url.fileName() );
}


void DocTreeViewImpl::setEditMode(bool mode)
{
    m_editMode = mode;   
    bool hasChild = m_currentItem->firstChild();
    
    if ( !hasChild && mode ){
        emit signalEnableTextedit( true );
        slotItemClicked( m_currentItem );
        return;
    }
    
    if ( !hasChild ){
        makeCurrentNodePersistent();
        slotItemClicked( m_currentItem );
    }
 
    emit signalEnableTextedit( false );
}


bool DocTreeViewImpl::selectItemByID(const QString &ID)
{
    kdDebug(100200) << "DocTreeViewImpl::selectItemByID(" << ID << ")" << endl;
    int ID2Int = ID.toInt(0, 10);
    if ( ID2Int < 1 )
        return false;
    QListViewItem *item = listView->findItem(ID, 3, Qt::ExactMatch | Qt::CaseSensitive);
    if ( !item )
        return false;
    bool hasChild = item->firstChild();
    if ( !hasChild )
        makeCurrentNodePersistent();
    listView->setSelected( item, true );
    listView->ensureItemVisible(item );
    slotItemClicked( item );
    return true;
}


void DocTreeViewImpl::slotItemClicked(QListViewItem *item)
{
    kdDebug(100200) << "DocTreeViewImpl::slotItemClicked()" << endl;
    
    if ( item != m_currentItem ){
        makeCurrentNodePersistent();
    }
    
    if ( item ){
        m_currentItem = static_cast<ListViewInterface*>(item);
        QString str;
        QTextStream msg(&str, IO_ReadWrite);
        msg.setEncoding(QTextStream::UnicodeUTF8);

        // check if item has a child and check state
        // of Edit Mode.
        bool editable = false;
        
        if ( !(m_currentItem->firstChild()) && m_editMode ){
            editable = true;
        }
        emit signalEnableTextedit( editable );
        recursiveTextCollector( m_currentItem, msg, !editable );
        emit signalContentChanged( str );
        
        // inform the Bookmark Manager about the new item.
        QString ID   = item->text(3);
        QString col0 = item->text(0);
        QString title = QString::null;
        if ( col0 == i18n("Paragraph") ){
            title = (m_currentItem->getData()).left(32);
            // canonify string
            title.replace( QRegExp("^( |\t|\n)+"), "");
            title.replace( QRegExp("( |\t|\n)$+"), "");            
        } else {
            title = col0.left(32);
        }
        emit signalNotifyBookmarkManager(ID, title);
    }
}


void DocTreeViewImpl::recursiveTextCollector(ListViewInterface* item, QTextStream &msg, bool header)
{
    kdDebug(100200) << "DocTreeViewImpl::recursiveTextCollector()" << endl;

    QString text;
    if ( header ){
        // if header==true get Headers
        msg << item->getRTFHeader();
        text = item->getRTFData();
    } else {
        // get raw data (Edit Mode)
        text = item->getRawData();    
    }
    if ( !text.isNull() )
        msg << text;
  
    // check if current item has a child
    ListViewInterface *i = static_cast<ListViewInterface*>(item->firstChild());

    while( i ){
        recursiveTextCollector( i, msg, header );
        i = static_cast<ListViewInterface*>(i->nextSibling());
    }
}


void DocTreeViewImpl::sayActiveNodeAndChilds()
{
    kdDebug(100200) << "DocTreeViewImpl::sayActiveNodeAndChilds()" << endl;

    m_stopped = false;
    if ( !(m_currentItem->firstChild()) ){
        // current item has no childs. It may be changed.
        makeCurrentNodePersistent();
    }
    
    recursiveSayNodes( m_currentItem );
    emit signalAllNodesProcessed();
}


void DocTreeViewImpl::recursiveSayNodes(ListViewInterface* item)
{
    kdDebug(100200) << "DocTreeViewImpl::recursiveSayNodes()" << endl;

    QString str;
    QTextStream msg(&str, IO_ReadWrite);
    msg.setEncoding(QTextStream::UnicodeUTF8);

    // write Text of item (heading)
    msg << item->getHeader();

    // get data of current item
    msg << item->getData() << "\n" << endl;

    // request say task
    if ( !m_stopped ){
        emit signalSetText( str );
    } else {
        return;
    }

    // check if current item has a child
    ListViewInterface *i = static_cast<ListViewInterface*>(item->firstChild());
    while( i ){
        recursiveSayNodes( i );
        i = static_cast<ListViewInterface*>(i->nextSibling());
    }
}


void DocTreeViewImpl::stop()
{
    kdDebug(100200) << "DocTreeViewImpl::stop()" << endl;
    m_stopped = true;
}


void DocTreeViewImpl::setNodeContent(QString &text)
{
    kdDebug(100200) << "DocTreeViewImpl::setNodeContent()" << endl;
    m_changedContent = text;
}


void DocTreeViewImpl::makeCurrentNodePersistent()
{
    kdDebug(100200) << "DocTreeViewImpl::slotMakeCurrentNodePersistent()" << endl;
    if ( m_changedContent.isNull() )
        return; // no changes were happen
        
    try{
        m_currentItem->setData(m_changedContent);
        m_changedContent = QString::null;
    }
    catch(QString err){
        KMessageBox::error( this, err, i18n("XML-Error") );
        m_changedContent = QString::null;        
    }
}    

/**
void DocTreeViewImpl::slotContextMenuReq(QListViewItem *item, const QPoint &pos, int col)
{
    kdDebug(100200) << "DocTreeViewImpl::slotContextMenuReq()" << endl;
    
    
}
*/
/**
void DocTreeViewImpl::contextMenuEvent(QContextMenuEvent *e)
{
    kdDebug(100200) << "DocTreeViewImpl::contextMenuEvent()" << endl;

    m_contextmenu = m_contextmenuhandler->getPopupMenu("test");
    if ( !m_contextmenu )
        return;
        
    m_contextmenu->exec( e->globalPos() );
}
*/

/******************************************
 * XML-Parser functions
 ******************************************/
//
// DocBook parser
//
void DocTreeViewImpl::parseBook(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseBook()" << endl;

    item->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    // TODO: Attributes Id, Lang

    // Elements
    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="bookinfo" ){
            parseBookInfo( node.toElement(), item );
        } else if ( node.isElement() && node.nodeName().lower()=="chapter" ){
            parseChapter( node.toElement(), item );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseBook()" << endl;
}


void DocTreeViewImpl::parseBookInfo(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseBookInfo()" << endl;

    Overview *overview = new Overview(item, i18n("Overview"));
    overview->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );
    QDomText data;

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                item->setText( 0, data.nodeValue() ); // assign title to RootItem
            }
        } else if ( node.isElement() && node.nodeName().lower()=="authorgroup" ){
            parseAuthorGroup( node.toElement(), overview );
        } else if ( node.isElement() && node.nodeName().lower()=="keywordset" ){
            parseKeywordSet( node.toElement(), overview );
        } else if ( node.isElement() && node.nodeName().lower()=="date" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                Date *date = new Date(overview, i18n("Date"));
                date->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );
                date->setText( 1, data.nodeValue() );
                date->setNode( data );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="releaseinfo" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                ReleaseInfo *relinfo = new ReleaseInfo(overview, i18n("Release"));
                relinfo->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );
                relinfo->setText( 1, data.nodeValue() );
                relinfo->setNode( data );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="abstract" ){
            parseAbstract( node.toElement(), overview );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseBookInfo()" << endl;
}


void DocTreeViewImpl::parseAuthorGroup(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseAuthorGroup()" << endl;

    // item = overview
    AuthorGroup *authorgroup = new AuthorGroup(item, i18n("Author(s)"));
    authorgroup->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="author" ){
            parseAuthor( node.toElement(), authorgroup );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseAuthorGroup()" << endl;
}


void DocTreeViewImpl::parseAuthor(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseAuthor()" << endl;

    // item = authorgroup
    QDomText data;
    QDomText firstname = QDomText();
    QDomText surname = QDomText();
    QString s_firstname = QString::null;
    QString s_surname = QString::null;
    
    Author *author = new Author(item);
    author->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="firstname" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                s_firstname = data.nodeValue();
                firstname = data;
            }
        } else if ( node.isElement() && node.nodeName().lower()=="surname" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                s_surname = data.nodeValue();
                surname = data;
            }
        }
        node = node.nextSibling();
    }
    author->setNode(firstname, surname);
    author->setText(0, i18n("Author") );
    author->setText(1, s_firstname + " " + s_surname);
    
    kdDebug(100200) << "--- leaving parseAuthor()" << endl;
}


void DocTreeViewImpl::parseKeywordSet(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseKeywordSet()" << endl;

    // item = overview
    KeywordSet *keywordset = new KeywordSet(item, i18n("Keywords"));
    keywordset->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomText data;
    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="keyword" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                Keyword *keyword = new Keyword(keywordset);
                keyword->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );
                keyword->setNode( data );
                keyword->setText(0, data.nodeValue() );
            }
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseKeywordSet()" << endl;
}


void DocTreeViewImpl::parseAbstract(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseAbstract()" << endl;

    // item = overview
    QDomText data;
    Abstract *abstract = new Abstract(item, i18n("Abstract"));
    abstract->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), abstract );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseAbstract()" << endl;
}


void DocTreeViewImpl::parseChapter(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseChapter()" << endl;
    
    QDomText data;
    Chapter *chapter = new Chapter(item, i18n("Chapter"));
    chapter->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                chapter->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), chapter );
        } else if ( node.isElement() && node.nodeName().lower()=="sect1" ){
            parseSect1( node.toElement(), chapter );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseChapter()" << endl;
}


void DocTreeViewImpl::parseSect1(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseSect1()" << endl;
    
    QDomText data;
    Sect1 *sect1 = new Sect1(item, i18n("Section Level 1"));
    sect1->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                sect1->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), sect1 );
        } else if ( node.isElement() && node.nodeName().lower()=="sect2" ){
            parseSect2( node.toElement(), sect1 );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseSect1()" << endl;
}


void DocTreeViewImpl::parseSect2(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseSect2()" << endl;
    
    QDomText data;
    Sect2 *sect2 = new Sect2(item, i18n("Section Level 2"));
    sect2->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                sect2->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), sect2 );
        } else if ( node.isElement() && node.nodeName().lower()=="sect3" ){
            parseSect3( node.toElement(), sect2 );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseSect2()" << endl;
}


void DocTreeViewImpl::parseSect3(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseSect3()" << endl;
    
    QDomText data;
    Sect3 *sect3 = new Sect3(item, i18n("Section Level 3"));
    sect3->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                sect3->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), sect3 );
        } else if ( node.isElement() && node.nodeName().lower()=="sect4" ){
            parseSect4( node.toElement(), sect3 );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseSect3()" << endl;
}


void DocTreeViewImpl::parseSect4(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseSect4()" << endl;
    
    QDomText data;
    Sect4 *sect4 = new Sect4(item, i18n("Section Level 4"));
    sect4->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                sect4->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), sect4 );
        } else if ( node.isElement() && node.nodeName().lower()=="sect5" ){
            parseSect5( node.toElement(), sect4 );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseSect4()" << endl;
}


void DocTreeViewImpl::parseSect5(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parseSect5()" << endl;
    
    QDomText data;
    Sect5 *sect5 = new Sect5(item, i18n("Section Level 4"));
    sect5->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );

    QDomNode node = element.firstChild();
    while( !node.isNull() ){
        if( node.isElement() && node.nodeName().lower()=="title" ){
            data = node.firstChild().toText();
            if( !data.isNull() ){
                sect5->setText( 0, data.nodeValue() );
            }
        } else if ( node.isElement() && node.nodeName().lower()=="para" ){
            parsePara( node.toElement(), sect5 );
        }
        node = node.nextSibling();
    }
    kdDebug(100200) << "--- leaving parseSect5()" << endl;
}


void DocTreeViewImpl::parsePara(const QDomElement &element, ListViewInterface *item)
{
    kdDebug(100200) << "+++ entering parsePara()" << endl;

    Para *para = new Para(item, i18n("Paragraph"));
    para->setText(3, QString("%1").arg(++m_idCounter).rightJustify(8,'0') );
    para->setNode( element );
    
    kdDebug(100200) << "--- leaving parsePara()" << endl;
}


