Skip to content

Commit

Permalink
[FEATURE] Add undo and redo on transaction groups (#4765)
Browse files Browse the repository at this point in the history
* [FEATURE] adds undo/redo for transaction groups

[needs-docs] the undo/redo now works with transcation groups. Just check
that there is no restriction in the transaction groups doc concerning
undo.

related to #14799

The undo/redo is implemented using SAVEPOINT.

The QgsTransaction interface has been enlarged to allow savepoints
creation and management. The savepoint is destroyed on
rollbackToSavepoint to have the same behavior has the sql ROLLBACK TO
SAVEPPOINT.

To avoid the creation of a savepoint for each feature modified in bulk
editing (e.g. paste, field calculator) the logic is a bit complicated: the
savepoint is created on QgsVectorLayer::editCommandStarted and the first
actual undo command (QgsVectorLayerUndoPassthroughCommand) is
responsible for the re-creation of the savepoint in case of undo-redo.
Since the behavior must be different in case edition doesn't take place
inside an edit command, a member function has been added to
QgsVectorLayer to expose the mEditCommandActive state.

Another (commented) tricky bit is the modification of the database
structure on add/delete attributes. On undo, the attribute is removed
before the rollback to savepoint, i.e. there is a useless ALTER TABLE
issued to restore the structure just before restoring it with the
ROLLBACK TO SAVEPOINT. This is necessary to make the provider
aware of the change of structure. It could be nicer/cleaner to have a way
to reload providers metadata.

The editPaste function has also been modified to use addFeatures instead of
addFeature (plural/singular), this is at the expense of an additional "cpy"
of the clipboard in memory, but it should improve perf with postgis provider.

* fixup operator aliases
  • Loading branch information
vmora authored and m-kuhn committed Sep 15, 2017
1 parent a081905 commit f63c302
Show file tree
Hide file tree
Showing 21 changed files with 1,091 additions and 108 deletions.
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -120,6 +120,7 @@
%Include qgsvectorlayerjoininfo.sip
%Include qgsvectorlayerlabeling.sip
%Include qgsvectorlayerundocommand.sip
%Include qgsvectorlayerundopassthroughcommand.sip
%Include qgsvectorlayerutils.sip
%Include qgsvectorsimplifymethod.sip
%Include qgsvirtuallayerdefinition.sip
Expand Down
44 changes: 44 additions & 0 deletions python/core/qgstransaction.sip
Expand Up @@ -110,6 +110,50 @@ class QgsTransaction : QObject /Abstract/
:rtype: bool
%End

QString createSavepoint( QString &error /Out/ );
%Docstring
creates a save point
returns empty string on error
returns the last created savepoint if it's not dirty
.. versionadded:: 3.0
:rtype: str
%End

QString createSavepoint( const QString &savePointId, QString &error /Out/ );
%Docstring
creates a save point
returns empty string on error
.. versionadded:: 3.0
:rtype: str
%End

bool rollbackToSavepoint( const QString &name, QString &error /Out/ );
%Docstring
rollback to save point, the save point is maintained and is "undertied"
.. versionadded:: 3.0
:rtype: bool
%End

void dirtyLastSavePoint();
%Docstring
dirty save point such that next call to createSavepoint will create a new one
.. versionadded:: 3.0
%End

QList< QString > savePoints() const;
%Docstring
returns savepoints
.. versionadded:: 3.0
:rtype: list of str
%End

bool lastSavePointIsDirty() const;
%Docstring
returns the last created savepoint
.. versionadded:: 3.0
:rtype: bool
%End

signals:

void afterRollback();
Expand Down
8 changes: 8 additions & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1804,6 +1804,14 @@ Returns the current blending mode for features
:rtype: bool
%End

bool isEditCommandActive() const;
%Docstring
Test if an edit command is active

.. versionadded:: 3.0
:rtype: bool
%End

signals:

void selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect );
Expand Down
1 change: 1 addition & 0 deletions python/core/qgsvectorlayereditbuffer.sip
Expand Up @@ -289,6 +289,7 @@ Updates an index in an attribute map to a new value (for updates of changed attr




};

