Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/yacs.git] / src / genericgui / SceneBlocItem.cxx
1 // Copyright (C) 2006-2012  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.
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 // --- for graphviz 2.8
35 #undef HAVE_CONFIG_H
36
37 #ifdef HAVE_DOTNEATO_H
38   #include <dotneato.h>
39 #else
40   #include <gvc.h>
41 #endif
42
43 #ifndef ND_coord_i
44 #define ND_coord_i(n) (n)->u.coord
45 #endif
46
47 #include "Resource.hxx"
48
49 //#define _DEVDEBUG_
50 #include "YacsTrace.hxx"
51
52 using namespace std;
53 using namespace YACS::ENGINE;
54 using namespace YACS::HMI;
55
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
58  */
59 #define DPI 72.
60
61 static GVC_t* aGvc = 0;
62
63
64 SceneBlocItem::SceneBlocItem(QGraphicsScene *scene, SceneItem *parent,
65                              QString label, Subject *subject)
66   : SceneComposedNodeItem(scene, parent, label, subject)
67 {
68   DEBTRACE("SceneBlocItem::SceneBlocItem " <<label.toStdString());
69   _format = "%1"; // --- format to convert a float without locale: ex. 9.81
70 }
71
72 SceneBlocItem::~SceneBlocItem()
73 {
74 }
75
76 //! Auto-arrange nodes inside a schema using Graphviz C API.
77 /*!
78  */
79
80 void SceneBlocItem::arrangeChildNodes()
81 {
82   DEBTRACE("SceneBlocItem::arrangeChildNodes");
83   clock_t start_t, end_t;
84   start_t = clock();
85
86
87   SubjectComposedNode *scnode = dynamic_cast<SubjectComposedNode*>(getSubject());
88   YASSERT(scnode);
89   ComposedNode *cnode = dynamic_cast<ComposedNode*>(scnode->getNode());
90   YASSERT(cnode);
91
92   // ---- Create a graphviz context
93
94   if(!aGvc)
95     {
96       DEBTRACE(setlocale(LC_ALL,NULL));
97       //Graphviz is sensitive to locale : set the mimimal one ("C")for numeric
98       setlocale(LC_NUMERIC, "C");
99       aginit();
100       aGvc = gvContext();
101     }
102
103   // ---- Create a graph
104
105   _graph = agopen((char*)( cnode->getName().c_str()), AGDIGRAPH);
106
107   // ---- Initialize and set attributes for the graph
108   
109   Agsym_t* attr;
110   if ( !(attr = agfindattr(_graph, (char *)"compound")))
111     attr = agraphattr(_graph, (char *)"compound", (char *)"false");
112   agxset(_graph, attr->index, (char *)"true");
113
114   if ( !(attr = agfindattr(_graph, (char *)"rankdir")))
115     attr = agraphattr(_graph, (char *)"rankdir", (char *)"TB");
116   agxset(_graph, attr->index, (char *)"LR");
117
118 //   if ( !(attr = agfindattr(_graph, (char *)"ordering")))
119 //     attr = agraphattr(_graph, (char *)"ordering", (char *)"" );
120 //   agxset(_graph, attr->index, (char *)"in" );
121   
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
125
126   // --- label is used to reserve place for bloc banners (adjust size with font !)
127
128   if ( !(attr = agfindattr(_graph, (char *)"label")))
129     attr = agraphattr(_graph, (char *)"label", (char *)"label");
130   agxset(_graph, attr->index, (char *)"myLabel");
131
132   if ( !(attr = agfindattr(_graph, (char *)"labelloc")))
133     attr = agraphattr(_graph, (char *)"labelloc", (char *)"top");
134   agxset(_graph, attr->index, (char *)"top");
135
136   if ( !(attr = agfindattr(_graph, (char *)"fontsize")))
137     attr = agraphattr(_graph, (char *)"fontsize", (char *)"24");
138   agxset(_graph, attr->index, (char *)"24");
139
140   if ( !(attr = agfindattr(_graph, (char *)"splines")))
141     attr = agraphattr(_graph, (char *)"splines", (char *)"");
142   agxset(_graph, attr->index, (char *)"");
143
144   // --- Initialize attributes for nodes
145
146   if ( !(attr = agfindattr( _graph->proto->n, (char *)"height")))
147     attr = agnodeattr(_graph, (char *)"height", (char *)"" );
148
149   if ( !(attr = agfindattr( _graph->proto->n, (char *)"width")))
150     attr = agnodeattr(_graph, (char *)"width", (char *)"" );
151
152   if ( !(attr = agfindattr( _graph->proto->n, (char *)"shape")))
153     attr = agnodeattr(_graph, (char *)"shape", (char *)"" );
154
155   if ( !(attr = agfindattr( _graph->proto->n, (char *)"fixedsize")))
156     attr = agnodeattr(_graph, (char *)"fixedsize", (char *)"false" );
157
158   // ---- Bind graph to graphviz context - must be done before layout
159   // ---- Compute a layout
160
161   try
162     {
163       getNodesInfo(cnode);
164       //      createGraphvizNodes(cnode);
165       DEBTRACE("end of graphviz input");
166 #ifdef _DEVDEBUG_
167       agwrite(_graph, stderr);
168 #endif
169 #ifdef HAVE_DOTNEATO_H
170       gvBindContext(aGvc, _graph);
171       dot_layout(_graph);
172 #else
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");
178 #ifdef _DEVDEBUG_
179       gvRenderFilename(aGvc, _graph, (char *)"dot", (char *)"graph2.dot");
180 #endif
181 #endif
182    }
183   catch (std::exception &e)
184     {
185       DEBTRACE("Exception Graphviz layout: " << e.what());
186       return;
187     }
188   catch (...)
189     {
190       DEBTRACE("Unknown Exception Graphviz layout ");
191       return;
192     }
193   {
194     end_t = clock();
195     double passe =  (end_t -start_t);
196     passe = passe/CLOCKS_PER_SEC;
197     DEBTRACE("graphviz : " << passe);
198     start_t = end_t;
199   }
200   DEBTRACE("start of display");
201   // ---- layout Canvas nodes recursively
202
203   arrangeCanvasNodes(cnode);
204
205   DEBTRACE("clean up graphviz");
206   // ---- Delete layout
207
208 #ifdef HAVE_DOTNEATO_H
209   dot_cleanup(_graph);
210 #else
211   gvFreeLayout(aGvc, _graph);
212 #endif
213
214   // ---- Free graph structures
215
216   agclose(_graph) ;
217
218   // ---- Free context and return number of errors
219
220 #ifndef HAVE_DOTNEATO_H
221   //gvFreeContext( aGvc );
222 #endif
223
224   // --- update scene
225   {
226     end_t = clock();
227     double passe =  (end_t -start_t);
228     passe = passe/CLOCKS_PER_SEC;
229     DEBTRACE("display : " << passe);
230     start_t = end_t;
231   }
232 }
233
234 void  SceneBlocItem::getNodesInfo(YACS::ENGINE::ComposedNode *cnode)
235 {
236   Proc *proc = GuiContext::getCurrent()->getProc();
237
238   // --- Create Nodes = direct descendants in the bloc
239
240   list<Node*> children = cnode->edGetDirectDescendants();
241   for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
242     {
243       Agnode_t* aNode = agnode(_graph, (char*)(proc->getChildName(*it).c_str()));
244       DEBTRACE("Add node in graph: " << aNode->name);
245
246       SubjectNode* snode = GuiContext::getCurrent()->_mapOfSubjectNode[(*it)];
247       SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[snode];
248       double nh = sci->getHeight();
249       double nw = sci->getWidth();
250       double lh = nh/DPI;
251       double lw = nw/DPI;
252
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);
256
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" );
262     }
263
264   // --- Create edges (i.e. links)
265
266   Agnode_t* aNode;
267   for (aNode = agfstnode(_graph); aNode; aNode = agnxtnode(_graph, aNode))
268   {
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)
274       {
275         DEBTRACE(" =========== problem here ! =============================");
276         continue; // Create edges only with outgoing nodes directly in bloc
277       }
278
279     // --- control link from node, keep only link staying inside the bloc
280
281     {
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)
286         {
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))
293             {
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);
299             }
300         }
301     }
302
303     // --- datalink from node, keep only link staying inside the bloc
304
305     {
306       list<OutPort*> outPortList = outNode->getSetOfOutPort();
307       list<OutPort*>::const_iterator itou = outPortList.begin();
308       for (; itou != outPortList.end(); ++itou)
309         {
310           set<InPort*> inPortList = (*itou)->edSetInPort();
311           set<InPort*>::const_iterator itin = inPortList.begin();
312           for (; itin != inPortList.end(); ++itin)
313             {
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))
318                 {
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);
324                 }
325             }
326         }
327     }
328   }
329 }
330
331
332 void SceneBlocItem::arrangeCanvasNodes(YACS::ENGINE::ComposedNode *cnode)
333 {
334   DEBTRACE("SceneBlocItem::arrangeCanvasNodes");
335   Proc *proc = GuiContext::getCurrent()->getProc();
336
337   SubjectNode* subCompo = GuiContext::getCurrent()->_mapOfSubjectNode[cnode];
338   SceneItem* sci = QtGuiContext::getQtCurrent()->_mapOfSceneItem[subCompo];
339   SceneComposedNodeItem *sceneCompo = dynamic_cast<SceneComposedNodeItem*>(sci);
340   YASSERT(sceneCompo);
341   qreal yHead = sceneCompo->getHeaderBottom() + Resource::Space_Margin;
342   qreal xOffset = Resource::Space_Margin;
343
344   list<Node*> children = cnode->edGetDirectDescendants();
345   for (list<Node*>::iterator it = children.begin(); it != children.end(); ++it)
346     {
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];
351
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.;
356
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));
360     }
361   sceneCompo->checkGeometryChange();
362   if (Scene::_autoComputeLinks)
363     {
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();
368     }
369
370 }