enable odf editor to actually edit existing items
authorKonrad Rosenbaum <konrad@silmor.de>
Tue, 6 Mar 2012 07:56:49 +0000 (08:56 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Tue, 6 Mar 2012 07:56:49 +0000 (08:56 +0100)
src/misc/dommodel.cpp
src/misc/lambda.h
src/templates/odfedit.cpp

index 5961e62..9404a64 100644 (file)
@@ -195,7 +195,9 @@ QDomNode MDomItemModel::node(const QModelIndex& index) const
                else return d->domCache[d->docidx].node.cloneNode();
        }
        if(!d->domCache.contains(index.internalId()))return QDomNode();
-       return d->domCache[index.internalId()].node.cloneNode();
+       QDomNode node=d->domCache[index.internalId()].node.cloneNode();
+       qDebug()<<"returning node"<<index.internalId()<<"type"<<node.nodeType()<<node.nodeName()<<node.nodeValue();
+       return node;
 }
 
 void MDomItemModel::setNode(const QModelIndex& index, const QDomNode& node)
@@ -206,19 +208,26 @@ void MDomItemModel::setNode(const QModelIndex& index, const QDomNode& node)
        if(nid<=0 || !d->domCache.contains(nid))return;
        int pid=d->domCache[nid].parent;
        if(pid<=0 || !d->domCache.contains(pid))return;
-       //tell the view it is getting nasty, TODO: find a better way
-       beginResetModel();
+       //invalidate children
+       int csize=d->domCache[nid].children.size()-1;
+       beginRemoveRows(index,0,csize);
+       for(int n:d->domCache[nid].children)d->removeNode(n);
+       d->domCache[nid].children.clear();
+       endRemoveRows();
        //replace the node in DOM
-       QDomNode nnode=d->domCache[pid].node.replaceChild(node.cloneNode(),d->domCache[nid].node);
-       //add the new node
-       int nnid=d->buildCache(nnode,pid);
-       //replace reference
-       int i=d->domCache[pid].children.indexOf(nid);
-       d->domCache[pid].children.replace(i,nnid);
-       //remove the old node (inrecursion==true, so the method does not mess with DOM)
-       d->removeNode(nid,true);
+       qDebug()<<"replacing node"<<nid<<"at"<<index<<"old"<<d->domCache[nid].node.nodeValue()<<"new"<<node.nodeValue()<<node.nodeType();
+       QDomNode nnode=node.cloneNode();
+       d->domCache[pid].node.replaceChild(nnode,d->domCache[nid].node);
+       d->domCache[nid].node=nnode;
+       qDebug()<<"   new nodes type is"<<nnode.nodeType()<<"value"<<nnode.nodeValue();
+       //add the new child-nodes
+       QDomNodeList nl=nnode.childNodes();
+       beginInsertRows(index,0,nl.size()-1);
+       for(int i=0;i<nl.size();i++)
+               d->domCache[nid].children.append(d->buildCache(nl.at(i),nid));
+       endInsertRows();
        //tell the view we are finished
-       endResetModel();
+       dataChanged(index,index);
 }
 
 int MDomItemModel::columnCount(const QModelIndex&) const
index 5408647..53f451d 100644 (file)
@@ -39,4 +39,19 @@ class MLambda:public QObject
                void call(){if(m_ptr)m_ptr();}
 };
 
+///clean-up function: executes a lambda expression when it is destructed (i.e. when the instance leaves its scope
+class MCleanup
+{
+       std::function<void()>m_ptr;
+       public:
+               ///instantiates a new cleanup object and remembers the given function for later execution
+               MCleanup(const std::function<void()>&f):m_ptr(f){}
+               
+               ///sets a new cleanup function, removing the old one
+               void setFunction(const std::function<void()>&f){m_ptr=f;}
+               
+               ///destructs the object and executes the cleanup function
+               ~MCleanup(){if(m_ptr)m_ptr();}
+};
+
 #endif
index d08066b..ad4e56c 100644 (file)
@@ -72,13 +72,14 @@ class DPTR_CLASS_NAME(MOdfEditor)
                QTreeView*mDomTree;
                MDomItemModel*mDomModel;
                //stack indexes
