Skip to content

Commit 7fd5ba8

Browse files
author
wonder
committedJun 14, 2009
Added infrastructure for vector editing undo/redo functionality.
Note - when implementing edit tools for vector layer: All editation should be done between beginEditCommand() and endEditCommand() calls so that the operation are stored. Note - when doing changes inside QgsVectorLayer code: When doing any changes inside QgsVectorLayer they should be done using edit*() functions and _not_ directly e.g. mChangedGeometries[fid] = (...) otherwise the change won't be stored in the undo stack and it would lead to invalid behaviour of undo. git-svn-id: http://svn.osgeo.org/qgis/trunk@10920 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 0a3adf8 commit 7fd5ba8

File tree

9 files changed

+718
-49
lines changed

9 files changed

+718
-49
lines changed
 

‎python/core/qgsmaplayer.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ public:
249249
*/
250250
virtual QString saveNamedStyle( const QString theURI, bool & theResultFlag );
251251

252+
/** Return pointer to layer's undo stack */
253+
QUndoStack* undoStack();
254+
252255
public slots:
253256

254257
/** Event handler for when a coordinate transform fails due to bad vertex error */

‎python/core/qgsvectorlayer.sip

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,25 @@ public:
389389
*/
390390
QgsVectorOverlay* findOverlayByType( const QString& typeName );
391391

392+
393+
/**
394+
* Create edit command for undo/redo operations
395+
* @param text text which is to be displayed in undo window
396+
*/
397+
void beginEditCommand(QString text);
398+
399+
/** Finish edit command and add it to undo/redo stack */
400+
void endEditCommand();
401+
402+
/** Destroy active command and deletes all changes in it */
403+
void destroyEditCommand();
404+
405+
/** Execute undo operation. To be called only from QgsVectorLayerUndoCommand. */
406+
// (not necessary) void undoEditCommand(QgsUndoCommand* cmd);
407+
408+
/** Execute redo operation. To be called only from QgsVectorLayerUndoCommand. */
409+
// (not necessary) void redoEditCommand(QgsUndoCommand* cmd);
410+
392411
public slots:
393412

394413
/** Select feature by its ID, optionally emit signal selectionChanged() */

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ SET(QGIS_CORE_SRCS
5151
qgsvectordataprovider.cpp
5252
qgsvectorfilewriter.cpp
5353
qgsvectorlayer.cpp
54+
qgsvectorlayerundocommand.cpp
5455
qgsvectoroverlay.cpp
5556

5657
composer/qgscomposeritem.cpp

‎src/core/qgsmaplayer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,3 +715,11 @@ QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag
715715

716716
return myErrorMessage;
717717
}
718+
719+
720+
721+
722+
QUndoStack* QgsMapLayer::undoStack()
723+
{
724+
return &mUndoStack;
725+
}

‎src/core/qgsmaplayer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <map>
2424

2525
#include <QObject>
26+
#include <QUndoStack>
2627

2728
#include "qgsrectangle.h"
2829

@@ -259,6 +260,9 @@ class CORE_EXPORT QgsMapLayer : public QObject
259260
*/
260261
virtual bool writeSymbology( QDomNode&, QDomDocument& doc, QString& errorMessage ) const = 0;
261262

263+
/** Return pointer to layer's undo stack */
264+
QUndoStack* undoStack();
265+
262266
public slots:
263267

264268
/** Event handler for when a coordinate transform fails due to bad vertex error */
@@ -356,6 +360,8 @@ class CORE_EXPORT QgsMapLayer : public QObject
356360
/** A flag that tells us whether to use the above vars to restrict layer visibility */
357361
bool mScaleBasedVisibility;
358362

363+
QUndoStack mUndoStack;
364+
359365
};
360366

361367
#endif

‎src/core/qgsvectorlayer.cpp

Lines changed: 378 additions & 47 deletions
Large diffs are not rendered by default.

