graphicslinkitem.cpp
1 /*
2  name: tools/editor/graphicslinkitem.cpp
3 
4  This file is part of ARCS - Augmented Reality Component System
5  (version 2-current), written by Jean-Yves Didier
6  for IBISC Laboratory (http://www.ibisc.univ-evry.fr)
7 
8  Copyright (C) 2013 Université d'Evry-Val d'Essonne
9 
10  This program is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 2 of the License, or
13  (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program. If not, see <http://www.gnu.org/licenses/>.
22 
23 
24  Please send bugreports with examples or suggestions to
25  jean-yves.didier__at__ibisc.univ-evry.fr
26 */
27 
28 
29 #include "graphicscomponentitem.h"
30 #include "graphicslinkitem.h"
31 #include "graphicsportitem.h"
32 #include "sheetview.h"
33 
34 
35 #include <iostream>
36 #include <arcs/arcsabstractcomponent.h>
37 #include <QBrush>
38 #include <QKeyEvent>
39 #include <QGraphicsScene>
40 #include <QGraphicsSceneMoveEvent>
41 #include <QStyle>
42 #include <QStyleOptionGraphicsItem>
43 
44 #ifndef MAX
45 #define MAX(A,B) (((A)>(B))?(A):(B))
46 #endif
47 
48 #ifndef MIN
49 #define MIN(A,B) (((A)<(B))?(A):(B))
50 #endif
51 
52 
53 GraphicsLinkItem::GraphicsLinkItem(QGraphicsItem *parent, ARCSConnection* c) : QGraphicsPathItem(parent),connection(c)
54 {
55  int i;
56  setAcceptHoverEvents(true);
57  setFlag(ItemIsSelectable,true);
58  markedForDeletion = false;
59 
60  source = 0;
61  destination = 0;
62  finalized = connection;
63  defaultPen = pen();
64 
65  for(i=0; i < 4; i++)
66  {
67  grips[i] = new GraphicsGripLinkItem(this);
68  grips[i]->hide();
69  if (i>0)
70  {
71  grips[i]->setPrevious(grips[i-1]);
72  grips[i-1]->setNext(grips[i]);
73  }
74  }
75 
76 
77  if (finalized)
78  {
79  QList<QPointF>& coords = connection->getCoordinates();
80  if (coords.count() >= 4)
81  {
82  for(i=0;i<4; i++)
83  grips[i]->setPos(coords[i]);
84  }
85  }
86 
87 }
88 
90 {
91  setSelected(false);
92 
93  // here we should access to each portItem to remove myself from their list
94  // nothing to do: just remove so that the parentItem does not access to this ?
95  if (destination)
96  destination->removeLinkItem(this);
97  if (source)
98  source->removeLinkItem(this);
99 
100 
101  // then we should remove my connection counterpart
103 
104  if ( markedForDeletion )
105  {
106  SheetView* view = dynamic_cast<SheetView*>(scene()->views()[0]);
107  if (!view)
108  return ;
109 
110  if (! connection )
111  return ;
112 
113  view->getSheet().removeConnection(*connection);
114  }
115 }
116 
117 
118 void GraphicsLinkItem::redraw(bool isSource)
119 {
120  // this is called when source or destination have moved.
121  // maybe we should differenciate according to which target has moved.
122 
123  if ( ! (source && destination) )
124  return ;
125 
126  /*QPointF orig = mapFromScene(source->scenePos());
127  QPointF dest = mapFromScene(destination->scenePos());
128  Configuration config = computeConfiguration(orig,dest);
129 
130  if (config != configuration)
131  {
132  computePath(orig,dest);
133  return ;
134  }
135 
136  if (isSource || destination == source)
137  {
138  grips[0]->setPos(grips[0]->x(), orig.y()) ;//orig + QPoint(20,0));
139 
140  // then grip 0 and grip 1 should move
141  // move on y direction anyway for grip1
142 
143  grips[1]->setPos(grips[1]->x(),grips[0]->y());
144 
145  if (configuration != Step)
146  grips[1]->setPos(grips[0]->x(),grips[1]->y());
147  }
148  if (!isSource || destination == source)
149  {
150  grips[3]->setPos(grips[3]->x(), dest.y()) ; //dest + QPoint(-20,0));
151  // grip 2 and grip 3 should move
152  // same reasoning as the previous one.
153 
154  grips[2]->setPos(grips[2]->x(), grips[2]->y());
155 
156  if (configuration != Step)
157  grips[2]->setPos(grips[3]->x(), grips[2]->y());
158  // idem
159  }
160 
161  connectGrips();*/
162  // this is a complete recomputation
163  if (isSource || connection->getCoordinates().count() < 4)
164  computePath(mapFromScene(source->scenePos()), mapFromScene(destination->scenePos()));
165  else
166  connectGrips();
167 }
168 
169 
170 void GraphicsLinkItem::setCurrentPoint(QPointF p)
171 {
172  if ( ! (source || destination) ) return ;
173 
174 
175  if (source)
176  computePath(QPointF(0,0),p);
177  else
178  computePath(p,QPoint(0,0));
179 }
180 
181 
182 GraphicsLinkItem::Configuration GraphicsLinkItem::computeConfiguration(QPointF orig, QPointF dest)
183 {
184  if (dest.x() - orig.x() > 40)
185  return Step;
186 
187  if (destination && source)
188  {
189  QRectF dstRect = mapFromScene(destination->parentItem()->sceneBoundingRect()).boundingRect();
190  QRectF srcRect = mapFromScene(source->parentItem()->sceneBoundingRect()).boundingRect();
191 
192  if ( (srcRect.y() < dstRect.y()) && (dstRect.y() < srcRect.y() + srcRect.height()) )
193  return Loop;
194  if ( (dstRect.y() < srcRect.y()) && (srcRect.y() < dstRect.y() + dstRect.height()) )
195  return Loop;
196  }
197  else
198  {
199  QRectF parentRect = mapFromScene(parentItem()->parentItem()->sceneBoundingRect()).boundingRect();
200 
201  if ( (parentRect.y() < orig.y()) && (orig.y() < parentRect.y() + parentRect.height()) )
202  return Loop;
203  }
204  return S_Shape;
205 }
206 
207 
208 void GraphicsLinkItem::computePath(QPointF orig, QPointF dest)
209 {
210  Configuration config = computeConfiguration(orig,dest);
211 
212  grips[0]->setPos(orig + QPoint(20,0));
213  grips[3]->setPos(dest + QPointF(-20,0));
214 
215  switch(config)
216  {
217  case Step:
218  grips[1]->setPos((grips[0]->x()+grips[3]->x())/2, grips[0]->y());
219  grips[2]->setPos(grips[1]->x(), grips[3]->y());
220  break;
221  case S_Shape:
222  grips[1]->setPos(grips[0]->x(),(grips[0]->y()+grips[3]->y())/2);
223  grips[2]->setPos(grips[3]->x(),grips[1]->y());
224  break;
225  case Loop:
226  {
227  int loopHeight = 0;
228  if (destination && source)
229  {
230  QRectF dstRect = mapFromScene(destination->parentItem()->sceneBoundingRect()).boundingRect();
231  QRectF srcRect = mapFromScene(source->parentItem()->sceneBoundingRect()).boundingRect();
232  loopHeight = MAX(srcRect.y(),dstRect.y()) + MAX(srcRect.height(),dstRect.height()) - orig.y() + 20 ;
233  }
234  else
235  {
236  QRectF parentRect = mapFromScene(parentItem()->parentItem()->sceneBoundingRect()).boundingRect();
237  loopHeight = parentRect.y() + parentRect.height() - orig.y() + 20 ;
238  }
239  grips[1]->setPos(grips[0]->x(),grips[0]->y()+loopHeight);
240  grips[2]->setPos(grips[3]->x(),grips[1]->y());
241  }
242  }
243  configuration = config;
244  connectGrips(orig,dest);
245 }
246 
247 
248 void GraphicsLinkItem::hoverEnterEvent(QGraphicsSceneHoverEvent */*event*/)
249 {
250  source->showName();
251  destination->showName();
252 
253  for (int i=0; i < 4; i++)
254  grips[i]->show();
255 }
256 
257 void GraphicsLinkItem::hoverLeaveEvent(QGraphicsSceneHoverEvent */*event*/)
258 {
259  source->hideName();
260  destination->hideName();
261  for (int i=0; i < 4; i++)
262  grips[i]->hide();
263 }
264 
265 
266 // this is defined in order to override Qt's default behaviour for selected items.
267 void GraphicsLinkItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
268 {
269  QStyleOptionGraphicsItem myOptions(*option);
270  if (myOptions.state & (QStyle::State_Selected))
271  {
272  QPen myPen(Qt::darkRed);
273  myPen.setWidth(2);
274  setPen(myPen);
275  myOptions.state = myOptions.state ^ QStyle::State_Selected ;
276  }
277  else
278  {
279  setPen(defaultPen);
280  }
281  QGraphicsPathItem::paint(painter,& myOptions, widget );
282 
283 }
284 
285 
286 QPainterPath GraphicsLinkItem::shape() const
287 {
288  if ((! source) || (! destination) )
289  return QGraphicsPathItem::shape();
290 
291  QPointF orig = mapFromScene(source->scenePos());
292  QPointF dest = mapFromScene(destination->scenePos());
293 
294  QPainterPath pp;
295 
296  QRectF rectLine(QPointF(MIN(orig.x(),grips[0]->x()),MIN(orig.y(),grips[0]->y())),
297  QPointF(MAX(orig.x(),grips[0]->x()),MAX(orig.y(),grips[0]->y())));
298 
299  pp.addRect(rectLine.adjusted(-2,-2,2,2));
300 
301  for (int i=0; i < 3;i++ )
302  {
303  rectLine = QRectF(QPointF(MIN(grips[i]->x(),grips[i+1]->x()), MIN(grips[i]->y(),grips[i+1]->y())),
304  QPointF(MAX(grips[i]->x(),grips[i+1]->x()), MAX(grips[i]->y(),grips[i+1]->y())));
305  pp.addRect(rectLine.adjusted(-3,-3,3,3));
306  }
307 
308  rectLine = QRectF(QPointF(MIN(dest.x(),grips[3]->x()),MIN(dest.y(),grips[3]->y())),
309  QPointF(MAX(dest.x(),grips[3]->x()),MAX(dest.y(),grips[3]->y())));
310  pp.addRect(rectLine.adjusted(-2,-2,2,2));
311 
312  return pp;
313 }
314 
315 void GraphicsLinkItem::connectGrips(QPointF orig, QPointF dest)
316 {
317  QPainterPath pp ;
318  pp.moveTo(orig);
319  pp.lineTo(grips[0]->pos());
320  pp.lineTo(grips[1]->pos());
321  pp.lineTo(grips[2]->pos());
322  pp.lineTo(grips[3]->pos());
323  pp.lineTo(dest);
324  setPath(pp);
325 
326  // here we will also update grip position
327  if (connection)
328  {
329  QList<QPointF>& coords = connection->getCoordinates();
330  coords.clear();
331  for (int i=0; i< 4; i++)
332  coords.append(grips[i]->pos());
333  //connection->setCoordinates(coords);
334  }
335 }
336 
337 
338 void GraphicsLinkItem::connectGrips()
339 {
340  // retrouver orig et dest ?
341  QPointF orig = mapFromScene(source->scenePos());
342  QPointF dest = mapFromScene(destination->scenePos());
343  connectGrips(orig,dest);
344 }
345 
346 
347 void GraphicsLinkItem::finalize()
348 {
349  if (! (destination && source))
350  return ;
351 
352  SheetView* view = dynamic_cast<SheetView*>(scene()->views()[0]);
353  if (!view)
354  return ;
355 
356  GraphicsComponentItem* sourceItem = dynamic_cast<GraphicsComponentItem*>(source->parentItem());
357  GraphicsComponentItem* destinationItem = dynamic_cast<GraphicsComponentItem*>(destination->parentItem());
358 
359  if (! (sourceItem && destinationItem))
360  return ;
361 
362  ARCSSheet& sheet = view->getSheet();
363  connection = & (sheet.addConnection(sourceItem->getComponent()->getProperty("id").toString(),
364  source->getName(),
365  destinationItem->getComponent()->getProperty("id").toString(),
366  destination->getName()));
367 
368  finalized = true;
369  //setFlag(ItemClipsToShape,true);
370 
371 }
372 
373 
374 
375 /******************************************************************************
376  GraphicsGripLinkItem
377 
378  ****************************************************************************/
379 
380 GraphicsGripLinkItem::GraphicsGripLinkItem(QGraphicsItem *parent):
381  QGraphicsRectItem(-5,-5,10,10, parent)
382 {
383 
384  setFlag(QGraphicsItem::ItemIsMovable,true);
385  setAcceptHoverEvents(true);
386  QBrush br=brush();
387  br.setStyle(Qt::SolidPattern);
388  br.setColor(Qt::magenta);
389 
390  setBrush(br);
391 
392  next = 0;
393  previous = 0;
394 }
395 
396 
397 void GraphicsGripLinkItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
398 {
399  // we must look for possible movements :
400  // if there is no previous or next, then moves are only possible along x
401  // otherwise, moves are possible along x and y !
402  /*QPointF lastPos = event->lastScenePos();
403  QPointF curPos = event->scenePos();*/
404 
405  // here we compute the move and dispatch x and y moves to the right grip.
406  QPointF deltaMove = parentItem()->mapFromScene(event->scenePos()) - pos();
407 
408 
409 
410  if (previous && next)
411  {
412  bool yMove = ! ((pos().y() == next->pos().y() && ! next->next) || (pos().y() == previous->pos().y() && ! previous->previous));
413 
414  if (pos().x() == next->pos().x() )
415  next->setPos(next->pos() + QPointF(deltaMove.x(),0) );
416  else
417  if (yMove)
418  next->setPos(next->pos() + QPointF(0,deltaMove.y()) );
419 
420 
421  if (pos().x() == previous->pos().x() )
422  previous->setPos(previous->pos() + QPointF(deltaMove.x(),0) );
423  else
424  if (yMove)
425  previous->setPos(previous->pos() + QPointF(0,deltaMove.y()) );
426 
427  if (yMove)
428  setPos(parentItem()->mapFromScene(event->scenePos()));
429  else
430  setPos(pos() + QPointF(deltaMove.x(),0));
431  }
432  else
433  {
434  if (previous)
435  if (previous->pos().y() != pos().y())
436  previous->setPos(previous->pos() + QPointF(deltaMove.x(),0) );
437  if (next)
438  if (next->pos().y() != pos().y())
439  next->setPos(next->pos() + QPointF(deltaMove.x(),0) );
440 
441  setPos(pos() + QPointF(deltaMove.x(),0));
442 
443  }
444 
445  dynamic_cast<GraphicsLinkItem*>(parentItem())->connectGrips();
446 
447  //setPos(parentItem()->mapFromScene(event->scenePos()));
448 }
Describes a connection.
QVariant getProperty(QString name)
Gets a meta-property from this component.
This class is a loop sending as much iterations as needed.
Definition: sample.h:44
ARCSConnection & addConnection(QString src, QString sgn, QString dst, QString slt, bool q=false, bool head=false)
Adds a connection to the sheet structure.
Definition: arcssheet.cpp:183
Maintains connections between objects.
Definition: arcssheet.h:48