QGIS API Documentation  3.0.2-Girona (307d082)
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 : qgis::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 : qgis::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 : qgis::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 : qgis::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 : qgis::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 : qgis::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 : qgis::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 
275 void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
276 {
277 }
278 
280 {
281  // nothing to draw here!
282 }
283 
284 void QgsLayoutItemGroup::resetBoundingRect()
285 {
286  mBoundingRectangle = QRectF();
287  for ( QgsLayoutItem *item : qgis::as_const( mItems ) )
288  {
289  updateBoundingRect( item );
290  }
291 }
292 
293 void QgsLayoutItemGroup::updateBoundingRect( QgsLayoutItem *item )
294 {
295  //update extent
296  if ( mBoundingRectangle.isEmpty() ) //we add the first item
297  {
298  mBoundingRectangle = QRectF( 0, 0, item->rect().width(), item->rect().height() );
299  setSceneRect( QRectF( item->pos().x(), item->pos().y(), item->rect().width(), item->rect().height() ) );
300 
301  if ( !qgsDoubleNear( item->rotation(), 0.0 ) )
302  {
303  setItemRotation( item->rotation() );
304  }
305  }
306  else
307  {
308  if ( !qgsDoubleNear( item->rotation(), rotation() ) )
309  {
310  //items have mixed rotation, so reset rotation of group
311  mBoundingRectangle = mapRectToScene( mBoundingRectangle );
312  setItemRotation( 0 );
313  mBoundingRectangle = mBoundingRectangle.united( item->mapRectToScene( item->rect() ) );
314  setSceneRect( mBoundingRectangle );
315  }
316  else
317  {
318  //items have same rotation, so keep rotation of group
319  mBoundingRectangle = mBoundingRectangle.united( mapRectFromItem( item, item->rect() ) );
320  QPointF newPos = mapToScene( mBoundingRectangle.topLeft().x(), mBoundingRectangle.topLeft().y() );
321  mBoundingRectangle = QRectF( 0, 0, mBoundingRectangle.width(), mBoundingRectangle.height() );
322  setSceneRect( QRectF( newPos.x(), newPos.y(), mBoundingRectangle.width(), mBoundingRectangle.height() ) );
323  }
324  }
325 }
326 
327 void QgsLayoutItemGroup::setSceneRect( const QRectF &rectangle )
328 {
329  mItemPosition = mLayout->convertFromLayoutUnits( rectangle.topLeft(), positionWithUnits().units() );
330  mItemSize = mLayout->convertFromLayoutUnits( rectangle.size(), sizeWithUnits().units() );
331  setScenePos( rectangle.topLeft() );
332  setRect( 0, 0, rectangle.width(), rectangle.height() );
333 }
void setVisibility(const bool visible) override
Sets whether the item is visible.
The class is used as a container of context for various read/write operations on other objects...
QgsLayoutPoint positionWithUnits() const
Returns the item&#39;s current position, including units.
void removeItems()
Removes all items from the group (but does not delete them).
QgsLayoutItemGroup(QgsLayout *layout)
Constructor for QgsLayoutItemGroup, belonging to the specified layout.
Base class for graphical items within a QgsLayout.
bool readPropertiesFromElement(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
void cleanup() override
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
static QgsLayoutItemGroup * create(QgsLayout *layout)
Returns a new group item for the specified layout.
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
A container for grouping several QgsLayoutItems.
virtual void setVisibility(const bool visible)
Sets whether the item is visible.
void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1) override
Attempts to move the item to a specified point.
int type() const override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
void addItem(QgsLayoutItem *item)
Adds an item to the group.
This class provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the point.
bool writePropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QList< QgsLayoutItem * > items() const
Returns a list of items contained by the group.
QString displayName() const override
Get item display name.
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
QPointer< QgsLayout > mLayout
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item&#39;s rotation, in degrees clockwise.
int page() const
Returns the page the item is currently on, with the first page returning 0.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the size.
QString id() const
Returns the item&#39;s ID name.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
void setParentGroup(QgsLayoutItemGroup *group)
Sets the item&#39;s parent group.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:43
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
virtual void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1)
Attempts to move the item to a specified point.
virtual QString uuid() const
Returns the item identification string.
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
void attemptResize(const QgsLayoutSize &size, bool includesFrame=false) override
Attempts to resize the item to a specified target size.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.