‎src/core/qgsvectorlayer.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include "qgsmaplayer.h"
2929
#include "qgsfeature.h"
3030
#include "qgssnapper.h"
31-
#include "qgsfeature.h"
3231
#include "qgsfield.h"
3332

3433
class QPainter;
@@ -42,16 +41,21 @@ class QgsMapToPixel;
4241
class QgsLabel;
4342
class QgsRectangle;
4443
class QgsRenderer;
44+
class QgsUndoCommand;
4545
class QgsVectorDataProvider;
4646
class QgsVectorOverlay;
4747

48-
class QgsGeometry;
4948
class QgsRectangle;
5049

50+
5151
typedef QList<int> QgsAttributeList;
5252
typedef QSet<int> QgsFeatureIds;
5353
typedef QSet<int> QgsAttributeIds;
5454

55+
56+
57+
58+
5559
/** \ingroup core
5660
* Vector layer backed by a data source provider.
5761
*/
@@ -453,6 +457,25 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
453457
*/
454458
QgsVectorOverlay* findOverlayByType( const QString& typeName );
455459

460+
461+
/**
462+
* Create edit command for undo/redo operations
463+
* @param text text which is to be displayed in undo window
464+
*/
465+
void beginEditCommand(QString text);
466+
467+
/** Finish edit command and add it to undo/redo stack */
468+
void endEditCommand();
469+
470+
/** Destroy active command and deletes all changes in it */
471+
void destroyEditCommand();
472+
473+
/** Execute undo operation. To be called only from QgsVectorLayerUndoCommand. */
474+
void undoEditCommand(QgsUndoCommand* cmd);
475+
476+
/** Execute redo operation. To be called only from QgsVectorLayerUndoCommand. */
477+
void redoEditCommand(QgsUndoCommand* cmd);
478+
456479
public slots:
457480
/** Select feature by its ID, optionally emit signal selectionChanged() */
458481
void select( int featureId, bool emitSignal = TRUE );
@@ -577,6 +600,19 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
577600
/**Update feature with uncommited geometry updates*/
578601
void updateFeatureGeometry( QgsFeature &f );
579602

603+
/** Record changed geometry, store in active command (if any) */
604+
void editGeometryChange( int featureId, QgsGeometry& geometry );
605+
606+
/** Record added feature, store in active command (if any) */
607+
void editFeatureAdd(QgsFeature& feature);
608+
609+
/** Record deleted feature, store in active command (if any) */
610+
void editFeatureDelete(int featureId);
611+
612+
/** Record changed attribute, store in active command (if any) */
613+
void editAttributeChange( int featureId, int field, QVariant value );
614+
615+
580616
private: // Private attributes
581617

