fix ODF editing, add copy item
authorKonrad Rosenbaum <konrad@silmor.de>
Sun, 16 Mar 2014 14:06:13 +0000 (15:06 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Sun, 16 Mar 2014 14:06:13 +0000 (15:06 +0100)
src/misc/domiterator.h [new file with mode: 0644]
src/misc/dommodel.cpp
src/templates/odfedit.cpp
src/templates/odfedit.h

diff --git a/src/misc/domiterator.h b/src/misc/domiterator.h
new file mode 100644 (file)
index 0000000..5161c53
--- /dev/null
@@ -0,0 +1,42 @@
+//
+// C++ Implementation: DOM node iterator for range based for loops
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+// originally introduced at http://silmor.de/qtstuff.domfor.php
+
+#ifndef SMOKE_DOM_ITERATOR_H
+#define SMOKE_DOM_ITERATOR_H
+
+class QDomNodeIterator
+{
+        int pos;
+        const QDomNodeList&container;
+public:
+        //constructors and assignments
+        QDomNodeIterator(const QDomNodeList&l,int p):pos(p),container(l){}
+        QDomNodeIterator(const QDomNodeIterator&)=default;
+        QDomNodeIterator(QDomNodeIterator&&)=default;
+        QDomNodeIterator& operator=(const QDomNodeIterator&)=default;
+        QDomNodeIterator& operator=(QDomNodeIterator&&)=default;
+        
+        //increment
+        QDomNodeIterator& operator++(){pos++;return *this;}
+        
+        //comparison
+        bool operator==(const QDomNodeIterator&o){return pos==o.pos && container==o.container;}
+        bool operator!=(const QDomNodeIterator&o){return pos!=o.pos || container!=o.container;}
+        
+        //indirection
+        QDomNode operator*(){return container.at(pos);}
+};
+
+//begin and end
+inline QDomNodeIterator begin(const QDomNodeList&l){return QDomNodeIterator(l,0);}
+inline QDomNodeIterator end(const QDomNodeList&l){return QDomNodeIterator(l,l.size());}
+
+
+#endif
index 7c81656..62f61ad 100644 (file)
@@ -2,7 +2,7 @@
 // C++ Implementation: DOM model
 //
 //
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2012
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2012-2014
 //
 // Copyright: See README/COPYING.GPL files that come with this distribution
 //
@@ -192,10 +192,10 @@ 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();
+               else return d->domCache[d->docidx].node;
        }
        if(!d->domCache.contains(index.internalId()))return QDomNode();
-       QDomNode node=d->domCache[index.internalId()].node.cloneNode();
+       QDomNode node=d->domCache[index.internalId()].node;
        qDebug()<<"returning node"<<index.internalId()<<"type"<<node.nodeType()<<node.nodeName()<<node.nodeValue();
        return node;
 }
