Salome HOME
merge from branch DEV tag mergeto_trunk_04apr08
[modules/yacs.git] / src / prs / YACSPrs_LoopNode.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA 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 "YACSPrs_LoopNode.h"
21 #include "YACSPrs_Def.h"
22
23 #include "SUIT_ResourceMgr.h"
24
25 #include <qpainter.h>
26
27 #include <ForLoop.hxx>
28 #include <WhileLoop.hxx>
29
30 using namespace YACS::ENGINE;
31
32 /*!
33   Constructor
34 */
35 YACSPrs_LoopNode::YACSPrs_LoopNode( SUIT_ResourceMgr* theMgr, QCanvas* theCanvas,
36                                     YACS::HMI::SubjectNode* theSNode, const bool& thePortUpdate ):
37   YACSPrs_InlineNode(theMgr, theCanvas, theSNode, false)
38 {
39   setNodeColor(LOOPNODE_COLOR);
40   setNodeSubColor(LOOPNODE_SUBCOLOR);
41   setBracketColor(LOOPNODE_BRACKETCOLOR);
42
43   setStoreColor(nodeColor());
44   setStoreSubColor(nodeSubColor());
45   myStoreBracketColor = myBracketColor;
46
47   myTopPixmap = myMgr->loadPixmap( "YACSPrs", QObject::tr( "ICON_TITLE_RARROW" ));
48   myBottomPixmap = myMgr->loadPixmap( "YACSPrs", QObject::tr( "ICON_TITLE_LARROW" ));
49   
50   if ( thePortUpdate ) {
51     //updatePorts(); // will be called in moveBy(...) function
52     if ( isFullDMode() )
53       moveBy(3*TITLE_HEIGHT/2+NODEBOUNDARY_MARGIN, 3*TITLE_HEIGHT/2+NODEBOUNDARY_MARGIN);
54     else if ( isControlDMode() )
55       moveBy(2*HOOKPOINT_SIZE+NODEBOUNDARY_MARGIN,2*HOOKPOINT_SIZE+NODEBOUNDARY_MARGIN);
56   }
57 }
58
59 /*!
60   Destructor
61 */
62 YACSPrs_LoopNode::~YACSPrs_LoopNode()
63 {
64 }
65
66 void YACSPrs_LoopNode::select(const QPoint& theMousePos, const bool toSelect)
67 {
68   if ( toSelect ) 
69   {
70     // unhilight the item under the mouse cursor
71     hilight(theMousePos, false);
72
73     bool isOnPort = false;
74     if (getBodyRect().contains(theMousePos, true) || getGateRect().contains(theMousePos, true))
75     {
76       // process ports
77       QPtrList<YACSPrs_Port> aPortList = getPortList();
78       for (YACSPrs_Port* aPort = aPortList.first(); aPort; aPort = aPortList.next()) {
79         if (aPort->getPortRect().contains(theMousePos, true)) {
80           isOnPort = true;
81           if ( aPort != mySelectedPort )
82           {
83             if ( mySelectedPort )
84             {
85               mySelectedPort->setSelected(false);
86               mySelectedPort->setColor(mySelectedPort->storeColor(), false, true, true);
87               synchronize( mySelectedPort, false );
88             }
89             else
90             {
91               setSelected(false);
92               getSEngine()->select(false);
93
94               setBracketColor( myStoreBracketColor );
95               setNodeSubColor( storeSubColor(), true );
96             }
97             aPort->setSelected(true);
98             aPort->setColor(aPort->Color().dark(130), false, true, true);
99             mySelectedPort = aPort;
100             synchronize( mySelectedPort, true );
101           }
102           break;
103         }
104       }
105     }
106     
107     if ( !isOnPort )
108     {
109       if ( mySelectedPort )
110       {
111         mySelectedPort->setSelected(false);
112         synchronize( mySelectedPort, false );
113         mySelectedPort = 0;
114       }
115       
116       if ( storeSubColor().dark(130) != nodeSubColor() )
117       {
118         myStoreBracketColor = myBracketColor;
119         setStoreSubColor( nodeSubColor() );
120         
121         setSelected(true);
122         getSEngine()->select(true);
123
124         setBracketColor( bracketColor().dark(130) );
125         setNodeSubColor( nodeSubColor().dark(130), true );
126       }
127     }
128   }
129   else
130   {
131     if ( mySelectedPort ) {
132       mySelectedPort->setSelected(false);
133       mySelectedPort->setColor(mySelectedPort->storeColor(), false, true, true);
134       synchronize( mySelectedPort, false );
135       mySelectedPort = 0;
136     }
137     else {
138       setSelected(false);
139       getSEngine()->select(false);
140
141       setBracketColor( myStoreBracketColor );
142       setNodeSubColor( storeSubColor(), true );
143     }
144   }
145 }
146
147 int YACSPrs_LoopNode::rtti() const
148 {
149   return 0;//YACSPrs_Canvas::Rtti_LoopNode;
150 }
151
152 QPointArray YACSPrs_LoopNode::constructAreaPoints(int theW, int theH) const
153 {
154   QPointArray aPnts;
155
156   if ( isControlDMode() )
157   {
158     aPnts = QPointArray(4);
159     aPnts[0] = QPoint((int)x(), (int)y()) + QPoint(-NODEBOUNDARY_MARGIN,-NODEBOUNDARY_MARGIN);
160     aPnts[1] = aPnts[0] + QPoint(theW, 0) + QPoint(NODEBOUNDARY_MARGIN,0);
161     aPnts[2] = aPnts[1] + QPoint(0, theH) + QPoint(0,NODEBOUNDARY_MARGIN);
162     aPnts[3] = aPnts[0] + QPoint(0, theH) + QPoint(0,NODEBOUNDARY_MARGIN);
163   }
164   else if ( isFullDMode() )
165   {
166     int h = theH-1;
167     
168     int aCorner = 3*TITLE_HEIGHT/2;
169     
170     aPnts = QPointArray(8);
171     QPoint p((int)x(), (int)y());
172     p = p + QPoint(-NODEBOUNDARY_MARGIN,0);
173     aPnts[0] = p;
174     aPnts[1] = p + QPoint(aCorner, -aCorner) + QPoint(NODEBOUNDARY_MARGIN,-NODEBOUNDARY_MARGIN);
175     aPnts[2] = aPnts[1] + QPoint(theW, 0) + QPoint(NODEBOUNDARY_MARGIN,0);
176     aPnts[3] = p + QPoint(theW, 0) + QPoint(NODEBOUNDARY_MARGIN,0);
177     aPnts[4] = aPnts[3] + QPoint(0, h) + QPoint(0,-NODEBOUNDARY_MARGIN/2);
178     aPnts[5] = aPnts[4] + QPoint(-aCorner, aCorner) + QPoint(-NODEBOUNDARY_MARGIN,NODEBOUNDARY_MARGIN);
179     aPnts[6] = aPnts[5] + QPoint(-theW, 0) + QPoint(-NODEBOUNDARY_MARGIN,0);
180     aPnts[7] = p + QPoint(0, h) + QPoint(0,-NODEBOUNDARY_MARGIN/2);
181   }
182
183   return aPnts;
184 }
185
186 void YACSPrs_LoopNode::setBracketColor(const QColor& theColor, bool theUpdate)
187 {
188   myBracketColor = theColor;
189   if ( canvas() && theUpdate )
190   { 
191     canvas()->setChanged(boundingRect());
192     canvas()->update();
193   }
194 }
195
196 void YACSPrs_LoopNode::updatePorts(bool theForce)
197 {
198   bool aDisp = isVisible();
199   if (aDisp) hide();
200
201   bool withCreate = theForce;
202
203   if (theForce)
204   {
205     if ( isFullDMode() )
206       myPortHeight = 2*PORT_MARGIN;
207     else
208       myPortHeight = 0;
209
210     myPortList.setAutoDelete(true);
211     //myPortList.clear();
212
213     QPtrList<YACSPrs_LabelPort> aDeletePortList;
214     for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
215     {
216       if( YACSPrs_LabelPort* aLabelPort = dynamic_cast<YACSPrs_LabelPort*>( aPort ) )
217       {
218         aDeletePortList.append( aLabelPort );
219         withCreate = true;
220       }
221     }
222
223     for (YACSPrs_Port* aPort = aDeletePortList.first(); aPort; aPort = aDeletePortList.next())
224       myPortList.remove( aPort );
225   }
226
227   // ForLoop and WhileLoop nodes have only 1 input port.
228   // ForLoop : its name is 'nsteps' ,
229   //           its type is 'int',
230   //           its value is a number of iterations.
231   // WhileLoop : its name is 'condition' ,
232   //             its type is 'bool',
233   //             its value is a while condition, iteration process breaks when condition switch to false.
234   // ForLoop and WhileLoop nodes have no output ports, but in presentation we want to display a 'label' port with
235   // name 'Body'. This 'label' port connects with help of 'case' link to 'Master' hook
236   // of the node, which is set as an internal node of the loop.
237
238   if ( myPortList.isEmpty() ) withCreate = true;
239
240   if ( isFullDMode() )
241   {
242     QRect r = getBodyRect();
243     int aPRectWidth = (int)(r.width()/2) - 2*PORT_MARGIN;
244     if ( aPRectWidth < PORT_WIDTH ) aPRectWidth = PORT_WIDTH;
245     
246     int ix = r.x() + PORT_MARGIN + 1;
247     int iy = r.y() + PORT_MARGIN;// + 1;
248     int ox = ix + aPRectWidth + 2*PORT_MARGIN;
249     int oy = r.y() + PORT_MARGIN;// + 1;
250     
251     ForLoop* aFLoop = dynamic_cast<ForLoop*>( getEngine() );
252     WhileLoop* aWLoop = dynamic_cast<WhileLoop*>( getEngine() );
253     
254     if ( withCreate )
255     { // create (and update)
256       // 'nsteps'/'condition' input port (name, type (and value) of the port will set in YACSPrs_InOutPort from port engine)
257       bool isConditionPortCreated = false;
258       InputPort* aConditionPort = 0;
259       if( aFLoop )
260         aConditionPort = aFLoop->edGetNbOfTimesInputPort();
261       else if( aWLoop )
262         aConditionPort = aWLoop->edGetConditionPort();
263
264       for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
265       {
266         if( !aPort->getName().compare( QString( aConditionPort->getName() ) ) )
267         {
268           isConditionPortCreated = true;
269           break;
270         }
271       }
272       if( !isConditionPortCreated )
273       {
274         YACSPrs_InOutPort* anInPort = 0;
275         if ( aFLoop )
276           anInPort = new YACSPrs_InOutPort(myMgr,canvas(),aFLoop->edGetNbOfTimesInputPort(),this);
277         else if ( aWLoop )
278           anInPort = new YACSPrs_InOutPort(myMgr,canvas(),aWLoop->edGetConditionPort(),this);
279       
280         if ( anInPort )
281         {
282           anInPort->setPortRect(QRect(ix, iy, aPRectWidth, PORT_HEIGHT));
283           anInPort->setColor(nodeSubColor());
284           anInPort->setStoreColor(nodeSubColor());
285           myPortList.append(anInPort);
286         }
287       }
288       
289       if ( aFLoop || aWLoop )
290       {
291         // get a set of internal loop nodes (in fact in ForLoop and WhileLoop we have only one internal node)
292         std::list<Node*> aNodes = aFLoop ? aFLoop->edGetDirectDescendants() : aWLoop->edGetDirectDescendants();
293         if ( aNodes.empty() )
294           myPortHeight += PORT_HEIGHT;
295         else
296         {
297           std::list<Node*>::iterator aNodesIter = aNodes.begin();
298           for (; aNodesIter != aNodes.end(); aNodesIter++)
299           { // output label port
300             YACSPrs_LabelPort* anOutPort = new YACSPrs_LabelPort(myMgr,canvas(),*aNodesIter,this);
301             anOutPort->setPortRect(QRect(ox, oy, aPRectWidth, PORT_HEIGHT));
302             anOutPort->setColor(nodeSubColor().dark(140));
303             anOutPort->setStoreColor(nodeSubColor().dark(140));
304             myPortList.append(anOutPort);
305             myPortHeight += PORT_HEIGHT;
306           }
307         }
308       }
309     }
310     else
311     { // only update
312       YACSPrs_Port* aPort;
313       for (aPort = myPortList.first(); aPort; aPort = myPortList.next())
314       { 
315         YACSPrs_InOutPort* anInPort = dynamic_cast<YACSPrs_InOutPort*>( aPort );
316         if ( anInPort )
317         { 
318           if ( !anInPort->isGate() && anInPort->isInput() )
319           { // input data (i.e. not Gate) ports
320             anInPort->setPortRect(QRect(ix, iy, aPRectWidth, PORT_HEIGHT), !isSelfMoving(), myArea);
321             iy += PORT_HEIGHT+PORT_SPACE;
322           }
323         }
324         else
325         { // not YACSPrs_InOutPort => it is YACSPrs_LabelPort => we not need to dynamic cast
326           aPort->setPortRect(QRect(ox, oy, aPRectWidth, PORT_HEIGHT), !isSelfMoving(), myArea);
327           oy += PORT_HEIGHT+PORT_SPACE;
328         }
329       }
330     }
331   }
332     
333   // can update gates only after body height will be defined
334   bool createGates = withCreate;
335   for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
336   {
337     if( YACSPrs_InOutPort* anIOPort = dynamic_cast<YACSPrs_InOutPort*>( aPort ) )
338     {
339       if ( anIOPort->isGate() ) 
340       { // gate ports are already created - we should only update them
341         createGates = false;
342         break;
343       }
344     }
345   }
346   updateGates(createGates);
347
348   if (theForce && myPointMaster)
349   {
350     QPoint aPnt = getConnectionMasterPoint();
351     myPointMaster->setCoords(aPnt.x(), aPnt.y());
352   }
353
354   if (aDisp) show();
355 }
356
357 int YACSPrs_LoopNode::maxWidth() const
358 {
359   if ( isControlDMode() )
360     return YACSPrs_ElementaryNode::maxWidth();
361
362   // Full view mode as a default
363   return YACSPrs_ElementaryNode::maxWidth() - 4*HOOKPOINT_SIZE + ( ( 4*HOOKPOINT_SIZE > 3*TITLE_HEIGHT ) ? 4*HOOKPOINT_SIZE-3*TITLE_HEIGHT : 0 );
364 }
365
366 int YACSPrs_LoopNode::minX() const
367 {
368   return YACSPrs_ElementaryNode::minX() + 2*HOOKPOINT_SIZE - ( ( 2*HOOKPOINT_SIZE > 3*TITLE_HEIGHT/2 ) ? 
369                                                                2*HOOKPOINT_SIZE :
370                                                                3*TITLE_HEIGHT/2 );
371 }
372
373 int YACSPrs_LoopNode::maxX() const
374 {
375   return YACSPrs_ElementaryNode::maxX() - 2*HOOKPOINT_SIZE + ( ( 2*HOOKPOINT_SIZE > 3*TITLE_HEIGHT/2 ) ? 
376                                                                2*HOOKPOINT_SIZE :
377                                                                3*TITLE_HEIGHT/2 );
378 }
379
380 void YACSPrs_LoopNode::drawPort(QPainter& thePainter)
381 {
382   QRect r = getBodyRect();
383   r.setHeight(r.height()+1);
384
385   thePainter.drawRect(r);
386   int x0 = (r.left() + r.right())/2;
387   thePainter.drawLine(x0, r.top(), x0, r.bottom());
388
389   int aRRectWidth = (x0 - r.left() - 2*PORT_MARGIN - 2*PORT_SPACE)/3;
390   int aRRectWidthLabel = x0 - r.left() - 2*PORT_MARGIN;
391   QRect aTRect = getTitleRect();
392   int aXRnd = aTRect.width()*myXRnd/aRRectWidth;
393   int aXRndLabel = aTRect.width()*myXRnd/aRRectWidthLabel;
394   int aYRnd = aTRect.height()*myYRnd/PORT_HEIGHT;
395
396   YACSPrs_Port* aPort;
397   for (aPort = myPortList.first(); aPort; aPort = myPortList.next())
398   {
399     YACSPrs_InOutPort* anInPort = dynamic_cast<YACSPrs_InOutPort*>( aPort );
400     YACSPrs_LabelPort* anOutPort = dynamic_cast<YACSPrs_LabelPort*>( aPort );
401     if ( anInPort && !anInPort->isGate() )
402       aPort->draw(thePainter, aXRnd, aYRnd);
403     if ( anOutPort )
404       aPort->draw(thePainter, aXRndLabel, aYRnd);
405   } 
406 }
407
408 void YACSPrs_LoopNode::drawFrame(QPainter& thePainter)
409 {
410   QRect aRect = getRect();
411   QRect aTRect = getTitleRect();
412
413   thePainter.drawRect(aRect);
414
415   if ( isFullDMode() )
416   {
417     QBrush savedB = thePainter.brush();
418     thePainter.setBrush(bracketColor());
419     
420     int aCorner =  3*TITLE_HEIGHT/2;
421     QPoint aP1(aRect.x(),            aRect.y());
422     QPoint aP2(aRect.x()+aCorner,    aRect.y()-aCorner);
423     QPoint aP3(aRect.right()+aCorner,aRect.y()-aCorner);
424     QPoint aP4(aRect.right(),        aRect.y());
425     QPoint aP5(aRect.x(),            aRect.bottom());
426     QPoint aP6(aRect.x()-aCorner,    aRect.bottom()+aCorner);
427     QPoint aP7(aRect.right()-aCorner,aRect.bottom()+aCorner);
428     QPoint aP8(aRect.right(),        aRect.bottom());
429     QPointArray aPAUp(4);
430     aPAUp.putPoints(0, 4, aP1.x(),aP1.y(), aP2.x(),aP2.y(), aP3.x(),aP3.y(), aP4.x(),aP4.y());
431     thePainter.drawPolygon( aPAUp );
432     QPointArray aPADown(4);
433     aPADown.putPoints(0, 4, aP5.x(),aP5.y(), aP6.x(),aP6.y(), aP7.x(),aP7.y(), aP8.x(),aP8.y());
434     thePainter.drawPolygon( aPADown );
435     
436     thePainter.setBrush(savedB);
437     
438     // draw top pixmap
439     QRect aTPRect(aRect.x()+aRect.width()/2-aCorner/3, aRect.y()-aCorner/2-aCorner/3,
440                   2*aCorner/3, 2*aCorner/3);
441     thePainter.drawPixmap(aTPRect,myTopPixmap);
442     
443     // draw bottom pixmap
444     QRect aBPRect(aRect.x()+aRect.width()/2-aCorner/3, aRect.bottom()+aCorner/2-aCorner/3,
445                   2*aCorner/3, 2*aCorner/3);
446     thePainter.drawPixmap(aBPRect,myBottomPixmap);
447   }
448
449   // draw bounding nodes' polygon if node is currently selected
450   if ( isSelected() ) drawBoundary(thePainter,5);
451 }
452
453 QPoint YACSPrs_LoopNode::getConnectionMasterPoint()
454 {
455   QRect aRect = getRect();
456   return QPoint(aRect.left()+aRect.width()/2, aRect.bottom()+3*TITLE_HEIGHT/2);
457 }
458
459 bool YACSPrs_LoopNode::checkArea(double dx, double dy)
460 {
461   // for constraint nodes' moving inside the Bloc-->
462   if ( !myIsInBloc ) return true;
463
464   QRect aRect = boundingRect();
465   aRect.moveBy((int)dx, (int)dy);
466   aRect.setRect(aRect.x() - ( ( 2*HOOKPOINT_SIZE > 3*TITLE_HEIGHT/2 ) ? 2*HOOKPOINT_SIZE-3*TITLE_HEIGHT/2 : 0 ) + NODEBOUNDARY_MARGIN,
467                 aRect.y(), maxWidth(), maxHeight());
468   if ( myArea.isValid() && myArea.contains(aRect) )
469     return true;
470   return false;
471   // <--
472 }
473
474 bool YACSPrs_LoopNode::synchronize( YACSPrs_Port* port, const bool toSelect )
475 {
476   return YACSPrs_InOutPort::synchronize( port, toSelect );
477 }
478