--- /dev/null
+//
+// C++ Implementation: DOM model
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2012
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "dommodel.h"
+
+#include <QDomDocument>
+#include <QDomElement>
+#include <QMap>
+#include <QSet>
+
+#include <QDebug>
+
+//switch this to en-/dis-able debug messages
+static inline
+// QDebug mDebug(){return QDebug(QtDebugMsg);}
+QNoDebug mDebug(){return QNoDebug();}
+
+#include <DPtr>
+
+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;
+ QList<int>children;
+ 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
+ QMap<int,DomCache>domCache;
+ //set of node types that are shown
+ QSet<QDomNode::NodeType>showType;
+ //functors for generation of the display
+ QMap<int,MDomModelFunctor> contentFromNode;
+ //config for element display
+ int elemAttrLen,elemTextLen;
+ //header data
+ QMap<QPair<int,int>,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<<QDomNode::ElementNode;
+ elemAttrLen=20;elemTextLen=30;
+}
+
+
+MDomItemModel::MDomItemModel(const QDomDocument& doc, QObject* parent)
+ : QAbstractItemModel(parent)
+{
+ setDomDocument(doc);
+}
+
+void MDomItemModel::setDomDocument(const QDomDocument& doc)
+{
+ beginResetModel();
+ d->domCache.clear();
+ d->docidx=d->buildCache(doc);
+ mDebug()<<"DOM model setting new document - found"<<d->domCache.size()<<"DOM nodes, root node is"<<d->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"<<idx<<"type"<<node.nodeType()<<"name"<<node.nodeName()<<"has"<<nl.size()<<"children";
+ for(int i=0;i<nl.size();i++)
+ dc.children.append(buildCache(nl.at(i),idx));
+ //insert this node
+ domCache.insert(idx,dc);
+ //return index
+ return idx;
+}
+
+void MDomItemModel::showNodeTypes(const QSet< QDomNode::NodeType >& 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"<<d->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"<<d->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"<<d->showType;
+ endResetModel();
+}
+
+void MDomItemModel::showAllNodeTypes()
+{
+ showNodeTypes(QSet<QDomNode::NodeType>()
+ <<QDomNode::ElementNode
+ <<QDomNode::AttributeNode
+ <<QDomNode::TextNode
+ <<QDomNode::CDATASectionNode
+ <<QDomNode::EntityReferenceNode
+ <<QDomNode::EntityNode
+ <<QDomNode::ProcessingInstructionNode
+ <<QDomNode::CommentNode
+ <<QDomNode::DocumentNode
+ <<QDomNode::DocumentTypeNode
+ <<QDomNode::DocumentFragmentNode
+ <<QDomNode::NotationNode
+ <<QDomNode::BaseNode
+ );
+}
+
+QSet< QDomNode::NodeType > 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;i<attrs.size();i++){
+ if(i)a+=" ";
+ QDomAttr att=attrs.item(i).toAttr();
+ a+=att.nodeName()+"=\""+att.nodeValue()+"\"";
+ }
+ if(a.size()>d->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;i<nl.size();i++){
+ QDomNode nd=nl.at(i);
+ if(nd.isCharacterData()){
+ if(t.size()>0)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"<<parent.row()<<parent.column()<<parent.internalId()<<"does not exist";
+ return 0;
+ }
+ int ret=0;
+ foreach(int cid,d->domCache[pid].children)
+ if(d->domCache.contains(cid) && d->showType.contains(d->domCache[cid].node.nodeType()))
+ ret++;
+ mDebug()<<"DOM Model - index"<<parent.row()<<parent.column()<<pid<<"has"<<ret<<"rows out of"<<d->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"<<row<<column;
+ return QModelIndex();
+ }
+ //discard invalid rows
+ if(row<0){
+ mDebug()<<"DOM Model - index for negative row requested"<<row<<column;
+ return QModelIndex();
+ }
+ //validate/find parent
+ int pid=-1;
+ if(!parent.isValid())pid=d->docidx;
+ else pid=parent.internalId();
+ if(pid<=0 || !d->domCache.contains(pid)){
+ mDebug()<<"DOM Model - index for invalid node requested"<<row<<column<<pid<<"parent is"<<parent.row()<<parent.column()<<parent.internalId();
+ return QModelIndex();
+ }
+ //try to find element
+ if(row>=d->domCache[pid].children.size()){
+ mDebug()<<"DOM Model - index of empty parent requested, parent:"<<parent.row()<<parent.column()<<parent.internalId();
+ return QModelIndex();
+ }
+ int rc=0;
+ foreach(int cid,d->domCache[pid].children){
+ if(!d->showType.contains(d->domCache[cid].node.nodeType()))
+ continue;
+ if(row==rc++){
+ mDebug()<<"DOM Model - creating index"<<row<<0<<cid<<"of parent"<<parent.row()<<parent.column()<<parent.internalId();
+ return createIndex(row,0,cid);
+ }
+ }
+ //failed
+ mDebug()<<"DOM Model - requested index out of range"<<row<<column<<"of parent"<<parent.row()<<parent.column()<<parent.internalId();
+ return QModelIndex();
+}
+
+QModelIndex MDomItemModel::parent(const QModelIndex& index) const
+{
+ if(!index.isValid() || !d->domCache.contains(index.internalId())){
+ mDebug()<<"DOM Model - requesting parent of"<<index.row()<<index.column()<<index.internalId()<<"- invalid item";
+ return QModelIndex();
+ }
+ //find parent
+ int pid=d->domCache[index.internalId()].parent;
+ if(pid<=0 || !d->domCache.contains(pid)){
+ mDebug()<<"DOM Model - requesting parent of"<<index.row()<<index.column()<<index.internalId()<<"- invalid parent";
+ return QModelIndex();
+ }
+ //find grand-parent
+ int gpid=d->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"<<index.row()<<index.column()<<index.internalId()<<"- invalid grand-parent, cannot search";
+ return QModelIndex();
+ }
+ //find row of the parent inside grand-parent
+ int row=0;
+ foreach(int nid,d->domCache[gpid].children){
+ if(nid==pid)break;
+ if(d->showType.contains(d->domCache[nid].node.nodeType()))row++;
+ }
+ mDebug()<<"DOM Model - requesting parent of"<<index.row()<<index.column()<<index.internalId()<<"- parent is"<<row<<0<<pid;
+ return createIndex(row,0,pid);
+}
+
+void MDomItemModel::removeNode(const QModelIndex& )
+{
+#warning implement
+}
+
+bool MDomItemModel::removeRows(int row, int count, const QModelIndex& parent)
+{
+#warning implement
+ return false;
+}
+
+void MDomItemModel::reparentChildNodes(const QModelIndex& )
+{
+#warning implement
+}
+
+void MDomItemModel::reparentNode(const QModelIndex& , const QDomNode& )
+{
+#warning implement
+}
+
+void MDomItemModel::setContentFromNodeCall(int r,MDomModelFunctor f)
+{
+ if(f)
+ d->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<int,int>(section,role));
+ else
+ return d->vertHeader.value(QPair<int,int>(section,role));
+}
+
+bool MDomItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role)
+{
+ if(orientation==Qt::Horizontal)
+ d->horizHeader.insert(QPair<int,int>(section,role),value);
+ else
+ d->vertHeader.insert(QPair<int,int>(section,role),value);
+ return true;
+}
--- /dev/null
+//
+// C++ Interface: odtrender
+//
+// Description:
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008-2011
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_DOMMODEL_H
+#define MAGICSMOKE_DOMMODEL_H
+
+#include <QAbstractItemModel>
+#include <DPtrBase>
+#include <QDomNode>
+
+#include <functional>
+
+class QDomNode;
+class QDomDocument;
+
+typedef std::function<QVariant(const QDomNode&)> 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<QDomNode::NodeType> 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<QDomNode::NodeType>&);
+ void addShowNodeType(QDomNode::NodeType);
+ void addShowNodeTypes(const QSet<QDomNode::NodeType>&);
+ 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
#include "odfedit.h"
#include "centbox.h"
#include "misc.h"
-#include "ticketrender.h"
+#include "odtrender.h"
+#include "dommodel.h"
#include "MOTicket"
#include "MOVoucher"
#include <QPointF>
#include <QPushButton>
#include <QScrollArea>
+#include <QScrollBar>
#include <QSettings>
#include <QSignalMapper>
#include <QSizeF>
#include <QTemporaryFile>
#include <QUnZip>
#include <QZip>
+#include <QTreeView>
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;
File(QString n,Type t,const QByteArray&c):name(n),type(t),content(c){}
};
QList<File> mFiles;
- //content.xml
- QDomDocument mContent;
- //widgets
};
DEFINE_DPTR(MOdfEditor);
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);
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 ="<<errln<<"column ="<<errcl<<"error ="<<err;
+ dov1=true;
+ }else{
+ //check for template element
+ QDomElement de=d->mContent.documentElement();
+ if(de.isNull()||de.tagName()!=tpename){
+ qDebug()<<"Template is not v2, trying to convert...";
+ dov1=true;
+ d->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 ="<<errln<<"column ="<<errcl<<"error ="<<err;
return;
+ }else{
+ qDebug()<<"Successfully converted the template.";
+// qDebug()<<"document has"<<d->mContent.documentElement().childNodes().size()<<"children and"<<d->mContent.documentElement().attributes().size()<<"attribs";
}
- //TODO: converter V1->V2
+// qDebug()<<"dump\n"<<d->mContent.toByteArray();
+ d->mDomModel->setDomDocument(d->mContent);
+ d->mDomTree->expandAll();
}
void MOdfEditor::openFile()