QGIS API Documentation  3.23.0-Master (22c16f2067)
qgslayoutitemgroup.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemgroup.cpp
3  -----------------------
4  begin : October 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgslayoutitemgroup.h"
18 #include "qgslayoutitemregistry.h"
19 #include "qgslayout.h"
20 #include "qgslayoututils.h"
21 #include "qgslayoutundostack.h"
23 
25  : QgsLayoutItem( layout )
26 {}
27 
29 {
30  //loop through group members and remove them from the scene
31  for ( QgsLayoutItem *item : std::as_const( mItems ) )
32  {
33  if ( !item )
34  continue;
35 
36  //inform model that we are about to remove an item from the scene
37  if ( mLayout )
38  mLayout->removeLayoutItem( item );
39  else
40  {
41  item->cleanup();
42  item->deleteLater();
43  }
44  }
45  mItems.clear();
47 }
48 
50 {
52 }
53 
55 {
56  //return id, if it's not empty
57  if ( !id().isEmpty() )
58  {
59  return id();
60  }
61  return tr( "<Group>" );
62 }
63 
65 {
66  return new QgsLayoutItemGroup( layout );
67 }
68 
70 {
71  if ( !item )
72  {
73  return;
74  }
75 
76  if ( mItems.contains( item ) )
77  {
78  return;
79  }
80 
81  mItems << QPointer< QgsLayoutItem >( item );
82  item->setParentGroup( this );
83 
84  updateBoundingRect( item );
85 }
86 
88 {
89  for ( QgsLayoutItem *item : std::as_const( mItems ) )
90  {
91  if ( !item )
92  continue;
93 
94  item->setParentGroup( nullptr );
95  }
96  mItems.clear();
97 }
98 
99 QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
100 {
101  QList<QgsLayoutItem *> val;
102  for ( QgsLayoutItem *item : std::as_const( mItems ) )
103  {
104  if ( !item )
105  continue;
106  val << item;
107  }
108  return val;
109 }
110 
111 void QgsLayoutItemGroup::setVisibility( const bool visible )
112 {
113  if ( !shouldBlockUndoCommands() )
114  mLayout->undoStack()->beginMacro( tr( "Set Group Visibility" ) );
115  //also set visibility for all items within the group
116  for ( QgsLayoutItem *item : std::as_const( mItems ) )
117  {
118  if ( !item )
119  continue;
120  bool prev = item->mBlockUndoCommands;
121  item->mBlockUndoCommands = mBlockUndoCommands;
122  item->setVisibility( visible );
123  item->mBlockUndoCommands = prev;
124  }
125  //lastly set visibility for group item itself
126  QgsLayoutItem::setVisibility( visible );
127 
128  if ( !shouldBlockUndoCommands() )
129  mLayout->undoStack()->endMacro();
130 }
131 
132 void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point, bool useReferencePoint, bool includesFrame, int page )
133 {
134  Q_UNUSED( useReferencePoint ) //groups should always have reference point in top left
135  if ( !mLayout )
136  return;
137 
138  if ( !shouldBlockUndoCommands() )
139  mLayout->undoStack()->beginMacro( tr( "Move group" ) );
140 
141  QPointF scenePoint;
142  if ( page < 0 )
143  scenePoint = mLayout->convertToLayoutUnits( point );
144  else
145  scenePoint = mLayout->pageCollection()->pagePositionToLayoutPosition( page, point );
146 
147  double deltaX = scenePoint.x() - pos().x();
148  double deltaY = scenePoint.y() - pos().y();
149 
150  //also move all items within the group
151  for ( QgsLayoutItem *item : std::as_const( mItems ) )
152  {
153  if ( !item )
154  continue;
155 
156  std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
157  if ( !shouldBlockUndoCommands() )
158  {
159  command.reset( createCommand( QString(), 0 ) );
160  command->saveBeforeState();
161  }
162 
163  item->attemptMoveBy( deltaX, deltaY );
164 
165  if ( command )
166  {
167  command->saveAfterState();
168  mLayout->undoStack()->push( command.release() );
169  }
170  }
171  //lastly move group item itself
172  QgsLayoutItem::attemptMove( point, includesFrame );
173  if ( !shouldBlockUndoCommands() )
174  mLayout->undoStack()->endMacro();
175  resetBoundingRect();
176 }
177 
178 void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includesFrame )
179 {
180  if ( !mLayout )
181  return;
182 
183  if ( !shouldBlockUndoCommands() )
184  mLayout->undoStack()->beginMacro( tr( "Resize Group" ) );
185 
186  QRectF oldRect = rect();
187  QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
188  QRectF newRect;
189  newRect.setSize( newSizeLayoutUnits );
190 
191  //also resize all items within the group
192  for ( QgsLayoutItem *item : std::as_const( mItems ) )
193  {
194  if ( !item )
195  continue;
196 
197  std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
198  if ( !shouldBlockUndoCommands() )
199  {
200  command.reset( createCommand( QString(), 0 ) );
201  command->saveBeforeState();
202  }
203 
204  QRectF itemRect = mapRectFromItem( item, item->rect() );
205  QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
206 
207  itemRect = itemRect.normalized();
208  QPointF newPos = mapToScene( itemRect.topLeft() );
209 
210  QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
211  item->attemptResize( itemSize, includesFrame );
212 
213  // translate new position to current item units
214  QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, item->positionWithUnits().units() );
215  item->attemptMove( itemPos, false );
216 
217  if ( command )
218  {
219  command->saveAfterState();
220  mLayout->undoStack()->push( command.release() );
221  }
222  }
224  if ( !shouldBlockUndoCommands() )
225  mLayout->undoStack()->endMacro();
226 
227  resetBoundingRect();
228 }
229 
230 bool QgsLayoutItemGroup::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
231 {
232  for ( QgsLayoutItem *item : mItems )
233  {
234  if ( !item )
235  continue;
236 
237  QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
238  childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
239  element.appendChild( childItem );
240  }
241  return true;
242 }
243 
244 bool QgsLayoutItemGroup::readPropertiesFromElement( const QDomElement &itemElement, const QDomDocument &, const QgsReadWriteContext & )
245 {
246  mItemUuids.clear();
247 
248  QDomNodeList elementNodes = itemElement.elementsByTagName( QStringLiteral( "ComposerItemGroupElement" ) );
249  for ( int i = 0; i < elementNodes.count(); ++i )
250  {
251  QDomNode elementNode = elementNodes.at( i );
252  if ( !elementNode.isElement() )
253  continue;
254 
255  QString uuid = elementNode.toElement().attribute( QStringLiteral( "uuid" ) );
256  mItemUuids << uuid;
257  }
258  return true;
259 }
260 
262 {
263  for ( const QString &uuid : std::as_const( mItemUuids ) )
264  {
265  QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
266  if ( item )
267  {
268  addItem( item );
269  }
270  }
271 
272  resetBoundingRect();
273 }
274 
276 {
277  return MustPlaceInOwnLayer;
278 }
279 
280 void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
281 {
282 }
283 
285 {
286  // nothing to draw here!
287 }
288 
289 void QgsLayoutItemGroup::resetBoundingRect()
290 {
291  mBoundingRectangle = QRectF();
292  for ( QgsLayoutItem *item : std::as_const( mItems ) )
293  {
294  updateBoundingRect( item );
295  }
296 }
297 
298 void QgsLayoutItemGroup::updateBoundingRect( QgsLayoutItem *item )
299 {
300  //update extent
301  if ( mBoundingRectangle.isEmpty() ) //we add the first item
302  {
303  mBoundingRectangle = QRectF( 0, 0, item->rect().width(), item->rect().height() );
304  setSceneRect( QRectF( item->pos().x(), item->pos().y(), item->rect().width(), item->rect().height() ) );
305 
306  if ( !qgsDoubleNear( item->rotation(), 0.0 ) )
307  {
308  setItemRotation( item->rotation() );
309  }
310  }
311  else
312  {
313  if ( !qgsDoubleNear( item->rotation(), rotation() ) )
314  {
315  //items have mixed rotation, so reset rotation of group
316  mBoundingRectangle = mapRectToScene( mBoundingRectangle );
317  setItemRotation( 0 );
318  mBoundingRectangle = mBoundingRectangle.united( item->mapRectToScene( item->rect() ) );
319  setSceneRect( mBoundingRectangle );
320  }
321  else
322  {
323  //items have same rotation, so keep rotation of group
324  mBoundingRectangle = mBoundingRectangle.united( mapRectFromItem( item, item->rect() ) );
325  QPointF newPos = mapToScene( mBoundingRectangle.topLeft().x(), mBoundingRectangle.topLeft().y() );
326  mBoundingRectangle = QRectF( 0, 0, mBoundingRectangle.width(), mBoundingRectangle.height() );
327  setSceneRect( QRectF( newPos.x(), newPos.y(), mBoundingRectangle.width(), mBoundingRectangle.height() ) );
328  }
329  }
330 }
331 
332 void QgsLayoutItemGroup::setSceneRect( const QRectF &rectangle )
333 {
334  mItemPosition = mLayout->convertFromLayoutUnits( rectangle.topLeft(), positionWithUnits().units() );
335  mItemSize = mLayout->convertFromLayoutUnits( rectangle.size(), sizeWithUnits().units() );
336  setScenePos( rectangle.topLeft() );
337  setRect( 0, 0, rectangle.width(), rectangle.height() );
338 }
A container for grouping several QgsLayoutItems.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void removeItems()
Removes all items from the group (but does not delete them).
void addItem(QgsLayoutItem *item)
Adds an item to the group.
QList< QgsLayoutItem * > items() const
Returns a list of items contained by the group.
void setVisibility(bool visible) override
Sets whether the item is visible.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
bool writePropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
int type() const override
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
void cleanup() override
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
void attemptResize(const QgsLayoutSize &size, bool includesFrame=false) override
Attempts to resize the item to a specified target size.
static QgsLayoutItemGroup * create(QgsLayout *layout)
Returns a new group item for the specified layout.
void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1) override
Attempts to move the item to a specified point.
bool readPropertiesFromElement(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QString displayName() const override
Gets item display name.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
Base class for graphical items within a QgsLayout.
friend class QgsLayoutItemGroup
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item's rotation, in degrees clockwise.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
QgsLayoutPoint positionWithUnits() const
Returns the item's current position, including units.
int page() const
Returns the page the item is currently on, with the first page returning 0.
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
virtual QString uuid() const
Returns the item identification string.
virtual void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1)
Attempts to move the item to a specified point.
QString id() const
Returns the item's ID name.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
void setParentGroup(QgsLayoutItemGroup *group)
Sets the item's parent group.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
This class provides a method of storing points, consisting of an x and y coordinate,...
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:41
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
The class is used as a container of context for various read/write operations on other objects.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1491