OpenWalnut  1.5.0dev
WQtNetworkArrow.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut 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 Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <algorithm>
26 #include <cmath>
27 #include <iostream>
28 #include <math.h>
29 #include <memory>
30 #include <string>
31 
32 #include <QGraphicsLineItem>
33 #include <QGraphicsPathItem>
34 #include <QPainterPath>
35 #include <QStyleOptionGraphicsItem>
36 
37 #include "../WMainWindow.h"
38 #include "../WQtGui.h"
39 #include "WQtNetworkArrow.h"
40 #include "WQtNetworkEditor.h"
41 #include "WQtNetworkInputPort.h"
42 #include "WQtNetworkOutputPort.h"
43 #include "WQtNetworkScene.h"
44 #include "core/kernel/combiner/WApplyCombiner.h"
45 
46 const QColor owRed( 248, 87, 87 );
47 const QColor owGreen( 115, 225, 115 );
48 
51 {
52  m_startPort = startPort;
53  m_endPort = endPort;
54 
55  setFlag( QGraphicsItem::ItemIsSelectable, true );
56  setPen( QPen( Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
57 
58  setAcceptHoverEvents( true );
59 }
60 
62 {
63  m_startPort->removeArrow( this );
64  if( m_endPort )
65  {
66  m_endPort->removeArrow( this );
67  }
68 }
69 
71 {
72  return Type;
73 }
74 
76 {
78 
79  // find items in area:
80  QList<QGraphicsItem *> items = scene->items( static_cast< qreal >( pos.x() - ( maxDistance / 2.0 ) ),
81  static_cast< qreal >( pos.y() - ( maxDistance / 2.0 ) ),
82  static_cast< qreal >( maxDistance ),
83  static_cast< qreal >( maxDistance ),
84  Qt::IntersectsItemShape,
85  Qt::AscendingOrder );
86 
87  // find all the connectors:
88  WQtNetworkInputPort* nearest = NULL;
89  float nearestDist = maxDistance;
90  for( int i = 0; i < items.size(); ++i )
91  {
92  QGraphicsItem* item = items[ i ];
93 
94  // is this a connector?
95  WQtNetworkInputPort* con = dynamic_cast< WQtNetworkInputPort* >( item );
96  if( !con )
97  {
98  continue;
99  }
100 
101  // is it compatible?
102  if( !con->getConnector()->connectable( m_startPort->getConnector() ) )
103  {
104  continue;
105  }
106 
107  // nearer than the previous one?
108  QPointF conPos = mapFromItem( con, con->rect().bottomRight() * 0.5 );
109  QPointF vec = pos - conPos;
110  float dist = sqrt( ( vec.x() * vec.x() ) + ( vec.y() * vec.y() ) );
111  // as we want euclidean dist:
112  if( nearestDist >= dist )
113  {
114  nearestDist = dist;
115  nearest = con;
116  }
117  }
118 
119  return nearest;
120 }
121 
122 void WQtNetworkArrow::updatePosition( QPointF deviate )
123 {
124  updatePosition( mapFromItem( m_endPort, m_startPort->rect().bottomRight() * 0.5 ), deviate );
125 }
126 
127 void WQtNetworkArrow::updatePosition( QPointF targetPoint, QPointF deviate )
128 {
129  QRectF sRect = m_startPort->rect();
130  QLineF tmpLine( mapFromItem( m_startPort, sRect.bottomRight() * 0.5 ),
131  targetPoint );
132 
133  m_line = QLineF( tmpLine.x1(), tmpLine.y1()+5, tmpLine.x2(), tmpLine.y2()-5 );
134 
135  QPainterPath path( m_line.p1() );
136 
137  // the control points
138  // Change some of these values to modify the bezier effect
139  double dx = std::abs( m_line.dx() );
140  double dy = std::abs( m_line.dy() );
141  double minCDist = 50.0;
142  double maxCDist = 250.0;
143  // this magic code is the result of try and error
144  dy = std::min( maxCDist, std::max( minCDist, std::max( dx * 0.5, dy ) * 0.5 ) );
145 
146  QPointF c1( m_line.p1() + QPointF( 0, +dy ) );
147  QPointF c2( m_line.p2() + QPointF( 0, -dy ) );
148 
149  // cubic bezier
150  path.cubicTo( c1, c2 + deviate, m_line.p2() );
151  setPath( path );
152 }
153 
155 {
156  return m_startPort;
157 }
158 
160 {
161  return m_endPort;
162 }
163 
164 QVariant WQtNetworkArrow::itemChange( GraphicsItemChange change,
165  const QVariant &value )
166 {
167  if( change == QGraphicsItem::ItemSelectedHasChanged )
168  {
169  changeColor( Qt::black );
170  }
171  return value;
172 }
173 
174 void WQtNetworkArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w )
175 {
176  QStyleOptionGraphicsItem *o = const_cast<QStyleOptionGraphicsItem*>( option );
177  o->state &= ~QStyle::State_Selected;
178 
179  QGraphicsPathItem::paint( painter, o, w );
180 
181  qreal arrowSize = 10;
182  QPointF arrowP1 = m_line.p2() + QPointF( 0.5 * arrowSize, -arrowSize );
183  QPointF arrowP2 = m_line.p2() + QPointF( -0.5 * arrowSize, -arrowSize );
184  m_arrowHead.clear();
185  m_arrowHead << m_line.p2() << arrowP1 << arrowP2;
186 
187  painter->setPen( QPen( m_color, 1, Qt::SolidLine ) );
188  painter->setBrush( m_color );
189  painter->drawPolygon( m_arrowHead );
190 }
191 
193 {
194  QRectF rect = shape().boundingRect();
195 
196  // add a few extra pixels for the arrow and the pen
197  qreal penWidth = 1;
198  qreal extra = ( penWidth + 10 ) / 2.0;
199  rect.adjust( -extra, -extra, extra, extra );
200 
201  return rect;
202 }
203 
204 QPainterPath WQtNetworkArrow::shape() const
205 {
206  QPainterPath path = QGraphicsPathItem::shape();
207  path.addPolygon( m_arrowHead );
208  return path;
209 }
210 
211 void WQtNetworkArrow::hoverEnterEvent( QGraphicsSceneHoverEvent * event )
212 {
213  Q_UNUSED( event );
214 
215  changeColor( Qt::black, 3 );
216 }
217 
218 void WQtNetworkArrow::hoverLeaveEvent( QGraphicsSceneHoverEvent * event )
219 {
220  Q_UNUSED( event );
221 
222  changeColor( Qt::black );
223 }
224 
225 void WQtNetworkArrow::changeColor( QColor color )
226 {
227  m_color = color;
228  setPen( QPen( m_color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
229 }
230 
231 void WQtNetworkArrow::changeColor( QColor color, float penWidth )
232 {
233  m_color = color;
234  setPen( QPen( m_color, penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
235 }
236 
237 void WQtNetworkArrow::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* mouseEvent )
238 {
239  QGraphicsItem::mouseDoubleClickEvent( mouseEvent );
240 
241  // ignore all buttons but the left one
242  if( mouseEvent->button() != Qt::LeftButton )
243  {
244  mouseEvent->ignore();
245  return;
246  }
247 
248  QList<QGraphicsItem *> startItem = scene()->items( mouseEvent->scenePos() );
249  if( !startItem.isEmpty() )
250  {
251  mouseEvent->accept();
252  getStartPort()->getConnector()->disconnect( getEndPort()->getConnector() );
253  }
254  else
255  {
256  mouseEvent->ignore();
257  }
258 }
259 
260 void WQtNetworkArrow::startDrag( const QPointF& pos )
261 {
262  m_snappedOff = false;
263  m_connectionDisconnect = false;
264  m_connectTo = NULL;
265 
266  // highlight
267  changeColor( owGreen, 3 );
268 
269  // click Point
270  m_clickPoint = pos;
271 }
272 
273 void WQtNetworkArrow::moveDrag( const QPointF& pos )
274 {
275  m_connectionDisconnect = false;
276  m_connectTo = NULL;
277 
278  QPointF currentPoint = pos;
279 
280  // deviate according to start pos
281  QPointF deviate = currentPoint - m_clickPoint;
282  float l = sqrt( ( deviate.x() * deviate.x() ) + ( deviate.y() * deviate.y() ) );
283 
284  // if moved far enough, snap to mouse
285  // NOTE: always be snapped of if there is no endport
286  m_snappedOff = !m_endPort || m_snappedOff || ( l > 100.0 ); // when snapped of once, never snap on again
287 
288  if( m_snappedOff )
289  {
290  changeColor( owRed, 3 );
291 
292  // can we snap somewhere?
293  WQtNetworkInputPort* nearestPort = findNearestCompatibleInput( currentPoint, 50.0 );
294  if( nearestPort )
295  {
296  changeColor( owGreen, 3 );
297  m_connectTo = nearestPort;
298  updatePosition( mapFromItem( nearestPort, nearestPort->rect().bottomRight() * 0.5 ), QPointF() );
299  }
300  else
301  {
302  m_connectionDisconnect = true;
303  updatePosition( currentPoint, QPointF() );
304  }
305  }
306  else
307  {
308  // m_snappedOff is defined to ensure that we have m_endPoint if not snapped
309  updatePosition( deviate );
310  }
311 }
312 
313 void WQtNetworkArrow::doneDrag( const QPointF& /*pos*/ )
314 {
315  if( m_endPort && ( m_connectTo == m_endPort ) )
316  {
317  updatePosition();
318  changeColor( Qt::black );
319 
320  m_connectTo = NULL;
321  m_connectionDisconnect = false;
322  return;
323  }
324 
325  // apply operations
326  if( m_connectTo )
327  {
328  // connect new
329  std::shared_ptr< WApplyCombiner > x( new WApplyCombiner( m_startPort->getConnector()->getModule(),
330  m_startPort->getConnector()->getName(), m_connectTo->getConnector()->getModule(), m_connectTo->getConnector()->getName() ) );
331 
332  // remove old connection if needed
333  if( m_endPort )
334  {
335  m_startPort->getConnector()->disconnect( m_endPort->getConnector() );
336  }
337 
338  // apply combiner
339  x->run();
340  return;
341  }
342 
343  // disconnect?
345  {
346  m_startPort->getConnector()->disconnect( m_endPort->getConnector() );
347  m_connectionDisconnect = false;
348  return;
349  }
350 
351  m_connectionDisconnect = false;
352  m_connectTo = NULL;
353 }
354 
355 void WQtNetworkArrow::mousePressEvent( QGraphicsSceneMouseEvent *mouseEvent )
356 {
357  if( mouseEvent->button() != Qt::LeftButton )
358  {
359  mouseEvent->ignore();
360  return;
361  }
362  mouseEvent->accept();
363 
364  startDrag( mouseEvent->pos() );
365 }
366 
367 void WQtNetworkArrow::mouseMoveEvent( QGraphicsSceneMouseEvent *mouseEvent )
368 {
369  mouseEvent->accept();
370  moveDrag( mouseEvent->pos() );
371 }
372 
373 void WQtNetworkArrow::mouseReleaseEvent( QGraphicsSceneMouseEvent *mouseEvent )
374 {
375  if( mouseEvent->button() != Qt::LeftButton )
376  {
377  mouseEvent->ignore();
378  return;
379  }
380  mouseEvent->accept();
381 
382  doneDrag( mouseEvent->pos() );
383 }
384 
Base class for all combiners which apply one connection between two connectors of two modules.
WQtNetworkEditor * getNetworkEditor()
Returns a pointer to the network editor object.
static WMainWindow * getMainWindow()
Returns the current main window instance or NULL if not existent.
Definition: WQtGui.cpp:88
WQtNetworkInputPort * getEndPort()
Returns the WQtNetworkInputPort where the arrow ends.
void updatePosition(QPointF deviate=QPointF())
Calculated the new position of the lines endpoints in the scene.
void hoverEnterEvent(QGraphicsSceneHoverEvent *event)
If the cursor enters the arrow, the arrow becomes geen.
WQtNetworkArrow(WQtNetworkOutputPort *outPort, WQtNetworkInputPort *inPort)
Constructor.
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
Double click on port.
void changeColor(QColor color)
This method changes the color of the arrow.
bool m_snappedOff
gets true once the arrow was pulled far away from original click position.
WQtNetworkInputPort * m_endPort
the end port
~WQtNetworkArrow()
Destructor.
WQtNetworkOutputPort * getStartPort()
Returns the WQtNetworkOutputPort where the arrow starts.
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
Start drawing an arrow temporary.
QPointF m_clickPoint
position where the click event was created.
QPainterPath shape() const
Reimplementation from QGraphicsItem.
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
Send a connect request to kernel when start- and endport are connectable.
QVariant itemChange(GraphicsItemChange change, const QVariant &value)
If the item is changed we want to get notified.
void doneDrag(const QPointF &pos)
Called when releasing the mouse.
QRectF boundingRect() const
Reimplementation form QGraphicsItem, because the arrowhead is added to the line.
int type() const
Reimplementation from QGraphicsItem.
QColor m_color
the current color
WQtNetworkInputPort * m_connectTo
connect to this port after mouse release.
bool m_connectionDisconnect
disconnect if true.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w)
Draw some customized stuff in the scene.
QPolygonF m_arrowHead
the arrowhead
WQtNetworkOutputPort * m_startPort
the start port
QLineF m_line
the line representing the arrow
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
If the cursor leaves the arrow, the arrow gets his default color.
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
Updates the temporary arrows endpoint.
void moveDrag(const QPointF &pos)
Update drag position.
WQtNetworkInputPort * findNearestCompatibleInput(QPointF pos, float maxDistance=100)
Search the next, compatible input port.
void startDrag(const QPointF &pos)
Start Drag.
WQtNetworkScene * getScene()
Returns the current scene.
This class represents the ports a module have.
std::shared_ptr< WModuleInputConnector > getConnector()
Returns the WModuleInputConnecter that belongs to this object.
This class represents the ports a module have.
std::shared_ptr< WModuleOutputConnector > getConnector()
Returns the WModuleOutputConnecter that belongs to this object.
virtual void removeArrow(WQtNetworkArrow *arrow)
Removes a specific arrow.
The scene containing the whole graph.