QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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 "moc_qgslayoutmousehandles.cpp"
20#include "qgis.h"
21#include "qgslayout.h"
22#include "qgslayoutitem.h"
23#include "qgslayoututils.h"
24#include "qgslayoutview.h"
26#include "qgslayoutsnapper.h"
27#include "qgslayoutitemgroup.h"
28#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;
104 if ( QgsLayoutViewToolSelect *tool = qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
105 {
106 const double searchTolerance = tool->searchToleranceInLayoutUnits();
107 const QRectF area( scenePoint.x() - searchTolerance, scenePoint.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance );
108 items = mLayout->items( area );
109 }
110 else
111 {
112 items = mLayout->items( scenePoint );
113 }
114 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem * item )
115 {
116 return !dynamic_cast<QgsLayoutItem *>( item );
117 } ), items.end() );
118
119 return items;
120}
121
122QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
123{
124 QList<QGraphicsItem *> res;
125 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
126 res.reserve( layoutItems.size() );
127 for ( QgsLayoutItem *item : layoutItems )
128 res << item;
129 return res;
130}
131
132bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
133{
134 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
135 return layoutItem->isLocked();
136 else
137 return false;
138}
139
140bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
141{
142 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
143 return layoutItem->isGroupMember();
144 else
145 return false;
146}
147
148QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
149{
150 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
151 return layoutItem->rectWithFrame();
152 else
153 return QRectF();
154}
155
156QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
157{
158 bool snapped = false;
159
160 const QList< QGraphicsItem * > selectedItems = selectedSceneItems();
161 QList< QGraphicsItem * > itemsToExclude;
162 expandItemList( selectedItems, itemsToExclude );
163
164 QList< QgsLayoutItem * > layoutItemsToExclude;
165 for ( QGraphicsItem *item : itemsToExclude )
166 layoutItemsToExclude << dynamic_cast< QgsLayoutItem * >( item );
167
168 //depending on the mode, we either snap just the single point, or all the bounds of the selection
169 QPointF snappedPoint;
170 switch ( mode )
171 {
172 case Item:
173 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
174 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
175 break;
176 case Point:
177 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
178 snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
179 break;
180 }
181
182 return snapped ? snappedPoint : originalPoint;
183}
184
185void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
186{
187 mItemCommand.reset( qgis::down_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
188 mItemCommand->saveBeforeState();
189}
190
191void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
192{
193 mItemCommand->saveAfterState();
194 mLayout->undoStack()->push( mItemCommand.release() );
195}
196
197void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
198{
199 mLayout->undoStack()->beginMacro( text );
200
201}
202
203void QgsLayoutMouseHandles::endMacroCommand()
204{
205 mLayout->undoStack()->endMacro();
206}
207
208void QgsLayoutMouseHandles::hideAlignItems()
209{
210 mHorizontalSnapLine->hide();
211 mVerticalSnapLine->hide();
212}
213
214void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
215{
216 for ( QGraphicsItem *item : items )
217 {
218 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
219 {
220 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
221 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
222 expandItemList( groupItems, collected );
223 }
224 else
225 {
226 collected << item;
227 }
228 }
229}
230
231
232void QgsLayoutMouseHandles::expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const
233{
234 for ( QGraphicsItem *item : items )
235 {
236 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
237 {
238 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
239 const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
240 expandItemList( groupItems, collected );
241 }
242 else
243 {
244 collected << item;
245 }
246 }
247}
248
249void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
250{
251 qgis::down_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
252}
253
254void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
255{
256 QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
257 layoutItem->attemptSetSceneRect( rect, true );
258}
259
260void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
261{
262 if ( !mView )
263 return;
264
265 mView->pushStatusMessage( message );
266}
267
268
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.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49