Skip to content

Commit 02acbb4

Browse files
committedOct 9, 2017
Fix undo/redo for groups
1 parent e17b32c commit 02acbb4

12 files changed

+502
-173
lines changed
 

‎python/core/layout/qgslayoutitemgroup.sip

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ class QgsLayoutItemGroup: QgsLayoutItem
3030
virtual QString displayName() const;
3131

3232

33+
static QgsLayoutItemGroup *create( QgsLayout *layout, const QVariantMap &settings ) /Factory/;
34+
%Docstring
35+
Returns a new group item for the specified ``layout``.
36+
37+
The caller takes responsibility for deleting the returned object.
38+
:rtype: QgsLayoutItemGroup
39+
%End
40+
3341
void addItem( QgsLayoutItem *item /Transfer/ );
3442
%Docstring
3543
Adds an ``item`` to the group. Ownership of the item
@@ -56,6 +64,11 @@ class QgsLayoutItemGroup: QgsLayoutItem
5664
virtual void attemptResize( const QgsLayoutSize &size );
5765

5866

67+
virtual bool writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const;
68+
69+
virtual bool readXml( const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context );
70+
71+
5972
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );
6073

6174

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ SET(QGIS_CORE_SRCS
366366
layout/qgslayoutguidecollection.cpp
367367
layout/qgslayoutitem.cpp
368368
layout/qgslayoutitemgroup.cpp
369+
layout/qgslayoutitemgroupundocommand.cpp
369370
layout/qgslayoutitemmap.cpp
370371
layout/qgslayoutitempage.cpp
371372
layout/qgslayoutitemregistry.cpp
@@ -716,6 +717,7 @@ SET(QGIS_CORE_MOC_HDRS
716717
layout/qgslayoutguidecollection.h
717718
layout/qgslayoutitem.h
718719
layout/qgslayoutitemgroup.h
720+
layout/qgslayoutitemgroupundocommand.h
719721
layout/qgslayoutitemmap.h
720722
layout/qgslayoutitempage.h
721723
layout/qgslayoutitemregistry.h

‎src/core/layout/qgslayout.cpp

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "qgsproject.h"
2424
#include "qgslayoutitemundocommand.h"
2525
#include "qgslayoutitemgroup.h"
26+
#include "qgslayoutitemgroupundocommand.h"
2627

2728
QgsLayout::QgsLayout( QgsProject *project )
2829
: mProject( project )
@@ -382,10 +383,16 @@ void QgsLayout::removeLayoutItem( QgsLayoutItem *item )
382383
{
383384
std::unique_ptr< QgsLayoutItemDeleteUndoCommand > deleteCommand;
384385
if ( !mBlockUndoCommands )
386+
{
387+
mUndoStack->beginMacro( tr( "Deleted item" ) );
385388
deleteCommand.reset( new QgsLayoutItemDeleteUndoCommand( item, tr( "Deleted item" ) ) );
389+
}
386390
removeLayoutItemPrivate( item );
387391
if ( deleteCommand )
392+
{
388393
mUndoStack->stack()->push( deleteCommand.release() );
394+
mUndoStack->endMacro();
395+
}
389396
}
390397

391398
QgsLayoutUndoStack *QgsLayout::undoStack()
@@ -456,20 +463,17 @@ QgsLayoutItemGroup *QgsLayout::groupItems( const QList<QgsLayoutItem *> &items )
456463
}
457464
QgsLayoutItemGroup *returnGroup = itemGroup.get();
458465
addLayoutItem( itemGroup.release() );
459-
mUndoStack->endMacro();
460-
461-
#if 0
462-
QgsGroupUngroupItemsCommand *c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Grouped, itemGroup, this, tr( "Items grouped" ) );
463-
connect( c, &QgsGroupUngroupItemsCommand::itemRemoved, this, &QgsComposition::itemRemoved );
464-
connect( c, &QgsGroupUngroupItemsCommand::itemAdded, this, &QgsComposition::sendItemAddedSignal );
465466

