Salome HOME
Update copyrights
[modules/yacs.git] / src / genericgui / SceneBlocItem.cxx
1 // Copyright (C) 2006-2019  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SceneBlocItem.hxx"
21 #include "Scene.hxx"
22 #include "Menus.hxx"
23 #include "QtGuiContext.hxx"
24 #include "guiObservers.hxx"
25 #include "ComposedNode.hxx"
26 #include "Proc.hxx"
27 #include "OutputPort.hxx"
28 #include "OutGate.hxx"
29 #include "InputPort.hxx"
30 #include "InGate.hxx"
31
32 #include <sstream>
33
34 #include <gvc.h>
35
36 #include "Resource.hxx"
37
38 //#define _DEVDEBUG_
39 #include "YacsTrace.hxx"
40
41 using namespace std;
42 using namespace YACS::ENGINE;
43 using namespace YACS::HMI;
44
45 /*! Definition in dot per inch for graphviz input:
46  *  size of elementary nodes are in pixel in Qt, and given in inches to graphviz
47  */
48 #define DPI 72.
49
50 SceneBlocItem::SceneBlocItem(QGraphicsScene *scene, SceneItem *parent,
51                              QString label, Subject *subject)
52   : SceneComposedNodeItem(scene, parent, label, subject)
53 {
54   DEBTRACE("SceneBlocItem::SceneBlocItem " <<label.toStdString());
55   _format = "%1"; // --- format to convert a float without locale: ex. 9.81
56 }
57
58 SceneBlocItem::~SceneBlocItem()
59 {
60 }
61
62 //! Auto-arrange nodes inside a schema using Graphviz C API.
63 /*!
64  */
65
66 void SceneBlocItem::arrangeChildNodes()
67 {
68 #ifdef _DEVDEBUG_
69   DEBTRACE("SceneBlocItem::arrangeChildNodes");
70   clock_t start_t, end_t;
71   start_t = clock();
72 #endif //_DEVDEBUG_
73   GVC_t* aGvc = 0;
74
75
76   SubjectComposedNode *scnode = dynamic_cast<SubjectComposedNode*>(getSubject());
77   YASSERT(scnode);
78   ComposedNode *cnode = dynamic_cast<ComposedNode*>(scnode->getNode());
79   YASSERT(cnode);
80
81   // ---- Create a graphviz context
82
83   if(!aGvc)
84     {
85       DEBTRACE(setlocale(LC_ALL,NULL));
86       //Graphviz is sensitive to locale : set the mimimal one ("C")for numeric
87       setlocale(LC_NUMERIC, "C");
88       //aginit();
89       aGvc = gvContext();
90     }
91
92   // ---- Create a graph
93
94   _graph = agopen((char*)( cnode->getName().c_str()), Agdirected, NULL);
95
96   // ---- Initialize and set attributes for the graph
97
98   agattr(_graph, AGRAPH, const_cast<char*>("compound"), const_cast<char*>("true"));
99   agattr(_graph, AGRAPH, const_cast<char*>("rankdir"), const_cast<char*>("LR"));
100   agattr(_graph, AGRAPH, const_cast<char*>("dpi"), const_cast<char*>("72"));
101   agattr(_graph, AGRAPH, const_cast<char*>("label"), const_cast<char*>("myLabel"));
102   agattr(_graph, AGRAPH, const_cast<char*>("labelloc"), const_cast<char*>("top"));
103   agattr(_graph, AGRAPH, const_cast<char*>("fontsize"), const_cast<char*>("24"));
104   agattr(_graph, AGRAPH, const_cast<char*>("splines"), const_cast<char*>(""));
105
106   // --- Initialize attributes for nodes
107
108   agattr(_graph, AGNODE, const_cast<char*>("height"), const_cast<char*>("" ));
109   agattr(_graph, AGNODE, const_cast<char*>("width"), const_cast<char*>("" ));
110   agattr(_graph, AGNODE, const_cast<char*>("shape"), const_cast<char*>("" ));
111   agattr(_graph, AGNODE, const_cast<char*>("fixedsize"), const_cast<char*>("false" ));
112
113   // ---- Bind graph to graphviz context - must be done before layout
114   // ---- Compute a layout
115
116   try
117     {
118       getNodesInfo(cnode);
119       //      createGraphvizNodes(cnode);
120       DEBTRACE("end of graphviz input");
121 #ifdef _DEVDEBUG_
122       agwrite(_graph, stderr);
123 #endif
124       //DEBTRACE("external render for test");
125       //gvRenderFilename(aGvc, _mainGraph, "dot", "graph1.dot");
126       DEBTRACE("compute layout");
127       gvLayout(aGvc, _graph, "dot");
128       DEBTRACE("external render for test");
129 #ifdef _DEVDEBUG_
130       gvRenderFilename(aGvc, _graph, "dot", "graph2.dot");
131 #endif
132    }
133   catch (std::exception &e)
134     {
135       DEBTRACE("Exception Graphviz layout: " << e.what());
136       return;
137     }
138   catch (...)
139     {
140       DEBTRACE("Unknown Exception Graphviz layout ");
141       return;
142     }
143 #ifdef _DEVDEBUG_
144   {
145     end_t = clock();
146     double passe =  (end_t -start_t);
147     passe = passe/CLOCKS_PER_SEC;
148     DEBTRACE("graphviz : " << passe);
149     start_t = end_t;
150   }
151 #endif //_DEVDEBUG_
152   DEBTRACE("start of display");
153   // ---- layout Canvas nodes recursively
154
155   arrangeCanvasNodes(cnode);
156
157   DEBTRACE("clean up graphviz");
158   // ---- Delete layout
159
160   gvFreeLayout(aGvc, _graph);
161
162   // ---- Free graph structures
163
164   agclose(_graph) ;
165
166   // ---- Free context and return number of errors
167
168   gvFreeContext( aGvc );
169
170   // --- update scene
171 #ifdef _DEVDEBUG_
172   {
173     end_t = clock();
174     double passe =  (end_t -start_t);
175     passe = passe/CLOCKS_PER_SEC;
176     DEBTRACE("display : " << passe);
177     start_t = end_t;
178   }
179 #endif //_DEVDEBUG_
180 }
181
182 void  SceneBlocItem::getNodesInfo(YACS::ENGINE::ComposedNode *cnode)
183 {
184   Proc *proc = GuiContext::getCurrent()->getProc();
185
186   // --- Create Nodes = direct descendants in the bloc
187
188   list<Node*> children = cnode->edGetDirectDescendants();
189   for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
190     {
191       Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()), 1);
192       DEBTRACE("Add node in graph: " << agnameof(aNode));
193
194       SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
195       SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
196       double nh = sci->getHeight();
197       double nw = sci->getWidth();
198       double lh = nh/DPI;
199       double lw = nw/DPI;
200
201       QString height, width;
202       height = QString(_format.c_str()).arg(lh, 0, 'g', 3);
203       width  = QString(_format.c_str()).arg(lw, 0, 'g', 3);
204
205       DEBTRACE(agnameof(aNode) << " (" << nh << "," << nw << ") = (" << height.toStdString()  << " ; " << width.toStdString() <<")");
206       agset(aNode, const_cast<char*>("height"),    height.toLatin1().data());
207       agset(aNode, const_cast<char*>("width"),     width.toLatin1().data());
208       agset(aNode, const_cast<char*>("shape"),     const_cast<char*>("box") );
209       agset(aNode, const_cast<char*>("fixedsize"), const_cast<char*>("true") );
210     }
211
212   // --- Create edges (i.e. links)
213
214   Agnode_t* aNode;
215   for (aNode = agfstnode(_graph); aNode; aNode = agnxtnode(_graph, aNode))
216   {
217     string aNodeName = agnameof(aNode);
218     DEBTRACE("--- tail node " << aNodeName);
219     Agnode_t* aTailNode = aNode;
220     Node* outNode = proc->getChildByName(string(agnameof(aTailNode)));
221     if (outNode->getFather() != cnode)
222       {
223         DEBTRACE(" =========== problem here ! =============================");
224         continue; // Create edges only with outgoing nodes directly in bloc
225       }
226
227     // --- control link from node, keep only link staying inside the bloc
228
229     {
230       OutGate *outGate = outNode->getOutGate();
231       list<InGate*> setOfInGate = outGate->edSetInGate();
232       list<InGate*>::const_iterator itin = setOfInGate.begin();
233       for (; itin != setOfInGate.end(); ++itin)
234         {
235           Node *inNode = (*itin)->getNode();
236           string inName = proc->getChildName(inNode);
237           DEBTRACE("--- control link from tail node: --- "<<inName);
238           // --- isInMyDescendance(this)   return this
239           //     isInMyDescendance(inNode) return direct child if inNode is a direct child or grandchild
240           if (Node *inDCNode = cnode->isInMyDescendance(inNode))
241             {
242               DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
243               string inDCName = proc->getChildName(inDCNode);
244               Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()), 1);
245               Agedge_t* anEdge    = agedge(_graph, aTailNode, aHeadNode, NULL, 1);
246               DEBTRACE("--- control link from tail node: --- " << agnameof(aNode) << " --> " << inDCName);
247             }
248         }
249     }
250
251     // --- datalink from node, keep only link staying inside the bloc
252
253     {
254       list<OutPort*> outPortList = outNode->getSetOfOutPort();
255       list<OutPort*>::const_iterator itou = outPortList.begin();
256       for (; itou != outPortList.end(); ++itou)
257         {
258           set<InPort*> inPortList = (*itou)->edSetInPort();
259           set<InPort*>::const_iterator itin = inPortList.begin();
260           for (; itin != inPortList.end(); ++itin)
261             {
262               Node *inNode = (*itin)->getNode();
263               string inName = proc->getChildName(inNode);
264               DEBTRACE("------ data link from tail node: ---- ");
265               if (Node *inDCNode = cnode->isInMyDescendance(inNode))
266                 {
267                   DEBTRACE("--- edge inside the bloc " << inDCNode->getName());
268                   string inDCName = proc->getChildName(inDCNode);
269                   Agnode_t* aHeadNode = agnode(_graph, (char*)(inDCName.c_str()), 1);
270                   Agedge_t* anEdge    = agedge(_graph, aTailNode, aHeadNode, NULL, 1);
271                   DEBTRACE("------ data link from tail node: ---- " << agnameof(aNode) << " --> " << inDCName);
272                 }
273             }
274         }
275     }
276   }
277 }
278
279
280 void SceneBlocItem::arrangeCanvasNodes(YACS::ENGINE::ComposedNode *cnode)
281 {
282   DEBTRACE("SceneBlocItem::arrangeCanvasNodes");
283   Proc *proc = GuiContext::getCurrent()->getProc();
284
285   SubjectNode* subCompo = GuiContext::getCurrent()->_mapOfSubjectNode[cnode];
286   SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subCompo];
287   SceneComposedNodeItem *sceneCompo = dynamic_cast<SceneComposedNodeItem*>(sci);
288   YASSERT(sceneCompo);
289   qreal yHead = sceneCompo->getHeaderBottom() + Resource::Space_Margin;
290   qreal xOffset = Resource::Space_Margin;
291
292   list<Node*> children = cnode->edGetDirectDescendants();
293   for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
294     {
295       Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()), 1);
296       DEBTRACE("Get node in graph: " << agnameof(aNode));
297       SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
298       SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
299
300       qreal xCenter = ND_coord(aNode).x;
301       qreal yCenter = ND_coord(aNode).y;
302       qreal halfWidth = sci->boundingRect().width()/2.;
303       qreal halfHeight = sci->boundingRect().height()/2.;
304
305       sci->setPos(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight);
306       SceneNodeItem *scni = dynamic_cast<SceneNodeItem*>(sci);
307       if (scni) scni->setExpandedPos(QPointF(xOffset + xCenter -halfWidth, yHead + yCenter -halfHeight));
308     }
309   sceneCompo->checkGeometryChange();
310   if (Scene::_autoComputeLinks)
311     {
312       YACS::HMI::SubjectProc* subproc = QtGuiContext::getQtCurrent()->getSubjectProc();
313       SceneItem *item = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subproc];
314       SceneComposedNodeItem *proc = dynamic_cast<SceneComposedNodeItem*>(item);
315       proc->rebuildLinks();
316     }
317
318 }