QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 ) { return !dynamic_cast<QgsLayoutItem *>( item ); } ), items.end() );
124
125 return items;
126}
127
128QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
129{
130 QList<QGraphicsItem *> res;
131 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
132 res.reserve( layoutItems.size() );
133 for ( QgsLayoutItem *item : layoutItems )
134 res << item;
135 return res;
136}
137
138bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
139{
140 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
141 return layoutItem->isLocked();
142 else
143 return false;
144}
145
146bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
147{
148 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
149 return layoutItem->isGroupMember();
150 else
151 return false;
152}
153
154QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
155{
156 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
157 return layoutItem->rectWithFrame();
158 else
159 return QRectF();
160}
161
162QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
163{
164 bool snapped = false;
165
166 const QList<QGraphicsItem *> selectedItems = selectedSceneItems();
167 QList<QGraphicsItem *> itemsToExclude;
168 expandItemList( selectedItems, itemsToExclude );
169
170 QList<QgsLayoutItem *> layoutItemsToExclude;
171 for ( QGraphicsItem *item : itemsToExclude )
172 layoutItemsToExclude << dynamic_cast<QgsLayoutItem *>( item );
173
174 //depending on the mode, we either snap just the single point, or all the bounds of the selection
175 QPointF snappedPoint;
176 switch ( mode )
177 {
178 case Item:
179 snappedPoint
180 = mLayout->snapper()
181 .snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude )
182 .topLeft();
183 break;
184 case Point:
185 snappedPoint
186 = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
187 break;
188 }
189
190 return snapped ? snappedPoint : originalPoint;
191}
192
193void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
194{
195 mItemCommand.reset( qgis::down_cast<QgsLayoutItem *>( item )->createCommand( QString(), 0 ) );
196 mItemCommand->saveBeforeState();
197}
198
199void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
200{
201 mItemCommand->saveAfterState();
202 mLayout->undoStack()->push( mItemCommand.release() );
203}
204
205void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
206{
207 mLayout->undoStack()->beginMacro( text );
208}
209
210void QgsLayoutMouseHandles::endMacroCommand()
211{
212 mLayout->undoStack()->endMacro();
213}
214
215void QgsLayoutMouseHandles::hideAlignItems()
216{
217 mHorizontalSnapLine->hide();
218 mVerticalSnapLine->hide();
219}
220
221void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
222{
223 for ( QGraphicsItem *item : items )
224 {
225 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
226 {
227 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
228 const QList<QgsLayoutItem *> groupItems = static_cast<QgsLayoutItemGroup *>( item )->items();
229 expandItemList( groupItems, collected );
230 }
231 else
232 {
233 collected << item;
234 }
235 }
236}
237
238
239void QgsLayoutMouseHandles::expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const
240{
241 for ( QGraphicsItem *item : items )
242 {
243 if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
244 {
245 // if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
246 const QList<QgsLayoutItem *> groupItems = static_cast<QgsLayoutItemGroup *>( item )->items();
247 expandItemList( groupItems, collected );
248 }
249 else
250 {
251 collected << item;
252 }
253 }
254}
255
256void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
257{
258 qgis::down_cast<QgsLayoutItem *>( item )->attemptMoveBy( deltaX, deltaY );
259}
260
261void QgsLayoutMouseHandles::rotateItem( QGraphicsItem *item, double deltaDegree, double deltaCenterX, double deltaCenterY )
262{
263 QgsLayoutItem *itm = qgis::down_cast<QgsLayoutItem *>( item );
264 QgsLayoutItem::ReferencePoint previousReferencePoint = itm->referencePoint();
266 itm->attemptMoveBy( deltaCenterX, deltaCenterY );
267 itm->setItemRotation( itm->itemRotation() + deltaDegree, true );
268 itm->setReferencePoint( previousReferencePoint );
269}
270
271void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
272{
273 QgsLayoutItem *layoutItem = qgis::down_cast<QgsLayoutItem *>( item );
274 layoutItem->attemptSetSceneRect( rect, true );
275}
276
277void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
278{
279 if ( !mView )
280 return;
281
282 mView->pushStatusMessage( message );
283}
284
285
286void QgsLayoutMouseHandles::mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event )
287{
288 QgsGraphicsViewMouseHandles::mouseDoubleClickEvent( event );
289
290 QList<QGraphicsItem *> items = selectedSceneItems();
291 if ( items.isEmpty() )
292 return;
293
294 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( items.first() );
295 if ( item == nullptr )
296 return;
297
298 // If item is a frame, use the multiFrame type
299 int itemtype = item->type();
300 if ( QgsLayoutFrame *frame = dynamic_cast<QgsLayoutFrame *>( item ) )
301 if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
302 itemtype = multiFrame->type();
303
304 int metadataId = QgsGui::layoutItemGuiRegistry()->metadataIdForItemType( itemtype );
305 if ( metadataId == -1 )
306 {
307 return;
308 }
309 QgsGui::layoutItemGuiRegistry()->itemMetadata( metadataId )->handleDoubleClick( item, mouseActionForScenePos( event->scenePos() ) );
310}
311
static QgsLayoutItemGuiRegistry * layoutItemGuiRegistry()
Returns the global layout item GUI registry, used for registering the GUI behavior of layout items.
Definition qgsgui.cpp:154
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