466-
undoStack()->push( c );
467+
std::unique_ptr< QgsLayoutItemGroupUndoCommand > c( new QgsLayoutItemGroupUndoCommand( QgsLayoutItemGroupUndoCommand::Grouped, returnGroup, this, tr( "Items grouped" ) ) );
468+
mUndoStack->stack()->push( c.release() );
467469
mProject->setDirty( true );
468-
//QgsDebugMsg( QString( "itemgroup after pushAddRemove has %1" ) .arg( itemGroup->items().size() ) );
469470

471+
#if 0
470472
emit composerItemGroupAdded( itemGroup );
471473
#endif
472474

475+
mUndoStack->endMacro();
476+
473477
return returnGroup;
474478
}
475479

@@ -481,25 +485,21 @@ QList<QgsLayoutItem *> QgsLayout::ungroupItems( QgsLayoutItemGroup *group )
481485
return ungroupedItems;
482486
}
483487

484-
#if 0 //TODO
485-
// group ownership transferred to QgsGroupUngroupItemsCommand
488+
mUndoStack->beginMacro( tr( "Ungrouped items" ) );
486489
// Call this before removing group items so it can keep note
487490
// of contents
488-
QgsGroupUngroupItemsCommand *c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Ungrouped, group, this, tr( "Items ungrouped" ) );
489-
connect( c, &QgsGroupUngroupItemsCommand::itemRemoved, this, &QgsComposition::itemRemoved );
490-
connect( c, &QgsGroupUngroupItemsCommand::itemAdded, this, &QgsComposition::sendItemAddedSignal );
491+
std::unique_ptr< QgsLayoutItemGroupUndoCommand > c( new QgsLayoutItemGroupUndoCommand( QgsLayoutItemGroupUndoCommand::Ungrouped, group, this, tr( "Items ungrouped" ) ) );
492+
mUndoStack->stack()->push( c.release() );
491493

492-
undoStack()->push( c );
493494
mProject->setDirty( true );
494495

495-
#endif
496-
497496
ungroupedItems = group->items();
498497
group->removeItems();
499498

500499
removeLayoutItem( group );
500+
mUndoStack->endMacro();
501+
501502
#if 0 //TODO
502-
// note: emits itemRemoved
503503
removeComposerItem( group, false, false );
504504
#endif
505505

@@ -554,7 +554,7 @@ void QgsLayout::removeLayoutItemPrivate( QgsLayoutItem *item )
554554
#if 0 //TODO
555555
emit itemRemoved( item );
556556
#endif
557-
item->deleteLater();
557+
delete item;
558558
}
559559

560560
void QgsLayout::updateZValues( const bool addUndoCommands )

‎src/core/layout/qgslayout.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
523523
friend class QgsLayoutItemDeleteUndoCommand;
524524
friend class QgsLayoutItemUndoCommand;
525525
friend class QgsLayoutUndoCommand;
526+
friend class QgsLayoutItemGroupUndoCommand;
526527
friend class QgsLayoutModel;
527528
};
528529

‎src/core/layout/qgslayoutitem.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,20 @@ void QgsLayoutItem::setVisibility( const bool visible )
139139
return;
140140
}
141141

142+
std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
142143
if ( !shouldBlockUndoCommands() )
143-
mLayout->undoStack()->beginCommand( this, visible ? tr( "Item shown" ) : tr( "Item hidden" ) );
144+
{
145+
command.reset( createCommand( visible ? tr( "Item shown" ) : tr( "Item hidden" ), 0 ) );
146+
command->saveBeforeState();
147+
}
144148

145149
QGraphicsItem::setVisible( visible );
146150

147-
if ( !shouldBlockUndoCommands() )
148-
mLayout->undoStack()->endCommand();
151+
if ( command )
152+
{
153+
command->saveAfterState();
154+
mLayout->undoStack()->stack()->push( command.release() );
155+
}
149156