@@ -275,7 +275,10 @@ void MDomItemModel::insertNodes(const QList< QDomNode >& newnodes, const QModelI
        //insert
        for(QDomNode nd:newnodes){
                QDomNode nn=nd.cloneNode();
-               pn.insertBefore(nn,sibl);
+                qDebug()<<"Attempting insert with"<<nn.nodeName()<<nn.nodeValue();
+               nn=pn.insertBefore(nn,sibl);
+                if(nn.isNull())
+                        qDebug()<<"!!!!ARG insert of DOM node failed! into"<<pn.nodeName()<<"before"<<(int)sibl.nodeType()<<sibl.nodeName()<<sibl.nodeValue()<<"which is child of"<<sibl.parentNode().nodeName();
                d->domCache[pid].children.insert(row,d->buildCache(nn,pid));
        }
        endInsertRows();
index 86dcbd6..7b45f37 100644 (file)
@@ -16,6 +16,7 @@
 #include "odtrender.h"
 #include "dommodel.h"
 #include "cleanup.h"
+#include "domiterator.h"
 
 #include "orderwin.h"
 #include "eventsummary.h"
@@ -91,11 +92,12 @@ class DPTR_CLASS_NAME(MOdfEditor)
                //tree menu
                QAction*maAddIntoCalc,*maWrapInCond,*maAddIntoComment;
                QAction*maInsBehindElse,*maWrapInLoop;
-               QAction*maInsBehindCalc,*maInsBehindComment,*maUnwrap,*maDelItem;
-               void setActions(const QList<QAction*>&);
+               QAction*maInsBehindCalc,*maInsBehindComment,*maUnwrap;
+                QAction*maInsItemInto,*maInsItemBehind,*maCpyItem,*maDelItem;
+               void setActions(const QList<QAction*>&,bool haveselect=true);
                //current node
                QModelIndex mCurIndex;
-               QDomNode mCurNode;
+               QDomNode mCurNode,mClipNode;
                bool mChanged;
                //test printing
                QString mTestFile;
@@ -158,6 +160,10 @@ MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent,
        d->maInsBehindElse=m->addAction(tr("Insert &Else behind current"),this,SLOT(insElse()));
        d->maAddIntoComment=m->addAction(tr("Insert Comment into current"),this,SLOT(insCommentIntoCurrent()));
        d->maInsBehindComment=m->addAction(tr("Insert Comment behind current"),this,SLOT(insCommentBehindCurrent()));
+        m->addSeparator();
+        d->maCpyItem=m->addAction(tr("Copy current item to clipboard"),this,SLOT(copyItem()));
+        d->maInsItemInto=m->addAction(tr("Insert clipboard into current"),this,SLOT(pasteItemInto()));
+        d->maInsItemBehind=m->addAction(tr("Insert clipboard behind current"),this,SLOT(pasteItemBehind()));
        m->addSeparator();
        d->maUnwrap=m->addAction(tr("Unwrap Loop/Condition"),this,SLOT(unwrapItem()));
        d->maDelItem=m->addAction(tr("&Remove Item"),this,SLOT(delItem()));
@@ -389,6 +395,7 @@ void MOdfEditor::parseTemplate(QByteArray bytes,bool istempl)
 
 void MOdfEditor::openFile(bool istemp)
 {
+        d->mClipNode.clear();
        QString title=istemp?tr("Open ODF Template"):tr("Open ODF File");
        QString flt=istemp?tr("ODF Template File (*.od?t);;All Files (*)"):tr("ODF File (*.od?);;All Files (*)");
        QString fn=QFileDialog::getOpenFileName(this,title,currentDir(),flt);
@@ -574,13 +581,13 @@ void MOdfEditor::selectionChange()
        MCleanup clean([&](){setChanged(false);});
        d->mCurIndex=QModelIndex();
        d->mCurNode=QDomNode();
-       d->setActions(QList<QAction*>());
+       d->setActions(QList<QAction*>(),false);
        //get selection
        QModelIndexList idxl=d->mDomTree->selectionModel()->selectedRows();
        if(idxl.size()!=1){
                emit switchStack(d->st_none);
                if(idxl.size()>1)
-                       d->setActions(QList<QAction*>()<<d->maWrapInCond<<d->maWrapInLoop);
+                       d->setActions(QList<QAction*>()<<d->maWrapInCond<<d->maWrapInLoop,false);
                return;
        }
        //check element type
@@ -732,7 +739,7 @@ void MOdfEditor::saveCurrentNode()
 //     qDebug()<<"done saving";
 }
 
-void MOdfEditor::Private::setActions(const QList< QAction* >& act)
+void MOdfEditor::Private::setActions(const QList< QAction* >& act,bool haveselect)
 {
        maAddIntoCalc->setEnabled(act.contains( maAddIntoCalc));
        maWrapInCond->setEnabled(act.contains( maWrapInCond));
@@ -743,6 +750,9 @@ void MOdfEditor::Private::setActions(const QList< QAction* >& act)
        maInsBehindComment->setEnabled(act.contains( maInsBehindComment));
        maUnwrap->setEnabled(act.contains(maUnwrap));
        maDelItem->setEnabled(act.contains(maDelItem));
+        maCpyItem->setEnabled(haveselect);
+        maInsItemBehind->setEnabled(haveselect && !mClipNode.isNull());
+        maInsItemInto->setEnabled(haveselect && !mClipNode.isNull());
 }
 
 void MOdfEditor::insCalcBehindCurrent()
@@ -849,7 +859,7 @@ void MOdfEditor::wrapInCond(QString tag)
                if(lrow<0)frow=lrow=idx.row();
                if(lrow<idx.row())lrow=idx.row();
                if(frow>idx.row())frow=idx.row();
-               ndlist<<d->mDomModel->node(idx);
+               ndlist<<d->mDomModel->node(idx).cloneNode(true);
        }
        if(ndlist.size()<1)return;
        //create new parent node
@@ -886,7 +896,7 @@ void MOdfEditor::unwrapItem()
        QModelIndex idx=d->mDomTree->currentIndex();
        if(!idx.isValid())return;
        //get the node and parent index
