QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
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 //select topmost item at position of event
79 selectedItem = scene()->componentItemAt( event->modelPoint() );
80
81 if ( !selectedItem )
82 {
83 //not clicking over an item, so start marquee selection
84 mIsSelecting = true;
85 mMousePressStartPos = event->pos();
86 mRubberBand->start( event->modelPoint(), Qt::KeyboardModifiers() );
87 return;
88 }
89
90 if ( ( event->modifiers() & Qt::ShiftModifier ) && ( selectedItem->isSelected() ) )
91 {
92 //SHIFT-clicking a selected item deselects it
93 selectedItem->setSelected( false );
94
95 //Check if we have any remaining selected items, and if so, update the item panel
96 const QList<QgsModelComponentGraphicItem *> selectedItems = scene()->selectedComponentItems();
97 if ( !selectedItems.isEmpty() )
98 {
99 emit itemFocused( selectedItems.at( 0 ) );
100 }
101 else
102 {
103 emit itemFocused( nullptr );
104 }
105 }
106 else
107 {
108 if ( ( !selectedItem->isSelected() ) && //keep selection if an already selected item pressed
109 !( event->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
110 {
111 scene()->setSelectedItem( selectedItem ); // clears existing selection
112 }
113 else
114 {
115 selectedItem->setSelected( true );
116 }
117 event->ignore();
118 emit itemFocused( selectedItem );
119
120 if ( !( event->modifiers() & Qt::ShiftModifier ) )
121 {
122 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
123 // to multiple items
124 QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
125 for ( QGraphicsItem *item : items )
126 {
127 if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast< QgsModelDesignerFlatButtonGraphicItem * >( item ) )
128 {
129 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
130 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
131 {
132 button->modelPressEvent( event );
133 event->accept();
134 return;
135 }
136 }
137 }
138 }
139 }
140
141 event->ignore();
142}
143
145{
146 if ( mIsSelecting )
147 {
148 mRubberBand->update( event->modelPoint(), Qt::KeyboardModifiers() );
149 }
150 else
151 {
152 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
153 // to multiple items
154 QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
155 for ( QGraphicsItem *item : items )
156 {
157 if ( mHoverEnteredItems.contains( item ) )
158 {
159 if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
160 {
161 component->modelHoverMoveEvent( event );
162 }
163 }
164 else
165 {
166 mHoverEnteredItems.append( item );
167 if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
168 {
169 component->modelHoverEnterEvent( event );
170 }
171 else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
172 {
173 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
174 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
175 button->modelHoverEnterEvent( event );
176 }
177 }
178 }
179 const QList< QGraphicsItem * > prevHovered = mHoverEnteredItems;
180 for ( QGraphicsItem *item : prevHovered )
181 {
182 if ( ! items.contains( item ) )
183 {
184 mHoverEnteredItems.removeAll( item );
185 if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
186 {
187 component->modelHoverLeaveEvent( event );
188 }
189 else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast<QgsModelDesignerFlatButtonGraphicItem *>( item ) )
190 {
191 // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on!
192 if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) )
193 button->modelHoverLeaveEvent( event );
194 }
195 }
196 }
197
198 event->ignore();
199 }
200}
201
203{
204 if ( !mIsSelecting )
205 {
206 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
207 // to multiple items
208 QList< QGraphicsItem * > items = scene()->items( event->modelPoint() );
209 for ( QGraphicsItem *item : items )
210 {
211 if ( QgsModelComponentGraphicItem *component = dynamic_cast< QgsModelComponentGraphicItem * >( item ) )
212 {
213 scene()->setSelectedItem( component ); // clears existing selection
214 component->modelDoubleClickEvent( event );
215 break;
216 }
217 }
218 }
219}
220
222{
223 if ( event->button() != Qt::LeftButton && mMouseHandles->shouldBlockEvent( event ) )
224 {
225 //swallow clicks while dragging/resizing items
226 return;
227 }
228
229 if ( !mIsSelecting || event->button() != Qt::LeftButton )
230 {
231 event->ignore();
232 return;
233 }
234
235 mIsSelecting = false;
236 bool wasClick = !isClickAndDrag( mMousePressStartPos, event->pos() );
237
238 // important -- we don't pass the event modifiers here, because we use them for a different meaning!
239 // (modifying how the selection interacts with the items, rather than modifying the selection shape)
240 QRectF rect = mRubberBand->finish( event->modelPoint() );
241
242 bool subtractingSelection = false;
243 if ( event->modifiers() & Qt::ShiftModifier )
244 {
245 //shift modifier means adding to selection, nothing required here
246 }
247 else if ( event->modifiers() & Qt::ControlModifier )
248 {
249 //control modifier means subtract from current selection
250 subtractingSelection = true;
251 }
252 else
253 {
254 //not adding to or removing from selection, so clear current selection
255 whileBlocking( scene() )->deselectAll();
256 }
257
258 //determine item selection mode, default to intersection
259 Qt::ItemSelectionMode selectionMode = Qt::IntersectsItemShape;
260 if ( event->modifiers() & Qt::AltModifier )
261 {
262 //alt modifier switches to contains selection mode
263 selectionMode = Qt::ContainsItemShape;
264 }
265
266 //find all items in rect
267 QList<QGraphicsItem *> itemList;
268 if ( wasClick )
269 itemList = scene()->items( rect.center(), selectionMode );
270 else
271 itemList = scene()->items( rect, selectionMode );
272 for ( QGraphicsItem *item : std::as_const( itemList ) )
273 {
274 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
275 {
276 if ( subtractingSelection )
277 {
278 componentItem->setSelected( false );
279 }
280 else
281 {
282 componentItem->setSelected( true );
283 }
284 if ( wasClick )
285 {
286 // found an item, and only a click - nothing more to do
287 break;
288 }
289 }
290 }
291
292 //update item panel
293 const QList<QgsModelComponentGraphicItem *> selectedItemList = scene()->selectedComponentItems();
294 if ( !selectedItemList.isEmpty() )
295 {
296 emit itemFocused( selectedItemList.at( 0 ) );
297 }
298 else
299 {
300 emit itemFocused( nullptr );
301 }
302 mMouseHandles->selectionChanged();
303}
304
305void QgsModelViewToolSelect::wheelEvent( QWheelEvent *event )
306{
307 if ( mMouseHandles->shouldBlockEvent( event ) )
308 {
309 //ignore wheel events while dragging/resizing items
310 return;
311 }
312 else
313 {
314 event->ignore();
315 }
316}
317
319{
320 if ( mMouseHandles->isDragging() || mMouseHandles->isResizing() )
321 {
322 return;
323 }
324 else
325 {
326 event->ignore();
327 }
328}
329
331{
332 if ( mIsSelecting )
333 {
334 mRubberBand->finish();
335 mIsSelecting = false;
336 }
338}
339
341{
342 return !mIsSelecting;
343}
344
345QgsModelViewMouseHandles *QgsModelViewToolSelect::mouseHandles()
346{
347 return mMouseHandles;
348}
349
350void QgsModelViewToolSelect::setScene( QgsModelGraphicsScene *scene )
351{
352 // existing handles are owned by previous layout
353 if ( mMouseHandles )
354 mMouseHandles->deleteLater();
355
356 //add mouse selection handles to scene, and initially hide
357 mMouseHandles = new QgsModelViewMouseHandles( view() );
358 mMouseHandles->hide();
359 mMouseHandles->setZValue( QgsModelGraphicsScene::MouseHandles );
360 scene->addItem( mMouseHandles );
361}
362
364{
365 mHoverEnteredItems.clear();
366}
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:5111