-               int st_none,st_tag,st_text,st_loop,st_calc,st_comment,st_cond;
+               int st_none,st_tag,st_text,st_loop,st_calc,st_comment,st_cond,st_special;
                //stack widgets
                QLineEdit*mLoopVar,*mCalcExpr,*mTagName,*mCondExpr;
                QTextEdit*mText,*mComment;
                QTableView*mTagAttrs;
                QStandardItemModel*mTagAttrModel;
-               bool mChanged;
+               QStackedWidget*mStack;
+               QLabel*mSpecial;
                //tree menu
                QAction*maAddIntoCalc,*maWrapInCond,*maAddIntoComment,*maInsBehindElse,*maWrapInLoop;
                QAction*maInsBehindCalc,*maInsBehindComment,*maUnwrap,*maDelItem;
@@ -86,6 +87,7 @@ class DPTR_CLASS_NAME(MOdfEditor)
                //current node
                QModelIndex mCurIndex;
                QDomNode mCurNode;
+               bool mChanged;
                //file contents
                struct File{
                        QString name;
@@ -96,7 +98,10 @@ class DPTR_CLASS_NAME(MOdfEditor)
                        File(QString n,Type t,const QByteArray&c):name(n),type(t),content(c){}
                };
                QList<File> mFiles;
-               Private():mChanged(false){st_none=st_tag=st_text=st_loop=st_calc=st_comment=st_cond=-1;}
+               Private():mChanged(false)
+               {
+                       st_none=st_tag=st_text=st_loop=st_calc=st_comment=st_cond=st_special=-1;
+               }
 };
 
 DEFINE_DPTR(MOdfEditor);
@@ -185,14 +190,22 @@ MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent,
        //the editors...
        QStackedWidget*stack;
        central->addWidget(stack=new QStackedWidget);
+       d->mStack=stack;
        connect(this,SIGNAL(switchStack(int)),stack,SLOT(setCurrentIndex(int)));
        d->st_none=stack->addWidget(new QWidget);
        QWidget*w;
        //text node editor
+       d->st_special=stack->addWidget(w=new QWidget);
+       w->setLayout(vl=new QVBoxLayout);
+       vl->addWidget(new QLabel(tr("<h1>Special Template Tag<h1>")));
+       vl->addWidget(d->mSpecial=new QLabel("..."));
+       vl->addStretch(1);
+       //text node editor
        d->st_text=stack->addWidget(w=new QWidget);
        w->setLayout(vl=new QVBoxLayout);
        vl->addWidget(new QLabel(tr("<h1>Plain Text<h1>")));
        vl->addWidget(d->mText=new QTextEdit);
+       connect(d->mText,SIGNAL(textChanged()),this,SLOT(setChanged()));
        //tag editor
        d->st_tag=stack->addWidget(w=new QWidget);
        w->setLayout(vl=new QVBoxLayout);
@@ -208,6 +221,19 @@ MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent,
        connect(d->mTagAttrModel,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this,SLOT(setChanged()));
        connect(d->mTagAttrModel,SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this,SLOT(setChanged()));
        connect(d->mTagAttrModel,SIGNAL(rowsInserted(const QModelIndex&,int,int)), this,SLOT(setChanged()));
+       vl->addLayout(hl=new QHBoxLayout);
+       MLambda *btn;QPushButton*p;
+       btn=new MLambda([=](){d->mTagAttrModel->insertRow(d->mTagAttrModel->rowCount());},this);
+       hl->addStretch(1);
+       hl->addWidget(p=new QPushButton("Add"));
+       connect(p,SIGNAL(clicked()),btn,SLOT(call()));
+       btn=new MLambda([=](){
+               QModelIndex idx=d->mTagAttrs->currentIndex();
+               if(idx.isValid())
+                       d->mTagAttrModel->removeRow(idx.row(),idx.parent());
+       },this);
+       hl->addWidget(p=new QPushButton("Remove"));
+       connect(p,SIGNAL(clicked()),btn,SLOT(call()));
        //loop editor
        d->st_loop=stack->addWidget(w=new QWidget);
        w->setLayout(vl=new QVBoxLayout);
@@ -249,6 +275,8 @@ MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent,
 
 void MOdfEditor::setChanged(bool ch)
 {
+       if(ch!=d->mChanged)
+//     qDebug()<<"set changed state"<<ch;
        d->mChanged=ch;
 }
 
