[QGIS Commit] r10920 - in trunk/qgis: python/core src/core
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Sun Jun 14 10:17:19 EDT 2009
Author: wonder
Date: 2009-06-14 10:17:18 -0400 (Sun, 14 Jun 2009)
New Revision: 10920
Added:
trunk/qgis/src/core/qgsvectorlayerundocommand.cpp
trunk/qgis/src/core/qgsvectorlayerundocommand.h
Modified:
trunk/qgis/python/core/qgsmaplayer.sip
trunk/qgis/python/core/qgsvectorlayer.sip
trunk/qgis/src/core/CMakeLists.txt
trunk/qgis/src/core/qgsmaplayer.cpp
trunk/qgis/src/core/qgsmaplayer.h
trunk/qgis/src/core/qgsvectorlayer.cpp
trunk/qgis/src/core/qgsvectorlayer.h
Log:
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.
Modified: trunk/qgis/python/core/qgsmaplayer.sip
===================================================================
--- trunk/qgis/python/core/qgsmaplayer.sip 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/python/core/qgsmaplayer.sip 2009-06-14 14:17:18 UTC (rev 10920)
@@ -249,6 +249,9 @@
*/
virtual QString saveNamedStyle( const QString theURI, bool & theResultFlag );
+ /** Return pointer to layer's undo stack */
+ QUndoStack* undoStack();
+
public slots:
/** Event handler for when a coordinate transform fails due to bad vertex error */
Modified: trunk/qgis/python/core/qgsvectorlayer.sip
===================================================================
--- trunk/qgis/python/core/qgsvectorlayer.sip 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/python/core/qgsvectorlayer.sip 2009-06-14 14:17:18 UTC (rev 10920)
@@ -389,6 +389,25 @@
*/
QgsVectorOverlay* findOverlayByType( const QString& typeName );
+
+ /**
+ * Create edit command for undo/redo operations
+ * @param text text which is to be displayed in undo window
+ */
+ void beginEditCommand(QString text);
+
+ /** Finish edit command and add it to undo/redo stack */
+ void endEditCommand();
+
+ /** Destroy active command and deletes all changes in it */
+ void destroyEditCommand();
+
+ /** Execute undo operation. To be called only from QgsVectorLayerUndoCommand. */
+ // (not necessary) void undoEditCommand(QgsUndoCommand* cmd);
+
+ /** Execute redo operation. To be called only from QgsVectorLayerUndoCommand. */
+ // (not necessary) void redoEditCommand(QgsUndoCommand* cmd);
+
public slots:
/** Select feature by its ID, optionally emit signal selectionChanged() */
Modified: trunk/qgis/src/core/CMakeLists.txt
===================================================================
--- trunk/qgis/src/core/CMakeLists.txt 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/src/core/CMakeLists.txt 2009-06-14 14:17:18 UTC (rev 10920)
@@ -51,6 +51,7 @@
qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp
qgsvectorlayer.cpp
+ qgsvectorlayerundocommand.cpp
qgsvectoroverlay.cpp
composer/qgscomposeritem.cpp
Modified: trunk/qgis/src/core/qgsmaplayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsmaplayer.cpp 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/src/core/qgsmaplayer.cpp 2009-06-14 14:17:18 UTC (rev 10920)
@@ -715,3 +715,11 @@
return myErrorMessage;
}
+
+
+
+
+QUndoStack* QgsMapLayer::undoStack()
+{
+ return &mUndoStack;
+}
Modified: trunk/qgis/src/core/qgsmaplayer.h
===================================================================
--- trunk/qgis/src/core/qgsmaplayer.h 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/src/core/qgsmaplayer.h 2009-06-14 14:17:18 UTC (rev 10920)
@@ -23,6 +23,7 @@
#include <map>
#include <QObject>
+#include <QUndoStack>
#include "qgsrectangle.h"
@@ -259,6 +260,9 @@
*/
virtual bool writeSymbology( QDomNode&, QDomDocument& doc, QString& errorMessage ) const = 0;
+ /** Return pointer to layer's undo stack */
+ QUndoStack* undoStack();
+
public slots:
/** Event handler for when a coordinate transform fails due to bad vertex error */
@@ -356,6 +360,8 @@
/** A flag that tells us whether to use the above vars to restrict layer visibility */
bool mScaleBasedVisibility;
+ QUndoStack mUndoStack;
+
};
#endif
Modified: trunk/qgis/src/core/qgsvectorlayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.cpp 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/src/core/qgsvectorlayer.cpp 2009-06-14 14:17:18 UTC (rev 10920)
@@ -67,6 +67,7 @@
#include "qgssinglesymbolrenderer.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectordataprovider.h"
+#include "qgsvectorlayerundocommand.h"
#include "qgsvectoroverlay.h"
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
@@ -102,7 +103,8 @@
mLabel( 0 ),
mLabelOn( false ),
mVertexMarkerOnlyForSelection(false),
- mFetching( false )
+ mFetching( false ),
+ mActiveCommand( NULL )
{
mActions = new QgsAttributeAction;
@@ -1480,7 +1482,7 @@
// providers will use their own new feature ID when we commit the new feature)
// and add to the known added features.
f.setFeatureId( addedIdLowWaterMark );
- mAddedFeatures.append( f );
+ editFeatureAdd( f );
mCachedGeometries[f.id()] = *f.geometry();
setModified( true );
@@ -1503,7 +1505,7 @@
if ( mDataProvider )
{
-
+ QgsGeometry geometry;
if ( !mChangedGeometries.contains( atFeatureId ) )
{
// first time this geometry has changed since last commit
@@ -1511,12 +1513,17 @@
{
return false;
}
- mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId];
+ geometry = mCachedGeometries[atFeatureId];
+ //mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId];
}
+ else
+ {
+ geometry = mChangedGeometries[atFeatureId];
+ }
+ geometry.insertVertex( x, y, beforeVertex );
+ mCachedGeometries[atFeatureId] = geometry;
+ editGeometryChange(atFeatureId, geometry);
- mChangedGeometries[atFeatureId].insertVertex( x, y, beforeVertex );
- mCachedGeometries[atFeatureId] = mChangedGeometries[atFeatureId];
-
setModified( true, true ); // only geometry was changed
return true;
@@ -1534,6 +1541,7 @@
if ( mDataProvider )
{
+ QgsGeometry geometry;
if ( !mChangedGeometries.contains( atFeatureId ) )
{
// first time this geometry has changed since last commit
@@ -1541,11 +1549,17 @@
{
return false;
}
- mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId];
+ geometry = mCachedGeometries[atFeatureId];
+ //mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId];
}
+ else
+ {
+ geometry = mChangedGeometries[atFeatureId];
+ }
- mChangedGeometries[atFeatureId].moveVertex( x, y, atVertex );
- mCachedGeometries[atFeatureId] = mChangedGeometries[atFeatureId];
+ geometry.moveVertex( x, y, atVertex );
+ mCachedGeometries[atFeatureId] = geometry;
+ editGeometryChange(atFeatureId, geometry);
setModified( true, true ); // only geometry was changed
@@ -1564,6 +1578,7 @@
if ( mDataProvider )
{
+ QgsGeometry geometry;
if ( !mChangedGeometries.contains( atFeatureId ) )
{
// first time this geometry has changed since last commit
@@ -1571,10 +1586,16 @@
{
return false;
}
- mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId];
+ geometry = mCachedGeometries[atFeatureId];
}
+ else
+ {
+ geometry = mChangedGeometries[atFeatureId];
+ }
- mChangedGeometries[atFeatureId].deleteVertex( atVertex );
+ geometry.deleteVertex( atVertex );
+ mCachedGeometries[atFeatureId] = geometry;
+ editGeometryChange(atFeatureId, geometry);
setModified( true, true ); // only geometry was changed
@@ -1637,7 +1658,8 @@
addRingReturnCode = f.geometry()->addRing( ring );
if ( addRingReturnCode == 0 )
{
- mChangedGeometries.insert( f.id(), *f.geometry() );
+ editGeometryChange( f.id(), *f.geometry() );
+
setModified( true, true );
break;
}
@@ -1667,12 +1689,15 @@
QgsGeometryMap::iterator changedIt = mChangedGeometries.find( selectedFeatureId );
if ( changedIt != mChangedGeometries.end() )
{
- int returnValue = changedIt->addIsland( ring );
- mCachedGeometries[selectedFeatureId] = *changedIt;
+ QgsGeometry geom = *changedIt;
+ int returnValue = geom.addIsland( ring );
+ editGeometryChange( selectedFeatureId, geom );
+ mCachedGeometries[selectedFeatureId] = geom;
return returnValue;
}
//look if id of selected feature belongs to an added feature
+ /*
for ( QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt )
{
if ( addedIt->id() == selectedFeatureId )
@@ -1681,6 +1706,7 @@
mCachedGeometries[selectedFeatureId] = *addedIt->geometry();
}
}
+ */
//is the feature contained in the view extent (mCachedGeometries) ?
QgsGeometryMap::iterator cachedIt = mCachedGeometries.find( selectedFeatureId );
@@ -1689,7 +1715,7 @@
int errorCode = cachedIt->addIsland( ring );
if ( errorCode == 0 )
{
- mChangedGeometries.insert( selectedFeatureId, *cachedIt );
+ editGeometryChange( selectedFeatureId, *cachedIt );
mCachedGeometries[selectedFeatureId] = *cachedIt;
setModified( true, true );
}
@@ -1705,7 +1731,7 @@
if ( fGeom )
{
int errorCode = fGeom->addIsland( ring );
- mChangedGeometries.insert( selectedFeatureId, *fGeom );
+ editGeometryChange( selectedFeatureId, *fGeom );
setModified( true, true );
delete fGeom;
return errorCode;
@@ -1722,10 +1748,14 @@
QgsGeometryMap::iterator changedIt = mChangedGeometries.find( featureId );
if ( changedIt != mChangedGeometries.end() )
{
- return changedIt->translate( dx, dy );
+ QgsGeometry geom = *changedIt;
+ int errorCode = geom.translate( dx, dy );
+ editGeometryChange( featureId, geom );
+ return errorCode;
}
//look if id of selected feature belongs to an added feature
+ /*
for ( QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt )
{
if ( addedIt->id() == featureId )
@@ -1733,6 +1763,7 @@
return addedIt->geometry()->translate( dx, dy );
}
}
+ */
//else look in mCachedGeometries to make access faster
QgsGeometryMap::iterator cachedIt = mCachedGeometries.find( featureId );
@@ -1741,7 +1772,7 @@
int errorCode = cachedIt->translate( dx, dy );
if ( errorCode == 0 )
{
- mChangedGeometries.insert( featureId, *cachedIt );
+ editGeometryChange( featureId, *cachedIt );
setModified( true, true );
}
return errorCode;
@@ -1757,7 +1788,7 @@
int errorCode = translateGeom.translate( dx, dy );
if ( errorCode == 0 )
{
- mChangedGeometries.insert( featureId, translateGeom );
+ editGeometryChange(featureId, translateGeom);
setModified( true, true );
}
return errorCode;
@@ -1830,7 +1861,7 @@
if ( splitFunctionReturn == 0 )
{
//change this geometry
- mChangedGeometries.insert( select_it->id(), *( select_it->geometry() ) );
+ editGeometryChange( select_it->id(), *( select_it->geometry() ) );
//update of cached geometries is necessary because we use addTopologicalPoints() later
mCachedGeometries[select_it->id()] = *( select_it->geometry() );
@@ -2590,7 +2621,7 @@
return false;
}
- mChangedGeometries[ fid ] = *geom;
+ editGeometryChange( fid, *geom );
mCachedGeometries[fid] = *geom;
setModified( true, true );
return true;
@@ -2602,29 +2633,7 @@
if ( !isEditable() )
return false;
- if ( fid >= 0 )
- {
- // changed attribute of existing feature
- if ( !mChangedAttributeValues.contains( fid ) )
- {
- mChangedAttributeValues.insert( fid, QgsAttributeMap() );
- }
-
- mChangedAttributeValues[fid].insert( field, value );
- }
- else
- {
- // updated added feature
- int i;
- for ( i = 0; i < mAddedFeatures.size() && mAddedFeatures[i].id() != fid; i++ )
- ;
-
- if ( i == mAddedFeatures.size() )
- return false;
-
- mAddedFeatures[i].changeAttribute( field, value );
- }
-
+ editAttributeChange( fid, field, value );
setModified( true, false );
if ( emitSignal )
@@ -2648,6 +2657,12 @@
return false;
mMaxUpdatedIndex++;
+
+ if (mActiveCommand != NULL)
+ {
+ mActiveCommand->storeAttributeAdd( mMaxUpdatedIndex, field );
+ }
+
mUpdatedFields.insert( mMaxUpdatedIndex, field );
mAddedAttributeIds.insert( mMaxUpdatedIndex );
@@ -2670,6 +2685,11 @@
!mDataProvider->fields().contains( index ) )
return false;
+ if (mActiveCommand != NULL)
+ {
+ mActiveCommand->storeAttributeDelete( index, mUpdatedFields[index] );
+ }
+
mDeletedAttributeIds.insert( index );
mAddedAttributeIds.remove( index );
mUpdatedFields.remove( index );
@@ -2690,7 +2710,7 @@
return true;
mSelectedFeatureIds.remove( fid ); // remove it from selection
- mDeletedFeatureIds.insert( fid );
+ editFeatureDelete( fid );
setModified( true, false );
@@ -2975,7 +2995,7 @@
mUpdatedFields.clear();
mMaxUpdatedIndex = -1;
-
+ undoStack()->clear();
emit editingStopped();
}
@@ -3715,3 +3735,314 @@
}
return 0; //not found
}
+
+
+void QgsVectorLayer::editGeometryChange( int featureId, QgsGeometry& geometry )
+{
+ if (mActiveCommand != NULL)
+ {
+ mActiveCommand->storeGeometryChange( featureId, mChangedGeometries[ featureId ], geometry );
+ }
+ mChangedGeometries[ featureId ] = geometry;
+}
+
+
+void QgsVectorLayer::editFeatureAdd(QgsFeature& feature)
+{
+ if (mActiveCommand != NULL)
+ {
+ mActiveCommand->storeFeatureAdd( feature );
+ }
+ mAddedFeatures.append( feature );
+}
+
+void QgsVectorLayer::editFeatureDelete(int featureId)
+{
+ if (mActiveCommand != NULL)
+ {
+ mActiveCommand->storeFeatureDelete( featureId );
+ }
+ mDeletedFeatureIds.insert( featureId );
+}
+
+void QgsVectorLayer::editAttributeChange( int featureId, int field, QVariant value)
+{
+ if (mActiveCommand != NULL)
+ {
+ QVariant original;
+ bool isFirstChange = true;
+ if (featureId < 0)
+ {
+ // work with added feature
+ for (int i = 0; i < mAddedFeatures.size(); i++ )
+ {
+ if ( mAddedFeatures[i].id() == featureId && mAddedFeatures[i].attributeMap().contains(field) )
+ {
+ original = mAddedFeatures[i].attributeMap()[field];
+ isFirstChange = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (mChangedAttributeValues.contains(featureId) && mChangedAttributeValues[featureId].contains(field))
+ {
+ original = mChangedAttributeValues[featureId][field];
+ isFirstChange = false;
+ }
+ }
+ mActiveCommand->storeAttributeChange( featureId, field, original, value, isFirstChange );
+ }
+
+ if ( featureId >= 0 )
+ {
+ // changed attribute of existing feature
+ if ( !mChangedAttributeValues.contains( featureId ) )
+ {
+ mChangedAttributeValues.insert( featureId, QgsAttributeMap() );
+ }
+
+ mChangedAttributeValues[featureId].insert( field, value );
+ }
+ else
+ {
+ // updated added feature
+ for ( int i = 0; i < mAddedFeatures.size(); i++ )
+ {
+ if ( mAddedFeatures[i].id() == featureId )
+ {
+ mAddedFeatures[i].changeAttribute( field, value );
+ break;
+ }
+ }
+ }
+}
+
+void QgsVectorLayer::beginEditCommand(QString text)
+{
+ if (mActiveCommand == NULL)
+ {
+ mActiveCommand = new QgsUndoCommand(this, text);
+ }
+}
+
+void QgsVectorLayer::endEditCommand()
+{
+ if (mActiveCommand != NULL)
+ {
+ undoStack()->push(mActiveCommand);
+ mActiveCommand = NULL;
+ }
+
+}
+
+void QgsVectorLayer::destroyEditCommand()
+{
+ if (mActiveCommand != NULL)
+ {
+ delete mActiveCommand;
+ mActiveCommand = NULL;
+ }
+
+}
+
+void QgsVectorLayer::redoEditCommand(QgsUndoCommand* cmd)
+{
+ QMap<int, QgsUndoCommand::GeometryChangeEntry>& geometryChange = cmd->mGeometryChange;
+ QgsFeatureIds& deletedFeatureIdChange = cmd->mDeletedFeatureIdChange;
+ QgsFeatureList& addedFeatures = cmd->mAddedFeatures;
+ QMap<int, QgsUndoCommand::AttributeChanges>& attributeChange = cmd->mAttributeChange;
+ QgsFieldMap& addedAttributes = cmd->mAddedAttributes;
+ QgsFieldMap& deletedAttributes = cmd->mDeletedAttributes;
+
+
+ // geometry changes
+ QMap<int, QgsUndoCommand::GeometryChangeEntry>::iterator it = geometryChange.begin();
+ for ( ; it != geometryChange.end(); ++it )
+ {
+ if (it.value().target == NULL)
+ {
+ mChangedGeometries.remove(it.key());
+ }
+ else
+ {
+ mChangedGeometries[it.key()] = *(it.value().target);
+ }
+ }
+
+ // deleted features
+ QgsFeatureIds::iterator delIt = deletedFeatureIdChange.begin();
+ for ( ; delIt != deletedFeatureIdChange.end(); ++delIt )
+ {
+ mDeletedFeatureIds.insert(*delIt);
+ }
+
+ // added features
+ QgsFeatureList::iterator addIt = addedFeatures.begin();
+ for ( ; addIt != addedFeatures.end(); ++addIt )
+ {
+ mAddedFeatures.append(*addIt);
+ }
+
+ // changed attributes
+ QMap<int, QgsUndoCommand::AttributeChanges>::iterator attrFeatIt = attributeChange.begin();
+ for ( ; attrFeatIt != attributeChange.end(); ++attrFeatIt )
+ {
+ int fid = attrFeatIt.key();
+ // for every changed attribute in feature
+ QMap<int, QgsUndoCommand::AttributeChangeEntry>::iterator attrChIt = attrFeatIt.value().begin();
+ for ( ; attrChIt != attrFeatIt.value().end(); ++attrChIt )
+ {
+ if (fid >= 0)
+ {
+ // existing feature
+ if (attrChIt.value().target.isNull())
+ {
+ mChangedAttributeValues[fid].remove(attrChIt.key());
+ }
+ else
+ {
+ mChangedAttributeValues[fid][attrChIt.key()] = attrChIt.value().target;
+ QgsFeature f;
+ featureAtId(fid, f, false, true);
+ f.changeAttribute( attrChIt.key(), attrChIt.value().target );
+ }
+ }
+ else
+ {
+ // added feature
+ for ( int i = 0; i < mAddedFeatures.size(); i++ )
+ {
+ if (mAddedFeatures[i].id() == fid)
+ {
+ mAddedFeatures[i].changeAttribute( attrChIt.key(), attrChIt.value().target );
+ break;
+ }
+ }
+
+ }
+
+ }
+ }
+
+ // added attributes
+ QgsFieldMap::iterator attrIt = addedAttributes.begin();
+ for ( ; attrIt != addedAttributes.end(); ++attrIt )
+ {
+ int attrIndex = attrIt.key();
+ mAddedAttributeIds.insert( attrIndex );
+ mUpdatedFields.insert( attrIndex, attrIt.value() );
+ }
+
+ // deleted attributes
+ QgsFieldMap::iterator dAttrIt = deletedAttributes.begin();
+ for ( ; dAttrIt != deletedAttributes.end(); ++dAttrIt )
+ {
+ int attrIndex = dAttrIt.key();
+ mDeletedAttributeIds.insert(attrIndex);
+ mUpdatedFields.remove(attrIndex);
+ }
+
+}
+
+void QgsVectorLayer::undoEditCommand(QgsUndoCommand* cmd)
+{
+ QMap<int, QgsUndoCommand::GeometryChangeEntry>& geometryChange = cmd->mGeometryChange;
+ QgsFeatureIds& deletedFeatureIdChange = cmd->mDeletedFeatureIdChange;
+ QgsFeatureList& addedFeatures = cmd->mAddedFeatures;
+ QMap<int, QgsUndoCommand::AttributeChanges>& attributeChange = cmd->mAttributeChange;
+ QgsFieldMap& addedAttributes = cmd->mAddedAttributes;
+ QgsFieldMap& deletedAttributes = cmd->mDeletedAttributes;
+
+ // deleted attributes
+ QgsFieldMap::iterator dAttrIt = deletedAttributes.begin();
+ for ( ; dAttrIt != deletedAttributes.end(); ++dAttrIt )
+ {
+ int attrIndex = dAttrIt.key();
+ mDeletedAttributeIds.remove( attrIndex );
+ mUpdatedFields.insert( attrIndex, dAttrIt.value() );
+ }
+
+ // added attributes
+ QgsFieldMap::iterator attrIt = addedAttributes.begin();
+ for ( ; attrIt != addedAttributes.end(); ++attrIt )
+ {
+ int attrIndex = attrIt.key();
+ mAddedAttributeIds.remove( attrIndex );
+ mUpdatedFields.remove( attrIndex );
+ }
+
+ // geometry changes
+ QMap<int, QgsUndoCommand::GeometryChangeEntry>::iterator it = geometryChange.begin();
+ for ( ; it != geometryChange.end(); ++it )
+ {
+ if (it.value().original == NULL)
+ {
+ mChangedGeometries.remove(it.key());
+ }
+ else
+ {
+ mChangedGeometries[it.key()] = *(it.value().original);
+ }
+ }
+
+ // deleted features
+ QgsFeatureIds::iterator delIt = deletedFeatureIdChange.begin();
+ for ( ; delIt != deletedFeatureIdChange.end(); ++delIt )
+ {
+ mDeletedFeatureIds.remove(*delIt);
+ }
+
+ // added features
+ QgsFeatureList::iterator addIt = addedFeatures.begin();
+ for ( ; addIt != addedFeatures.end(); ++addIt )
+ {
+ QgsFeatureList::iterator addedIt = mAddedFeatures.begin();
+ for ( ; addedIt != mAddedFeatures.end(); ++addedIt )
+ {
+ if (addedIt->id() == addIt->id())
+ {
+ mAddedFeatures.erase(addedIt);
+ break; // feature was found so move to next one
+ }
+ }
+ }
+
+ // updated attributes
+ QMap<int, QgsUndoCommand::AttributeChanges>::iterator attrFeatIt = attributeChange.begin();
+ for ( ; attrFeatIt != attributeChange.end(); ++attrFeatIt )
+ {
+ int fid = attrFeatIt.key();
+ QMap<int, QgsUndoCommand::AttributeChangeEntry>::iterator attrChIt = attrFeatIt.value().begin();
+ for ( ; attrChIt != attrFeatIt.value().end(); ++attrChIt )
+ {
+ if (fid >= 0)
+ {
+ if (attrChIt.value().isFirstChange)
+ {
+ mChangedAttributeValues[fid].remove(attrChIt.key());
+ }
+ else
+ {
+ mChangedAttributeValues[fid][attrChIt.key()] = attrChIt.value().original;
+ }
+ }
+ else
+ {
+ // added feature TODO:
+ for ( int i = 0; i < mAddedFeatures.size(); i++ )
+ {
+ if ( mAddedFeatures[i].id() == fid )
+ {
+ mAddedFeatures[i].changeAttribute( attrChIt.key(), attrChIt.value().original );
+ break;
+ }
+ }
+
+ }
+ emit attributeValueChanged( fid, attrChIt.key(), attrChIt.value().original );
+ }
+ }
+
+}
Modified: trunk/qgis/src/core/qgsvectorlayer.h
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.h 2009-06-14 07:31:56 UTC (rev 10919)
+++ trunk/qgis/src/core/qgsvectorlayer.h 2009-06-14 14:17:18 UTC (rev 10920)
@@ -28,7 +28,6 @@
#include "qgsmaplayer.h"
#include "qgsfeature.h"
#include "qgssnapper.h"
-#include "qgsfeature.h"
#include "qgsfield.h"
class QPainter;
@@ -42,16 +41,21 @@
class QgsLabel;
class QgsRectangle;
class QgsRenderer;
+class QgsUndoCommand;
class QgsVectorDataProvider;
class QgsVectorOverlay;
-class QgsGeometry;
class QgsRectangle;
+
typedef QList<int> QgsAttributeList;
typedef QSet<int> QgsFeatureIds;
typedef QSet<int> QgsAttributeIds;
+
+
+
+
/** \ingroup core
* Vector layer backed by a data source provider.
*/
@@ -453,6 +457,25 @@
*/
QgsVectorOverlay* findOverlayByType( const QString& typeName );
+
+ /**
+ * Create edit command for undo/redo operations
+ * @param text text which is to be displayed in undo window
+ */
+ void beginEditCommand(QString text);
+
+ /** Finish edit command and add it to undo/redo stack */
+ void endEditCommand();
+
+ /** Destroy active command and deletes all changes in it */
+ void destroyEditCommand();
+
+ /** Execute undo operation. To be called only from QgsVectorLayerUndoCommand. */
+ void undoEditCommand(QgsUndoCommand* cmd);
+
+ /** Execute redo operation. To be called only from QgsVectorLayerUndoCommand. */
+ void redoEditCommand(QgsUndoCommand* cmd);
+
public slots:
/** Select feature by its ID, optionally emit signal selectionChanged() */
void select( int featureId, bool emitSignal = TRUE );
@@ -577,6 +600,19 @@
/**Update feature with uncommited geometry updates*/
void updateFeatureGeometry( QgsFeature &f );
+ /** Record changed geometry, store in active command (if any) */
+ void editGeometryChange( int featureId, QgsGeometry& geometry );
+
+ /** Record added feature, store in active command (if any) */
+ void editFeatureAdd(QgsFeature& feature);
+
+ /** Record deleted feature, store in active command (if any) */
+ void editFeatureDelete(int featureId);
+
+ /** Record changed attribute, store in active command (if any) */
+ void editAttributeChange( int featureId, int field, QVariant value );
+
+
private: // Private attributes
/** Update threshold for drawing features as they are read. A value of zero indicates
@@ -678,6 +714,8 @@
QSet<int> mFetchConsidered;
QgsGeometryMap::iterator mFetchChangedGeomIt;
QgsFeatureList::iterator mFetchAddedFeaturesIt;
+
+ QgsUndoCommand * mActiveCommand;
};
#endif
Added: trunk/qgis/src/core/qgsvectorlayerundocommand.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayerundocommand.cpp (rev 0)
+++ trunk/qgis/src/core/qgsvectorlayerundocommand.cpp 2009-06-14 14:17:18 UTC (rev 10920)
@@ -0,0 +1,116 @@
+
+#include "qgsvectorlayerundocommand.h"
+
+#include "qgsgeometry.h"
+#include "qgsvectorlayer.h"
+
+#include "qgslogger.h"
+
+QgsUndoCommand::GeometryChangeEntry::GeometryChangeEntry()
+ : original(NULL), target(NULL)
+{
+}
+
+void QgsUndoCommand::GeometryChangeEntry::setOriginalGeometry(QgsGeometry& orig)
+{
+ if (orig.type() != QGis::UnknownGeometry)
+ {
+ original = new QgsGeometry(orig);
+ }
+ else
+ {
+ original = NULL;
+ }
+}
+
+void QgsUndoCommand::GeometryChangeEntry::setTargetGeometry(QgsGeometry& dest)
+{
+ if (dest.type() != QGis::UnknownGeometry)
+ {
+ target = new QgsGeometry(dest);
+ }
+ else
+ {
+ target = NULL;
+ }
+}
+
+
+QgsUndoCommand::GeometryChangeEntry::~GeometryChangeEntry()
+{
+ delete original;
+ delete target;
+}
+
+
+QgsUndoCommand::QgsUndoCommand(QgsVectorLayer* vlayer, QString text)
+ : QUndoCommand()
+{
+ setText(text);
+ mLayer = vlayer;
+ mFirstRun = true;
+}
+
+void QgsUndoCommand::redo()
+{
+ // when the command is added to the undo stack, the redo() function is called
+ // but we have already done the changes in the layer, so we ignore the first redo() call
+ if (mFirstRun)
+ {
+ mFirstRun = false;
+ return;
+ }
+
+ mLayer->redoEditCommand(this);
+}
+
+void QgsUndoCommand::undo()
+{
+ mLayer->undoEditCommand(this);
+}
+
+
+void QgsUndoCommand::storeGeometryChange(int featureId, QgsGeometry& original, QgsGeometry& target)
+{
+ if ( mGeometryChange.contains( featureId ) )
+ {
+ // geometry has been modified already: just alter the resulting geometry
+ mGeometryChange[featureId].setTargetGeometry(target);
+ }
+ else
+ {
+ // create new entry about changed geometry
+ mGeometryChange.insert( featureId, GeometryChangeEntry() );
+ mGeometryChange[featureId].setOriginalGeometry(original);
+ mGeometryChange[featureId].setTargetGeometry(target);
+ }
+}
+
+void QgsUndoCommand::storeAttributeChange(int featureId, int field, QVariant original, QVariant target, bool isFirstChange)
+{
+ AttributeChangeEntry entry;
+ entry.isFirstChange = isFirstChange;
+ entry.original = original;
+ entry.target = target;
+ mAttributeChange[featureId].insert(field, entry);
+}
+
+void QgsUndoCommand::storeAttributeAdd(int index, const QgsField & value)
+{
+ mAddedAttributes.insert( index, value );
+}
+
+void QgsUndoCommand::storeAttributeDelete(int index, const QgsField & orig)
+{
+ mDeletedAttributes.insert(index, orig );
+}
+
+void QgsUndoCommand::storeFeatureDelete(int featureId)
+{
+ mDeletedFeatureIdChange.insert(featureId);
+}
+
+void QgsUndoCommand::storeFeatureAdd(QgsFeature& feature)
+{
+ mAddedFeatures.append(feature);
+}
Added: trunk/qgis/src/core/qgsvectorlayerundocommand.h
===================================================================
--- trunk/qgis/src/core/qgsvectorlayerundocommand.h (rev 0)
+++ trunk/qgis/src/core/qgsvectorlayerundocommand.h 2009-06-14 14:17:18 UTC (rev 10920)
@@ -0,0 +1,147 @@
+
+#ifndef QGSVECTORLAYERUNDOCOMMAND_H
+#define QGSVECTORLAYERUNDOCOMMAND_H
+
+#include <QUndoCommand>
+
+#include <QVariant>
+#include <QSet>
+#include <QList>
+
+#include "qgsfield.h"
+#include "qgsfeature.h"
+
+class QgsGeometry;
+class QgsVectorLayer;
+
+
+// TODO: copied from qgsvectorlayer.h
+typedef QList<int> QgsAttributeList;
+typedef QSet<int> QgsFeatureIds;
+typedef QSet<int> QgsAttributeIds;
+
+
+
+/**
+ * Class to support universal undo command sequence for application, basic for
+ */
+class QgsUndoCommand : public QUndoCommand
+{
+ public:
+
+ /** change structure for attribute for undo/redo purpose */
+ class AttributeChangeEntry
+ {
+ public:
+ bool isFirstChange;
+ QVariant original;
+ QVariant target;
+ };
+
+ typedef QMap<int, AttributeChangeEntry> AttributeChanges;
+
+ /** change structure to geometry for undo/redo purpose */
+ class GeometryChangeEntry
+ {
+ public:
+ GeometryChangeEntry();
+ ~GeometryChangeEntry();
+
+ void setOriginalGeometry(QgsGeometry& orig);
+ void setTargetGeometry(QgsGeometry& target);
+
+ QgsGeometry* original;
+ QgsGeometry* target;
+ };
+
+
+ QgsUndoCommand(QgsVectorLayer* layer, QString text);
+
+ /**
+ * Necessary function to provide undo operation
+ */
+ void undo();
+
+ /**
+ * Necessary function to provide redo operation
+ */
+ void redo();
+
+ /**
+ * Function to store changes in geometry to be returned to this state after undo/redo
+ * @param featureId id of feature edited
+ * @param original original geometry of feature which was changed
+ * @param target changed geometry which was changed
+ */
+ void storeGeometryChange(int featureId, QgsGeometry& original, QgsGeometry& target);
+
+ /**
+ * Stores changes of attributes for the feature to be returned to this state after undo/redo
+ * @param featureId id of feature for which this chaged is stored
+ * @param field field identifier of field which was changed
+ * @param original original value of attribute before change
+ * @param target target value of attribute after change
+ * @param isFirstChange flag if this change is the first one
+ */
+ void storeAttributeChange(int featureId, int field, QVariant original, QVariant target, bool isFirstChange);
+
+ /**
+ * Add id of feature to deleted list to be reverted if needed afterwards
+ * @param featureId id of feature which is to be deleted
+ */
+ void storeFeatureDelete(int featureId);
+
+ /**
+ * Add new feature to list of new features to be stored for undo/redo operations.
+ * @param feature feature which is to be added
+ */
+ void storeFeatureAdd(QgsFeature& feature);
+
+ /**
+ * Add new attribute to list of attributes to be used for attributes of features for undo/redo operations.
+ * @param index index of attribute which is to be added
+ * @param value field description which is to be stored
+ */
+ void storeAttributeAdd(int index, const QgsField & value);
+
+ /**
+ * Add deleted attribute which is to be stored for undo/redo operations.
+ * @param index index od attribute definition which is to be deleted
+ * @param orig deleted field's description
+ */
+ void storeAttributeDelete(int index, const QgsField & orig);
+
+ private:
+ /** Variable to disable first run of undo, because it's automaticaly done after push */
+ bool mFirstRun;
+
+ /** Layer on which operations should be performed */
+ QgsVectorLayer* mLayer;
+
+ /** Map of changes of geometry for features it describes changes of geometry */
+ QMap<int, GeometryChangeEntry> mGeometryChange;
+
+ /** Map of changes of atrributes for features which describes changes of attributes */
+ QMap<int, AttributeChanges> mAttributeChange;
+
+ /** Deleted feature IDs which are not commited. Note a feature can be added and then deleted
+ again before the change is committed - in that case the added feature would be removed
+ from mAddedFeatures only and *not* entered here.
+ */
+ QgsFeatureIds mDeletedFeatureIdChange;
+
+ /** added attributes fields which are not commited */
+ QgsFieldMap mAddedAttributes;
+
+ /** deleted attributes fields which are not commited */
+ QgsFieldMap mDeletedAttributes;
+
+ /** New features which are not commited. Note a feature can be added and then changed,
+ therefore the details here can be overridden by mChangedAttributeValues and mChangedGeometries.
+ */
+ QgsFeatureList mAddedFeatures;
+
+ friend class QgsVectorLayer;
+};
+
+#endif
More information about the QGIS-commit
mailing list