From 31aab60ce34efcfcd9b287aabdc5deb1de1e031d Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Wed, 8 Feb 2012 21:45:46 +0100 Subject: [PATCH] add a model for XML, editing is still missing --- src/misc/dommodel.cpp | 401 +++++++++++++++++++++++++++++++++++++++++++++ src/misc/dommodel.h | 83 ++++++++++ src/misc/misc.pri | 6 +- src/templates/odfedit.cpp | 62 ++++++- 4 files changed, 543 insertions(+), 9 deletions(-) create mode 100644 src/misc/dommodel.cpp create mode 100644 src/misc/dommodel.h diff --git a/src/misc/dommodel.cpp b/src/misc/dommodel.cpp new file mode 100644 index 0000000..3b287e4 --- /dev/null +++ b/src/misc/dommodel.cpp @@ -0,0 +1,401 @@ +// +// C++ Implementation: DOM model +// +// +// Author: Konrad Rosenbaum , (C) 2012 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#include "dommodel.h" + +#include +#include +#include +#include + +#include + +//switch this to en-/dis-able debug messages +static inline +// QDebug mDebug(){return QDebug(QtDebugMsg);} +QNoDebug mDebug(){return QNoDebug();} + +#include + +class DPTR_CLASS_NAME(MDomItemModel) +{ + public: + //nextid: counter for internal DOM node ids + //docid: id of the document node + int nextidx,docidx; + //cache structure that holds one node each + struct DomCache{ + QDomNode node; + QListchildren; + int parent; + DomCache():parent(-1){} + DomCache(const DomCache&)=default; + DomCache(const QDomNode&node_,int parent_=-1) + :node(node_),parent(parent_){} + }; + //cache of all DOM nodes, index is the internal ID + QMapdomCache; + //set of node types that are shown + QSetshowType; + //functors for generation of the display + QMap contentFromNode; + //config for element display + int elemAttrLen,elemTextLen; + //header data + QMap,QVariant>horizHeader,vertHeader; + + Private(); + + int buildCache(const QDomNode&,int p=-1); +}; + +DEFINE_DPTR(MDomItemModel); + +MDomItemModel::MDomItemModel(QObject* parent) + : QAbstractItemModel(parent) +{ +} + +MDomItemModel::Private::Private() + :nextidx(1),docidx(-1) +{ + showType<domCache.clear(); + d->docidx=d->buildCache(doc); + mDebug()<<"DOM model setting new document - found"<domCache.size()<<"DOM nodes, root node is"<docidx; + endResetModel(); +} + +int MDomItemModel::Private::buildCache(const QDomNode& node,int parent) +{ + if(node.isNull())return -1; + //create this node + int idx=nextidx++; + DomCache dc(node,parent); + //recursively add children + QDomNodeList nl=node.childNodes(); + mDebug()<<"node"<& nt) +{ + if(nt==d->showType)return; + beginResetModel(); + d->showType=nt; + if(!d->showType.contains(QDomNode::ElementNode)) + d->showType.insert(QDomNode::ElementNode); + mDebug()<<"DOM Model - showing these types"<showType; + endResetModel(); +} + +void MDomItemModel::addShowNodeType(QDomNode::NodeType nt) +{ + if(d->showType.contains(nt))return; + beginResetModel(); + d->showType.insert(nt); + mDebug()<<"DOM Model - showing these types"<showType; + endResetModel(); +} + +void MDomItemModel::addShowNodeTypes(const QSet< QDomNode::NodeType >& nt) +{ + if(nt.isEmpty())return; + beginResetModel(); + foreach(QDomNode::NodeType n,nt) + if(!d->showType.contains(n)) + d->showType.insert(n); + mDebug()<<"DOM Model - showing these types"<showType; + endResetModel(); +} + +void MDomItemModel::showAllNodeTypes() +{ + showNodeTypes(QSet() + < MDomItemModel::shownNodeTypes()const +{ + return d->showType; +} + +void MDomItemModel::showElementProperties(int ma,int mt) +{ + if(d->elemAttrLen == ma && d->elemTextLen == mt)return; + beginResetModel(); + d->elemAttrLen=ma; + d->elemTextLen=mt; + endResetModel(); +} + +QDomNode MDomItemModel::node(const QModelIndex& index) const +{ + if(!index.isValid()){ + if(!d->domCache.contains(d->docidx)) return QDomNode(); + else return d->domCache[d->docidx].node.cloneNode(); + } + if(!d->domCache.contains(index.internalId()))return QDomNode(); + return d->domCache[index.internalId()].node.cloneNode(); +} + +void MDomItemModel::setNode(const QModelIndex& index, const QDomNode& ) +{ +#warning implement +} + +int MDomItemModel::columnCount(const QModelIndex& parent) const +{ + return 1; +} + +QDomDocument MDomItemModel::domDocument() const +{ + if(d->docidx>=0 && d->domCache.contains(d->docidx)) + return d->domCache[d->docidx].node.cloneNode().toDocument(); + else + return QDomDocument(); +} + +QVariant MDomItemModel::data(const QModelIndex& index, int role) const +{ + if(role==DomNodeRole)return QVariant::fromValue(node(index)); + //root index + QDomNode node; + if(!index.isValid()){ + if(d->domCache.contains(d->docidx)) + node=d->domCache[d->docidx].node; + }else{ + if(d->domCache.contains(index.internalId())) + node=d->domCache[index.internalId()].node; + } + //check node + if(node.isNull())return QVariant(); + //try to find callback + if(d->contentFromNode.contains(role))return d->contentFromNode[role](node); + //no callback: display role + if(role==Qt::DisplayRole)return displayFromNode(node); + //fall back + return QVariant(); +} + +QVariant MDomItemModel::displayFromNode(const QDomNode& node) const +{ + if(node.isNull())return "NULL node"; + switch(node.nodeType()){ + case QDomNode::ElementNode:{ + QString r="tag <"+node.nodeName(); + QString a,t; + //append attribs + if(d->elemAttrLen>=0 && node.hasAttributes()){ + auto attrs=node.attributes(); + QString a; + for(int i=0;id->elemAttrLen){ + a.truncate(d->elemAttrLen); + a+="..."; + } + r+=" "+a.trimmed(); + } + r+=">"; + //append text + if(d->elemTextLen>0){ + QString t; + QDomNodeList nl=node.childNodes(); + for(int i=0;i0)t+=" "; + t+=QString(nd.nodeValue()).replace('\n',' '); + } + } + if(t.size()>d->elemTextLen){ + t.truncate(d->elemTextLen); + t+="..."; + } + r+=t.trimmed(); + } + return r; + } + case QDomNode::AttributeNode: + return "attribute "+node.nodeName()+"="+node.nodeValue(); + case QDomNode::TextNode: + case QDomNode::CDATASectionNode: + return "text \""+QString(node.nodeValue()).replace('\n',' ')+"\""; + default: + return "node ("+QString::number(node.nodeType())+"): "+node.nodeName()+" - \""+node.nodeValue()+"\""; + } +} + +int MDomItemModel::rowCount(const QModelIndex& parent) const +{ + int pid=-1; + if(!parent.isValid())pid=d->docidx; + else pid=parent.internalId(); + if(!d->domCache.contains(pid)){ + mDebug()<<"DOM Model - index"<domCache[pid].children) + if(d->domCache.contains(cid) && d->showType.contains(d->domCache[cid].node.nodeType())) + ret++; + mDebug()<<"DOM Model - index"<domCache[pid].children.size()<<"items"; + return ret; +} + +QModelIndex MDomItemModel::index(int row, int column, const QModelIndex& parent) const +{ + //only col 0 is valid for now + if(column!=0){ + mDebug()<<"DOM Model - index for wrong column requested"<docidx; + else pid=parent.internalId(); + if(pid<=0 || !d->domCache.contains(pid)){ + mDebug()<<"DOM Model - index for invalid node requested"<=d->domCache[pid].children.size()){ + mDebug()<<"DOM Model - index of empty parent requested, parent:"<domCache[pid].children){ + if(!d->showType.contains(d->domCache[cid].node.nodeType())) + continue; + if(row==rc++){ + mDebug()<<"DOM Model - creating index"<domCache.contains(index.internalId())){ + mDebug()<<"DOM Model - requesting parent of"<domCache[index.internalId()].parent; + if(pid<=0 || !d->domCache.contains(pid)){ + mDebug()<<"DOM Model - requesting parent of"<domCache[pid].parent; + //is grand-parent invalid? (parent must be the root node) + if(gpid<=0 || !d->domCache.contains(gpid)){ + mDebug()<<"DOM Model - requesting parent of"<domCache[gpid].children){ + if(nid==pid)break; + if(d->showType.contains(d->domCache[nid].node.nodeType()))row++; + } + mDebug()<<"DOM Model - requesting parent of"<contentFromNode.insert(r,f); + else + d->contentFromNode.remove(r); +} + +QVariant MDomItemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation==Qt::Horizontal) + return d->horizHeader.value(QPair(section,role)); + else + return d->vertHeader.value(QPair(section,role)); +} + +bool MDomItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role) +{ + if(orientation==Qt::Horizontal) + d->horizHeader.insert(QPair(section,role),value); + else + d->vertHeader.insert(QPair(section,role),value); + return true; +} diff --git a/src/misc/dommodel.h b/src/misc/dommodel.h new file mode 100644 index 0000000..030a348 --- /dev/null +++ b/src/misc/dommodel.h @@ -0,0 +1,83 @@ +// +// C++ Interface: odtrender +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2008-2011 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#ifndef MAGICSMOKE_DOMMODEL_H +#define MAGICSMOKE_DOMMODEL_H + +#include +#include +#include + +#include + +class QDomNode; +class QDomDocument; + +typedef std::function MDomModelFunctor; + +///This is a specialized model type that shows and allows to manipulate a DOM tree. +///The model holds a copy of the DOM tree, so the original is not changed even if the model changes. +///You can retrieve a current copy of the model's DOM tree by calling domDocument() +class MDomItemModel:public QAbstractItemModel +{ + Q_OBJECT + DECLARE_DPTR(d); + public: + MDomItemModel(QObject* parent = 0); + MDomItemModel(const QDomDocument&, QObject* parent = 0); + + virtual QDomDocument domDocument()const; + + static const int DomNodeRole=Qt::UserRole; + + virtual int columnCount(const QModelIndex&parent=QModelIndex())const; + virtual QVariant data(const QModelIndex&index, int role=Qt::DisplayRole)const; + virtual QModelIndex index(int row, int column, const QModelIndex&parent=QModelIndex())const; + virtual QModelIndex parent(const QModelIndex&index)const; + virtual int rowCount(const QModelIndex&parent=QModelIndex())const; + + virtual QSet shownNodeTypes()const; + + virtual QDomNode node(const QModelIndex&index)const; + + bool setHeaderData(int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole)const; + + public slots: + virtual void setDomDocument(const QDomDocument&); + + void showNodeTypes(const QSet&); + void addShowNodeType(QDomNode::NodeType); + void addShowNodeTypes(const QSet&); + void showAllNodeTypes(); + + void showElementProperties(int maxattr,int maxtext); + + virtual void setNode(const QModelIndex&index,const QDomNode&); + virtual bool removeRows(int row, int count, const QModelIndex&parent=QModelIndex()); + virtual void removeNode(const QModelIndex&); + virtual void reparentNode(const QModelIndex&,const QDomNode&); + virtual void reparentChildNodes(const QModelIndex&); + + void setContentFromNodeCall(int role,MDomModelFunctor callback); + + virtual void clear(){setDomDocument(QDomDocument());} + + private: + ///default implementation for Display-Role + QVariant displayFromNode(const QDomNode&)const; +}; + +Q_DECLARE_METATYPE(QDomNode); +Q_DECLARE_METATYPE(MDomModelFunctor); + +#endif diff --git a/src/misc/misc.pri b/src/misc/misc.pri index 0793c37..8cc486c 100644 --- a/src/misc/misc.pri +++ b/src/misc/misc.pri @@ -5,7 +5,8 @@ HEADERS += \ $$PWD/sclock.h \ $$PWD/formula.h \ $$PWD/lambda.h \ - $$PWD/msengine.h + $$PWD/msengine.h \ + $$PWD/dommodel.h SOURCES += \ $$PWD/code39.cpp \ @@ -14,7 +15,8 @@ SOURCES += \ $$PWD/waitcursor.cpp \ $$PWD/sclock.cpp \ $$PWD/formula.cpp \ - $$PWD/msengine.cpp + $$PWD/msengine.cpp \ + $$PWD/dommodel.cpp INCLUDEPATH += $$PWD QMAKE_CXXFLAGS += -std=gnu++0x \ No newline at end of file diff --git a/src/templates/odfedit.cpp b/src/templates/odfedit.cpp index 13da05e..409bcf1 100644 --- a/src/templates/odfedit.cpp +++ b/src/templates/odfedit.cpp @@ -13,7 +13,8 @@ #include "odfedit.h" #include "centbox.h" #include "misc.h" -#include "ticketrender.h" +#include "odtrender.h" +#include "dommodel.h" #include "MOTicket" #include "MOVoucher" @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +53,7 @@ #include #include #include +#include class DPTR_CLASS_NAME(MOdfEditor) @@ -58,6 +61,11 @@ class DPTR_CLASS_NAME(MOdfEditor) public: //template file QString mFileName; + //content.xml + QDomDocument mContent; + //widgets + QTreeView*mDomTree; + MDomItemModel*mDomModel; //file contents struct File{ QString name; @@ -68,9 +76,6 @@ class DPTR_CLASS_NAME(MOdfEditor) File(QString n,Type t,const QByteArray&c):name(n),type(t),content(c){} }; QList mFiles; - //content.xml - QDomDocument mContent; - //widgets }; DEFINE_DPTR(MOdfEditor); @@ -109,6 +114,17 @@ MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent, QHBoxLayout*hl,*hl2; QPushButton*p; + central->addWidget(d->mDomTree=new QTreeView); + d->mDomTree->setModel(d->mDomModel=new MDomItemModel(this)); + d->mDomTree->setEditTriggers(QAbstractItemView::NoEditTriggers); + d->mDomModel->setHeaderData(0,Qt::Horizontal,tr("Document XML Tree"),Qt::DisplayRole); + d->mDomModel->showAllNodeTypes(); +// d->mDomModel->addShowNodeType(QDomNode::AttributeNode); + d->mDomTree->setSelectionMode(QAbstractItemView::SingleSelection); + d->mDomTree->setSelectionBehavior(QAbstractItemView::SelectRows); + d->mDomTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + d->mDomTree->header()->setResizeMode(0,QHeaderView::ResizeToContents); + d->mDomTree->header()->setStretchLastSection(false); //statusbar statusBar()->setSizeGripEnabled(true); @@ -153,11 +169,43 @@ void MOdfEditor::loadFile(QString fn) void MOdfEditor::parseTemplate(QByteArray bytes) { - if(!d->mContent.setContent(bytes)){ - QMessageBox::warning(this,tr("Warning"),tr("Unable to interpret template data.")); + d->mContent.clear(); + d->mDomModel->clear(); + //convert V1->V2 + QString err;int errln,errcl; + bool dov1=false; + const QString tpename=OdfTemplatePrefix+":template"; + if(!d->mContent.setContent(bytes,false,&err,&errln,&errcl)){ + qDebug()<<"Hmm, not XML, trying version 1 converter..."; + qDebug()<<" Info: line ="<mContent.clear(); + } + } + //conversion process + if(!dov1){ + d->mDomModel->setDomDocument(d->mContent); + d->mDomTree->expandAll(); + return; + } + //try again + if(!d->mContent.setContent(MOdtRenderer::convertV1toV2(bytes),false,&err,&errln,&errcl)){ + qDebug()<<"Hmm, definitely not XML - even after conversion, aborting..."; + qDebug()<<" Info: line ="<mContent.documentElement().childNodes().size()<<"children and"<mContent.documentElement().attributes().size()<<"attribs"; } - //TODO: converter V1->V2 +// qDebug()<<"dump\n"<mContent.toByteArray(); + d->mDomModel->setDomDocument(d->mContent); + d->mDomTree->expandAll(); } void MOdfEditor::openFile() -- 1.7.2.5