-       QDomElement el=d->mDomModel->node(idx).toElement();
+       QDomElement el=d->mDomModel->node(idx).cloneNode(true).toElement();
        QModelIndex pidx=idx.parent();
        const int row=idx.row();
        //remove the original
@@ -896,3 +906,96 @@ void MOdfEditor::unwrapItem()
        //expand the tree
        d->expandTree(pidx);
 }
+
+void MOdfEditor::pasteItemBehind()
+{
+        //get current
+        QModelIndex idx=d->mDomTree->currentIndex();
+        if(!idx.isValid())return;
+        QDomNode cnode=d->mDomModel->node(idx);
+        if(cnode.isNull())return;
+        //check
+        if(d->mClipNode.isNull()){
+                QMessageBox::warning(this,tr("Warning"),tr("There is nothing in the clipboard. Please copy a node first."));
+                return;
+        }
+        //insert item
+        d->mDomModel->insertNode(d->mClipNode,idx.parent(),idx.row()+1);
+}
+
+void MOdfEditor::pasteItemInto()
+{
+        //get current
+        QModelIndex idx=d->mDomTree->currentIndex();
+        if(!idx.isValid())return;
+        QDomNode cnode=d->mDomModel->node(idx);
+        if(cnode.isNull())return;
+        //check
+        if(d->mClipNode.isNull()){
+                QMessageBox::warning(this,tr("Warning"),tr("There is nothing in the clipboard. Please copy a node first."));
+                return;
+        }
+        //insert item
+        d->mDomModel->insertNode(d->mClipNode,idx,0);
+        //make sure it is visible
+        d->expandTree(idx);
+}
+
+static inline
+QDomNode xclonenode(const QDomNode&node)
+{
+        if(node.isNull())return QDomNode();
+        QDomDocument doc=node.ownerDocument();
+        QDomNode nn;
+        switch(node.nodeType()){
+                case QDomNode::ElementNode:
+                        nn=doc.createElement(node.nodeName());
+                        for(const QDomNode&ond:node.childNodes())
+                                nn.appendChild(xclonenode(ond));
+                        break;
+                case QDomNode::AttributeNode:
+                        nn=doc.createAttribute(node.nodeName());
+                        nn.setNodeValue(node.nodeValue());
+                        break;
+                case QDomNode::TextNode:
+                        nn=doc.createTextNode(node.nodeValue());
+                        break;
+                case QDomNode::CharacterDataNode:
+                case QDomNode::CDATASectionNode:
+                        nn=doc.createCDATASection(node.nodeValue());
+                        break;
+                case QDomNode::ProcessingInstructionNode:
+                        nn=doc.createProcessingInstruction(node.nodeName(),node.nodeValue());
+                        break;
+                case QDomNode::CommentNode:
+                        nn=doc.createComment(node.nodeValue());
+                        break;
+                //those are currently not supported or it is completely useless to copy them
+                case QDomNode::EntityReferenceNode:
+                case QDomNode::EntityNode:
+                case QDomNode::DocumentNode:
+                case QDomNode::DocumentTypeNode:
+                case QDomNode::DocumentFragmentNode:
+                case QDomNode::NotationNode:
+                case QDomNode::BaseNode:
+                        return QDomNode();
+        }
+        return nn;
+}
+
+void MOdfEditor::copyItem()
+{
+        //get current
+        QModelIndex idx=d->mDomTree->currentIndex();
+        if(!idx.isValid())return;
+        QDomNode cnode=d->mDomModel->node(idx);
+        if(cnode.isNull())return;
+        //remember
+        if(!d->mClipNode.isNull())d->mClipNode.clear();
+        d->mClipNode=cnode.cloneNode(true);
+        //housekeeping
+        d->maInsItemBehind->setEnabled(!d->mClipNode.isNull());
+        d->maInsItemInto->setEnabled(!d->mClipNode.isNull());
+        if(d->mClipNode.isNull())
+                QMessageBox::warning(this,tr("Warning"),tr("Sorry, this kinde of node cannot be copied."));
+}
index 241b5d7..c901699 100644 (file)
@@ -79,6 +79,12 @@ class MOdfEditor:public QMainWindow
                void wrapInLoop();
                ///helper: unwrap the loop or condition
                void unwrapItem();
+                ///helper: copy arbitrary item to clipboard
+                void copyItem();
+                ///helper: insert clipboard item into current
+                void pasteItemInto();
+                ///helper: insert clipboard item after current
+                void pasteItemBehind();
                
                ///helper: test new template on an order
                void testOrder();