QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
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
17#include "moc_qgsmodelviewtoolselect.cpp"
24#include "qgsmodelgraphicitem.h"
25
27 : QgsModelViewTool( view, tr( "Select" ) )
28{
29 setCursor( Qt::ArrowCursor );
30
31 mRubberBand.reset( new QgsModelViewRectangularRubberBand( view ) );
32 mRubberBand->setBrush( QBrush( QColor( 224, 178, 76, 63 ) ) );
33 mRubberBand->setPen( QPen( QBrush( QColor( 254, 58, 29, 100 ) ), 0, Qt::DotLine ) );
34
35 mLinkTool.reset( new QgsModelViewToolLink( view ) );
36}
37
39{
40 if ( mMouseHandles )
41 {
42 // want to force them to be removed from the scene
43 if ( mMouseHandles->scene() )
44 mMouseHandles->scene()->removeItem( mMouseHandles );
45 delete mMouseHandles;
46 }
47}
48
50{
51 if ( mMouseHandles->shouldBlockEvent( event ) )
52 {
53 //swallow clicks while dragging/resizing items
54 return;
55 }
56
57 if ( mMouseHandles->isVisible() )
58 {
59 //selection handles are being shown, get mouse action for current cursor position
60 Qgis::MouseHandlesAction mouseAction = mMouseHandles->mouseActionForScenePos( event->modelPoint() );
61
62 if ( mouseAction != Qgis::MouseHandlesAction::MoveItem
64 && mouseAction != Qgis::MouseHandlesAction::SelectItem )
65 {
66 //mouse is over a resize handle, so propagate event onward
67 event->ignore();
68 return;
69 }
70 }
71
72 if ( event->button() != Qt::LeftButton )
73 {
74 event->ignore();
75 return;
76 }
77
78
79 QgsModelComponentGraphicItem *selectedItem = nullptr;
80
81 //select topmost item at position of event
82 selectedItem = scene()->componentItemAt( event->modelPoint() );
83
84 if ( !selectedItem )
85 {
86 //not clicking over an item, so start marquee selection
87 mIsSelecting = true;
88 mMousePressStartPos = event->pos();
89 mRubberBand->start( event->modelPoint(), Qt::KeyboardModifiers() );
90 return;
91 }
92
93 if ( ( event->modifiers() & Qt::ShiftModifier ) && ( selectedItem->isSelected() ) )
94 {
95 //SHIFT-clicking a selected item deselects it
96 selectedItem->setSelected( false );
97
98 //Check if we have any remaining selected items, and if so, update the item panel
99 const QList<QgsModelComponentGraphicItem *> selectedItems = scene()->selectedComponentItems();
100 if ( !selectedItems.isEmpty() )
101 {
102 emit itemFocused( selectedItems.at( 0 ) );
103 }
104 else
105 {
106 emit itemFocused( nullptr );
107 }
108 }
109 else
110 {
111 if ( ( !selectedItem->isSelected() ) && //keep selection if an already selected item pressed
112 !( event->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
113 {
114 scene()->setSelectedItem( selectedItem ); // clears existing selection
115 }
116 else
117 {
118 selectedItem->setSelected( true );
119 }
120 event->ignore();
121 emit itemFocused( selectedItem );
122
123 if ( !( event->modifiers() & Qt::ShiftModifier ) )
124 {
125 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
126 // to multiple items
127 QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
128 for ( QGraphicsItem *item : items )
129 {
130 if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )
131 {
132 // Start link tool
133 mLinkTool->setFromSocket( socket );
134 view()->setTool( mLinkTool.get() );
135 }
136 else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
137 {
138 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
139 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
140 {
141 button->modelPressEvent( event );
142 event->accept();
143 return;
144 }
145 }
146 }
147 }
148 }
149
150 event->ignore();
151}
152
154{
155 if ( mIsSelecting )
156 {
157 mRubberBand->update( event->modelPoint(), Qt::KeyboardModifiers() );
158 }
159 else
160 {
161 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
162 // to multiple items
163 QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
164 for ( QGraphicsItem *item : items )
165 {
166 if ( mHoverEnteredItems.contains( item ) )
167 {
168 if ( QgsModelComponentGraphicItem *component = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
169 {
170 component->modelHoverMoveEvent( event );
171 }
172 }
173 else
174 {
175 mHoverEnteredItems.append( item );
176 if ( QgsModelComponentGraphicItem *component = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
177 {
178 component->modelHoverEnterEvent( event );
179 }
180 else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
181 {
182 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
183 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
184 button->modelHoverEnterEvent( event );
185 }
186 }
187 }
188 const QList<QGraphicsItem *> prevHovered = mHoverEnteredItems;
189 for ( QGraphicsItem *item : prevHovered )
190 {
191 if ( !items.contains( item ) )
192 {
193 mHoverEnteredItems.removeAll( item );
194 if ( QgsModelComponentGraphicItem *component = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
195 {
196 component->modelHoverLeaveEvent( event );
197 }
198 else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
199 {
200 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
201 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
202 button->modelHoverLeaveEvent( event );
203 }
204 }
205 }
206
207 event->ignore();
208 }
209}
210
212{
213 if ( !mIsSelecting )
214 {
215 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
216 // to multiple items
217 QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
218 for ( QGraphicsItem *item : items )
219 {
220 if ( QgsModelComponentGraphicItem *component = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
221 {
222 scene()->setSelectedItem( component ); // clears existing selection
223 component->modelDoubleClickEvent( event );
224 break;
225 }
226 }
227 }
228}
229
231{
232 if ( event->button() != Qt::LeftButton && mMouseHandles->shouldBlockEvent( event ) )
233 {
234 //swallow clicks while dragging/resizing items
235 return;
236 }
237
238 if ( !mIsSelecting || event->button() != Qt::LeftButton )
239 {
240 event->ignore();
241 return;
242 }
243
244 mIsSelecting = false;
245 bool wasClick = !isClickAndDrag( mMousePressStartPos, event->pos() );
246
247 // important -- we don't pass the event modifiers here, because we use them for a different meaning!
248 // (modifying how the selection interacts with the items, rather than modifying the selection shape)
249 QRectF rect = mRubberBand->finish( event->modelPoint() );
250
251 bool subtractingSelection = false;
252 if ( event->modifiers() & Qt::ShiftModifier )
253 {
254 //shift modifier means adding to selection, nothing required here
255 }
256 else if ( event->modifiers() & Qt::ControlModifier )
257 {
258 //control modifier means subtract from current selection
259 subtractingSelection = true;
260 }
261 else
262 {
263 //not adding to or removing from selection, so clear current selection
264 whileBlocking( scene() )->deselectAll();
265 }
266
267 //determine item selection mode, default to intersection
268 Qt::ItemSelectionMode selectionMode = Qt::IntersectsItemShape;
269 if ( event->modifiers() & Qt::AltModifier )
270 {
271 //alt modifier switches to contains selection mode
272 selectionMode = Qt::ContainsItemShape;
273 }
274
275 //find all items in rect
276 QList<QGraphicsItem *> itemList;
277 if ( wasClick )
278 itemList = scene()->items( rect.center(), selectionMode );
279 else
280 itemList = scene()->items( rect, selectionMode );
281 for ( QGraphicsItem *item : std::as_const( itemList ) )
282 {
283 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
284 {
285 if ( subtractingSelection )
286 {
287 componentItem->setSelected( false );
288 }
289 else
290 {
291 componentItem->setSelected( true );
292 }
293 if ( wasClick )
294 {
295 // found an item, and only a click - nothing more to do
296 break;
297 }
298 }
299 }
300
301 //update item panel
302 const QList<QgsModelComponentGraphicItem *> selectedItemList = scene()->selectedComponentItems();
303 if ( !selectedItemList.isEmpty() )
304 {
305 emit itemFocused( selectedItemList.at( 0 ) );
306 }
307 else
308 {
309 emit itemFocused( nullptr );
310 }
311 mMouseHandles->selectionChanged();
312}
313
314void QgsModelViewToolSelect::wheelEvent( QWheelEvent *event )
315{
316 if ( mMouseHandles->shouldBlockEvent( event ) )
317 {
318 //ignore wheel events while dragging/resizing items
319 return;
320 }
321 else
322 {
323 event->ignore();
324 }
325}
326
328{
329 if ( mMouseHandles->isDragging() || mMouseHandles->isResizing() )
330 {
331 return;
332 }
333 else
334 {
335 event->ignore();
336 }
337}
338
340{
341 if ( mIsSelecting )
342 {
343 mRubberBand->finish();
344 mIsSelecting = false;
345 }
347}
348
350{
351 return !mIsSelecting;
352}
353
354QgsModelViewMouseHandles *QgsModelViewToolSelect::mouseHandles()
355{
356 return mMouseHandles;
357}
358
359void QgsModelViewToolSelect::setScene( QgsModelGraphicsScene *scene )
360{
361 // existing handles are owned by previous layout
362 if ( mMouseHandles )
363 mMouseHandles->deleteLater();
364
365 //add mouse selection handles to scene, and initially hide
366 mMouseHandles = new QgsModelViewMouseHandles( view() );
367 mMouseHandles->hide();
368 mMouseHandles->setZValue( QgsModelGraphicsScene::MouseHandles );
369 scene->addItem( mMouseHandles );
370}
371
373{
374 mHoverEnteredItems.clear();
375}
MouseHandlesAction
Action to be performed by the mouse handles.
Definition qgis.h:5820
@ SelectItem
Select item.
A mouse event which is the result of a user interaction with a QgsModelGraphicsView.
QPointF modelPoint() const
Returns the event point location in model coordinates.
A rectangular rubber band for use within QgsModelGraphicsView widgets.
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:6206