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)
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
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;
//current node
QModelIndex mCurIndex;
QDomNode mCurNode;
+ bool mChanged;
//file contents
struct File{
QString name;
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);
//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);
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);
void MOdfEditor::setChanged(bool ch)
{
+ if(ch!=d->mChanged)
+// qDebug()<<"set changed state"<<ch;
d->mChanged=ch;
}
void MOdfEditor::selectionChange()
{
saveCurrentNode();
+ MCleanup clean([&](){setChanged(false);});
d->mCurIndex=QModelIndex();
d->mCurNode=QDomNode();
d->setActions(QList<QAction*>());
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
{
//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)