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