150157
//inform model that visibility has changed
151158
if ( mLayout )
@@ -729,6 +736,7 @@ bool QgsLayoutItem::writePropertiesToElement( QDomElement &element, QDomDocument
729736
element.setAttribute( QStringLiteral( "position" ), mItemPosition.encodePoint() );
730737
element.setAttribute( QStringLiteral( "size" ), mItemSize.encodeSize() );
731738
element.setAttribute( QStringLiteral( "rotation" ), QString::number( rotation() ) );
739+
element.setAttribute( QStringLiteral( "groupUuid" ), mParentGroupUuid );
732740

733741
element.setAttribute( "zValue", QString::number( zValue() ) );
734742
element.setAttribute( "visibility", isVisible() );
@@ -807,6 +815,15 @@ bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &element, const
807815
attemptResize( QgsLayoutSize::decodeSize( element.attribute( QStringLiteral( "size" ) ) ) );
808816
setItemRotation( element.attribute( QStringLiteral( "rotation" ), QStringLiteral( "0" ) ).toDouble() );
809817

818+
mParentGroupUuid = element.attribute( QStringLiteral( "groupUuid" ) );
819+
if ( !mParentGroupUuid.isEmpty() )
820+
{
821+
if ( QgsLayoutItemGroup *group = parentGroup() )
822+
{
823+
group->addItem( this );
824+
}
825+
}
826+
810827
//TODO
811828
/*
812829
// temporary for groups imported from templates

‎src/core/layout/qgslayoutitemgroup.cpp

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ QgsLayoutItemGroup::QgsLayoutItemGroup( QgsLayout *layout )
2525

2626
QgsLayoutItemGroup::~QgsLayoutItemGroup()
2727
{
28-
if ( mLayout )
29-
mLayout->undoStack()->beginMacro( tr( "Removed group" ) );
3028
//loop through group members and remove them from the scene
3129
for ( QgsLayoutItem *item : qgsAsConst( mItems ) )
3230
{
@@ -37,10 +35,8 @@ QgsLayoutItemGroup::~QgsLayoutItemGroup()
3735
if ( mLayout )
3836
mLayout->removeLayoutItem( item );
3937
else
40-
item->deleteLater();
38+
delete item;
4139
}
42-
if ( mLayout )
43-
mLayout->undoStack()->endMacro();
4440
}
4541

4642
int QgsLayoutItemGroup::type() const
@@ -63,6 +59,11 @@ QString QgsLayoutItemGroup::displayName() const
6359
return tr( "<Group>" );
6460
}
6561

62+
QgsLayoutItemGroup *QgsLayoutItemGroup::create( QgsLayout *layout, const QVariantMap & )
63+
{
64+
return new QgsLayoutItemGroup( layout );
65+
}
66+
6667
void QgsLayoutItemGroup::addItem( QgsLayoutItem *item )
6768
{
6869
if ( !item )
@@ -107,7 +108,7 @@ QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
107108

108109
void QgsLayoutItemGroup::setVisibility( const bool visible )
109110
{
110-
if ( mLayout )
111+
if ( !shouldBlockUndoCommands() )
111112
mLayout->undoStack()->beginMacro( tr( "Set group visibility" ) );
112113
//also set visibility for all items within the group
113114
for ( QgsLayoutItem *item : qgsAsConst( mItems ) )
@@ -118,7 +119,7 @@ void QgsLayoutItemGroup::setVisibility( const bool visible )
118119
}
119120
//lastly set visibility for group item itself
120121
QgsLayoutItem::setVisibility( visible );
121-
if ( mLayout )
122+
if ( !shouldBlockUndoCommands() )
122123
mLayout->undoStack()->endMacro();
123124
}
124125

@@ -127,7 +128,8 @@ void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point )
127128
if ( !mLayout )
128129
return;
129130

130-
mLayout->undoStack()->beginMacro( tr( "Moved group" ) );
131+
if ( !shouldBlockUndoCommands() )
132+
mLayout->undoStack()->beginMacro( tr( "Moved group" ) );
131133

132134
QPointF scenePoint = mLayout->convertToLayoutUnits( point );
133135
double deltaX = scenePoint.x() - pos().x();
@@ -139,7 +141,12 @@ void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point )
139141
if ( !item )
140142
continue;
141143

142-
mLayout->undoStack()->beginCommand( item, QString() );
144+
std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
145+
if ( !shouldBlockUndoCommands() )
146+
{
147+
command.reset( createCommand( QString(), 0 ) );
148+
command->saveBeforeState();
149+
}
143150

144151
// need to convert delta from layout units -> item units
145152
QgsLayoutPoint itemPos = item->positionWithUnits();
@@ -148,11 +155,16 @@ void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point )
148155
itemPos.setY( itemPos.y() + deltaPos.y() );
149156
item->attemptMove( itemPos );
150157

151-
mLayout->undoStack()->endCommand();
158+
if ( command )
159+
{
160+
command->saveAfterState();
161+
mLayout->undoStack()->stack()->push( command.release() );
162+
}
152163
}
153164
//lastly move group item itself
154165
QgsLayoutItem::attemptMove( point );
155-
mLayout->undoStack()->endMacro();
166+
if ( !shouldBlockUndoCommands() )
167+
mLayout->undoStack()->endMacro();
156168
resetBoundingRect();
157169
}
158170

@@ -161,7 +173,8 @@ void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size )
161173
if ( !mLayout )
162174
return;
163175

164-
mLayout->undoStack()->beginMacro( tr( "Resized group" ) );
176+
if ( !shouldBlockUndoCommands() )
177+
mLayout->undoStack()->beginMacro( tr( "Resized group" ) );
165178

166179
QRectF oldRect = rect();
167180
QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
@@ -174,6 +187,13 @@ void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size )
174187
if ( !item )
175188
continue;
176189

190+
std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
191+
if ( !shouldBlockUndoCommands() )
192+
{
193+
command.reset( createCommand( QString(), 0 ) );
194+
command->saveBeforeState();
195+
}
196+
177197
QRectF itemRect = mapRectFromItem( item, item->rect() );
178198
QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
179199

@@ -186,11 +206,76 @@ void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size )
186206

187207
QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
188208
item->attemptResize( itemSize );
209+
210+
if ( command )
211+
{
212+
command->saveAfterState();
213+
mLayout->undoStack()->stack()->push( command.release() );
214+
}
189215
}
190216
QgsLayoutItem::attemptResize( size );
191-
mLayout->undoStack()->endMacro();
217+
if ( !shouldBlockUndoCommands() )
218+
mLayout->undoStack()->endMacro();
219+
220+
resetBoundingRect();
221+
}
222+
223+
bool QgsLayoutItemGroup::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const
224+
{
225+
QDomElement element = document.createElement( QStringLiteral( "LayoutItem" ) );
226+
element.setAttribute( QStringLiteral( "type" ), stringType() );
227+
228+
writePropertiesToElement( element, document, context );
229+
230+
for ( QgsLayoutItem *item : mItems )
231+
{
232+
if ( !item )
233+
continue;
234+
235+
QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
236+
childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
237+
element.appendChild( childItem );
238+
}
239+
240+
parentElement.appendChild( element );
241+
242+
return true;
243+
}
244+
245+
bool QgsLayoutItemGroup::readXml( const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context )
246+
{
247+
if ( itemElement.nodeName() != QStringLiteral( "LayoutItem" ) || itemElement.attribute( QStringLiteral( "type" ) ) != stringType() )
248+
{
249+
return false;
250+
}
251+
252+
bool result = readPropertiesFromElement( itemElement, document, context );
253+
254+
QList<QgsLayoutItem *> items;
255+
mLayout->layoutItems( items );
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+
266+
for ( QgsLayoutItem *item : qgsAsConst( items ) )
267+
{
268+
if ( item && ( item->mUuid == uuid /* TODO || item->mTemplateUuid == uuid */ ) )
269+
{
270+
addItem( item );
271+
break;
272+
}
273+
}
274+
}
192275

193276
resetBoundingRect();
277+
278+
return result;
194279
}
195280

196281
void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )

0 commit comments

Comments
 (0)
Please sign in to comment.