QGIS API Documentation 3.41.0-Master (cea29feecf2)
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 "qgsgui.h"
22#include "qgslayout.h"
23#include "qgslayoutitem.h"
24#include "qgslayoututils.h"
25#include "qgslayoutview.h"
28#include "qgslayoutsnapper.h"
29#include "qgslayoutitemgroup.h"
30#include "qgslayoutmultiframe.h"
31#include "qgslayoutframe.h"
32#include "qgslayoutundostack.h"
34#include <QGraphicsView>
35#include <QGraphicsSceneHoverEvent>
36#include <QPainter>
37#include <QWidget>
38#include <limits>
39
41
42QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *view )
43 : QgsGraphicsViewMouseHandles( view )
44 , mLayout( layout )
45 , mView( view )
46{
47 //listen for selection changes, and update handles accordingly
48 connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
49
50 mHorizontalSnapLine = mView->createSnapLine();
51 mHorizontalSnapLine->hide();
52 layout->addItem( mHorizontalSnapLine );
53 mVerticalSnapLine = mView->createSnapLine();
54 mVerticalSnapLine->hide();
55 layout->addItem( mVerticalSnapLine );
56}
57
58void QgsLayoutMouseHandles::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
59{
60 paintInternal( painter, mLayout->renderContext().isPreviewRender(), mLayout->renderContext().boundingBoxesVisible(), true, option, widget );
61}
62
63void QgsLayoutMouseHandles::selectionChanged()
64{
65 //listen out for selected items' size and rotation changed signals
66 const QList<QGraphicsItem *> itemList = layout()->items();
67 for ( QGraphicsItem *graphicsItem : itemList )
68 {
69 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
70 if ( !item )
71 continue;
72
73 if ( item->isSelected() )
74 {
75 connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
76 connect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
77 connect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
78 connect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
79 }
80 else
81 {
82 disconnect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
83 disconnect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
84 disconnect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
85 disconnect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
86 }
87 }
88
89 resetStatusBar();
90 updateHandles();
91}
92
93void QgsLayoutMouseHandles::setViewportCursor( Qt::CursorShape cursor )
94{
95 //workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
96 //rather then setting it directly here
97
98 if ( qobject_cast<QgsLayoutViewToolSelect *>( mView->tool() ) )
99 {
100 mView->viewport()->setCursor( cursor );
101 }
102}
103
104QList<QGraphicsItem *> QgsLayoutMouseHandles::sceneItemsAtPoint( QPointF scenePoint )
105{
106 QList<QGraphicsItem *> items;
107 if ( QgsLayoutViewToolSelect *tool = qobject_cast<QgsLayoutViewToolSelect *>( mView->tool() ) )
108 {
109 const double searchTolerance = tool->searchToleranceInLayoutUnits();
110 const QRectF area( scenePoint.x() - searchTolerance, scenePoint.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance );
111 items = mLayout->items( area );
112 }
113 else
114 {
115 items = mLayout->items( scenePoint );
116 }
117 items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem *item ) {
118 return !dynamic_cast<QgsLayoutItem *>( item );
119 } ),
120 items.end() );
121
122 return items;
123}
124
125QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
126{
127 QList<QGraphicsItem *> res;
128 const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
129 res.reserve( layoutItems.size() );
130 for ( QgsLayoutItem *item : layoutItems )
131 res << item;
132 return res;
133}
134
135bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
136{
137 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
138 return layoutItem->isLocked();
139 else
140 return false;
141}
142
143bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
144{
145 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
146 return layoutItem->isGroupMember();
147 else
148 return false;
149}
150
151QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
152{
153 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
154 return layoutItem->rectWithFrame();
155 else
156 return QRectF();
157}
158
159QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
160{
161 bool snapped = false;
162
163 const QList<QGraphicsItem *> selectedItems = selectedSceneItems();
164 QList<QGraphicsItem *> itemsToExclude;
165 expandItemList( selectedItems, itemsToExclude );
166
167 QList<QgsLayoutItem *> layoutItemsToExclude;
168 for ( QGraphicsItem *item : itemsToExclude )
169 layoutItemsToExclude << dynamic_cast<QgsLayoutItem *>( item );
170
171 //depending on the mode, we either snap just the single point, or all the bounds of the selection
172 QPointF snappedPoint;
173 switch ( mode )
174 {
175 case Item:
176 snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
177 break;
178 case Point:
179 snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr, snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
180 break;
181 }
182
183 return snapped ? snappedPoint : originalPoint;
184}
185
186void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
187{
188 mItemCommand.reset( qgis::down_cast<QgsLayoutItem *>( item )->createCommand( QString(), 0 ) );
189 mItemCommand->saveBeforeState();
190}
191
192void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
193{
194 mItemCommand->saveAfterState();
195 mLayout->undoStack()->push( mItemCommand.release() );
196}
197
198void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
199{
200 mLayout->undoStack()->beginMacro( text );
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
269void QgsLayoutMouseHandles::mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event )
270{
271 QgsGraphicsViewMouseHandles::mouseDoubleClickEvent( event );
272
273 QList<QGraphicsItem *> items = selectedSceneItems();
274 if ( items.isEmpty() )
275 return;
276
277 QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( items.first() );
278 if ( item == nullptr )
279 return;
280
281 // If item is a frame, use the multiFrame type
282 int itemtype = item->type();
283 if ( QgsLayoutFrame *frame = dynamic_cast<QgsLayoutFrame *>( item ) )
284 if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
285 itemtype = multiFrame->type();
286
287 int metadataId = QgsGui::layoutItemGuiRegistry()->metadataIdForItemType( itemtype );
288 if ( metadataId == -1 )
289 {
290 return;
291 }
292 QgsGui::layoutItemGuiRegistry()->itemMetadata( metadataId )->handleDoubleClick( item, mouseActionForScenePos( event->scenePos() ) );
293}
294
static QgsLayoutItemGuiRegistry * layoutItemGuiRegistry()
Returns the global layout item GUI registry, used for registering the GUI behavior of layout items.
Definition qgsgui.cpp:140
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.
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.
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 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:49