QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
20#include <limits>
21
22#include "qgis.h"
23#include "qgsgui.h"
24#include "qgslayout.h"
25#include "qgslayoutframe.h"
26#include "qgslayoutitem.h"
27#include "qgslayoutitemgroup.h"
29#include "qgslayoutmultiframe.h"
31#include "qgslayoutsnapper.h"
32#include "qgslayoutundostack.h"
33#include "qgslayoututils.h"
34#include "qgslayoutview.h"
36
37#include <QGraphicsSceneHoverEvent>
38#include <QGraphicsView>
39#include <QPainter>
40#include <QWidget>
41
42#include "moc_qgslayoutmousehandles.cpp"
43
45
46QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *view )
47 : QgsGraphicsViewMouseHandles( view )
48 , mLayout( layout )
49 , mView( view )
50{
51 setRotationEnabled( true );
52
53 //listen for selection changes, and update handles accordingly
54 connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
55
56 mHorizontalSnapLine = mView->createSnapLine();
57 mHorizontalSnapLine->hide();
58 layout->addItem( mHorizontalSnapLine );
59 mVerticalSnapLine = mView->createSnapLine();
60 mVerticalSnapLine->hide();
61 layout->addItem( mVerticalSnapLine );
62}
63
64void QgsLayoutMouseHandles::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
65{
66 paintInternal( painter, mLayout->renderContext().isPreviewRender(), mLayout->renderContext().boundingBoxesVisible(), true, option, widget );
67}
68
69void QgsLayoutMouseHandles::selectionChanged()
70{
71 //listen out for selected items' size and rotation changed signals
72 const QList<QGraphicsItem *> itemList = layout()->items();
73 for ( QGraphicsItem *graphicsItem : itemList )
74 {
75 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
76 if ( !item )
77 continue;
78
79 if ( item->isSelected() )
80 {
81 connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
82 connect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
83 connect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
84 connect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
85 }
86 else
87 {
88 disconnect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
89 disconnect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
90 disconnect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
91 disconnect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
92 }
93 }
94
95 resetStatusBar();
96 updateHandles();
97}
98
99void QgsLayoutMouseHandles::setViewportCursor( Qt::CursorShape cursor )
100{
101 //workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
102 //rather then setting it directly here
103
104 if ( qobject_cast<QgsLayoutViewToolSelect *>( mView->tool() ) )
105 {
106 mView->viewport()->setCursor( cursor );
107 }
108}
109
110QList<QGraphicsItem *> QgsLayoutMouseHandles::sceneItemsAtPoint( QPointF scenePoint )
111{
112 QList<QGraphicsItem *> items;
113 if ( QgsLayoutViewToolSelect *tool = qobject_cast<QgsLayoutViewToolSelect *>( mView->tool() ) )
114 {
115 const double searchTolerance = tool->searchToleranceInLayoutUnits();
116 const QRectF area( scenePoint.x() - searchTolerance, scenePoint.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance );
117 items = mLayout->items( area );
118 }
119 else
120 {
121 items = mLayout->items( scenePoint );
122 }
123 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem *item ) {
124 return !dynamic_cast<QgsLayoutItem *>( item );
125 } ),
126 items.end() );
127
128 return items;
129}
130
131QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
132{
133 QList<QGraphicsItem *> res;
134 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
135 res.reserve( layoutItems.size() );
136 for ( QgsLayoutItem *item : layoutItems )
137 res << item;
138 return res;
139}
140
141bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
142{
143 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
144 return layoutItem->isLocked();
145 else
146 return false;
147}
148
149bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
150{
151 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
152 return layoutItem->isGroupMember();
153 else
154 return false;
155}
156
157QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
158{
159 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
160 return layoutItem->rectWithFrame();
161 else
162 return QRectF();
163}
164
165QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
166{
167 bool snapped = false;
168
169 const QList<QGraphicsItem *> selectedItems = selectedSceneItems();
170 QList<QGraphicsItem *> itemsToExclude;
171 expandItemList( selectedItems, itemsToExclude );
172
173 QList<QgsLayoutItem *> layoutItemsToExclude;
174 for ( QGraphicsItem *item : itemsToExclude )
175 layoutItemsToExclude << dynamic_cast<QgsLayoutItem *>( item );
176
177 //depending on the mode, we either snap just the single point, or all the bounds of the selection
178 QPointF snappedPoint;
179 switch ( mode )
180 {
181 case Item:
182 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
183 break;
184 case Point:
185 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
186 break;
187 }
188
189 return snapped ? snappedPoint : originalPoint;
190}
191
192void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
193{
194 mItemCommand.reset( qgis::down_cast<QgsLayoutItem *>( item )->createCommand( QString(), 0 ) );
195 mItemCommand->saveBeforeState();
196}
197
198void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
199{
200 mItemCommand->saveAfterState();
201 mLayout->undoStack()->push( mItemCommand.release() );
202}
203
204void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
205{
206 mLayout->undoStack()->beginMacro( text );
207}
208
209void QgsLayoutMouseHandles::endMacroCommand()
210{
211 mLayout->undoStack()->endMacro();
212}
213
214void QgsLayoutMouseHandles::hideAlignItems()
215{
216 mHorizontalSnapLine->hide();
217 mVerticalSnapLine->hide();
218}
219
220void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
221{
222 for ( QGraphicsItem *item : items )
223 {
224 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
225 {
226 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
227 const QList<QgsLayoutItem *> groupItems = static_cast<QgsLayoutItemGroup *>( item )->items();
228 expandItemList( groupItems, collected );
229 }
230 else
231 {
232 collected << item;
233 }
234 }
235}
236
237
238void QgsLayoutMouseHandles::expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const
239{
240 for ( QGraphicsItem *item : items )
241 {
242 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
243 {
244 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
245 const QList<QgsLayoutItem *> groupItems = static_cast<QgsLayoutItemGroup *>( item )->items();
246 expandItemList( groupItems, collected );
247 }
248 else
249 {
250 collected << item;
251 }
252 }
253}
254
255void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
256{
257 qgis::down_cast<QgsLayoutItem *>( item )->attemptMoveBy( deltaX, deltaY );
258}
259
260void QgsLayoutMouseHandles::rotateItem( QGraphicsItem *item, double deltaDegree, double deltaCenterX, double deltaCenterY )
261{
262 QgsLayoutItem *itm = qgis::down_cast<QgsLayoutItem *>( item );
263 QgsLayoutItem::ReferencePoint previousReferencePoint = itm->referencePoint();
265 itm->attemptMoveBy( deltaCenterX, deltaCenterY );
266 itm->setItemRotation( itm->itemRotation() + deltaDegree, true );
267 itm->setReferencePoint( previousReferencePoint );
268}
269
270void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
271{
272 QgsLayoutItem *layoutItem = qgis::down_cast<QgsLayoutItem *>( item );
273 layoutItem->attemptSetSceneRect( rect, true );
274}
275
276void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
277{
278 if ( !mView )
279 return;
280
281 mView->pushStatusMessage( message );
282}
283
284
285void QgsLayoutMouseHandles::mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event )
286{
287 QgsGraphicsViewMouseHandles::mouseDoubleClickEvent( event );
288
289 QList<QGraphicsItem *> items = selectedSceneItems();
290 if ( items.isEmpty() )
291 return;
292
293 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( items.first() );
294 if ( item == nullptr )
295 return;
296
297 // If item is a frame, use the multiFrame type
298 int itemtype = item->type();
299 if ( QgsLayoutFrame *frame = dynamic_cast<QgsLayoutFrame *>( item ) )
300 if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
301 itemtype = multiFrame->type();
302
303 int metadataId = QgsGui::layoutItemGuiRegistry()->metadataIdForItemType( itemtype );
304 if ( metadataId == -1 )
305 {
306 return;
307 }
308 QgsGui::layoutItemGuiRegistry()->itemMetadata( metadataId )->handleDoubleClick( item, mouseActionForScenePos( event->scenePos() ) );
309}
310
static QgsLayoutItemGuiRegistry * layoutItemGuiRegistry()
Returns the global layout item GUI registry, used for registering the GUI behavior of layout items.
Definition qgsgui.cpp:151
Base class for frame items, which form a layout multiframe item.
virtual void handleDoubleClick(QgsLayoutItem *item, Qgis::MouseHandlesAction action)
Called when a layout item is double-clicked.
A container for grouping several QgsLayoutItems.
int metadataIdForItemType(int type) const
Returns the GUI item metadata ID which corresponds to the specified layout item type.
QgsLayoutItemAbstractGuiMetadata * itemMetadata(int metadataId) const
Returns the metadata for the specified item metadataId.
Base class for graphical items within a QgsLayout.
double itemRotation() const
Returns the current rotation for the item, in degrees clockwise.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item's rotation, in degrees clockwise.
void rotationChanged(double newRotation)
Emitted on item rotation change.
ReferencePoint referencePoint() const
Returns the reference point for positioning of the layout item.
ReferencePoint
Fixed position reference point.
@ Middle
Center of item.
int type() const override
Returns a unique graphics item type identifier.
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 setReferencePoint(ReferencePoint point)
Sets the reference point for positioning of the layout item.
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.
Abstract base class for layout items with the ability to distribute the content to several frames (Qg...
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:50