/************************************************************************
Expand Down
1 change: 0 additions & 1 deletion python/core/qgsvectorlayerundocommand.sip
Expand Up @@ -221,7 +221,6 @@ class QgsVectorLayerUndoCommandRenameAttribute : QgsVectorLayerUndoCommand

};


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
255 changes: 255 additions & 0 deletions python/core/qgsvectorlayerundopassthroughcommand.sip
@@ -0,0 +1,255 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvectorlayerundopassthroughcommand.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/






class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
{
%Docstring
Undo command for vector layer in transaction group mode.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommand
\param buffer associated edit buffer
\param text text associated with command
%End

bool hasError() const;
%Docstring
Returns error status
:rtype: bool
%End

protected:

bool rollBackToSavePoint();
%Docstring
Rollback command, release savepoint or set error status
save point must be set prior to call
error satus should be false prior to call
:rtype: bool
%End

bool setSavePoint();
%Docstring
Set the command savepoint or set error status
error satus should be false prior to call
:rtype: bool
%End

void setError();
%Docstring
Set error flag and append "failed" to text
%End

};


class QgsVectorLayerUndoPassthroughCommandAddFeatures : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for adding a feature to a vector layer in transaction group mode.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandAddFeatures( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsFeatureList &features );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandAddFeatures
\param buffer associated edit buffer
\param features features to add to layer
%End

virtual void undo();
virtual void redo();

QgsFeatureList features() const;
%Docstring
List of features (added feaures can be modified by default values from database)
:rtype: QgsFeatureList
%End

};



class QgsVectorLayerUndoPassthroughCommandDeleteFeatures : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for deleting features from a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandDeleteFeatures( QgsVectorLayerEditBuffer *buffer /Transfer/, const QgsFeatureIds &fids );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandDeleteFeatures
\param buffer associated edit buffer
\param fids feature IDs of features to delete from layer
%End

virtual void undo();
virtual void redo();

};


class QgsVectorLayerUndoPassthroughCommandChangeGeometry : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for changing feature geometry from a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandChangeGeometry( QgsVectorLayerEditBuffer *buffer /Transfer/, const QgsFeatureId &fid, const QgsGeometry &geom );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandChangeGeometry
\param buffer associated edit buffer
\param fid feature ID of feature to change
\param geom new geometru
%End

virtual void undo();
virtual void redo();

};


class QgsVectorLayerUndoPassthroughCommandChangeAttribute: QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for changing attr value from a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandChangeAttribute( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsFeatureId fid, int field, const QVariant &newValue );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttribute
\param buffer associated edit buffer
\param fid feature ID of feature
\param field
\param newValue
%End

virtual void undo();
virtual void redo();

};


class QgsVectorLayerUndoPassthroughCommandAddAttribute : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for adding attri to a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandAddAttribute( QgsVectorLayerEditBuffer *buffer /Transfer/, const QgsField &field );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandAddAttribute
\param buffer associated edit buffer
\param field
%End

virtual void undo();
virtual void redo();

};


class QgsVectorLayerUndoPassthroughCommandDeleteAttribute : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for deleting attri of a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandDeleteAttribute( QgsVectorLayerEditBuffer *buffer /Transfer/, int attr );
%Docstring
Constructor for QgsVectorLayerUndoCommandDeleteAttribute
\param buffer associated edit buffer
\param attr
%End

virtual void undo();
virtual void redo();

};


class QgsVectorLayerUndoPassthroughCommandRenameAttribute : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for deleting attri of a vector layer in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandRenameAttribute( QgsVectorLayerEditBuffer *buffer /Transfer/, int attr, const QString &newName );
%Docstring
Constructor for QgsVectorLayerUndoCommandRenameAttribute
\param buffer associated edit buffer
\param attr
\param newName
%End

virtual void undo();
virtual void redo();

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvectorlayerundopassthroughcommand.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
15 changes: 10 additions & 5 deletions src/app/qgisapp.cpp
Expand Up @@ -7862,8 +7862,8 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )

QgsExpressionContext context = pasteVectorLayer->createExpressionContext();

QgsFeatureIds newIds;

QgsFeatureList newFeatures;
QgsFeatureList::const_iterator featureIt = features.constBegin();
while ( featureIt != features.constEnd() )
{
Expand Down Expand Up @@ -7907,13 +7907,18 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )

// now create new feature using pasted feature as a template. This automatically handles default
// values and field constraints
QgsFeature newFeature = QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, dstAttr, &context );
pasteVectorLayer->addFeature( newFeature );
newIds << newFeature.id();

newFeatures << QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, dstAttr, &context );
++featureIt;
}

pasteVectorLayer->addFeatures( newFeatures );
QgsFeatureIds newIds;
for ( const QgsFeature &f : qgsAsConst( newFeatures ) )
{
newIds << f.id();
}


pasteVectorLayer->selectByIds( newIds );
pasteVectorLayer->endEditCommand();
pasteVectorLayer->updateExtents();
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -301,6 +301,7 @@ SET(QGIS_CORE_SRCS
qgsvectorlayerrenderer.cpp
qgsvectorlayertools.cpp
qgsvectorlayerundocommand.cpp
qgsvectorlayerundopassthroughcommand.cpp
qgsvectorlayerutils.cpp
qgsvectorsimplifymethod.cpp
qgsvirtuallayerdefinition.cpp
Expand Down Expand Up @@ -906,6 +907,7 @@ SET(QGIS_CORE_HDRS
qgsvectorlayerlabeling.h
qgsvectorlayerrenderer.h
qgsvectorlayerundocommand.h
qgsvectorlayerundopassthroughcommand.h
qgsvectorlayerutils.h
qgsvectorsimplifymethod.h
qgsvirtuallayerdefinition.h
Expand Down

0 comments on commit f63c302

Please sign in to comment.