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