/* dtkComposerGraph.cpp --- * * Author: Julien Wintz * Copyright (C) 2008-2011 - Julien Wintz, Inria. * Created: Thu Feb 9 14:43:33 2012 (+0100) * Version: $Id: d4042d8bd9c578f283ff4777ec6681132e3e382f $ * Last-Updated: mar. févr. 4 15:13:06 2014 (+0100) * By: Nicolas Niclausse * Update #: 2429 */ /* Commentary: * */ /* Change log: * */ #include "dtkComposerGraph.h" #include "dtkComposerGraphEdge.h" #include "dtkComposerGraphLayouter.h" #include "dtkComposerGraphNode.h" #include "dtkComposerGraphNodeBegin.h" #include "dtkComposerGraphNodeEnd.h" #include "dtkComposerGraphNodeLeaf.h" #include "dtkComposerGraphNodeSelectBranch.h" #include "dtkComposerGraphNodeSetInputs.h" #include "dtkComposerGraphNodeSetOutputs.h" #include "dtkComposerGraphNodeSetVariables.h" #include "dtkComposerNode.h" #include "dtkComposerNodeComposite.h" #include "dtkComposerNodeControl.h" #include "dtkComposerNodeControlCase.h" #include "dtkComposerNodeControlDoWhile.h" #include "dtkComposerNodeControlFor.h" #include "dtkComposerNodeControlForEach.h" #include "dtkComposerNodeControlMap.h" #include "dtkComposerNodeControlIf.h" #include "dtkComposerNodeControlWhile.h" #include "dtkComposerNodeLeaf.h" #include "dtkComposerSceneEdge.h" #include "dtkComposerSceneNode.h" #include "dtkComposerSceneNodeComposite.h" #include "dtkComposerSceneNodeControl.h" #include "dtkComposerSceneNodeLeaf.h" #include "dtkComposerScenePort.h" #include #include #include // ///////////////////////////////////////////////////////////////// // dtkComposerGraphPrivate // ///////////////////////////////////////////////////////////////// class dtkComposerGraphPrivate { public: bool exists(dtkComposerSceneEdge *edge); bool exists(dtkComposerGraphNode *src, dtkComposerGraphNode *destination); public: void addDummyEdge(dtkComposerGraphNode *source, dtkComposerGraphNode *dest, dtkComposerSceneNode *node, int id = 0); void remDummyEdge(dtkComposerGraphEdge *edge, dtkComposerSceneNode *node); void removeDummyEdge(dtkComposerGraphNode *source, dtkComposerGraphNode *dest, dtkComposerSceneNode *node); void addNode(dtkComposerSceneNode *node, dtkComposerGraphNode *node_g, dtkComposerGraphNode *parent_g); void remNode(dtkComposerSceneNode *node, dtkComposerGraphNode *node_g, dtkComposerGraphNode *parent_g); public: dtkComposerGraphNode *begin(dtkComposerSceneNode *node); dtkComposerGraphNode *end(dtkComposerSceneNode *node); public: QMultiHash edges; // for each control node, we need to create several graph nodes ("statements") QMultiHash nodes; // for each control node, we need to create several graph edges, not linked to any scene edge QMultiHash dummy_edges; public: dtkComposerGraph *q; }; bool dtkComposerGraphPrivate::exists(dtkComposerSceneEdge *edge) { // for control nodes, the graph dtkComposerGraphNode *s = this->nodes.value(edge->source()->node()); dtkComposerGraphNode *d = this->nodes.value(edge->destination()->node()); foreach (dtkComposerGraphEdge *e, this->edges) if (e->source() == s && e->destination() == d) return true; return false; } bool dtkComposerGraphPrivate::exists(dtkComposerGraphNode *s, dtkComposerGraphNode *d) { foreach (dtkComposerGraphEdge *e, this->edges) if (e->source() == s && e->destination() == d) return true; return false; } dtkComposerGraphNode *dtkComposerGraphPrivate::begin(dtkComposerSceneNode *node) { if (!node) return NULL; if (!dynamic_cast(node)) { foreach (dtkComposerGraphNode *n, this->nodes.values(node)) { if (dynamic_cast(n) ) return n; } } else { //Leaf return this->nodes.value(node); } return NULL; } dtkComposerGraphNode *dtkComposerGraphPrivate::end(dtkComposerSceneNode *node) { if (!dynamic_cast(node)) { foreach (dtkComposerGraphNode *n, this->nodes.values(node)) { if (dynamic_cast(n) ) return n; } } else { //Leaf return this->nodes.value(node); } return NULL; } // add graph edge not related to scene edge (for a scene control nodes) void dtkComposerGraphPrivate::addDummyEdge(dtkComposerGraphNode *source, dtkComposerGraphNode *destination, dtkComposerSceneNode *node, int id) { dtkComposerGraphEdge *e = new dtkComposerGraphEdge; e->setSource(source); e->setDestination(destination); e->setId(id); e->source()->addSuccessor(e->destination(), id); e->destination()->addPredecessor(e->source()); dummy_edges.insertMulti(node, e); edges.insertMulti(0, e); q->addItem(e); } void dtkComposerGraphPrivate::remDummyEdge(dtkComposerGraphEdge *edge, dtkComposerSceneNode *node) { q->removeItem(edge); edges.remove(0, edge); edge->source()->removeSuccessor(edge->destination()); edge->destination()->removePredecessor(edge->source()); if (node) dummy_edges.remove(node, edge); delete edge; } void dtkComposerGraphPrivate::removeDummyEdge(dtkComposerGraphNode *source, dtkComposerGraphNode *destination, dtkComposerSceneNode *node) { foreach ( dtkComposerGraphEdge *e, dummy_edges.values(node)) if ((e->source() == begin(node)) && e->destination() == end(node) ) remDummyEdge(e, node); } void dtkComposerGraphPrivate::addNode(dtkComposerSceneNode *node, dtkComposerGraphNode *node_g, dtkComposerGraphNode *parent_g) { nodes.insertMulti(node, node_g); if (parent_g) parent_g->addChild(node_g); q->addItem(node_g); } void dtkComposerGraphPrivate::remNode(dtkComposerSceneNode *node, dtkComposerGraphNode *node_g, dtkComposerGraphNode *parent_g) { if (parent_g) parent_g->removeChild(node_g); q->removeItem(node_g); delete node_g; this->nodes.remove(node, node_g); } // ///////////////////////////////////////////////////////////////// // dtkComposerGraph // ///////////////////////////////////////////////////////////////// dtkComposerGraph::dtkComposerGraph(void) : QGraphicsScene(), d(new dtkComposerGraphPrivate) { d->q = this; this->connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged())); this->setItemIndexMethod(NoIndex); } dtkComposerGraph::~dtkComposerGraph(void) { // qDeleteAll(d->edges); // qDeleteAll(d->nodes); delete d; d = NULL; } dtkComposerGraphNode *dtkComposerGraph::root(void) { foreach (dtkComposerGraphNode *node, d->nodes.values()) if (node->predecessors().count() == 0) return node; return NULL; } void dtkComposerGraph::addNode(dtkComposerSceneNode *node) { dtkComposerNode *wrapee = node->wrapee(); dtkComposerGraphNodeBegin *begin; dtkComposerGraphNodeEnd *end; if (dynamic_cast(wrapee)) { begin = new dtkComposerGraphNodeBegin(wrapee, "Begin Control"); end = new dtkComposerGraphNodeEnd(wrapee, "End Control"); begin->setKind(dtkComposerGraphNode::BeginLoop) ; begin->setEnd(end); end->setBegin(begin); begin->setGraph(this); end->setGraph(this); dtkComposerGraphNode *outputs = new dtkComposerGraphNodeSetOutputs(wrapee); dtkComposerGraphNode *select = new dtkComposerGraphNodeSelectBranch(wrapee); d->addNode(node, begin, d->begin(node->parent())); if (dynamic_cast(wrapee)) { delete outputs; } else { d->addNode(node, outputs, begin); } d->addNode(node, select, begin); d->addNode(node, end, d->begin(node->parent())); if (dynamic_cast(wrapee)) { dtkComposerGraphNode *vars = new dtkComposerGraphNodeSetVariables(wrapee); d->addNode(node, vars, begin); dtkComposerGraphNode *inputs = new dtkComposerGraphNodeSetInputs(wrapee); d->addNode(node, inputs, begin); QList blocks = dynamic_cast(node)->blocks(); foreach (dtkComposerSceneNodeComposite *block, blocks) this->addNode(block); int cond = 0; int main = 1; int incr = 2; d->addDummyEdge( begin, inputs, node); d->addDummyEdge( inputs, d->begin(blocks[cond]), node); d->addDummyEdge( d->end(blocks[cond]), select, node); d->addDummyEdge( select, d->begin(blocks[main]), node); d->addDummyEdge( select, end, node, 1); d->addDummyEdge(d->end(blocks[main]), outputs, node); d->addDummyEdge( outputs, d->begin(blocks[incr]), node); d->addDummyEdge( d->end(blocks[incr]), vars, node); d->addDummyEdge( vars, d->begin(blocks[cond]), node); vars->setEndLoop(); } else if (dynamic_cast(wrapee) || dynamic_cast(wrapee)) { dtkComposerGraphNode *vars = new dtkComposerGraphNodeSetVariables(wrapee); d->addNode(node, vars, begin); dtkComposerGraphNode *inputs = new dtkComposerGraphNodeSetInputs(wrapee); d->addNode(node, inputs, begin); QList blocks = dynamic_cast(node)->blocks(); foreach (dtkComposerSceneNodeComposite *block, blocks) this->addNode(block); int body = 0; d->addDummyEdge( begin, inputs, node); d->addDummyEdge( inputs, select, node); d->addDummyEdge( select, vars, node); d->addDummyEdge( vars, d->begin(blocks[body]), node); d->addDummyEdge( select, end, node, 1); d->addDummyEdge( d->end(blocks[body]), outputs, node); d->addDummyEdge(outputs, select, node); outputs->setEndLoop(); } else if (dynamic_cast(wrapee)) { dtkComposerGraphNode *inputs = new dtkComposerGraphNodeSetInputs(wrapee); d->addNode(node, inputs, begin); QList blocks = dynamic_cast(node)->blocks(); foreach (dtkComposerSceneNodeComposite *block, blocks) this->addNode(block); int cond_block = 0; int body_block = 1; d->addDummyEdge( begin, inputs, node); d->addDummyEdge( inputs, d->begin(blocks[cond_block]), node); d->addDummyEdge( d->end(blocks[cond_block]), select, node); d->addDummyEdge( select, d->begin(blocks[body_block]), node); d->addDummyEdge( select, end, node, 1); d->addDummyEdge(d->end(blocks[body_block]), outputs, node); d->addDummyEdge( outputs, d->begin(blocks[cond_block]), node); outputs->setEndLoop(); } else if (dynamic_cast(wrapee)) { dtkComposerGraphNode *inputs = new dtkComposerGraphNodeSetInputs(wrapee); d->addNode(node, inputs, begin); QList blocks = dynamic_cast(node)->blocks(); foreach (dtkComposerSceneNodeComposite *block, blocks) this->addNode(block); int cond_block = 1; int body_block = 0; d->addDummyEdge( begin, inputs, node); d->addDummyEdge( inputs, d->begin(blocks[body_block]), node); d->addDummyEdge( d->end(blocks[body_block]), outputs, node); d->addDummyEdge( outputs, d->begin(blocks[cond_block]), node); d->addDummyEdge( d->end(blocks[cond_block]), select, node); d->addDummyEdge( select, d->begin(blocks[body_block]), node); d->addDummyEdge( select, end, node, 1); select->setEndLoop(); } else if (dynamic_cast(wrapee)) { begin->setKind(dtkComposerGraphNode::BeginIf) ; QList blocks = dynamic_cast(node)->blocks(); foreach (dtkComposerSceneNodeComposite *block, blocks) this->addNode(block); int then_block = 0; int else_block = 1; d->addDummyEdge( begin, select, node); d->addDummyEdge( select, d->begin(blocks[then_block]), node); d->addDummyEdge( select, d->begin(blocks[else_block]), node, 1); d->addDummyEdge(d->end(blocks[then_block]), end, node); d->addDummyEdge(d->end(blocks[else_block]), end, node); d->end(blocks[then_block])->setEndLoop(); d->end(blocks[else_block])->setEndLoop(); } else if (dynamic_cast(wrapee) ) { begin->setKind(dtkComposerGraphNode::BeginIf) ; d->addDummyEdge( begin, select, node); d->addDummyEdge( outputs, end, node); this->addBlock(node); } d->removeDummyEdge( d->begin(node->parent()), d->end(node->parent()), node->parent()); d->addDummyEdge( d->begin(node->parent()), begin, node->parent()); d->addDummyEdge( end, d->end(node->parent()), node->parent()); } else if (dynamic_cast(wrapee)) { // Leaf node dtkComposerGraphNode *leaf = new dtkComposerGraphNodeLeaf(wrapee, node->title()); d->addNode(node, leaf, d->begin(node->parent())); // if composite was empty, need to remove dummy edge if (d->dummy_edges.values(node->parent()).count() == 1) { d->removeDummyEdge( d->begin(node->parent()), d->end(node->parent()), node->parent()); } d->addDummyEdge( d->begin(node->parent()), leaf, node->parent()); d->addDummyEdge( leaf, d->end(node->parent()), node->parent()); } else if (dtkComposerSceneNodeComposite *composite = dynamic_cast(node)) { begin = new dtkComposerGraphNodeBegin(wrapee, "Begin" + node->title()); end = new dtkComposerGraphNodeEnd(wrapee, "End" + node->title()); begin->setKind(dtkComposerGraphNode::BeginComposite) ; begin->setEnd(end); begin->setGraph(this); end->setBegin(begin); if (!composite->root()) d->addNode(node, begin, d->begin(node->parent())); else d->addNode(node, begin, NULL); d->addNode(node, end, d->begin(node->parent())); // empty composite, add dummy edge between 'begin' and 'end' d->addDummyEdge( begin, end, node); if (!composite->root() && !(dynamic_cast(node->parent())) ) { d->removeDummyEdge( d->begin(node->parent()), d->end(node->parent()), node->parent()); d->addDummyEdge( d->begin(node->parent()), begin, node->parent()); d->addDummyEdge( end, d->end(node->parent()), node->parent()); } } } void dtkComposerGraph::removeNode(dtkComposerSceneNode *node) { dtkTrace() << "removing node" << node->title(); dtkComposerSceneNode *parent = node->parent(); dtkTrace() << "node parent is " << parent->title(); // For control nodes, we have to remove its blocks and then it's pending dummy edges if (dtkComposerSceneNodeControl *control = dynamic_cast(node)) { //remove dummy edges for this foreach (dtkComposerGraphEdge *e, d->dummy_edges.values(node)) d->remDummyEdge(e, node); foreach ( dtkComposerSceneNodeComposite *block, control->blocks()) this->removeNode(block); } if (!dynamic_cast(parent)) { // remove dummy edges for this node on the parent dtkComposerGraphNode *parent_begin = d->begin(parent); dtkComposerGraphNode *parent_end = d->end(parent); dtkTrace() << " removing dummy edges of parent"; foreach ( dtkComposerGraphEdge *e, d->dummy_edges.values(parent)) { if ((e->source() == parent_begin && e->destination() == d->begin(node) ) || (e->destination() == parent_end && e->source() == d->end(node))) d->remDummyEdge(e, parent); } // if parent composite is empty after removal, add back dummy edge if (d->dummy_edges.values(parent).count() == 0) { dtkTrace() << " add back dummy edge of empty parent"; d->addDummyEdge( parent_begin, parent_end, parent); } } if (dynamic_cast(node)) { foreach (dtkComposerGraphEdge *e, d->dummy_edges.values(node)) d->remDummyEdge(e, node); } foreach (dtkComposerGraphNode *n, d->nodes.values(node)) { if (dynamic_cast(node) && !(dynamic_cast(n) || dynamic_cast(n) )) d->remNode(node, n, d->begin(node)); else d->remNode(node, n, d->begin(parent)); } d->nodes.remove(node); } void dtkComposerGraph::addBlock(dtkComposerSceneNode *node) { dtkComposerNode *wrapee = node->wrapee(); dtkComposerGraphNode *inputs_block = new dtkComposerGraphNodeSetInputs(wrapee); if (dynamic_cast(wrapee)) { QList blocks = dynamic_cast(node)->blocks(); d->addNode(blocks.last(), inputs_block, d->begin(node)); this->addNode(blocks.last()); dtkComposerGraphNode *select = NULL; dtkComposerGraphNode *outputs = NULL; foreach (dtkComposerGraphNode *n, d->nodes.values(node)) { if (dynamic_cast(n) ) select = n; if (dynamic_cast(n) ) outputs = n; } dtkComposerSceneNode *noderef = blocks.last(); d->addDummyEdge(select, inputs_block, noderef, blocks.count() - 1); d->addDummyEdge(inputs_block, d->begin(blocks.last()), noderef); d->addDummyEdge(d->end(blocks.last()), outputs, noderef); d->end(blocks.last())->setEndLoop(); } else { dtkWarn() << "addGroup called on unsupported node" << node->title(); return; } } void dtkComposerGraph::removeBlock(dtkComposerSceneNode *node) { foreach (dtkComposerGraphEdge *e, d->dummy_edges.values(node)) d->remDummyEdge(e, node); foreach (dtkComposerGraphNode *n, d->nodes.values(node)) if (dynamic_cast(n)) d->remNode(node, n, d->begin(node->parent())); this->removeNode(node); } void dtkComposerGraph::reparentNode(dtkComposerSceneNode *node, dtkComposerSceneNode *newparent) { // remove dummy edges for this node on the parent dtkComposerSceneNode *oldparent = node->parent(); dtkComposerGraphNode *oldparent_g = d->begin(node->parent()); dtkComposerGraphNode *newparent_g = d->begin(newparent); oldparent_g->removeChild(d->begin(node)); newparent_g->addChild(d->begin(node)); if (d->begin(node) != d->end(node)) { // not a leaf node oldparent_g->removeChild(d->end(node)); newparent_g->addChild(d->end(node)); } foreach ( dtkComposerGraphEdge *e, d->dummy_edges.values(oldparent)) { if ((e->source() == d->begin(oldparent) && e->destination() == d->begin(node) ) || (e->destination() == d->end(oldparent) && e->source() == d->end(node))) d->remDummyEdge(e, oldparent); } // if oldparent composite is empty after removal, add back dummy edge if (d->dummy_edges.values(oldparent).count() == 0) d->addDummyEdge( d->begin(oldparent), d->end(oldparent), oldparent); // if new composite parent was empty, need to remove dummy edge if (d->dummy_edges.values(newparent).count() == 1) d->removeDummyEdge( d->begin(newparent), d->end(newparent), newparent); d->addDummyEdge( d->begin(newparent), d->begin(node), newparent); d->addDummyEdge( d->end(node), d->end(newparent), newparent); } void dtkComposerGraph::addEdge(dtkComposerSceneEdge *edge) { dtkComposerSceneNode *scene_src = edge->source()->node(); dtkComposerSceneNode *scene_dest = edge->destination()->node(); if (!d->nodes.contains(edge->source()->node()) && !dynamic_cast(scene_src->parent())) return; if (!d->nodes.contains(edge->destination()->node()) && !dynamic_cast(scene_dest->parent())) return; if (d->exists(edge)) return; dtkComposerGraphEdge *e = new dtkComposerGraphEdge; dtkComposerGraphNode *src; dtkComposerGraphNode *dest; // if the destination is a control node port, then we must use the control node parent begin/end if (dynamic_cast(scene_dest->parent()) && (scene_dest != scene_src->parent())) scene_dest = scene_dest->parent(); if (dynamic_cast(scene_src->parent()) && (scene_src != scene_dest->parent()) ) scene_src = scene_src->parent(); if (scene_src->parent() == edge->destination()->node()) { // edge to parent composite: there is already, at least, a dummy edge: do nothing delete e; return; } else if (scene_dest->parent() == edge->source()->node()) { // edge from parent composite: there is already, at least, a dummy edge: do nothing delete e; return; } else { dtkTrace() << " add regular edge" << scene_src->title() << scene_dest->title(); src = d->end(scene_src); dest = d->begin(scene_dest); // need to remove dummy edges that start with src // need to remove dummy edges that ends with dest foreach ( dtkComposerGraphEdge *e, d->dummy_edges.values()) { if ((e->source() == src ) || (e->destination() == dest)) { // FIXME: is the condition ok for dummy edges in control nodes ? dtkTrace() << " remove dummy edge because of addEdge" << e->source()->title() << e->destination()->title() ; d->remDummyEdge(e, d->dummy_edges.key(e)); } } } e->setSource(src); e->setDestination(dest); if (d->exists(src, dest)) { delete e; return; } src->addSuccessor(dest); dest->addPredecessor(src); d->edges.insertMulti(edge, e); this->addItem(e); } void dtkComposerGraph::removeEdge(dtkComposerSceneEdge *edge) { if (!d->edges.contains(edge)) return; QList edges = d->edges.values(edge); // first remove edges d->edges.remove(edge); foreach (dtkComposerGraphEdge *e, edges) { e->source()->removeSuccessor(e->destination()); e->destination()->removePredecessor(e->source()); this->removeItem(e); delete e; } // if destination has edges on its input, don't add a dummy edge bool doit = true; foreach (dtkComposerGraphEdge *e, d->edges.values()) doit = doit && (e->destination() != d->begin(edge->destination()->node())); if (doit) { d->addDummyEdge( d->begin(edge->source()->owner()->parent()), d->begin(edge->destination()->owner()), edge->source()->owner()->parent()); } doit = true; // if source has edges on its output, don't add a dummy edge foreach (dtkComposerGraphEdge *e, d->edges.values()) doit = doit && (e->source() != d->end(edge->source()->node())); if (doit) { d->addDummyEdge( d->end(edge->source()->owner()), d->end(edge->destination()->owner()->parent()), edge->destination()->owner()->parent()); } } dtkComposerGraphEdgeList dtkComposerGraph::edges(void) { return dtkComposerGraphEdgeList(d->edges.values()); } dtkComposerGraphNodeList dtkComposerGraph::nodes(void) { return dtkComposerGraphNodeList(d->nodes.values()); } void dtkComposerGraph::removeNode(dtkComposerGraphNode *node) { foreach (dtkComposerGraphNode *n, d->nodes.values()) if ((n = node)) d->nodes.remove(d->nodes.key(n)); } void dtkComposerGraph::removeEdge(dtkComposerGraphEdge *edge) { foreach (dtkComposerGraphEdge *e, d->edges.values()) if ((e = edge)) { d->edges.remove(d->edges.key(e), e); } } QString dtkComposerGraph::toString(void) { QStringList txt; foreach (dtkComposerGraphEdge *e, d->edges.values()) { txt << e->source()->title() + " -> " + e->destination()->title(); } return txt.join("\n"); } void dtkComposerGraph::clear(void) { d->dummy_edges.clear(); foreach (dtkComposerGraphEdge *e, d->edges.values()) { this->removeItem(e); delete e; e = NULL; } d->edges.clear(); foreach (dtkComposerGraphNode *n, d->nodes.values()) { this->removeItem(n); delete n; n = NULL; } d->nodes.clear(); emit cleared(); } void dtkComposerGraph::layout(void) { dtkComposerGraphLayouter layouter; layouter.setGraph(this); layouter.layout(); this->update(); } void dtkComposerGraph::onSelectionChanged(void) { QList selection = this->selectedItems(); if (selection.count() != 1) return; dtkComposerGraphNode *node = dynamic_cast(selection.first()); if (!node) return; dtkComposerSceneNode *n = d->nodes.key(node); if (n) n->setSelected(true); } dtkGraph dtkComposerGraph::graph(void) { dtkGraph g; foreach (dtkComposerGraphNode *node, d->nodes.values() ) { g.addNode(node); } foreach (dtkComposerGraphEdge *edge, d->edges.values() ) { dtkGraphEdge e; e.setSource(edge->source()); e.setDestination(edge->destination()); g.addEdge(e); } return g; } dtkGraph dtkComposerGraph::subgraph(dtkComposerGraphNode *from, dtkComposerGraphNode *to) { // dtkTrace() << "generate dtk graph from composer graph"; dtkGraph g = this->graph(); dtkGraph sg = g.subgraph(from, to); // remove nested nodes: keep only child nodes of from dtkComposerGraphNodeList childs = from->childs(); // check all childs if they are BeginControl; if yes, need to remove subgraph of control from the current subgraph foreach (dtkComposerGraphNode *n, childs ) { // dtkTrace() << "check child node" << n->title(); if (n->kind() == dtkComposerGraphNode::BeginLoop || n->kind() == dtkComposerGraphNode::BeginIf) { // dtkTrace() << "begin loop/if" ; QObject *end = static_cast(n)->end(); dtkGraph sg_control = g.subgraph(n, end ); foreach (QObject *o, sg_control.nodes()) { if (o != n && o != end) { sg.deleteNode(o); } } dtkGraphEdge e(n, end) ; sg.addEdge(e); // add edge between begin and end control } else if (n->endloop()) { sg.deleteEdge(dtkGraphEdge(n, sg.succcessors(n)[0])); } else if (n->kind() == dtkComposerGraphNode::BeginComposite ) { // need to check also childs of composite // dtkTrace() << "need to check childs of composite" ; dtkComposerGraphNode *end = static_cast(n)->end(); dtkGraph ssg = subgraph(n, end); //recursive call dtkGraph full_ssg = sg.subgraph(n, end); bool removed = false; foreach (QObject *o, full_ssg.nodes()) { if (!ssg.contains(o) && sg.contains(o) && o != n && o != end) { // dtkTrace() << "removing" << o->objectName(); removed = true; sg.deleteNode(o); } } if (removed) { // dtkTrace() << "ssg edges count:" << ssg.edges().count(); for (int i = 0; i < ssg.edges().count(); ++i) { if (!sg.edges().contains(ssg.edges().at(i))) { sg.addEdge( ssg.edges().at(i)); // add edge between begin and end control } } } } } sg.deleteNode(from); sg.deleteNode(to); // dtkTrace() << sg.description(); return sg; }