Salome HOME
merge from branch DEV tag mergeto_trunk_04apr08
[modules/yacs.git] / src / prs / YACSPrs_SwitchNode.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_SwitchNode.h"
21 #include "YACSPrs_Def.h"
22
23 #include "QxGraph_Canvas.h"
24 #include "QxGraph_Prs.h"
25
26 #include "SUIT_ResourceMgr.h"
27
28 #include <qpainter.h>
29
30 #include <Switch.hxx>
31
32 using namespace YACS::ENGINE;
33
34 /*!
35   Constructor
36 */
37 YACSPrs_SwitchNode::YACSPrs_SwitchNode( SUIT_ResourceMgr* theMgr, QCanvas* theCanvas, YACS::HMI::SubjectNode* theSNode ):
38   YACSPrs_InlineNode(theMgr, theCanvas, theSNode, false)
39 {
40   setNodeColor(SWITCHNODE_COLOR);
41   setNodeSubColor(SWITCHNODE_SUBCOLOR);
42
43   setStoreColor(nodeColor());
44   setStoreSubColor(nodeSubColor());
45
46   myTitlePixmap = myMgr->loadPixmap( "YACSPrs", QObject::tr( "ICON_TITLE_RARROW" ));
47   
48   //updatePorts(); // will be called in moveBy(...) function
49   moveBy(2*HOOKPOINT_SIZE+NODEBOUNDARY_MARGIN,2*HOOKPOINT_SIZE+2*TITLE_HEIGHT+NODEBOUNDARY_MARGIN);
50 }
51
52 /*!
53   Destructor
54 */
55 YACSPrs_SwitchNode::~YACSPrs_SwitchNode()
56 {
57 }
58
59 int YACSPrs_SwitchNode::rtti() const
60 {
61   return 0;//YACSPrs_Canvas::Rtti_SwitchNode;
62 }
63
64 QPointArray YACSPrs_SwitchNode::constructAreaPoints(int theW, int theH) const
65 {
66   QPointArray aPnts;
67
68   if ( isControlDMode() )
69   {
70     aPnts = QPointArray(4);
71     aPnts[0] = QPoint((int)x(), (int)y()) + QPoint(-NODEBOUNDARY_MARGIN,-NODEBOUNDARY_MARGIN);
72     aPnts[1] = aPnts[0] + QPoint(theW, 0) + QPoint(NODEBOUNDARY_MARGIN,0);
73     aPnts[2] = aPnts[1] + QPoint(0, theH) + QPoint(0,NODEBOUNDARY_MARGIN);
74     aPnts[3] = aPnts[0] + QPoint(0, theH) + QPoint(0,NODEBOUNDARY_MARGIN);
75   }
76   else if ( isFullDMode() )
77   {
78     int aCorner = 2*TITLE_HEIGHT;
79     
80     aPnts = QPointArray(5);
81     QPoint p((int)x(), (int)y());
82     aPnts[0] = p + QPoint(-NODEBOUNDARY_MARGIN,0);
83     aPnts[1] = p + QPoint(theW/2, -aCorner) + QPoint(0,-NODEBOUNDARY_MARGIN);
84     aPnts[2] = p + QPoint(theW, 0) + QPoint(NODEBOUNDARY_MARGIN,0);
85     aPnts[3] = aPnts[2] + QPoint(0, theH) + QPoint(0,NODEBOUNDARY_MARGIN/2);
86     aPnts[4] = p + QPoint(0, theH) + QPoint(-NODEBOUNDARY_MARGIN,NODEBOUNDARY_MARGIN/2);
87   }
88
89   return aPnts;
90 }
91
92 void YACSPrs_SwitchNode::updatePorts(bool theForce)
93 {
94   bool aDisp = isVisible();
95   if (aDisp) hide();
96
97   bool withCreate = theForce;
98
99   if (theForce)
100   {
101     if ( isFullDMode() )
102       myPortHeight = 2*PORT_MARGIN;
103     else
104       myPortHeight = 0;
105
106     myPortList.setAutoDelete(true);
107     //myPortList.clear();
108
109     QPtrList<YACSPrs_LabelPort> aDeletePortList;
110     for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
111     {
112       if( YACSPrs_LabelPort* aLabelPort = dynamic_cast<YACSPrs_LabelPort*>( aPort ) )
113       {
114         aDeletePortList.append( aLabelPort );
115         withCreate = true;
116       }
117     }
118
119     for (YACSPrs_Port* aPort = aDeletePortList.first(); aPort; aPort = aDeletePortList.next())
120       myPortList.remove( aPort );
121   }
122
123   // Switch node has only 1 input port: its name is 'select',
124   //                                    its type is 'int',
125   //                                    its value is a number of active case (i.e. the case to execute)
126   // Switch node has no output ports, but in presentation we want to display a switch cases with
127   // its IDs (the number of cases and its IDs are set by user at creation process of switch node )
128   // as a 'label' ports. Each 'label' port connects with help of 'case' link to 'Master' hook
129   // of the node, which is set to this case ID.
130
131   if ( myPortList.isEmpty() ) withCreate = true;
132
133   if ( isFullDMode() )
134   {
135     QRect r = getBodyRect();
136     int aPRectWidth = (int)(r.width()/2) - 2*PORT_MARGIN;
137     if ( aPRectWidth < PORT_WIDTH ) aPRectWidth = PORT_WIDTH;
138     
139     int ix = r.x() + PORT_MARGIN + 1;
140     int iy = r.y() + PORT_MARGIN;// + 1;
141     int ox = ix + aPRectWidth + 2*PORT_MARGIN;
142     int oy = r.y() + PORT_MARGIN;// + 1;
143     
144     if ( withCreate )
145     { // create (and update)
146       // 'select' input port (name, type (and value) of the port will set in YACSPrs_InOutPort from port engine)
147       Switch* aSEngine = dynamic_cast<Switch*>( getEngine() );
148       if ( aSEngine )
149       {
150         bool isConditionPortCreated = false;
151         InputPort* aConditionPort = aSEngine->edGetConditionPort();
152         for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
153         {
154           if( !aPort->getName().compare( QString( aConditionPort->getName() ) ) )
155           {
156             isConditionPortCreated = true;
157             break;
158           }
159         }
160         if( !isConditionPortCreated )
161         {
162           YACSPrs_InOutPort* anInPort = new YACSPrs_InOutPort(myMgr,canvas(),aConditionPort,this);
163           anInPort->setPortRect(QRect(ix, iy, aPRectWidth, PORT_HEIGHT));
164           anInPort->setColor(nodeSubColor());
165           anInPort->setStoreColor(nodeSubColor());
166           myPortList.append(anInPort);
167         }
168         
169         // get a set of internal case nodes
170         std::list<Node*> aNodes = aSEngine->edGetDirectDescendants();
171         if ( aNodes.empty() )
172           myPortHeight += PORT_HEIGHT;
173         else
174         {
175           std::list<Node*>::iterator aNodesIter = aNodes.begin();
176           
177           // get default node
178           Node* aDefaultNode = aSEngine->getChildByShortName(Switch::DEFAULT_NODE_NAME);
179           
180           int aMinCaseId, aMaxCaseId;
181           aMinCaseId = aMaxCaseId = aSEngine->getRankOfNode(*aNodesIter);
182           // a list of case nodes ordered from minimum to maximum case id
183           std::list<Node*> aCaseNodes;
184           for (; aNodesIter != aNodes.end(); aNodesIter++)
185           {
186             if ( *aNodesIter == aDefaultNode) continue;
187             
188             // less than min => push front
189             if ( aMinCaseId >= aSEngine->getRankOfNode(*aNodesIter) ) {
190               aCaseNodes.push_front(*aNodesIter);
191               aMinCaseId = aSEngine->getRankOfNode(*aNodesIter);
192             }
193             // in the middle
194             else if ( aMinCaseId < aSEngine->getRankOfNode(*aNodesIter)
195                       &&
196                       aMaxCaseId > aSEngine->getRankOfNode(*aNodesIter) ) {
197               std::list<Node*>::iterator aCaseNodesIter = aCaseNodes.begin();
198               for (std::list<Node*>::iterator anIt = aCaseNodesIter;
199                    anIt++ != aCaseNodes.end();
200                    aCaseNodesIter++, anIt = aCaseNodesIter) {
201                 if ( aSEngine->getRankOfNode(*aNodesIter) >= aSEngine->getRankOfNode(*aCaseNodesIter)
202                      &&
203                      aSEngine->getRankOfNode(*aNodesIter) <= aSEngine->getRankOfNode(*anIt) ) {
204                   aCaseNodes.insert(anIt,*aNodesIter);
205                   break;
206                 }
207               }
208             }
209             // more than max => push back
210             else if ( aMaxCaseId <= aSEngine->getRankOfNode(*aNodesIter) ) {
211               aCaseNodes.push_back(*aNodesIter);
212               aMaxCaseId = aSEngine->getRankOfNode(*aNodesIter);
213             }
214           }
215           if ( aDefaultNode )
216             aCaseNodes.push_back(aDefaultNode);
217           
218           int heightIncr = 0;
219           std::list<Node*>::iterator aCaseNodesIter = aCaseNodes.begin();
220           for (; aCaseNodesIter != aCaseNodes.end(); aCaseNodesIter++)
221           { // (in fact we have to get from user number of switch cases)
222             // output label ports       
223             YACSPrs_LabelPort* anOutPort = new YACSPrs_LabelPort(myMgr,canvas(),*aCaseNodesIter,this,
224                                                                  true,aSEngine->getRankOfNode(*aCaseNodesIter));
225             anOutPort->setPortRect(QRect(ox, oy+heightIncr, aPRectWidth, PORT_HEIGHT));
226             anOutPort->setColor(nodeSubColor().dark(140));
227             anOutPort->setStoreColor(nodeSubColor().dark(140));
228             if ( *aCaseNodesIter == aDefaultNode) anOutPort->setName(Switch::DEFAULT_NODE_NAME);
229             myPortList.append(anOutPort);
230             heightIncr += PORT_HEIGHT+PORT_SPACE;
231           }
232           
233           myPortHeight += aNodes.size()*PORT_HEIGHT + (aNodes.size()-1)*PORT_SPACE;
234         }
235       }
236     }
237     else
238     { // only update
239       YACSPrs_Port* aPort;
240       for (aPort = myPortList.first(); aPort; aPort = myPortList.next())
241       {
242         YACSPrs_InOutPort* anIOPort = dynamic_cast<YACSPrs_InOutPort*>( aPort );
243         if ( anIOPort )
244         {
245           if ( !anIOPort->isGate() && anIOPort->isInput() )
246           { // input data (i.e. not Gate) ports
247             anIOPort->setPortRect(QRect(ix, iy, aPRectWidth, PORT_HEIGHT), !isSelfMoving(), myArea);
248             iy += PORT_HEIGHT+PORT_SPACE;
249           }
250         }
251         else
252         { // not YACSPrs_InOutPort => it is YACSPrs_LabelPort (output!) => we not need to dynamic cast
253           aPort->setPortRect(QRect(ox, oy, aPRectWidth, PORT_HEIGHT), !isSelfMoving(), myArea);
254           oy += PORT_HEIGHT+PORT_SPACE;
255         }
256       }
257     }
258   }
259
260   // can update gates only after body height will be defined
261   bool createGates = withCreate;
262   for (YACSPrs_Port* aPort = myPortList.first(); aPort; aPort = myPortList.next())
263   {
264     if( YACSPrs_InOutPort* anIOPort = dynamic_cast<YACSPrs_InOutPort*>( aPort ) )
265     {
266       if ( anIOPort->isGate() ) 
267       { // gate ports are already created - we should only update them
268         createGates = false;
269         break;
270       }
271     }
272   }
273   updateGates(createGates);
274
275   if (theForce && myPointMaster)
276   {
277     QPoint aPnt = getConnectionMasterPoint();
278     myPointMaster->setCoords(aPnt.x(), aPnt.y());
279   }
280
281   if (aDisp) show();
282 }
283
284 void YACSPrs_SwitchNode::drawPort(QPainter& thePainter)
285 {
286   QRect r = getBodyRect();
287   r.setHeight(r.height()+1);
288
289   thePainter.drawRect(r);
290   int x0 = (r.left() + r.right())/2;
291   thePainter.drawLine(x0, r.top(), x0, r.bottom());
292
293   int aRRectWidth = (x0 - r.left() - 2*PORT_MARGIN - 2*PORT_SPACE)/3;
294   int aRRectWidthLabel = x0 - r.left() - 2*PORT_MARGIN;
295   QRect aTRect = getTitleRect();
296   int aXRnd = aTRect.width()*myXRnd/aRRectWidth;
297   int aXRndLabel = aTRect.width()*myXRnd/aRRectWidthLabel;
298   int aYRnd = aTRect.height()*myYRnd/PORT_HEIGHT;
299
300   YACSPrs_Port* aPort;
301   for (aPort = myPortList.first(); aPort; aPort = myPortList.next())
302   {
303     YACSPrs_InOutPort* anInPort = dynamic_cast<YACSPrs_InOutPort*>( aPort );
304     YACSPrs_LabelPort* anOutPort = dynamic_cast<YACSPrs_LabelPort*>( aPort );
305     if ( anInPort && !anInPort->isGate() )
306       aPort->draw(thePainter, aXRnd, aYRnd);
307     if ( anOutPort )
308       aPort->draw(thePainter, aXRndLabel, aYRnd);
309   } 
310 }
311
312 void YACSPrs_SwitchNode::drawFrame(QPainter& thePainter)
313 {
314   QRect aRect = getRect();
315   QRect aTRect = getTitleRect();
316   int aXRnd = aTRect.width()*myXRnd/aRect.width();
317   int aYRnd = aTRect.height()*myYRnd/aRect.height();
318
319   if ( isControlDMode() )
320     thePainter.drawRoundRect(aRect,aXRnd,aYRnd);
321   else if ( isFullDMode() )
322   {
323     QPen savedP = thePainter.pen();
324     thePainter.setPen(NoPen);
325     
326     // calculate width and height for acrs
327     int w = 4*(aXRnd*aRect.width()/100)/3;
328     int h = 4*(aYRnd*aRect.height()/100)/3;
329     
330     // draw chords without pen
331     thePainter.drawChord( aRect.x(),aRect.y(), w,h, 90*16, 90*16 );
332     thePainter.drawChord( aRect.right()-w+1,aRect.y(), w,h, 0*16, 90*16 );
333     thePainter.drawChord( aRect.right()-w+1,aRect.bottom()-h+1, w,h, 270*16, 90*16 );
334     thePainter.drawChord( aRect.x(),aRect.bottom()-h+1, w,h, 180*16, 90*16 );
335     
336     //thePainter.drawRoundRect(aRect,aXRnd,aYRnd);
337     int aCorner =  2*TITLE_HEIGHT;
338     QPoint aP1(aRect.x()+(w-1)/2,aRect.y());
339     QPoint aP2(aRect.x()+aRect.width()/2,aRect.y()-aCorner);
340     QPoint aP3(aRect.right()-(w-1)/2,aRect.y());
341     QPoint aP4(aRect.right(),aRect.y()+h/2-1);
342     QPoint aP5(aRect.right(),aRect.bottom()-h/2+1);
343     QPoint aP6(aRect.right()-(w-1)/2,aRect.bottom());
344     QPoint aP7(aRect.x()+(w-1)/2,aRect.bottom());
345     QPoint aP8(aRect.x(),aRect.bottom()-h/2+1);
346     QPoint aP9(aRect.x(),aRect.y()+h/2-1);
347     QPointArray aPA(9);
348     aPA.putPoints(0, 9, 
349                   aP1.x(),aP1.y(), aP2.x(),aP2.y(), aP3.x(),aP3.y(), aP4.x(),aP4.y(),
350                   aP5.x(),aP5.y(), aP6.x(),aP6.y(), aP7.x(),aP7.y(), aP8.x(),aP8.y(), aP9.x(),aP9.y());
351     thePainter.drawPolygon( aPA );
352     thePainter.setPen(savedP);
353     
354     // draw arcs
355     thePainter.drawArc( aRect.x(),aRect.y(), w,h, 90*16, 90*16 );
356     thePainter.drawArc( aRect.right()-w+1,aRect.y(), w,h, 0*16, 90*16 );
357     thePainter.drawArc( aRect.right()-w+1,aRect.bottom()-h+1, w,h, 270*16, 90*16 );
358     thePainter.drawArc( aRect.x(),aRect.bottom()-h+1, w,h, 180*16, 90*16 );
359     
360     // draw line segments
361     thePainter.drawLine(aP1,aP2);
362     thePainter.drawLine(aP2,aP3);
363     thePainter.drawLine(aP4,aP5);
364     thePainter.drawLine(aP6,aP7);
365     thePainter.drawLine(aP8,aP9);
366     
367     // draw title pixmap
368     QRect aTPRect(aRect.x()+aRect.width()/2-aCorner/3, aRect.y()-(aCorner-NODE_MARGIN)/2-aCorner/3,
369                   2*aCorner/3, 2*aCorner/3);
370     thePainter.drawPixmap(aTPRect,myTitlePixmap);
371   }
372
373   // draw bounding nodes' polygon if node is currently selected
374   if ( isSelected() ) drawBoundary(thePainter,(isFullDMode() ? 3 : 2));
375 }