1 // Copyright (C) 2006-2012 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "SceneBlocItem.hxx"
23 #include "QtGuiContext.hxx"
24 #include "guiObservers.hxx"
25 #include "ComposedNode.hxx"
27 #include "OutputPort.hxx"
28 #include "OutGate.hxx"
29 #include "InputPort.hxx"
34 // --- for graphviz 2.8
37 #ifdef HAVE_DOTNEATO_H
44 #define ND_coord_i(n) (n)->u.coord
47 #include "Resource.hxx"
50 #include "YacsTrace.hxx"
53 using namespace YACS::ENGINE;
54 using namespace YACS::HMI;
56 /*! Definition in dot per inch for graphviz input:
57 * size of elementary nodes are in pixel in Qt, and given in inches to graphviz
61 static GVC_t* aGvc = 0;
64 SceneBlocItem::SceneBlocItem(QGraphicsScene *scene, SceneItem *parent,
65 QString label, Subject *subject)
66 : SceneComposedNodeItem(scene, parent, label, subject)
68 DEBTRACE("SceneBlocItem::SceneBlocItem " <<label.toStdString());
69 _format = "%1"; // --- format to convert a float without locale: ex. 9.81
72 SceneBlocItem::~SceneBlocItem()
76 //! Auto-arrange nodes inside a schema using Graphviz C API.
80 void SceneBlocItem::arrangeChildNodes()
82 DEBTRACE("SceneBlocItem::arrangeChildNodes");
83 clock_t start_t, end_t;
87 SubjectComposedNode *scnode = dynamic_cast<SubjectComposedNode*>(getSubject());
89 ComposedNode *cnode = dynamic_cast<ComposedNode*>(scnode->getNode());
92 // ---- Create a graphviz context
96 DEBTRACE(setlocale(LC_ALL,NULL));
97 //Graphviz is sensitive to locale : set the mimimal one ("C")for numeric
98 setlocale(LC_NUMERIC, "C");
103 // ---- Create a graph
105 _graph = agopen((char*)( cnode->getName().c_str()), AGDIGRAPH);
107 // ---- Initialize and set attributes for the graph
110 if ( !(attr = agfindattr(_graph, (char *)"compound")))
111 attr = agraphattr(_graph, (char *)"compound", (char *)"false");
112 agxset(_graph, attr->index, (char *)"true");
114 if ( !(attr = agfindattr(_graph, (char *)"rankdir")))
115 attr = agraphattr(_graph, (char *)"rankdir", (char *)"TB");
116 agxset(_graph, attr->index, (char *)"LR");
118 // if ( !(attr = agfindattr(_graph, (char *)"ordering")))
119 // attr = agraphattr(_graph, (char *)"ordering", (char *)"" );
120 // agxset(_graph, attr->index, (char *)"in" );
122 if ( !(attr = agfindattr(_graph, (char *)"dpi")))
123 attr = agraphattr(_graph, (char *)"dpi", (char *)"72");
124 agxset(_graph, attr->index, (char *)"72"); // --- must be coherent with #define DPI
126 // --- label is used to reserve place for bloc banners (adjust size with font !)
128 if ( !(attr = agfindattr(_graph, (char *)"label")))
129 attr = agraphattr(_graph, (char *)"label", (char *)"label");
130 agxset(_graph, attr->index, (char *)"myLabel");
132 if ( !(attr = agfindattr(_graph, (char *)"labelloc")))
133 attr = agraphattr(_graph, (char *)"labelloc", (char *)"top");
134 agxset(_graph, attr->index, (char *)"top");
136 if ( !(attr = agfindattr(_graph, (char *)"fontsize")))
137 attr = agraphattr(_graph, (char *)"fontsize", (char *)"24");
138 agxset(_graph, attr->index, (char *)"24");
140 if ( !(attr = agfindattr(_graph, (char *)"splines")))
141 attr = agraphattr(_graph, (char *)"splines", (char *)"");
142 agxset(_graph, attr->index, (char *)"");
144 // --- Initialize attributes for nodes
146 if ( !(attr = agfindattr( _graph->proto->n, (char *)"height")))
147 attr = agnodeattr(_graph, (char *)"height", (char *)"" );
149 if ( !(attr = agfindattr( _graph->proto->n, (char *)"width")))
150 attr = agnodeattr(_graph, (char *)"width", (char *)"" );
152 if ( !(attr = agfindattr( _graph->proto->n, (char *)"shape")))
153 attr = agnodeattr(_graph, (char *)"shape", (char *)"" );
155 if ( !(attr = agfindattr( _graph->proto->n, (char *)"fixedsize")))
156 attr = agnodeattr(_graph, (char *)"fixedsize", (char *)"false" );
158 // ---- Bind graph to graphviz context - must be done before layout
159 // ---- Compute a layout
164 // createGraphvizNodes(cnode);
165 DEBTRACE("end of graphviz input");
167 agwrite(_graph, stderr);
169 #ifdef HAVE_DOTNEATO_H
170 gvBindContext(aGvc, _graph);
173 //DEBTRACE("external render for test");
174 //gvRenderFilename(aGvc, _mainGraph, "dot", "graph1.dot");
175 DEBTRACE("compute layout");
176 gvLayout(aGvc, _graph, (char *)"dot");
177 DEBTRACE("external render for test");
179 gvRenderFilename(aGvc, _graph, (char *)"dot", (char *)"graph2.dot");
183 catch (std::exception &e)
185 DEBTRACE("Exception Graphviz layout: " << e.what());
190 DEBTRACE("Unknown Exception Graphviz layout ");
195 double passe = (end_t -start_t);
196 passe = passe/CLOCKS_PER_SEC;
197 DEBTRACE("graphviz : " << passe);
200 DEBTRACE("start of display");
201 // ---- layout Canvas nodes recursively
203 arrangeCanvasNodes(cnode);
205 DEBTRACE("clean up graphviz");
206 // ---- Delete layout
208 #ifdef HAVE_DOTNEATO_H
211 gvFreeLayout(aGvc, _graph);
214 // ---- Free graph structures
218 // ---- Free context and return number of errors
220 #ifndef HAVE_DOTNEATO_H
221 //gvFreeContext( aGvc );
227 double passe = (end_t -start_t);
228 passe = passe/CLOCKS_PER_SEC;
229 DEBTRACE("display : " << passe);
234 void SceneBlocItem::getNodesInfo(YACS::ENGINE::ComposedNode *cnode)
236 Proc *proc = GuiContext::getCurrent()->getProc();
238 // --- Create Nodes = direct descendants in the bloc
240 list<Node*> children = cnode->edGetDirectDescendants();
241 for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
243 Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()));
244 DEBTRACE("Add node in graph: " << aNode->name);
246 SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
247 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
248 double nh = sci->getHeight();
249 double nw = sci->getWidth();
253 QString height, width;
254 height = QString(_format.c_str()).arg(lh, 0, 'g', 3);
255 width = QString(_format.c_str()).arg(lw, 0, 'g', 3);
257 DEBTRACE(aNode->name << " (" << nh << "," << nw << ") = (" << height.toStdString() << " ; " << width.toStdString() <<")");
258 agxset( aNode, agfindattr(_graph->proto->n,(char *)"height")->index, (char*)(height.toAscii().data()));
259 agxset( aNode, agfindattr(_graph->proto->n,(char *)"width")->index, (char*)(width.toAscii().data()));
260 agxset( aNode, agfindattr(_graph->proto->n,(char *)"shape")->index, (char *)"box" );
261 agxset( aNode, agfindattr(_graph->proto->n,(char *)"fixedsize")->index, (char *)"true" );
264 // --- Create edges (i.e. links)
267 for (aNode = agfstnode(_graph); aNode; aNode = agnxtnode(_graph, aNode))
269 string aNodeName = aNode->name;
270 DEBTRACE("--- tail node " << aNode->name);
271 Agnode_t* aTailNode = aNode;
272 Node* outNode = proc->getChildByName(string(aTailNode->name));
273 if (outNode->getFather() != cnode)
275 DEBTRACE(" =========== problem here ! =============================");
276 continue; // Create edges only with outgoing nodes directly in bloc
279 // --- control link from node, keep only link staying inside the bloc
282 OutGate *outGate = outNode->getOutGate();
283 set<InGate*> setOfInGate = outGate->edSetInGate();
284 set<InGate*>::const_iterator itin = setOfInGate.begin();
285 for (; itin != setOfInGate.end(); ++itin)
287 Node *inNode = (*itin)->getNode();
288 string inName = proc->getChildName(inNode);
289 DEBTRACE("--- control link from tail node: --- "<<inName);
290 // --- isInMyDescendance(this) return this
291 // isInMyDescendance(inNode) return direct child if inNode is a direct child or grandchild
292 if (Node *inDCNode = cnode->isInMyDescendance(inNode))
294 DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
295 string inDCName = proc->getChildName(inDCNode);
296 Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()));
297 Agedge_t* anEdge = agedge(_graph, aTailNode, aHeadNode);
298 DEBTRACE("--- control link from tail node: --- " << aNode->name << " --> " << inDCName);
303 // --- datalink from node, keep only link staying inside the bloc
306 list<OutPort*> outPortList = outNode->getSetOfOutPort();
307 list<OutPort*>::const_iterator itou = outPortList.begin();
308 for (; itou != outPortList.end(); ++itou)
310 set<InPort*> inPortList = (*itou)->edSetInPort();
311 set<InPort*>::const_iterator itin = inPortList.begin();
312 for (; itin != inPortList.end(); ++itin)
314 Node *inNode = (*itin)->getNode();
315 string inName = proc->getChildName(inNode);
316 DEBTRACE("------ data link from tail node: ---- ");
317 if (Node *inDCNode = cnode->isInMyDescendance(inNode))
319 DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
320 string inDCName = proc->getChildName(inDCNode);
321 Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()));
322 Agedge_t* anEdge = agedge(_graph, aTailNode, aHeadNode);
323 DEBTRACE("------ data link from tail node: ---- " << aNode->name << " --> " << inDCName);
332 void SceneBlocItem::arrangeCanvasNodes(YACS::ENGINE::ComposedNode *cnode)
334 DEBTRACE("SceneBlocItem::arrangeCanvasNodes");
335 Proc *proc = GuiContext::getCurrent()->getProc();
337 SubjectNode* subCompo = GuiContext::getCurrent()->_mapOfSubjectNode[cnode];
338 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subCompo];
339 SceneComposedNodeItem *sceneCompo = dynamic_cast<SceneComposedNodeItem*>(sci);
341 qreal yHead = sceneCompo->getHeaderBottom() + Resource::Space_Margin;
342 qreal xOffset = Resource::Space_Margin;
344 list<Node*> children = cnode->edGetDirectDescendants();
345 for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
347 Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()));
348 DEBTRACE("Get node in graph: " << aNode->name);
349 SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
350 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
352 qreal xCenter = ND_coord_i(aNode).x;
353 qreal yCenter = ND_coord_i(aNode).y;
354 qreal halfWidth = sci->boundingRect().width()/2.;
355 qreal halfHeight = sci->boundingRect().height()/2.;
357 sci->setPos(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight);
358 SceneNodeItem *scni = dynamic_cast<SceneNodeItem*>(sci);
359 if (scni) scni->setExpandedPos(QPointF(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight));
361 sceneCompo->checkGeometryChange();
362 if (Scene::_autoComputeLinks)
364 YACS::HMI::SubjectProc* subproc = QtGuiContext::getQtCurrent()->getSubjectProc();
365 SceneItem *item = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subproc];
366 SceneComposedNodeItem *proc = dynamic_cast<SceneComposedNodeItem*>(item);
367 proc->rebuildLinks();