QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
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
19#include "qgslayout.h"
22#include "qgslayoutundostack.h"
23#include "qgslayoututils.h"
24
25#include "moc_qgslayoutitemgroup.cpp"
26
30
35
37{
38 //loop through group members and remove them from the scene
39 for ( QgsLayoutItem *item : std::as_const( mItems ) )
40 {
41 if ( !item )
42 continue;
43
44 //inform model that we are about to remove an item from the scene
45 if ( mLayout )
46 mLayout->removeLayoutItem( item );
47 else
48 {
49 item->cleanup();
50 item->deleteLater();
51 }
52 }
53 mItems.clear();
55}
56
61
63{
64 //return id, if it's not empty
65 if ( !id().isEmpty() )
66 {
67 return id();
68 }
69 return tr( "<Group>" );
70}
71
76
78{
79 if ( !item )
80 {
81 return;
82 }
83
84 if ( mItems.contains( item ) )
85 {
86 return;
87 }
88
89 mItems << QPointer< QgsLayoutItem >( item );
90 item->setParentGroup( this );
91
92 updateBoundingRect();
93}
94
96{
97 for ( QgsLayoutItem *item : std::as_const( mItems ) )
98 {
99 if ( !item )
100 continue;
101
102 item->setParentGroup( nullptr );
103 }
104 mItems.clear();
105}
106
107QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
108{
109 QList<QgsLayoutItem *> val;
110 for ( QgsLayoutItem *item : std::as_const( mItems ) )
111 {
112 if ( !item )
113 continue;
114 val << item;
115 }
116 return val;
117}
118
119void QgsLayoutItemGroup::setVisibility( const bool visible )
120{
121 if ( !shouldBlockUndoCommands() )
122 mLayout->undoStack()->beginMacro( tr( "Set Group Visibility" ) );
123 //also set visibility for all items within the group
124 for ( QgsLayoutItem *item : std::as_const( mItems ) )
125 {
126 if ( !item )
127 continue;
128 bool prev = item->mBlockUndoCommands;
129 item->mBlockUndoCommands = mBlockUndoCommands;
130 item->setVisibility( visible );
131 item->mBlockUndoCommands = prev;
132 }
133 //lastly set visibility for group item itself
135
136 if ( !shouldBlockUndoCommands() )
137 mLayout->undoStack()->endMacro();
138}
139
140void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point, bool useReferencePoint, bool includesFrame, int page )
141{
142 Q_UNUSED( useReferencePoint ) //groups should always have reference point in top left
143 if ( !mLayout )
144 return;
145
146 if ( !shouldBlockUndoCommands() )
147 mLayout->undoStack()->beginMacro( tr( "Move group" ) );
148
149 QPointF scenePoint;
150 if ( page < 0 )
151 scenePoint = mLayout->convertToLayoutUnits( point );
152 else
153 scenePoint = mLayout->pageCollection()->pagePositionToLayoutPosition( page, point );
154
155 double deltaX = scenePoint.x() - pos().x();
156 double deltaY = scenePoint.y() - pos().y();
157
158 //also move all items within the group
159 for ( QgsLayoutItem *item : std::as_const( mItems ) )
160 {
161 if ( !item )
162 continue;
163
164 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
165 if ( !shouldBlockUndoCommands() )
166 {
167 command.reset( createCommand( QString(), 0 ) );
168 command->saveBeforeState();
169 }
170
171 item->attemptMoveBy( deltaX, deltaY );
172
173 if ( command )
174 {
175 command->saveAfterState();
176 mLayout->undoStack()->push( command.release() );
177 }
178 }
179 //lastly move group item itself
180 QgsLayoutItem::attemptMove( point, includesFrame );
181 if ( !shouldBlockUndoCommands() )
182 mLayout->undoStack()->endMacro();
183 updateBoundingRect();
184}
185
186void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includesFrame )
187{
188 if ( !mLayout )
189 return;
190
191 if ( !shouldBlockUndoCommands() )
192 mLayout->undoStack()->beginMacro( tr( "Resize Group" ) );
193
194 QRectF oldRect = rect();
195 QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
196 QRectF newRect;
197 newRect.setSize( newSizeLayoutUnits );
198
199 //also resize all items within the group
200 for ( QgsLayoutItem *item : std::as_const( mItems ) )
201 {
202 if ( !item )
203 continue;
204
205 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
206 if ( !shouldBlockUndoCommands() )
207 {
208 command.reset( createCommand( QString(), 0 ) );
209 command->saveBeforeState();
210 }
211
212 const QRectF originalItemRect = item->rect();
213 QRectF itemRect = mapRectFromItem( item, originalItemRect );
214 QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
215
216 itemRect = itemRect.normalized();
217 QPointF newPos = mapToScene( itemRect.topLeft() );
218
219 QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
220 item->attemptResize( itemSize, includesFrame );
221
222 // translate new position to current item units
223 QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, item->positionWithUnits().units() );
224 item->attemptMove( itemPos, false );
225
226 if ( command )
227 {
228 command->saveAfterState();
229 mLayout->undoStack()->push( command.release() );
230 }
231 }
233 if ( !shouldBlockUndoCommands() )
234 mLayout->undoStack()->endMacro();
235
236 updateBoundingRect();
237}
238
239bool QgsLayoutItemGroup::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
240{
241 for ( QgsLayoutItem *item : mItems )
242 {
243 if ( !item )
244 continue;
245
246 QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
247 childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
248 element.appendChild( childItem );
249 }
250 return true;
251}
252
253bool QgsLayoutItemGroup::readPropertiesFromElement( const QDomElement &itemElement, const QDomDocument &, const QgsReadWriteContext & )
254{
255 mItemUuids.clear();
256
257 QDomNodeList elementNodes = itemElement.elementsByTagName( QStringLiteral( "ComposerItemGroupElement" ) );
258 for ( int i = 0; i < elementNodes.count(); ++i )
259 {
260 QDomNode elementNode = elementNodes.at( i );
261 if ( !elementNode.isElement() )
262 continue;
263
264 QString uuid = elementNode.toElement().attribute( QStringLiteral( "uuid" ) );
265 mItemUuids << uuid;
266 }
267 return true;
268}
269
271{
272 for ( const QString &uuid : std::as_const( mItemUuids ) )
273 {
274 QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
275 if ( item )
276 {
277 addItem( item );
278 }
279 }
280
281 updateBoundingRect();
282}
283
288
289void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
290{
291}
292
294{
295 // nothing to draw here!
296}
297
298
299void QgsLayoutItemGroup::updateBoundingRect()
300{
301
302 if ( mItems.isEmpty() )
303 {
304 setRect( QRectF() );
305 return;
306 }
307
308 //check if all child items have same rotation
309 auto itemIter = mItems.constBegin();
310
311 //start with rotation of first child
312 double rotation = ( *itemIter )->rotation();
313
314 //iterate through remaining children, checking if they have same rotation
315 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
316 {
317 if ( !qgsDoubleNear( ( *itemIter )->rotation(), rotation ) )
318 {
319 //item has a different rotation
320 rotation = 0.0;
321 break;
322 }
323 }
324 setScenePos( QPointF( 0, 0 ) );
325 setItemRotation( rotation );
326
327 itemIter = mItems.constBegin();
328
329 // start with handle bounds of first child
330 QRectF groupRect = mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
331 QRectF groupRectWithFrame = mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
332
333 //iterate through remaining children, expanding the bounds as required
334 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
335 {
336 groupRect |= mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
337 groupRectWithFrame |= mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
338 }
339
340 mItemSize = mLayout->convertFromLayoutUnits( groupRect.size(), sizeWithUnits().units() );
341 mItemPosition = mLayout->convertFromLayoutUnits( mapToScene( groupRect.topLeft() ), positionWithUnits().units() );
342 setRect( 0, 0, groupRect.width(), groupRect.height() );
343 setPos( mapToScene( groupRect.topLeft() ) );
344
345 QPointF bleedShift = groupRectWithFrame.topLeft() - groupRect.topLeft();
346 mRectWithFrame = QRectF( bleedShift, groupRectWithFrame.size() );
347}
348
350{
351 return mRectWithFrame;
352}
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
QRectF rectWithFrame() const override
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
void removeItems()
Removes all items from the group (but does not delete them).
void addItem(QgsLayoutItem *item)
Adds an item to the group.
QgsLayoutItemGroup(QgsLayout *layout)
Constructor for QgsLayoutItemGroup, belonging to the specified layout.
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.
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.
friend class QgsLayout
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.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
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
Provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
A container for the context for various read/write operations on 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:6607