mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Allow to undo/redo composer grouping/ungrouping
Fixes #11371 (crash on ungrouping after moving the group) and more undo/redo related issues. Enable pending test for the crash (now passing) and add many more undo/redo related ones (including signals testing). Includes a new QgsGroupUngroupItemsCommand class and its SIP bindings.
This commit is contained in:
parent
efdbb87406
commit
a211c982cf
@ -874,6 +874,8 @@ class QgsComposition : QGraphicsScene
|
||||
void composerPolylineAdded( QgsComposerPolyline* polyline );
|
||||
/** Is emitted when a new composer html has been added to the view*/
|
||||
void composerHtmlFrameAdded( QgsComposerHtml* html, QgsComposerFrame* frame );
|
||||
/** Is emitted when a new item group has been added to the view*/
|
||||
void composerItemGroupAdded( QgsComposerItemGroup* group );
|
||||
/** Is emitted when new composer label has been added to the view*/
|
||||
void composerLabelAdded( QgsComposerLabel* label );
|
||||
/** Is emitted when new composer map has been added to the view*/
|
||||
|
43
python/core/composer/qgsgroupungroupitemscommand.sip
Normal file
43
python/core/composer/qgsgroupungroupitemscommand.sip
Normal file
@ -0,0 +1,43 @@
|
||||
/** A composer command class for grouping / ungrouping composer items.
|
||||
*
|
||||
* If mState == Ungrouped, the command owns the group item
|
||||
*/
|
||||
class QgsGroupUngroupItemsCommand: QObject, QUndoCommand
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include "qgsgroupungroupitemscommand.h"
|
||||
%End
|
||||
|
||||
public:
|
||||
|
||||
/** Command kind, and state */
|
||||
enum State
|
||||
{
|
||||
Grouped = 0,
|
||||
Ungrouped
|
||||
};
|
||||
|
||||
/** Create a group or ungroup command
|
||||
*
|
||||
* @param s command kind (@see State)
|
||||
* @param item the group item being created or ungrouped
|
||||
* @param c the composition including this group
|
||||
* @param text command label
|
||||
* @param parent parent command, if any
|
||||
*
|
||||
*/
|
||||
QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent = nullptr );
|
||||
~QgsGroupUngroupItemsCommand();
|
||||
|
||||
void redo();
|
||||
void undo();
|
||||
|
||||
signals:
|
||||
/** Signals addition of an item (the group) */
|
||||
void itemAdded( QgsComposerItem* item );
|
||||
/** Signals removal of an item (the group) */
|
||||
void itemRemoved( QgsComposerItem* item );
|
||||
|
||||
};
|
||||
|
||||
|
@ -168,6 +168,7 @@
|
||||
%Include auth/qgsauthmethod.sip
|
||||
|
||||
%Include composer/qgsaddremoveitemcommand.sip
|
||||
%Include composer/qgsgroupungroupitemscommand.sip
|
||||
%Include composer/qgsaddremovemultiframecommand.sip
|
||||
%Include composer/qgsatlascomposition.sip
|
||||
%Include composer/qgscomposerarrow.sip
|
||||
|
@ -268,6 +268,7 @@ SET(QGIS_CORE_SRCS
|
||||
composer/qgscomposerutils.cpp
|
||||
composer/qgscomposition.cpp
|
||||
composer/qgsdoubleboxscalebarstyle.cpp
|
||||
composer/qgsgroupungroupitemscommand.cpp
|
||||
composer/qgslegendmodel.cpp
|
||||
composer/qgsnumericscalebarstyle.cpp
|
||||
composer/qgspaperitem.cpp
|
||||
@ -535,6 +536,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
composer/qgscomposertablev2.h
|
||||
composer/qgscomposertexttable.h
|
||||
composer/qgscomposition.h
|
||||
composer/qgsgroupungroupitemscommand.h
|
||||
composer/qgslegendmodel.h
|
||||
composer/qgspaperitem.h
|
||||
|
||||
|
@ -36,6 +36,7 @@ QgsAddRemoveItemCommand::~QgsAddRemoveItemCommand()
|
||||
|
||||
void QgsAddRemoveItemCommand::redo()
|
||||
{
|
||||
QUndoCommand::redo(); // call redo() on all childs
|
||||
if ( mFirstRun )
|
||||
{
|
||||
mFirstRun = false;
|
||||
@ -46,6 +47,7 @@ void QgsAddRemoveItemCommand::redo()
|
||||
|
||||
void QgsAddRemoveItemCommand::undo()
|
||||
{
|
||||
QUndoCommand::undo(); // call undo() on all childs, in reverse order
|
||||
if ( mFirstRun )
|
||||
{
|
||||
mFirstRun = false;
|
||||
@ -58,6 +60,7 @@ void QgsAddRemoveItemCommand::switchState()
|
||||
{
|
||||
if ( mState == Added )
|
||||
{
|
||||
// Remove
|
||||
if ( mComposition )
|
||||
{
|
||||
mComposition->itemsModel()->setItemRemoved( mItem );
|
||||
@ -68,6 +71,7 @@ void QgsAddRemoveItemCommand::switchState()
|
||||
}
|
||||
else //Removed
|
||||
{
|
||||
// Add
|
||||
if ( mComposition )
|
||||
{
|
||||
mComposition->itemsModel()->setItemRestored( mItem );
|
||||
|
@ -36,7 +36,7 @@ QgsComposerItemGroup::~QgsComposerItemGroup()
|
||||
//loop through group members and remove them from the scene
|
||||
Q_FOREACH ( QgsComposerItem* item, mItems )
|
||||
{
|
||||
if ( !item )
|
||||
if ( !item || item->isRemoved() )
|
||||
continue;
|
||||
|
||||
//inform model that we are about to remove an item from the scene
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "qgscomposerattributetablev2.h"
|
||||
#include "qgsaddremovemultiframecommand.h"
|
||||
#include "qgscomposermultiframecommand.h"
|
||||
#include "qgsgroupungroupitemscommand.h"
|
||||
#include "qgspaintenginehack.h"
|
||||
#include "qgspaperitem.h"
|
||||
#include "qgsproject.h"
|
||||
@ -1589,6 +1590,10 @@ void QgsComposition::addItemsFromXML( const QDomElement& elem, const QDomDocumen
|
||||
QgsComposerItemGroup *newGroup = new QgsComposerItemGroup( this );
|
||||
newGroup->readXML( groupElem, doc );
|
||||
addItem( newGroup );
|
||||
if ( addUndoCommands )
|
||||
{
|
||||
pushAddRemoveCommand( newGroup, tr( "Group added" ) );
|
||||
}
|
||||
}
|
||||
|
||||
//Since this function adds items grouped by type, and each item is added to end of
|
||||
@ -2014,14 +2019,28 @@ QgsComposerItemGroup *QgsComposition::groupItems( QList<QgsComposerItem *> items
|
||||
}
|
||||
|
||||
QgsComposerItemGroup* itemGroup = new QgsComposerItemGroup( this );
|
||||
QgsDebugMsg( QString( "itemgroup created with %1 items (%2 to be added)" ) .arg( itemGroup->items().size() ).arg( items.size() ) );
|
||||
|
||||
QList<QgsComposerItem*>::iterator itemIter = items.begin();
|
||||
for ( ; itemIter != items.end(); ++itemIter )
|
||||
{
|
||||
itemGroup->addItem( *itemIter );
|
||||
QgsDebugMsg( QString( "itemgroup now has %1" )
|
||||
.arg( itemGroup->items().size() ) );
|
||||
}
|
||||
|
||||
addItem( itemGroup );
|
||||
|
||||
QgsGroupUngroupItemsCommand* c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Grouped, itemGroup, this, tr( "Items grouped" ) );
|
||||
QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
|
||||
QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
|
||||
|
||||
undoStack()->push( c );
|
||||
QgsProject::instance()->setDirty( true );
|
||||
//QgsDebugMsg( QString( "itemgroup after pushAddRemove has %1" ) .arg( itemGroup->items().size() ) );
|
||||
|
||||
emit composerItemGroupAdded( itemGroup );
|
||||
|
||||
return itemGroup;
|
||||
}
|
||||
|
||||
@ -2033,6 +2052,17 @@ QList<QgsComposerItem *> QgsComposition::ungroupItems( QgsComposerItemGroup* gro
|
||||
return ungroupedItems;
|
||||
}
|
||||
|
||||
// group ownership transferred to QgsGroupUngroupItemsCommand
|
||||
// Call this before removing group items so it can keep note
|
||||
// of contents
|
||||
QgsGroupUngroupItemsCommand* c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Ungrouped, group, this, tr( "Items ungrouped" ) );
|
||||
QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
|
||||
QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
|
||||
|
||||
undoStack()->push( c );
|
||||
QgsProject::instance()->setDirty( true );
|
||||
|
||||
|
||||
QSet<QgsComposerItem*> groupedItems = group->items();
|
||||
QSet<QgsComposerItem*>::iterator itemIt = groupedItems.begin();
|
||||
for ( ; itemIt != groupedItems.end(); ++itemIt )
|
||||
@ -2041,10 +2071,9 @@ QList<QgsComposerItem *> QgsComposition::ungroupItems( QgsComposerItemGroup* gro
|
||||
}
|
||||
|
||||
group->removeItems();
|
||||
removeComposerItem( group, false, false );
|
||||
|
||||
emit itemRemoved( group );
|
||||
delete( group );
|
||||
// note: emits itemRemoved
|
||||
removeComposerItem( group, false, false );
|
||||
|
||||
return ungroupedItems;
|
||||
}
|
||||
@ -2636,6 +2665,7 @@ void QgsComposition::addComposerTableFrame( QgsComposerAttributeTableV2 *table,
|
||||
emit composerTableFrameAdded( table, frame );
|
||||
}
|
||||
|
||||
/* public */
|
||||
void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool createCommand, const bool removeGroupItems )
|
||||
{
|
||||
QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
|
||||
@ -2644,25 +2674,36 @@ void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool creat
|
||||
{
|
||||
mItemsModel->setItemRemoved( item );
|
||||
removeItem( item );
|
||||
emit itemRemoved( item );
|
||||
|
||||
QgsDebugMsg( QString( "removeComposerItem called, createCommand:%1 removeGroupItems:%2" )
|
||||
.arg( createCommand ).arg( removeGroupItems ) );
|
||||
|
||||
QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
|
||||
if ( itemGroup && removeGroupItems )
|
||||
{
|
||||
//add add/remove item command for every item in the group
|
||||
QUndoCommand* parentCommand = new QUndoCommand( tr( "Remove item group" ) );
|
||||
QgsDebugMsg( QString( "itemGroup && removeGroupItems" ) );
|
||||
|
||||
// Takes ownership of itemGroup
|
||||
QgsAddRemoveItemCommand* parentCommand = new QgsAddRemoveItemCommand(
|
||||
QgsAddRemoveItemCommand::Removed, itemGroup, this,
|
||||
tr( "Remove item group" ) );
|
||||
connectAddRemoveCommandSignals( parentCommand );
|
||||
|
||||
//add add/remove item command for every item in the group
|
||||
QSet<QgsComposerItem*> groupedItems = itemGroup->items();
|
||||
QgsDebugMsg( QString( "itemGroup contains %1 items" ) .arg( groupedItems.size() ) );
|
||||
QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
|
||||
for ( ; it != groupedItems.end(); ++it )
|
||||
{
|
||||
mItemsModel->setItemRemoved( *it );
|
||||
removeItem( *it );
|
||||
QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
|
||||
connectAddRemoveCommandSignals( subcommand );
|
||||
emit itemRemoved( *it );
|
||||
}
|
||||
|
||||
undoStack()->push( parentCommand );
|
||||
emit itemRemoved( itemGroup );
|
||||
delete itemGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2674,19 +2715,13 @@ void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool creat
|
||||
{
|
||||
multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
|
||||
item->beginItemCommand( tr( "Frame deleted" ) );
|
||||
emit itemRemoved( item );
|
||||
item->endItemCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit itemRemoved( item );
|
||||
pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emit itemRemoved( item );
|
||||
}
|
||||
|
||||
//check if there are frames left. If not, remove the multi frame
|
||||
if ( frameItem && multiFrame )
|
||||
@ -2823,6 +2858,11 @@ void QgsComposition::sendItemAddedSignal( QgsComposerItem* item )
|
||||
emit selectedItemChanged( frame );
|
||||
return;
|
||||
}
|
||||
QgsComposerItemGroup* group = dynamic_cast<QgsComposerItemGroup*>( item );
|
||||
if ( group )
|
||||
{
|
||||
emit composerItemGroupAdded( group );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsComposition::updatePaperItems()
|
||||
|
@ -1119,6 +1119,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
||||
void composerPolylineAdded( QgsComposerPolyline* polyline );
|
||||
/** Is emitted when a new composer html has been added to the view*/
|
||||
void composerHtmlFrameAdded( QgsComposerHtml* html, QgsComposerFrame* frame );
|
||||
/** Is emitted when a new item group has been added to the view*/
|
||||
void composerItemGroupAdded( QgsComposerItemGroup* group );
|
||||
/** Is emitted when new composer label has been added to the view*/
|
||||
void composerLabelAdded( QgsComposerLabel* label );
|
||||
/** Is emitted when new composer map has been added to the view*/
|
||||
|
96
src/core/composer/qgsgroupungroupitemscommand.cpp
Normal file
96
src/core/composer/qgsgroupungroupitemscommand.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/***************************************************************************
|
||||
qgsgroupungroupitemscommand.cpp
|
||||
---------------------------
|
||||
begin : 2016-06-09
|
||||
copyright : (C) 2016 by Sandro Santilli
|
||||
email : strk at kbt dot io
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsgroupungroupitemscommand.h"
|
||||
#include "qgscomposeritem.h"
|
||||
#include "qgscomposeritemgroup.h"
|
||||
#include "qgscomposition.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgscomposermodel.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
QgsGroupUngroupItemsCommand::QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent ):
|
||||
QUndoCommand( text, parent ), mGroup( item ), mComposition( c ), mState( s ), mFirstRun( true )
|
||||
{
|
||||
mItems = mGroup->items();
|
||||
}
|
||||
|
||||
QgsGroupUngroupItemsCommand::~QgsGroupUngroupItemsCommand()
|
||||
{
|
||||
if ( mState == Ungrouped )
|
||||
{
|
||||
//command class stores the item if ungrouped from the composition
|
||||
delete mGroup;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsGroupUngroupItemsCommand::redo()
|
||||
{
|
||||
if ( mFirstRun )
|
||||
{
|
||||
mFirstRun = false;
|
||||
return;
|
||||
}
|
||||
switchState();
|
||||
}
|
||||
|
||||
void QgsGroupUngroupItemsCommand::undo()
|
||||
{
|
||||
if ( mFirstRun )
|
||||
{
|
||||
mFirstRun = false;
|
||||
return;
|
||||
}
|
||||
switchState();
|
||||
}
|
||||
|
||||
void QgsGroupUngroupItemsCommand::switchState()
|
||||
{
|
||||
if ( mState == Grouped )
|
||||
{
|
||||
// ungroup
|
||||
if ( mComposition )
|
||||
{
|
||||
// This is probably redundant
|
||||
mComposition->itemsModel()->setItemRemoved( mGroup );
|
||||
mComposition->removeItem( mGroup );
|
||||
}
|
||||
mGroup->removeItems();
|
||||
emit itemRemoved( mGroup );
|
||||
mState = Ungrouped;
|
||||
}
|
||||
else //Ungrouped
|
||||
{
|
||||
// group
|
||||
if ( mComposition )
|
||||
{
|
||||
//delete mGroup; mGroup = new QgsComposerItemGroup( mCompoiser );
|
||||
QSet<QgsComposerItem*>::iterator itemIter = mItems.begin();
|
||||
for ( ; itemIter != mItems.end(); ++itemIter )
|
||||
{
|
||||
mGroup->addItem( *itemIter );
|
||||
QgsDebugMsg( QString( "itemgroup now has %1" ) .arg( mGroup->items().size() ) );
|
||||
}
|
||||
// Add the group
|
||||
mComposition->itemsModel()->setItemRestored( mGroup );
|
||||
mComposition->addItem( mGroup );
|
||||
}
|
||||
mState = Grouped;
|
||||
emit itemAdded( mGroup );
|
||||
}
|
||||
QgsProject::instance()->setDirty( true );
|
||||
}
|
75
src/core/composer/qgsgroupungroupitemscommand.h
Normal file
75
src/core/composer/qgsgroupungroupitemscommand.h
Normal file
@ -0,0 +1,75 @@
|
||||
/***************************************************************************
|
||||
qgsgroupungroupitemscommand.h
|
||||
------------------------
|
||||
begin : 2016-06-09
|
||||
copyright : (C) 2016 by Sandro Santilli
|
||||
email : strk at kbt dot io
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSGROUPUNGROUPITEMSCOMMAND_H
|
||||
#define QGSGROUPUNGROUPITEMSCOMMAND_H
|
||||
|
||||
#include <QUndoCommand>
|
||||
#include "qgscomposeritemgroup.h"
|
||||
class QgsComposerItem;
|
||||
class QgsComposition;
|
||||
|
||||
/** A composer command class for grouping / ungrouping composer items.
|
||||
*
|
||||
* If mState == Ungrouped, the command owns the group item
|
||||
*/
|
||||
class CORE_EXPORT QgsGroupUngroupItemsCommand: public QObject, public QUndoCommand
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Command kind, and state */
|
||||
enum State
|
||||
{
|
||||
Grouped = 0,
|
||||
Ungrouped
|
||||
};
|
||||
|
||||
/** Create a group or ungroup command
|
||||
*
|
||||
* @param s command kind (@see State)
|
||||
* @param item the group item being created or ungrouped
|
||||
* @param c the composition including this group
|
||||
* @param text command label
|
||||
* @param parent parent command, if any
|
||||
*
|
||||
*/
|
||||
QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent = nullptr );
|
||||
~QgsGroupUngroupItemsCommand();
|
||||
|
||||
void redo() override;
|
||||
void undo() override;
|
||||
|
||||
signals:
|
||||
/** Signals addition of an item (the group) */
|
||||
void itemAdded( QgsComposerItem* item );
|
||||
/** Signals removal of an item (the group) */
|
||||
void itemRemoved( QgsComposerItem* item );
|
||||
|
||||
private:
|
||||
QgsComposerItemGroup* mGroup;
|
||||
QSet<QgsComposerItem*> mItems;
|
||||
QgsComposition* mComposition;
|
||||
State mState;
|
||||
bool mFirstRun; //flag to prevent execution when the command is pushed to the QUndoStack
|
||||
|
||||
//changes between added / removed state
|
||||
void switchState();
|
||||
};
|
||||
|
||||
#endif // QGSGROUPUNGROUPITEMSCOMMAND_H
|
@ -17,11 +17,14 @@
|
||||
|
||||
#include "qgscomposeritemgroup.h"
|
||||
#include "qgscomposerlabel.h"
|
||||
#include "qgscomposerpolygon.h"
|
||||
#include "qgscomposition.h"
|
||||
#include "qgsmultirenderchecker.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtTest/QSignalSpy>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class TestQgsComposerGroup : public QObject
|
||||
@ -48,6 +51,9 @@ class TestQgsComposerGroup : public QObject
|
||||
void undoRedo(); //test that group/ungroup undo/redo commands don't crash
|
||||
|
||||
private:
|
||||
|
||||
void dumpUndoStack( const QUndoStack&, QString prefix = "" ) const;
|
||||
|
||||
QgsComposition *mComposition;
|
||||
QgsMapSettings *mMapSettings;
|
||||
QgsComposerLabel* mItem1;
|
||||
@ -56,6 +62,18 @@ class TestQgsComposerGroup : public QObject
|
||||
QString mReport;
|
||||
};
|
||||
|
||||
// private
|
||||
void TestQgsComposerGroup::dumpUndoStack( const QUndoStack& us, QString prefix ) const
|
||||
{
|
||||
if ( ! prefix.isEmpty() ) prefix += ": ";
|
||||
for ( int i = 0; i < us.count(); ++i )
|
||||
{
|
||||
QgsDebugMsg( QString( "%4US %1: %2%3" )
|
||||
.arg( i ). arg( i >= us.index() ? "-" : "" )
|
||||
.arg( us.text( i ) ) .arg( prefix ) );
|
||||
}
|
||||
}
|
||||
|
||||
void TestQgsComposerGroup::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
@ -161,36 +179,257 @@ void TestQgsComposerGroup::deleteGroup()
|
||||
QCOMPARE( items.size(), 1 );
|
||||
QVERIFY( mItem1->isRemoved() );
|
||||
QVERIFY( mItem2->isRemoved() );
|
||||
QVERIFY( mGroup->isRemoved() );
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE( QgsComposerItemGroup * );
|
||||
Q_DECLARE_METATYPE( QgsComposerPolygon * );
|
||||
Q_DECLARE_METATYPE( QgsComposerItem * );
|
||||
|
||||
void TestQgsComposerGroup::undoRedo()
|
||||
{
|
||||
#if 0 //expected fail - see #11371
|
||||
QgsComposerPolygon *item1, *item2;
|
||||
int polygonsAdded = 0;
|
||||
int groupsAdded = 0;
|
||||
int itemsRemoved = 0;
|
||||
|
||||
qRegisterMetaType<QgsComposerPolygon *>();
|
||||
QSignalSpy spyPolygonAdded( mComposition, SIGNAL( composerPolygonAdded( QgsComposerPolygon* ) ) );
|
||||
QCOMPARE( spyPolygonAdded.count(), 0 );
|
||||
|
||||
qRegisterMetaType<QgsComposerItemGroup *>();
|
||||
QSignalSpy spyGroupAdded( mComposition, SIGNAL( composerItemGroupAdded( QgsComposerItemGroup* ) ) );
|
||||
QCOMPARE( spyGroupAdded.count(), 0 );
|
||||
|
||||
qRegisterMetaType<QgsComposerItem *>();
|
||||
QSignalSpy spyItemRemoved( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
|
||||
QCOMPARE( spyItemRemoved.count(), 0 );
|
||||
|
||||
//test for crash when undo/redoing with groups
|
||||
// Set initial condition
|
||||
QUndoStack *us = mComposition->undoStack();
|
||||
QgsDebugMsg( QString( "clearing" ) );
|
||||
us->clear();
|
||||
QgsDebugMsg( QString( "clearing completed" ) );
|
||||
QList<QgsComposerItem*> items;
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 1 ); // paper only
|
||||
QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
|
||||
//create some items
|
||||
mItem1 = new QgsComposerLabel( mComposition );
|
||||
mComposition->addItem( mItem1 );
|
||||
mItem2 = new QgsComposerLabel( mComposition );
|
||||
mComposition->addItem( mItem2 );
|
||||
item1 = new QgsComposerPolygon( QPolygonF( QRectF( 0, 0, 1, 1 ) ), mComposition ); //QgsComposerLabel( mComposition );
|
||||
mComposition->addComposerPolygon( item1 );
|
||||
QCOMPARE( spyPolygonAdded.count(), ++polygonsAdded );
|
||||
item2 = new QgsComposerPolygon( QPolygonF( QRectF( -1, -2, 1, 1 ) ), mComposition ); //QgsComposerLabel( mComposition );
|
||||
mComposition->addComposerPolygon( item2 );
|
||||
QCOMPARE( spyPolygonAdded.count(), ++polygonsAdded );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
|
||||
QgsDebugMsg( QString( "addedItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
|
||||
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
|
||||
//dumpUndoStack(*us, "after initial items addition");
|
||||
|
||||
//group items
|
||||
QList<QgsComposerItem*> items;
|
||||
items << mItem1 << mItem2;
|
||||
items.clear();
|
||||
items << item1 << item2;
|
||||
mGroup = mComposition->groupItems( items );
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
QCOMPARE( mGroup->items().size(), 2 );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
|
||||
QVERIFY( ! mGroup->isRemoved() );
|
||||
QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
|
||||
//dumpUndoStack(*us, "after initial items addition");
|
||||
|
||||
//move, and ungroup
|
||||
mGroup->beginCommand( "move" );
|
||||
//move group
|
||||
QgsDebugMsg( QString( "moving group" ) );
|
||||
mGroup->beginCommand( "move group" );
|
||||
mGroup->move( 10.0, 20.0 );
|
||||
mGroup->endCommand();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
QgsDebugMsg( QString( "groupItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
QCOMPARE( mGroup->items().size(), 2 );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
|
||||
QVERIFY( ! mGroup->isRemoved() );
|
||||
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
|
||||
|
||||
//ungroup
|
||||
QgsDebugMsg( QString( "ungrouping" ) );
|
||||
mComposition->ungroupItems( mGroup );
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QVERIFY( mGroup->isRemoved() );
|
||||
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) ); // should not rely on this
|
||||
//dumpUndoStack(*us, "after ungroup");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: Remove item group
|
||||
|
||||
//undo
|
||||
mComposition->undoStack()->undo();
|
||||
//undo (groups again) -- crashed here before #11371 got fixed
|
||||
QgsDebugMsg( QString( "undo ungrouping" ) );
|
||||
us->undo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
QCOMPARE( mGroup->items().size(), 2 ); // WARNING: might not be alive anymore
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
|
||||
QVERIFY( ! mGroup->isRemoved() );
|
||||
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
|
||||
//dumpUndoStack(*us, "after undo ungroup");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//redo
|
||||
mComposition->undoStack()->redo();
|
||||
#endif
|
||||
//remove group
|
||||
QgsDebugMsg( QString( "remove group" ) );
|
||||
mComposition->removeComposerItem( mGroup, true, true );
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
itemsRemoved += 3; // the group and the two items
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 1 ); // paper only
|
||||
QgsDebugMsg( QString( "remove stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
//dumpUndoStack(*us, "after remove group");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: Remove item group
|
||||
|
||||
//undo remove group
|
||||
QgsDebugMsg( QString( "undo remove group" ) );
|
||||
us->undo();
|
||||
polygonsAdded += 2;
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( mGroup->items().size(), 2 );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
//dumpUndoStack(*us, "after undo remove group");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//undo move group
|
||||
QgsDebugMsg( QString( "undo move group" ) );
|
||||
us->undo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
QCOMPARE( mGroup->items().size(), 2 );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QCOMPARE( item1->isGroupMember(), true );
|
||||
QCOMPARE( item2->isGroupMember(), true );
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
|
||||
QVERIFY( ! mGroup->isRemoved() );
|
||||
QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
|
||||
//dumpUndoStack(*us, "after undo move group");
|
||||
// US 0: Items grouped
|
||||
// US 1: -move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//undo group
|
||||
QgsDebugMsg( QString( "undo group" ) );
|
||||
us->undo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
|
||||
//QCOMPARE( mGroup->items().size(), 2 ); // not important
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
|
||||
QCOMPARE( item1->isGroupMember(), false );
|
||||
QCOMPARE( item2->isGroupMember(), false );
|
||||
QVERIFY( ! item1->isRemoved() );
|
||||
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
|
||||
QVERIFY( ! item2->isRemoved() );
|
||||
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
|
||||
QVERIFY( mGroup->isRemoved() );
|
||||
//QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
|
||||
//dumpUndoStack(*us, "after undo group");
|
||||
// US 0: -Items grouped
|
||||
// US 1: -move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//redo group
|
||||
QgsDebugMsg( QString( "redo group" ) );
|
||||
us->redo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QCOMPARE( item1->isGroupMember(), true );
|
||||
QCOMPARE( item2->isGroupMember(), true );
|
||||
//// QCOMPARE( mGroup->pos(), QPointF( 0, 0 ) ); // getting nan,nan here
|
||||
//dumpUndoStack(*us, "after redo group");
|
||||
// US 0: Items grouped
|
||||
// US 1: -move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//redo move group
|
||||
QgsDebugMsg( QString( "redo move group" ) );
|
||||
us->redo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
|
||||
QCOMPARE( item1->isGroupMember(), true );
|
||||
QCOMPARE( item2->isGroupMember(), true );
|
||||
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
|
||||
//dumpUndoStack(*us, "after redo move group");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: -Remove item group
|
||||
|
||||
//redo remove group
|
||||
QgsDebugMsg( QString( "redo remove group" ) );
|
||||
us->redo();
|
||||
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
|
||||
QCOMPARE( spyGroupAdded.count(), groupsAdded );
|
||||
itemsRemoved += 3; // 1 group, 2 contained items
|
||||
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
|
||||
mComposition->composerItems( items );
|
||||
QCOMPARE( items.size(), 1 ); // paper only
|
||||
QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
//dumpUndoStack(*us, "after redo remove group");
|
||||
// US 0: Items grouped
|
||||
// US 1: move group
|
||||
// US 2: Remove item group
|
||||
|
||||
//unwind the whole stack
|
||||
us->clear();
|
||||
|
||||
QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
|
||||
}
|
||||
|
||||
QTEST_MAIN( TestQgsComposerGroup )
|
||||
|
Loading…
x
Reference in New Issue
Block a user