QGIS API Documentation 3.27.0-Master (75dc696944)
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 "qgslogger.h"
21#include "qgsproject.h"
22#include "qgslayout.h"
23#include "qgslayoutitem.h"
24#include "qgslayoututils.h"
25#include "qgslayoutview.h"
27#include "qgslayoutsnapper.h"
28#include "qgslayoutitemgroup.h"
29#include "qgslayoutundostack.h"
30#include <QGraphicsView>
31#include <QGraphicsSceneHoverEvent>
32#include <QPainter>
33#include <QWidget>
34#include <limits>
35
37
38QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *view )
39 : QgsGraphicsViewMouseHandles( view )
40 , mLayout( layout )
41 , mView( view )
42{
43 //listen for selection changes, and update handles accordingly
44 connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
45
46 mHorizontalSnapLine = mView->createSnapLine();
47 mHorizontalSnapLine->hide();
48 layout->addItem( mHorizontalSnapLine );
49 mVerticalSnapLine = mView->createSnapLine();
50 mVerticalSnapLine->hide();
51 layout->addItem( mVerticalSnapLine );
52}
53
54void QgsLayoutMouseHandles::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
55{
56 paintInternal( painter, mLayout->renderContext().isPreviewRender(),
57 mLayout->renderContext().boundingBoxesVisible(), true, option, widget );
58}
59
60void QgsLayoutMouseHandles::selectionChanged()
61{
62 //listen out for selected items' size and rotation changed signals
63 const QList<QGraphicsItem *> itemList = layout()->items();
64 for ( QGraphicsItem *graphicsItem : itemList )
65 {
66 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
67 if ( !item )
68 continue;
69
70 if ( item->isSelected() )
71 {
72 connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
73 connect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
74 connect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
75 connect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
76 }
77 else
78 {
79 disconnect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
80 disconnect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
81 disconnect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
82 disconnect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
83 }
84 }
85
86 resetStatusBar();
87 updateHandles();
88}
89
90void QgsLayoutMouseHandles::setViewportCursor( Qt::CursorShape cursor )
91{
92 //workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
93 //rather then setting it directly here
94
95 if ( qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
96 {
97 mView->viewport()->setCursor( cursor );
98 }
99}
100
101QList<QGraphicsItem *> QgsLayoutMouseHandles::sceneItemsAtPoint( QPointF scenePoint )
102{
103 QList< QGraphicsItem * > items = mLayout->items( scenePoint );
104 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem * item )
105 {
106 return !dynamic_cast<QgsLayoutItem *>( item );
107 } ), items.end() );
108
109 return items;
110}
111
112QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
113{
114 QList<QGraphicsItem *> res;
115 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
116 res.reserve( layoutItems.size() );
117 for ( QgsLayoutItem *item : layoutItems )
118 res << item;
119 return res;
120}
121
122bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
123{
124 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
125 return layoutItem->isLocked();
126 else
127 return false;
128}
129
130bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
131{
132 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
133 return layoutItem->isGroupMember();
134 else
135 return false;
136}
137
138QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
139{
140 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
141 return layoutItem->rectWithFrame();
142 else
143 return QRectF();
144}
145
146QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
147{
148 bool snapped = false;
149
150 const QList< QGraphicsItem * > selectedItems = selectedSceneItems();
151 QList< QGraphicsItem * > itemsToExclude;
152 expandItemList( selectedItems, itemsToExclude );
153
154 QList< QgsLayoutItem * > layoutItemsToExclude;
155 for ( QGraphicsItem *item : itemsToExclude )
156 layoutItemsToExclude << dynamic_cast< QgsLayoutItem * >( item );
157
158 //depending on the mode, we either snap just the single point, or all the bounds of the selection
159 QPointF snappedPoint;
160 switch ( mode )
161 {
162 case Item:
163 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
164 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
165 break;
166 case Point:
167 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
168 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
169 break;
170 }
171
172 return snapped ? snappedPoint : originalPoint;
173}
174
175void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
176{
177 mItemCommand.reset( qgis::down_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
178 mItemCommand->saveBeforeState();
179}
180
181void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
182{
183 mItemCommand->saveAfterState();
184 mLayout->undoStack()->push( mItemCommand.release() );
185}
186
187void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
188{
189 mLayout->undoStack()->beginMacro( text );
190
191}
192
193void QgsLayoutMouseHandles::endMacroCommand()
194{
195 mLayout->undoStack()->endMacro();
196}
197
198void QgsLayoutMouseHandles::hideAlignItems()
199{
200 mHorizontalSnapLine->hide();
201 mVerticalSnapLine->hide();
202}
203
204void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
205{
206 for ( QGraphicsItem *item : items )
207 {
208 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
209 {
210 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
211 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
212 collected.reserve( collected.size() + groupItems.size() );
213 for ( QgsLayoutItem *groupItem : groupItems )
214 collected.append( groupItem );
215 }
216 else
217 {
218 collected << item;
219 }
220 }
221}
222
223void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
224{
225 qgis::down_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
226}
227
228void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
229{
230 QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
231 layoutItem->attemptSetSceneRect( rect, true );
232}
233
234void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
235{
236 if ( !mView )
237 return;
238
239 mView->pushStatusMessage( message );
240}
241
242
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:50
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51