QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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 = mLayout->items( scenePoint );
103 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem * item )
104 {
105 return !dynamic_cast<QgsLayoutItem *>( item );
106 } ), items.end() );
107
108 return items;
109}
110
111QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
112{
113 QList<QGraphicsItem *> res;
114 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
115 res.reserve( layoutItems.size() );
116 for ( QgsLayoutItem *item : layoutItems )
117 res << item;
118 return res;
119}
120
121bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
122{
123 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
124 return layoutItem->isLocked();
125 else
126 return false;
127}
128
129bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
130{
131 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
132 return layoutItem->isGroupMember();
133 else
134 return false;
135}
136
137QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
138{
139 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
140 return layoutItem->rectWithFrame();
141 else
142 return QRectF();
143}
144
145QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
146{
147 bool snapped = false;
148
149 const QList< QGraphicsItem * > selectedItems = selectedSceneItems();
150 QList< QGraphicsItem * > itemsToExclude;
151 expandItemList( selectedItems, itemsToExclude );
152
153 QList< QgsLayoutItem * > layoutItemsToExclude;
154 for ( QGraphicsItem *item : itemsToExclude )
155 layoutItemsToExclude << dynamic_cast< QgsLayoutItem * >( item );
156
157 //depending on the mode, we either snap just the single point, or all the bounds of the selection
158 QPointF snappedPoint;
159 switch ( mode )
160 {
161 case Item:
162 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
163 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
164 break;
165 case Point:
166 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
167 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
168 break;
169 }
170
171 return snapped ? snappedPoint : originalPoint;
172}
173
174void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
175{
176 mItemCommand.reset( qgis::down_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
177 mItemCommand->saveBeforeState();
178}
179
180void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
181{
182 mItemCommand->saveAfterState();
183 mLayout->undoStack()->push( mItemCommand.release() );
184}
185
186void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
187{
188 mLayout->undoStack()->beginMacro( text );
189
190}
191
192void QgsLayoutMouseHandles::endMacroCommand()
193{
194 mLayout->undoStack()->endMacro();
195}
196
197void QgsLayoutMouseHandles::hideAlignItems()
198{
199 mHorizontalSnapLine->hide();
200 mVerticalSnapLine->hide();
201}
202
203void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
204{
205 for ( QGraphicsItem *item : items )
206 {
207 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
208 {
209 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
210 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
211 collected.reserve( collected.size() + groupItems.size() );
212 for ( QgsLayoutItem *groupItem : groupItems )
213 collected.append( groupItem );
214 }
215 else
216 {
217 collected << item;
218 }
219 }
220}
221
222void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
223{
224 qgis::down_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
225}
226
227void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
228{
229 QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
230 layoutItem->attemptSetSceneRect( rect, true );
231}
232
233void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
234{
235 if ( !mView )
236 return;
237
238 mView->pushStatusMessage( message );
239}
240
241
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 attemptMoveBy(double deltaX, double deltaY)
Attempts to shift the item's position by a specified deltaX and deltaY, in layout units.
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.
A graphical widget to display and interact with QgsLayouts.
Definition: qgslayoutview.h:51
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50