@@ -415,6 +443,7 @@ void MOdfEditor::saveXmlPart(QIODevice& fd)
 void MOdfEditor::selectionChange()
 {
        saveCurrentNode();
+       MCleanup clean([&](){setChanged(false);});
        d->mCurIndex=QModelIndex();
        d->mCurNode=QDomNode();
        d->setActions(QList<QAction*>());
@@ -474,9 +503,10 @@ void MOdfEditor::selectionChange()
                        d->setActions(QList<QAction*>()<<d->maInsBehindCalc<<d->maInsBehindComment<<d->maAddIntoCalc<<d->maAddIntoComment<<d->maWrapInCond<<d->maWrapInLoop<<d->maUnwrap<<d->maDelItem<<(allowElse?d->maInsBehindElse:nullptr));
                        return;
                }
-               if(nn[1]=="template"){
-                       emit switchStack(d->st_none);
+               if(nn[1]=="template" || nn[1]=="else"){
+                       emit switchStack(d->st_special);
                        d->setActions(QList<QAction*>());
+                       d->mSpecial->setText(tr("<b>Tag Type:</b> %1").arg(htmlize(nn[1])));
                        return;
                }
                //else: fall through to normal tags
@@ -515,11 +545,61 @@ void MOdfEditor::saveCurrentNode()
 {
        //nothing to do?
        if(!d->mChanged)return;
-#warning implement
-
-       qDebug()<<"fake saving item";
+       setChanged(false);//prevent it from calling itself
+       //make sure we are in non-changed state afterwards
+       MCleanup cleanchanged([&](){setChanged(false);});
+       //what is being displayed?
+       const int itemtype=d->mStack->currentIndex();
+       //non-changeable types
+       if(!d->mCurIndex.isValid() || itemtype==d->st_none || itemtype==d->st_special){
+               qDebug()<<"Warning: invalid item, or uneditable item type - why is it changed though?";
+               return;
+       }
+       //get current item
+       QDomNode cnode=d->mCurNode;
+       QModelIndex cidx=d->mCurIndex;
+       //remember new current item
+       QModelIndex ncidx=d->mDomTree->currentIndex();
+       d->mDomTree->setCurrentIndex(QModelIndex());
+       MCleanup cleanindex([&](){d->mDomTree->setCurrentIndex(ncidx);});
+       //handle item
+       if(itemtype==d->st_comment){
+               cnode.setNodeValue(d->mComment->toPlainText());
+       }else if(itemtype==d->st_text){
+               cnode.setNodeValue(d->mText->toPlainText());
+       }else if(itemtype==d->st_tag){
+               QDomElement el=cnode.toElement();
+               QMap<QString,QString>nattr;
+               for(int i=0;i<d->mTagAttrModel->rowCount();i++)
+                       nattr.insert(
+                         d->mTagAttrModel->data(d->mTagAttrModel->index(i,0)).toString(),
+                         d->mTagAttrModel->data(d->mTagAttrModel->index(i,1)).toString()
+                       );
+               QDomNamedNodeMap oattr=el.attributes();
+               for(int i=0;i<oattr.size();i++){
+                       QString nm=oattr.item(i).nodeName();
+                       if(!nattr.contains(nm))
+                               el.removeAttribute(nm);
+               }
+               for(QString nm:nattr.keys()){
+                       el.setAttribute(nm,nattr[nm]);
+               }
+               el.setTagName(d->mTagName->text());
+//             unneccessary, since it is shared: cnode=el;
+       }else if(itemtype==d->st_calc){
+               cnode.toElement().setAttribute("exec",d->mCalcExpr->text());
+       }else if(itemtype==d->st_loop){
+               cnode.toElement().setAttribute("variable",d->mLoopVar->text());
+       }else if(itemtype==d->st_cond){
+               cnode.toElement().setAttribute("select",d->mCondExpr->text());
+       }else{
+               qDebug()<<"Internal Error: unknown node type"<<itemtype<<"- cannot retrieve data and save it.";
+               return;
+       }
+//     qDebug()<<"saving item"<<cidx<<"type"<<itemtype;
        //done!
-       d->mChanged=false;
+       d->mDomModel->setNode(cidx,cnode);
+//     qDebug()<<"done saving";
 }
 
 void MOdfEditor::Private::setActions(const QList< QAction* >& act)