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