QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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
6  email : [email protected]
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 
18 #include "qgslayoutmousehandles.h"
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 
38 QgsLayoutMouseHandles::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 
54 void 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 
60 void 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 
90 void 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 
101 QList<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 
112 QList<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 
122 bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
123 {
124  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
125  return layoutItem->isLocked();
126  else
127  return false;
128 }
129 
130 bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
131 {
132  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
133  return layoutItem->isGroupMember();
134  else
135  return false;
136 }
137 
138 QRectF 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 
146 QPointF 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 
175 void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
176 {
177  mItemCommand.reset( dynamic_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
178  mItemCommand->saveBeforeState();
179 }
180 
181 void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
182 {
183  mItemCommand->saveAfterState();
184  mLayout->undoStack()->push( mItemCommand.release() );
185 }
186 
187 void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
188 {
189  mLayout->undoStack()->beginMacro( text );
190 
191 }
192 
193 void QgsLayoutMouseHandles::endMacroCommand()
194 {
195  mLayout->undoStack()->endMacro();
196 }
197 
198 void QgsLayoutMouseHandles::hideAlignItems()
199 {
200  mHorizontalSnapLine->hide();
201  mVerticalSnapLine->hide();
202 }
203 
204 void 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 
223 void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
224 {
225  dynamic_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
226 }
227 
228 void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
229 {
230  QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
231  layoutItem->attemptSetSceneRect( rect, true );
232 }
233 
234 void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
235 {
236  if ( !mView )
237  return;
238 
239  mView->pushStatusMessage( message );
240 }
241 
242 
qgslayoutitemgroup.h
qgslayoutundostack.h
QgsLayoutItem::sizePositionChanged
void sizePositionChanged()
Emitted when the item's size or position changes.
QgsLayoutItem::attemptSetSceneRect
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.
Definition: qgslayoutitem.cpp:514
qgslayoutview.h
qgis.h
QgsLayoutItemRegistry::LayoutGroup
@ LayoutGroup
Grouped item.
Definition: qgslayoutitemregistry.h:309
qgslayoututils.h
QgsLayoutItem::lockChanged
void lockChanged()
Emitted if the item's lock status changes.
qgslayoutitem.h
qgslayoutviewtoolselect.h
QgsLayoutItem::frameChanged
void frameChanged()
Emitted if the item's frame style changes.
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:112
qgslayout.h
QgsLayoutItem::rotationChanged
void rotationChanged(double newRotation)
Emitted on item rotation change.
QgsLayoutItemGroup
Definition: qgslayoutitemgroup.h:28
qgslayoutsnapper.h
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
QgsLayoutView
Definition: qgslayoutview.h:49
qgslayoutmousehandles.h
qgslogger.h
qgsproject.h
QgsLayoutItem::isLocked
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
Definition: qgslayoutitem.h:397