QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsmodelviewtoolselect.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmodelviewtoolselect.cpp
3  ---------------------------
4  Date : March 2020
5  Copyright : (C) 2020 Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmodelviewtoolselect.h"
17 #include "qgsmodelviewmouseevent.h"
18 #include "qgsmodelgraphicsview.h"
19 #include "qgsprocessingmodelalgorithm.h"
20 #include "qgsmodelgraphicsscene.h"
23 #include "qgsmodelgraphicitem.h"
24 
26  : QgsModelViewTool( view, tr( "Select" ) )
27 {
28  setCursor( Qt::ArrowCursor );
29 
30  mRubberBand.reset( new QgsModelViewRectangularRubberBand( view ) );
31  mRubberBand->setBrush( QBrush( QColor( 224, 178, 76, 63 ) ) );
32  mRubberBand->setPen( QPen( QBrush( QColor( 254, 58, 29, 100 ) ), 0, Qt::DotLine ) );
33 }
34 
36 {
37  if ( mMouseHandles )
38  {
39  // want to force them to be removed from the scene
40  if ( mMouseHandles->scene() )
41  mMouseHandles->scene()->removeItem( mMouseHandles );
42  delete mMouseHandles;
43  }
44 }
45 
47 {
48  if ( mMouseHandles->shouldBlockEvent( event ) )
49  {
50  //swallow clicks while dragging/resizing items
51  return;
52  }
53 
54  if ( mMouseHandles->isVisible() )
55  {
56  //selection handles are being shown, get mouse action for current cursor position
57  QgsGraphicsViewMouseHandles::MouseAction mouseAction = mMouseHandles->mouseActionForScenePos( event->modelPoint() );
58 
59  if ( mouseAction != QgsGraphicsViewMouseHandles::MoveItem
60  && mouseAction != QgsGraphicsViewMouseHandles::NoAction
61  && mouseAction != QgsGraphicsViewMouseHandles::SelectItem )
62  {
63  //mouse is over a resize handle, so propagate event onward
64  event->ignore();
65  return;
66  }
67  }
68 
69  if ( event->button() != Qt::LeftButton )
70  {
71  event->ignore();
72  return;
73  }
74 
75 
76  QgsModelComponentGraphicItem *selectedItem = nullptr;
77 
78  QList<QgsModelComponentGraphicItem *> selectedItems = scene()->selectedComponentItems();
79 
80  //select topmost item at position of event
81  selectedItem = scene()->componentItemAt( event->modelPoint() );
82 
83  if ( !selectedItem )
84  {
85  //not clicking over an item, so start marquee selection
86  mIsSelecting = true;
87  mMousePressStartPos = event->pos();
88  mRubberBand->start( event->modelPoint(), Qt::KeyboardModifiers() );
89  return;
90  }
91 
92  if ( ( event->modifiers() & Qt::ShiftModifier ) && ( selectedItem->isSelected() ) )
93  {
94  //SHIFT-clicking a selected item deselects it
95  selectedItem->setSelected( false );
96 
97  //Check if we have any remaining selected items, and if so, update the item panel
98  const QList<QgsModelComponentGraphicItem *> selectedItems = scene()->selectedComponentItems();
99  if ( !selectedItems.isEmpty() )
100  {
101  emit itemFocused( selectedItems.at( 0 ) );
102  }
103  else
104  {
105  emit itemFocused( nullptr );
106  }
107  }
108  else
109  {
110  if ( ( !selectedItem->isSelected() ) && //keep selection if an already selected item pressed
111  !( event->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
112  {
113  scene()->setSelectedItem( selectedItem ); // clears existing selection
114  }
115  else
116  {
117  selectedItem->setSelected( true );
118  }
119  event->ignore();
120  emit itemFocused( selectedItem );
121 
122  if ( !( event->modifiers() & Qt::ShiftModifier ) )
123  {
124  // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
125  // to multiple items
126  QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
127  for ( QGraphicsItem *item : items )
128  {
129  if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast< QgsModelDesignerFlatButtonGraphicItem * >( item ) )
130  {
131  // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
132  if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
133  {
134  button->modelPressEvent( event );
135  event->accept();
136  return;
137  }
138  }
139  }
140  }
141  }
142 
143  event->ignore();
144 }
145 
147 {
148  if ( mIsSelecting )
149  {
150  mRubberBand->update( event->modelPoint(), Qt::KeyboardModifiers() );
151  }
152  else
153  {
154  // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
155  // to multiple items
156  QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
157  for ( QGraphicsItem *item : items )
158  {
159  if ( mHoverEnteredItems.contains( item ) )
160  {
161  if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
162  {
163  component->modelHoverMoveEvent( event );
164  }
165  }
166  else
167  {
168  mHoverEnteredItems.append( item );
169  if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
170  {
171  component->modelHoverEnterEvent( event );
172  }
173  else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
174  {
175  // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
176  if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
177  button->modelHoverEnterEvent( event );
178  }
179  }
180  }
181  const QList< QGraphicsItem * > prevHovered = mHoverEnteredItems;
182  for ( QGraphicsItem *item : prevHovered )
183  {
184  if ( ! items.contains( item ) )
185  {
186  mHoverEnteredItems.removeAll( item );
187  if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
188  {
189  component->modelHoverLeaveEvent( event );
190  }
191  else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
192  {
193  // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
194  if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
195  button->modelHoverLeaveEvent( event );
196  }
197  }
198  }
199 
200  event->ignore();
201  }
202 }
203 
205 {
206  if ( !mIsSelecting )
207  {
208  // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
209  // to multiple items
210  QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
211  for ( QGraphicsItem *item : items )
212  {
213  if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
214  {
215  scene()->setSelectedItem( component ); // clears existing selection
216  component->modelDoubleClickEvent( event );
217  break;
218  }
219  }
220  }
221 }
222 
224 {
225  if ( event->button() != Qt::LeftButton && mMouseHandles->shouldBlockEvent( event ) )
226  {
227  //swallow clicks while dragging/resizing items
228  return;
229  }
230 
231  if ( !mIsSelecting || event->button() != Qt::LeftButton )
232  {
233  event->ignore();
234  return;
235  }
236 
237  mIsSelecting = false;
238  bool wasClick = !isClickAndDrag( mMousePressStartPos, event->pos() );
239 
240  // important -- we don't pass the event modifiers here, because we use them for a different meaning!
241  // (modifying how the selection interacts with the items, rather than modifying the selection shape)
242  QRectF rect = mRubberBand->finish( event->modelPoint() );
243 
244  bool subtractingSelection = false;
245  if ( event->modifiers() & Qt::ShiftModifier )
246  {
247  //shift modifier means adding to selection, nothing required here
248  }
249  else if ( event->modifiers() & Qt::ControlModifier )
250  {
251  //control modifier means subtract from current selection
252  subtractingSelection = true;
253  }
254  else
255  {
256  //not adding to or removing from selection, so clear current selection
257  whileBlocking( scene() )->deselectAll();
258  }
259 
260  //determine item selection mode, default to intersection
261  Qt::ItemSelectionMode selectionMode = Qt::IntersectsItemShape;
262  if ( event->modifiers() & Qt::AltModifier )
263  {
264  //alt modifier switches to contains selection mode
265  selectionMode = Qt::ContainsItemShape;
266  }
267 
268  //find all items in rect
269  QList<QGraphicsItem *> itemList;
270  if ( wasClick )
271  itemList = scene()->items( rect.center(), selectionMode );
272  else
273  itemList = scene()->items( rect, selectionMode );
274  for ( QGraphicsItem *item : qgis::as_const( itemList ) )
275  {
276  if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
277  {
278  if ( subtractingSelection )
279  {
280  componentItem->setSelected( false );
281  }
282  else
283  {
284  componentItem->setSelected( true );
285  }
286  if ( wasClick )
287  {
288  // found an item, and only a click - nothing more to do
289  break;
290  }
291  }
292  }
293 
294  //update item panel
295  const QList<QgsModelComponentGraphicItem *> selectedItemList = scene()->selectedComponentItems();
296  if ( !selectedItemList.isEmpty() )
297  {
298  emit itemFocused( selectedItemList.at( 0 ) );
299  }
300  else
301  {
302  emit itemFocused( nullptr );
303  }
304  mMouseHandles->selectionChanged();
305 }
306 
307 void QgsModelViewToolSelect::wheelEvent( QWheelEvent *event )
308 {
309  if ( mMouseHandles->shouldBlockEvent( event ) )
310  {
311  //ignore wheel events while dragging/resizing items
312  return;
313  }
314  else
315  {
316  event->ignore();
317  }
318 }
319 
321 {
322  if ( mMouseHandles->isDragging() || mMouseHandles->isResizing() )
323  {
324  return;
325  }
326  else
327  {
328  event->ignore();
329  }
330 }
331 
333 {
334  if ( mIsSelecting )
335  {
336  mRubberBand->finish();
337  mIsSelecting = false;
338  }
340 }
341 
343 {
344  return !mIsSelecting;
345 }
346 
347 QgsModelViewMouseHandles *QgsModelViewToolSelect::mouseHandles()
348 {
349  return mMouseHandles;
350 }
351 
352 void QgsModelViewToolSelect::setScene( QgsModelGraphicsScene *scene )
353 {
354  // existing handles are owned by previous layout
355  if ( mMouseHandles )
356  mMouseHandles->deleteLater();
357 
358  //add mouse selection handles to scene, and initially hide
359  mMouseHandles = new QgsModelViewMouseHandles( view() );
360  mMouseHandles->hide();
361  mMouseHandles->setZValue( QgsModelGraphicsScene::MouseHandles );
362  scene->addItem( mMouseHandles );
363 }
364 
366 {
367  mHoverEnteredItems.clear();
368 }
A QgsModelViewMouseEvent is the result of a user interaction with the mouse on a QgsModelGraphicsView...
QPointF modelPoint() const
Returns the event point location in model coordinates.
QgsModelViewRectangularRubberBand is rectangular rubber band for use within QgsModelGraphicsView widg...
void modelReleaseEvent(QgsModelViewMouseEvent *event) override
Mouse release event for overriding.
void setScene(QgsModelGraphicsScene *scene)
Sets the a scene.
void modelPressEvent(QgsModelViewMouseEvent *event) override
Mouse press event for overriding.
void wheelEvent(QWheelEvent *event) override
Mouse wheel event for overriding.
void deactivate() override
Called when tool is deactivated.
void modelDoubleClickEvent(QgsModelViewMouseEvent *event) override
Mouse double-click event for overriding.
void modelMoveEvent(QgsModelViewMouseEvent *event) override
Mouse move event for overriding.
void resetCache()
Resets the internal cache following a scene change.
QgsModelViewMouseHandles * mouseHandles()
Returns the view's mouse handles.
void keyPressEvent(QKeyEvent *event) override
Key press event for overriding.
bool allowItemInteraction() override
Returns true if the tool allows interaction with component graphic items.
QgsModelViewToolSelect(QgsModelGraphicsView *view)
Constructor for QgsModelViewToolSelect.
Abstract base class for all model designer view tools.
QgsModelGraphicsView * view() const
Returns the view associated with the tool.
void itemFocused(QgsModelComponentGraphicItem *item)
Emitted when an item is "focused" by the tool, i.e.
bool isClickAndDrag(QPoint startViewPoint, QPoint endViewPoint) const
Returns true if a mouse press/release operation which started at startViewPoint and ended at endViewP...
void setCursor(const QCursor &cursor)
Sets a user defined cursor for use when the tool is active.
virtual void deactivate()
Called when tool is deactivated.
QgsModelGraphicsScene * scene() const
Returns the scene associated with the tool.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:263