582618
/** Update threshold for drawing features as they are read. A value of zero indicates
@@ -678,6 +714,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
678714
QSet<int> mFetchConsidered;
679715
QgsGeometryMap::iterator mFetchChangedGeomIt;
680716
QgsFeatureList::iterator mFetchAddedFeaturesIt;
717+
718+
QgsUndoCommand * mActiveCommand;
681719
};
682720

683721
#endif
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
2+
#include "qgsvectorlayerundocommand.h"
3+
4+
#include "qgsgeometry.h"
5+
#include "qgsvectorlayer.h"
6+
7+
#include "qgslogger.h"
8+
9+
QgsUndoCommand::GeometryChangeEntry::GeometryChangeEntry()
10+
: original(NULL), target(NULL)
11+
{
12+
}
13+
14+
void QgsUndoCommand::GeometryChangeEntry::setOriginalGeometry(QgsGeometry& orig)
15+
{
16+
if (orig.type() != QGis::UnknownGeometry)
17+
{
18+
original = new QgsGeometry(orig);
19+
}
20+
else
21+
{
22+
original = NULL;
23+
}
24+
}
25+
26+
void QgsUndoCommand::GeometryChangeEntry::setTargetGeometry(QgsGeometry& dest)
27+
{
28+
if (dest.type() != QGis::UnknownGeometry)
29+
{
30+
target = new QgsGeometry(dest);
31+
}
32+
else
33+
{
34+
target = NULL;
35+
}
36+
}
37+
38+
39+
QgsUndoCommand::GeometryChangeEntry::~GeometryChangeEntry()
40+
{
41+
delete original;
42+
delete target;
43+
}
44+
45+
46+
QgsUndoCommand::QgsUndoCommand(QgsVectorLayer* vlayer, QString text)
47+
: QUndoCommand()
48+
{
49+
setText(text);
50+
mLayer = vlayer;
51+
mFirstRun = true;
52+
}
53+
54+
void QgsUndoCommand::redo()
55+
{
56+
// when the command is added to the undo stack, the redo() function is called
57+
// but we have already done the changes in the layer, so we ignore the first redo() call
58+
if (mFirstRun)
59+
{
60+
mFirstRun = false;
61+
return;
62+
}
63+
64+
mLayer->redoEditCommand(this);
65+
}
66+
67+
void QgsUndoCommand::undo()
68+
{
69+
mLayer->undoEditCommand(this);
70+
}
71+
72+
73+
void QgsUndoCommand::storeGeometryChange(int featureId, QgsGeometry& original, QgsGeometry& target)
74+
{
75+
if ( mGeometryChange.contains( featureId ) )
76+
{
77+
// geometry has been modified already: just alter the resulting geometry
78+
mGeometryChange[featureId].setTargetGeometry(target);
79+
}
80+
else
81+
{
82+
// create new entry about changed geometry
83+
mGeometryChange.insert( featureId, GeometryChangeEntry() );
84+
mGeometryChange[featureId].setOriginalGeometry(original);
85+
mGeometryChange[featureId].setTargetGeometry(target);
86+
}
87+
}
88+
89+
void QgsUndoCommand::storeAttributeChange(int featureId, int field, QVariant original, QVariant target, bool isFirstChange)
90+
{
91+
AttributeChangeEntry entry;
92+
entry.isFirstChange = isFirstChange;
93+
entry.original = original;
94+
entry.target = target;
95+
mAttributeChange[featureId].insert(field, entry);
96+
}
97+
98+
void QgsUndoCommand::storeAttributeAdd(int index, const QgsField & value)
99+
{
100+
mAddedAttributes.insert( index, value );
101+
}
102+
103+
void QgsUndoCommand::storeAttributeDelete(int index, const QgsField & orig)
104+
{
105+
mDeletedAttributes.insert(index, orig );
106+
}
107+
108+
void QgsUndoCommand::storeFeatureDelete(int featureId)
109+
{
110+
mDeletedFeatureIdChange.insert(featureId);
111+
}
112+
113+
void QgsUndoCommand::storeFeatureAdd(QgsFeature& feature)
114+
{
115+
mAddedFeatures.append(feature);
116+
}

‎src/core/qgsvectorlayerundocommand.h

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
2+
#ifndef QGSVECTORLAYERUNDOCOMMAND_H
3+
#define QGSVECTORLAYERUNDOCOMMAND_H
4+
5+
#include <QUndoCommand>
6+
7+
#include <QVariant>
8+
#include <QSet>
9+
#include <QList>
10+
11+
#include "qgsfield.h"
12+
#include "qgsfeature.h"
13+
14+
class QgsGeometry;
15+
class QgsVectorLayer;
16+
17+
18+
// TODO: copied from qgsvectorlayer.h
19+
typedef QList<int> QgsAttributeList;
20+
typedef QSet<int> QgsFeatureIds;
21+
typedef QSet<int> QgsAttributeIds;
22+
23+
24+
25+
/**
26+
* Class to support universal undo command sequence for application, basic for
27+
*/
28+
class QgsUndoCommand : public QUndoCommand
29+
{
30+
public:
31+
32+
/** change structure for attribute for undo/redo purpose */
33+
class AttributeChangeEntry
34+
{
35+
public:
36+
bool isFirstChange;
37+
QVariant original;
38+
QVariant target;
39+
};
40+
41+
typedef QMap<int, AttributeChangeEntry> AttributeChanges;
42+
43+
/** change structure to geometry for undo/redo purpose */
44+
class GeometryChangeEntry
45+
{
46+
public:
47+
GeometryChangeEntry();
48+
~GeometryChangeEntry();
49+
50+
void setOriginalGeometry(QgsGeometry& orig);
51+
void setTargetGeometry(QgsGeometry& target);
52+
53+
QgsGeometry* original;
54+
QgsGeometry* target;
55+
};
56+
57+
58+
QgsUndoCommand(QgsVectorLayer* layer, QString text);
59+
60+
/**
61+
* Necessary function to provide undo operation
62+
*/
63+
void undo();
64+
65+
/**
66+
* Necessary function to provide redo operation
67+
*/
68+
void redo();
69+
70+
/**
71+
* Function to store changes in geometry to be returned to this state after undo/redo
72+
* @param featureId id of feature edited
73+
* @param original original geometry of feature which was changed
74+
* @param target changed geometry which was changed
75+
*/
76+
void storeGeometryChange(int featureId, QgsGeometry& original, QgsGeometry& target);
77+
78+
/**
79+
* Stores changes of attributes for the feature to be returned to this state after undo/redo
80+
* @param featureId id of feature for which this chaged is stored
81+
* @param field field identifier of field which was changed
82+
* @param original original value of attribute before change
83+
* @param target target value of attribute after change
84+
* @param isFirstChange flag if this change is the first one
85+
*/
86+
void storeAttributeChange(int featureId, int field, QVariant original, QVariant target, bool isFirstChange);
87+
88+
/**
89+
* Add id of feature to deleted list to be reverted if needed afterwards
90+
* @param featureId id of feature which is to be deleted
91+
*/
92+
void storeFeatureDelete(int featureId);
93+
94+
/**
95+
* Add new feature to list of new features to be stored for undo/redo operations.
96+
* @param feature feature which is to be added
97+
*/
98+
void storeFeatureAdd(QgsFeature& feature);
99+
100+
/**
101+
* Add new attribute to list of attributes to be used for attributes of features for undo/redo operations.
102+
* @param index index of attribute which is to be added
103+
* @param value field description which is to be stored
104+
*/
105+
void storeAttributeAdd(int index, const QgsField & value);
106+
107+
/**
108+
* Add deleted attribute which is to be stored for undo/redo operations.
109+
* @param index index od attribute definition which is to be deleted
110+
* @param orig deleted field's description
111+
*/
112+
void storeAttributeDelete(int index, const QgsField & orig);
113+
114+
private:
115+
/** Variable to disable first run of undo, because it's automaticaly done after push */
116+
bool mFirstRun;
117+
118+
/** Layer on which operations should be performed */
119+
QgsVectorLayer* mLayer;
120+
121+
/** Map of changes of geometry for features it describes changes of geometry */
122+
QMap<int, GeometryChangeEntry> mGeometryChange;
123+
124+
/** Map of changes of atrributes for features which describes changes of attributes */
125+
QMap<int, AttributeChanges> mAttributeChange;
126+
127+
/** Deleted feature IDs which are not commited. Note a feature can be added and then deleted
128+
again before the change is committed - in that case the added feature would be removed
129+
from mAddedFeatures only and *not* entered here.
130+
*/
131+
QgsFeatureIds mDeletedFeatureIdChange;
132+
133+
/** added attributes fields which are not commited */
134+
QgsFieldMap mAddedAttributes;
135+
136+
/** deleted attributes fields which are not commited */
137+
QgsFieldMap mDeletedAttributes;
138+
139+
/** New features which are not commited. Note a feature can be added and then changed,
140+
therefore the details here can be overridden by mChangedAttributeValues and mChangedGeometries.
141+
*/
142+
QgsFeatureList mAddedFeatures;
143+
144+
friend class QgsVectorLayer;
145+
};
146+
147+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.