dataChanged(index,index);
}
+void MDomItemModel::insertNodes(const QDomNodeList& newnodes, const QModelIndex& parent, int row)
+{
+ QList<QDomNode>nnl;
+ for(int i=0;i<newnodes.size();i++)nnl<<newnodes.at(i);
+ insertNodes(nnl,parent,row);
+}
+
void MDomItemModel::insertNode(const QDomNode& newnode, const QModelIndex& parent, int row)
{
+ insertNodes(QList<QDomNode>()<<newnode,parent,row);
+}
+
+void MDomItemModel::insertNodes(const QList< QDomNode >& newnodes, const QModelIndex& parent, int row)
+{
+ if(newnodes.size()<1)return;
//check parent validity
if(!parent.isValid())return;
int pid=parent.internalId();
//only add to tags (TODO: there are a few other node types that can have children)
if(!d->domCache[pid].node.isElement())return;
//get the parent and a clone of the new node
- QDomNode nn=newnode.cloneNode();
QDomElement pn=d->domCache[pid].node.toElement();
const int rcnt=rowCount(parent);
//handle appending
if(row<0 || row>=rcnt){
- beginInsertRows(parent,rcnt,rcnt);
- pn.appendChild(nn);
- d->domCache[pid].children.append(d->buildCache(nn,pid));
+ const int rmax=rcnt+newnodes.size()-1;
+ beginInsertRows(parent,rcnt,rmax);
+ for(QDomNode nd:newnodes){
+ QDomNode nn=nd.cloneNode();
+ pn.appendChild(nn);
+ d->domCache[pid].children.append(d->buildCache(nn,pid));
+ }
endInsertRows();
return;
}
//handle inserting
//get current occupant of this row
QDomNode sibl=node(index(row,0,parent));
- beginInsertRows(parent,row,row);
+ const int rmax=row+newnodes.size()-1;
+ beginInsertRows(parent,row,rmax);
//insert
- pn.insertBefore(nn,sibl);
- d->domCache[pid].children.insert(row,d->buildCache(nn,pid));
+ for(QDomNode nd:newnodes){
+ QDomNode nn=nd.cloneNode();
+ pn.insertBefore(nn,sibl);
+ d->domCache[pid].children.insert(row,d->buildCache(nn,pid));
+ }
endInsertRows();
}
if(!d->domCache.contains(pid))
return false;
//check the parent has those children
- if(rowCount(parent)>(row+count))
+ if(rowCount(parent)<(row+count))
return false;
//warn the view
beginRemoveRows(parent,row,row+count-1);
foreach(int cid,cids)d->removeNode(cid);
//update view
endRemoveRows();
+ return true;
}
-void MDomItemModel::reparentNode(const QModelIndex& index, const QDomElement& el)
-{
- //find index and parent
- if(!index.isValid())return;
- int nid=index.internalId();
- if(!d->domCache.contains(nid))return;
- int pid=d->domCache[nid].parent;
- if(pid<0 || !d->domCache.contains(pid))return;
- //invalidate model, TODO: find a less invasive call
- beginResetModel();
- //reparent both
- QDomElement nelem=el.cloneNode().toElement();
- int neid=d->buildCache(nelem,pid);
- QDomNode sibl=d->domCache[nid].node.previousSibling();
- nelem.appendChild(d->domCache[nid].node);
- if(sibl.isNull())
- d->domCache[pid].node.insertBefore(nelem,QDomNode());
- else
- d->domCache[pid].node.insertAfter(nelem,sibl);
- //correct references
- d->domCache[nid].parent=neid;
- int oidx=d->domCache[pid].children.indexOf(nid);
- d->domCache[pid].children.replace(oidx,neid);
- d->domCache[neid].children.append(nid);
- //update view
- endResetModel();
-}
-
-void MDomItemModel::reparentNode(const QModelIndex& node, const QModelIndex& newparent, int row)
-{
- //get node ids
- if(!node.isValid() || !newparent.isValid())return;
- int nid=node.internalId();
- int npid=newparent.internalId();
- if(nid<=0 || npid<=0 || !d->domCache.contains(nid) || !d->domCache.contains(npid))
- return;
- int opid=d->domCache[nid].parent;
- if(opid<=0 || !d->domCache.contains(opid))return;
- //get position in new parent
- int cnt=0;
- QDomNode neigh;
- foreach(int cid,d->domCache[npid].children){
- if(!d->showType.contains(d->domCache[cid].node.nodeType()))
- continue;
- //TODO:this is probably wrong
- if(cnt++>row)break;
- neigh=d->domCache[cid].node;
- }
- //TODO:insert node in new parent
- //TODO:move logical IDs
- //TODO:tell view
-#warning implement
-}
-
-void MDomItemModel::reparentNode(const QModelIndexList& nodes, const QModelIndex& newparent, int row)
-{
-#warning implement
-}
-
-
void MDomItemModel::setContentFromNodeCall(int role,MDomModelFunctor f)
{
if(role==DomNodeRole)return;
virtual void setNode(const QModelIndex&index,const QDomNode&);
///insert a node into the parent at given position (if no position is given: append)
virtual void insertNode(const QDomNode&newnode,const QModelIndex&parent,int row=-1);
+ ///insert a list of nodes into the parent at given position (if no position is given: append)
+ virtual void insertNodes(const QDomNodeList&newnodes,const QModelIndex&parent,int row=-1);
+ ///insert a list of nodes into the parent at given position (if no position is given: append)
+ virtual void insertNodes(const QList<QDomNode>&newnodes,const QModelIndex&parent,int row=-1);
///removes the nodes at the given position and their child nodes
virtual bool removeRows(int row, int count, const QModelIndex&parent=QModelIndex());
///removes the node at the given position and its child nodes
virtual void removeNode(const QModelIndex&);
- /** places the node at the given index inside the new node and then places the new node at that position - in fact moving the node at index one hierarchy level down
- \param index the node to be reparented
- \param newnode the node that will replace the node and take it as its new child*/
- virtual void reparentNode(const QModelIndex&index,const QDomElement&newnode);
- /** places the node in a new parent that is already inside the model
- \param node the node to be reparented
- \param newparent the new parent of this node
- \param row the position inside the parent where to insert that node, if row is greater than the current size of the parent or if it is negative the node will be appended
- this call invalidates the new and old parent index of the node*/
- virtual void reparentNode(const QModelIndex&node,const QModelIndex&newparent,int row=-1);
- /** places the nodes in a new parent that is already inside the model
- \param nodes the nodes to be reparented
- \param newparent the new parent of this node
- \param row the position inside the parent where to insert the new nodes - they will be inserted in the order they are contained in the list, if row is greater than the current size of the parent or if it is negative the node will be appended
- this call invalidates the new and old parent indexes of the nodes*/
- virtual void reparentNode(const QModelIndexList&nodes,const QModelIndex&newparent,int row=-1);
/**Customize the way the model returns data to the view, per default the model only has code to generate a text representation for Qt::DisplayRole. You can override any role, including Qt::DisplayRole, except for DomNodeRole. The callback takes a const reference to QDomNode as argument and should convert it to a QVariant appropriate for the role.
\param role the role to be customized
//widgets
QTreeView*mDomTree;
MDomItemModel*mDomModel;
+ void expandTree(const QModelIndex&);
//stack indexes
int st_none,st_tag,st_text,st_loop,st_calc,st_comment,st_cond,st_special;
//stack widgets
QMenu*medit=m=mb->addMenu(tr("&Edit"));
d->maAddIntoCalc=m->addAction(tr("Insert &Calculation into current"),this,SLOT(insCalcIntoCurrent()));
d->maInsBehindCalc=m->addAction(tr("Insert Calculation behind current"),this,SLOT(insCalcBehindCurrent()));
- d->maWrapInCond=m->addAction(tr("&Wrap in Condition"));
- d->maWrapInLoop=m->addAction(tr("Wrap in &Loop"));
+ d->maWrapInCond=m->addAction(tr("&Wrap in Condition"),this,SLOT(wrapInCond()));
+ d->maWrapInLoop=m->addAction(tr("Wrap in &Loop"),this,SLOT(wrapInLoop()));
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->maUnwrap=m->addAction(tr("Unwrap Loop/Condition"));
+ d->maUnwrap=m->addAction(tr("Unwrap Loop/Condition"),this,SLOT(unwrapItem()));
d->maDelItem=m->addAction(tr("&Remove Item"),this,SLOT(delItem()));
m=mb->addMenu(tr("&Data"));
//insert calculation
QDomNode nn=cnode.ownerDocument().createElement(OdfTemplatePrefix+":calculate");
d->mDomModel->insertNode(nn,idx,0);
+ //make sure it is visible
+ d->expandTree(idx);
}
void MOdfEditor::insElse()
//insert comment
QDomNode nn=cnode.ownerDocument().createComment(tr("new comment"));
d->mDomModel->insertNode(nn,idx,0);
+ //make sure it is visible
+ d->expandTree(idx);
+}
+
+void MOdfEditor::wrapInCond(QString tag)
+{
+ //get selection
+ const QModelIndexList &idxl=d->mDomTree->selectionModel()->selectedRows();
+ if(idxl.size()==0)return;
+ //keep only items that have no parent in the list
+ // (i.e. eliminate children, since this algo is recursive anyway)
+ QModelIndexList idxl2;
+ for(QModelIndex idx:idxl)
+ if(!idxl.contains(idx.parent()))idxl2<<idx;
+ //sanity check: do all remaining items have the same parent?
+ QModelIndex pidx=idxl2[0].parent();
+ for(QModelIndex idx:idxl2)
+ if(idx.parent()!=pidx){
+ QMessageBox::warning(this,tr("Warning"),tr("The selected items do not have a common parent, this means I cannot wrap them without screwing up the DOM tree. Please check your selection!"));
+ return;
+ }
+ //get first and last index, get copies of nodes
+ int frow=-1,lrow=-1;
+ QList<QDomNode> ndlist;
+ qSort(idxl2.begin(),idxl2.end(),[](const QModelIndex&a,const QModelIndex&b){return a.row()<b.row();});
+ for(QModelIndex idx:idxl2){
+ 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);
+ }
+ if(ndlist.size()<1)return;
+ //create new parent node
+ QDomElement np=ndlist[0].ownerDocument().createElement(OdfTemplatePrefix+":"+tag);
+ //copy the moved items in
+ for(QDomNode nd:ndlist)np.appendChild(nd);
+ //remove the old items
+ d->mDomModel->removeRows(frow,lrow-frow+1,pidx);
+ //add the new item
+ d->mDomModel->insertNode(np,pidx,frow);
+ //expand
+ d->expandTree(d->mDomModel->index(frow,0,pidx));
+}
+
+void MOdfEditor::Private::expandTree(const QModelIndex& idx)
+{
+ //expand this
+ mDomTree->expand(idx);
+ //recurse
+ const int rc=mDomModel->rowCount(idx);
+ for(int i=0;i<rc;i++)
+ expandTree(mDomModel->index(i,0,idx));
+}
+
+
+void MOdfEditor::wrapInLoop()
+{
+ wrapInCond("loop");
+}
+
+void MOdfEditor::unwrapItem()
+{
+ //get the item
+ QModelIndex idx=d->mDomTree->currentIndex();
+ if(!idx.isValid())return;
+ //get the node and parent index
+ QDomElement el=d->mDomModel->node(idx).toElement();
+ QModelIndex pidx=idx.parent();
+ const int row=idx.row();
+ //remove the original
+ d->mDomModel->removeNode(idx);
+ //insert the children into the parent node at the correct position
+ d->mDomModel->insertNodes(el.childNodes(),pidx,row);
+ //expand the tree
+ d->expandTree(pidx);
}
void insElse();
///helper: delete an item
void delItem();
+ ///helper: wrap marked items in a condition
+ void wrapInCond(QString tag="if");
+ ///helper: wrap marked items in a loop
+ void wrapInLoop();
+ ///helper: unwrap the loop or condition
+ void unwrapItem();
signals:
///used to switch to the correct editor widget
void switchStack(int);