1 // Copyright (C) 2006-2022 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, or (at your option) any later version.
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"
36 #include <Qtx.h> // for Localizer
38 #include "Resource.hxx"
41 #include "YacsTrace.hxx"
44 using namespace YACS::ENGINE;
45 using namespace YACS::HMI;
47 /*! Definition in dot per inch for graphviz input:
48 * size of elementary nodes are in pixel in Qt, and given in inches to graphviz
52 SceneBlocItem::SceneBlocItem(QGraphicsScene *scene, SceneItem *parent,
53 QString label, Subject *subject)
54 : SceneComposedNodeItem(scene, parent, label, subject)
56 DEBTRACE("SceneBlocItem::SceneBlocItem " <<label.toStdString());
57 _format = "%1"; // --- format to convert a float without locale: ex. 9.81
60 SceneBlocItem::~SceneBlocItem()
64 //! Auto-arrange nodes inside a schema using Graphviz C API.
68 void SceneBlocItem::arrangeChildNodes()
71 DEBTRACE("SceneBlocItem::arrangeChildNodes");
72 clock_t start_t, end_t;
77 SubjectComposedNode *scnode = dynamic_cast<SubjectComposedNode*>(getSubject());
79 ComposedNode *cnode = dynamic_cast<ComposedNode*>(scnode->getNode());
82 // ---- Create a graphviz context
84 // Graphviz is sensitive to locale : set the mimimal one ("C")for numeric
88 GVC_t* aGvc = gvContext();
90 // ---- Create a graph
92 _graph = agopen((char*)( cnode->getName().c_str()), Agdirected, NULL);
94 // ---- Initialize and set attributes for the graph
96 agattr(_graph, AGRAPH, const_cast<char*>("compound"), const_cast<char*>("true"));
97 agattr(_graph, AGRAPH, const_cast<char*>("rankdir"), const_cast<char*>("LR"));
98 agattr(_graph, AGRAPH, const_cast<char*>("dpi"), const_cast<char*>("72"));
99 agattr(_graph, AGRAPH, const_cast<char*>("label"), const_cast<char*>("myLabel"));
100 agattr(_graph, AGRAPH, const_cast<char*>("labelloc"), const_cast<char*>("top"));
101 agattr(_graph, AGRAPH, const_cast<char*>("fontsize"), const_cast<char*>("24"));
102 agattr(_graph, AGRAPH, const_cast<char*>("splines"), const_cast<char*>(""));
104 // --- Initialize attributes for nodes
106 agattr(_graph, AGNODE, const_cast<char*>("height"), const_cast<char*>("" ));
107 agattr(_graph, AGNODE, const_cast<char*>("width"), const_cast<char*>("" ));
108 agattr(_graph, AGNODE, const_cast<char*>("shape"), const_cast<char*>("" ));
109 agattr(_graph, AGNODE, const_cast<char*>("fixedsize"), const_cast<char*>("false" ));
111 // ---- Bind graph to graphviz context - must be done before layout
112 // ---- Compute a layout
117 // createGraphvizNodes(cnode);
118 DEBTRACE("end of graphviz input");
120 agwrite(_graph, stderr);
122 //DEBTRACE("external render for test");
123 //gvRenderFilename(aGvc, _mainGraph, "dot", "graph1.dot");
124 DEBTRACE("compute layout");
125 gvLayout(aGvc, _graph, "dot");
126 DEBTRACE("external render for test");
128 gvRenderFilename(aGvc, _graph, "dot", "graph2.dot");
131 catch (std::exception &e)
133 DEBTRACE("Exception Graphviz layout: " << e.what());
138 DEBTRACE("Unknown Exception Graphviz layout ");
144 double passe = (end_t -start_t);
145 passe = passe/CLOCKS_PER_SEC;
146 DEBTRACE("graphviz : " << passe);
150 DEBTRACE("start of display");
151 // ---- layout Canvas nodes recursively
153 arrangeCanvasNodes(cnode);
155 DEBTRACE("clean up graphviz");
156 // ---- Delete layout
158 gvFreeLayout(aGvc, _graph);
160 // ---- Free graph structures
164 // ---- Free context and return number of errors
166 gvFreeContext( aGvc );
172 double passe = (end_t -start_t);
173 passe = passe/CLOCKS_PER_SEC;
174 DEBTRACE("display : " << passe);
180 void SceneBlocItem::getNodesInfo(YACS::ENGINE::ComposedNode *cnode)
182 Proc *proc = GuiContext::getCurrent()->getProc();
184 // --- Create Nodes = direct descendants in the bloc
186 list<Node*> children = cnode->edGetDirectDescendants();
187 for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
189 Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()), 1);
190 DEBTRACE("Add node in graph: " << agnameof(aNode));
192 SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
193 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
194 double nh = sci->getHeight();
195 double nw = sci->getWidth();
199 QString height, width;
200 height = QString(_format.c_str()).arg(lh, 0, 'g', 3);
201 width = QString(_format.c_str()).arg(lw, 0, 'g', 3);
203 DEBTRACE(agnameof(aNode) << " (" << nh << "," << nw << ") = (" << height.toStdString() << " ; " << width.toStdString() <<")");
204 agset(aNode, const_cast<char*>("height"), height.toLatin1().data());
205 agset(aNode, const_cast<char*>("width"), width.toLatin1().data());
206 agset(aNode, const_cast<char*>("shape"), const_cast<char*>("box") );
207 agset(aNode, const_cast<char*>("fixedsize"), const_cast<char*>("true") );
210 // --- Create edges (i.e. links)
212 Agnode_t* aNode = NULL;
213 for (aNode = agfstnode(_graph); aNode; aNode = agnxtnode(_graph, aNode))
215 string aNodeName = agnameof(aNode);
216 DEBTRACE("--- tail node " << aNodeName);
217 Agnode_t* aTailNode = aNode;
218 Node* outNode = proc->getChildByName(string(agnameof(aTailNode)));
219 if (outNode->getFather() != cnode)
221 DEBTRACE(" =========== problem here ! =============================");
222 continue; // Create edges only with outgoing nodes directly in bloc
225 // --- control link from node, keep only link staying inside the bloc
228 OutGate *outGate = outNode->getOutGate();
229 list<InGate*> setOfInGate = outGate->edSetInGate();
230 list<InGate*>::const_iterator itin = setOfInGate.begin();
231 for (; itin != setOfInGate.end(); ++itin)
233 Node *inNode = (*itin)->getNode();
234 string inName = proc->getChildName(inNode);
235 DEBTRACE("--- control link from tail node: --- "<<inName);
236 // --- isInMyDescendance(this) return this
237 // isInMyDescendance(inNode) return direct child if inNode is a direct child or grandchild
238 if (Node *inDCNode = cnode->isInMyDescendance(inNode))
240 DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
241 string inDCName = proc->getChildName(inDCNode);
242 Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()), 1);
243 Agedge_t* anEdge = agedge(_graph, aTailNode, aHeadNode, NULL, 1);
244 DEBTRACE("--- control link from tail node: --- " << agnameof(aNode) << " --> " << inDCName);
249 // --- datalink from node, keep only link staying inside the bloc
252 list<OutPort*> outPortList = outNode->getSetOfOutPort();
253 list<OutPort*>::const_iterator itou = outPortList.begin();
254 for (; itou != outPortList.end(); ++itou)
256 set<InPort*> inPortList = (*itou)->edSetInPort();
257 set<InPort*>::const_iterator itin = inPortList.begin();
258 for (; itin != inPortList.end(); ++itin)
260 Node *inNode = (*itin)->getNode();
261 string inName = proc->getChildName(inNode);
262 DEBTRACE("------ data link from tail node: ---- ");
263 if (Node *inDCNode = cnode->isInMyDescendance(inNode))
265 DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
266 string inDCName = proc->getChildName(inDCNode);
267 Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()), 1);
268 Agedge_t* anEdge = agedge(_graph, aTailNode, aHeadNode, NULL, 1);
269 DEBTRACE("------ data link from tail node: ---- " << agnameof(aNode) << " --> " << inDCName);
278 void SceneBlocItem::arrangeCanvasNodes(YACS::ENGINE::ComposedNode *cnode)
280 DEBTRACE("SceneBlocItem::arrangeCanvasNodes");
281 Proc *proc = GuiContext::getCurrent()->getProc();
283 SubjectNode* subCompo = GuiContext::getCurrent()->_mapOfSubjectNode[cnode];
284 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subCompo];
285 SceneComposedNodeItem *sceneCompo = dynamic_cast<SceneComposedNodeItem*>(sci);
287 qreal yHead = sceneCompo->getHeaderBottom() + Resource::Space_Margin;
288 qreal xOffset = Resource::Space_Margin;
290 list<Node*> children = cnode->edGetDirectDescendants();
291 for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
293 Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()), 1);
294 DEBTRACE("Get node in graph: " << agnameof(aNode));
295 SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
296 SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
298 qreal xCenter = ND_coord(aNode).x;
299 qreal yCenter = ND_coord(aNode).y;
300 qreal halfWidth = sci->boundingRect().width()/2.;
301 qreal halfHeight = sci->boundingRect().height()/2.;
303 sci->setPos(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight);
304 SceneNodeItem *scni = dynamic_cast<SceneNodeItem*>(sci);
305 if (scni) scni->setExpandedPos(QPointF(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight));
307 sceneCompo->checkGeometryChange();
308 if (Scene::_autoComputeLinks)
310 YACS::HMI::SubjectProc* subproc = QtGuiContext::getQtCurrent()->getSubjectProc();
311 SceneItem *item = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subproc];
312 SceneComposedNodeItem *proc = dynamic_cast<SceneComposedNodeItem*>(item);
313 proc->rebuildLinks();