QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayoutmousehandles.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutmousehandles.cpp
3 ------------------------
4 begin : September 2017
5 copyright : (C) 2017 by Nyall Dawson
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgis.h"
20#include "qgslayout.h"
21#include "qgslayoutitem.h"
22#include "qgslayoututils.h"
23#include "qgslayoutview.h"
25#include "qgslayoutsnapper.h"
26#include "qgslayoutitemgroup.h"
27#include "qgslayoutundostack.h"
29#include <QGraphicsView>
30#include <QGraphicsSceneHoverEvent>
31#include <QPainter>
32#include <QWidget>
33#include <limits>
34
36
37QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *view )
38 : QgsGraphicsViewMouseHandles( view )
39 , mLayout( layout )
40 , mView( view )
41{
42 //listen for selection changes, and update handles accordingly
43 connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
44
45 mHorizontalSnapLine = mView->createSnapLine();
46 mHorizontalSnapLine->hide();
47 layout->addItem( mHorizontalSnapLine );
48 mVerticalSnapLine = mView->createSnapLine();
49 mVerticalSnapLine->hide();
50 layout->addItem( mVerticalSnapLine );
51}
52
53void QgsLayoutMouseHandles::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
54{
55 paintInternal( painter, mLayout->renderContext().isPreviewRender(),
56 mLayout->renderContext().boundingBoxesVisible(), true, option, widget );
57}
58
59void QgsLayoutMouseHandles::selectionChanged()
60{
61 //listen out for selected items' size and rotation changed signals
62 const QList<QGraphicsItem *> itemList = layout()->items();
63 for ( QGraphicsItem *graphicsItem : itemList )
64 {
65 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
66 if ( !item )
67 continue;
68
69 if ( item->isSelected() )
70 {
71 connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
72 connect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
73 connect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
74 connect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
75 }
76 else
77 {
78 disconnect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
79 disconnect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
80 disconnect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
81 disconnect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
82 }
83 }
84
85 resetStatusBar();
86 updateHandles();
87}
88
89void QgsLayoutMouseHandles::setViewportCursor( Qt::CursorShape cursor )
90{
91 //workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
92 //rather then setting it directly here
93
94 if ( qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
95 {
96 mView->viewport()->setCursor( cursor );
97 }
98}
99
100QList<QGraphicsItem *> QgsLayoutMouseHandles::sceneItemsAtPoint( QPointF scenePoint )
101{
102 QList< QGraphicsItem * > items;
103 if ( QgsLayoutViewToolSelect *tool = qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
104 {
105 const double searchTolerance = tool->searchToleranceInLayoutUnits();
106 const QRectF area( scenePoint.x() - searchTolerance, scenePoint.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance );
107 items = mLayout->items( area );
108 }
109 else
110 {
111 items = mLayout->items( scenePoint );
112 }
113 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem * item )
114 {
115 return !dynamic_cast<QgsLayoutItem *>( item );
116 } ), items.end() );
117
118 return items;
119}
120
121QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
122{
123 QList<QGraphicsItem *> res;
124 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
125 res.reserve( layoutItems.size() );
126 for ( QgsLayoutItem *item : layoutItems )
127 res << item;
128 return res;
129}
130
131bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
132{
133 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
134 return layoutItem->isLocked();
135 else
136 return false;
137}
138
139bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
140{
141 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
142 return layoutItem->isGroupMember();
143 else
144 return false;
145}
146
147QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
148{
149 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
150 return layoutItem->rectWithFrame();
151 else
152 return QRectF();
153}
154
155QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
156{
157 bool snapped = false;
158
159 const QList< QGraphicsItem * > selectedItems = selectedSceneItems();
160 QList< QGraphicsItem * > itemsToExclude;
161 expandItemList( selectedItems, itemsToExclude );
162
163 QList< QgsLayoutItem * > layoutItemsToExclude;
164 for ( QGraphicsItem *item : itemsToExclude )
165 layoutItemsToExclude << dynamic_cast< QgsLayoutItem * >( item );
166
167 //depending on the mode, we either snap just the single point, or all the bounds of the selection
168 QPointF snappedPoint;
169 switch ( mode )
170 {
171 case Item:
172 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
173 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
174 break;
175 case Point:
176 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
177 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
178 break;
179 }
180
181 return snapped ? snappedPoint : originalPoint;
182}
183
184void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
185{
186 mItemCommand.reset( qgis::down_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
187 mItemCommand->saveBeforeState();
188}
189
190void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
191{
192 mItemCommand->saveAfterState();
193 mLayout->undoStack()->push( mItemCommand.release() );
194}
195
196void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
197{
198 mLayout->undoStack()->beginMacro( text );
199
200}
201
202void QgsLayoutMouseHandles::endMacroCommand()
203{
204 mLayout->undoStack()->endMacro();
205}
206
207void QgsLayoutMouseHandles::hideAlignItems()
208{
209 mHorizontalSnapLine->hide();
210 mVerticalSnapLine->hide();
211}
212
213void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
214{
215 for ( QGraphicsItem *item : items )
216 {
217 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
218 {
219 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
220 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
221 expandItemList( groupItems, collected );
222 }
223 else
224 {
225 collected << item;
226 }
227 }
228}
229
230
231void QgsLayoutMouseHandles::expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const
232{
233 for ( QGraphicsItem *item : items )
234 {
235 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
236 {
237 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
238 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
239 expandItemList( groupItems, collected );
240 }
241 else
242 {
243 collected << item;
244 }
245 }
246}
247
248void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
249{
250 qgis::down_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
251}
252
253void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
254{
255 QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
256 layoutItem->attemptSetSceneRect( rect, true );
257}
258
259void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
260{
261 if ( !mView )
262 return;
263
264 mView->pushStatusMessage( message );
265}
266
267
A container for grouping several QgsLayoutItems.
Base class for graphical items within a QgsLayout.
void rotationChanged(double newRotation)
Emitted on item rotation change.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
void sizePositionChanged()
Emitted when the item's size or position changes.
void lockChanged()
Emitted if the item's lock status changes.
void frameChanged()
Emitted if the item's frame style changes.
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
Layout view tool for selecting items in the layout.
A graphical widget to display and interact with QgsLayouts.
Definition: qgslayoutview.h:50
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49