diff --git a/python/core/qgsvectorlayer.sip b/python/core/qgsvectorlayer.sip index cfb7e786fe5..aff4a20f449 100644 --- a/python/core/qgsvectorlayer.sip +++ b/python/core/qgsvectorlayer.sip @@ -941,6 +941,13 @@ class QgsVectorLayer : QgsMapLayer /** Removes an alias (a display name) for attributes to display in dialogs */ void remAttributeAlias( int attIndex ); + /** Renames an attribute field (but does not commit it). + * @param attIndex attribute index + * @param newName new name of field + * @note added in QGIS 2.16 + */ + bool renameAttribute( int attIndex, const QString& newName ); + /** * Adds a tab (for the attribute editor form) holding groups and fields * diff --git a/python/core/qgsvectorlayereditbuffer.sip b/python/core/qgsvectorlayereditbuffer.sip index a6bcb59b813..b99ec2d1498 100644 --- a/python/core/qgsvectorlayereditbuffer.sip +++ b/python/core/qgsvectorlayereditbuffer.sip @@ -43,6 +43,12 @@ class QgsVectorLayerEditBuffer : QObject /** Delete an attribute field (but does not commit it) */ virtual bool deleteAttribute( int attr ); + /** Renames an attribute field (but does not commit it) + * @param attr attribute index + * @param newName new name of field + * @note added in QGIS 2.16 + */ + virtual bool renameAttribute( int attr, const QString& newName ); /** Attempts to commit any changes to disk. Returns the result of the attempt. @@ -98,10 +104,25 @@ class QgsVectorLayerEditBuffer : QObject void attributeAdded( int idx ); void attributeDeleted( int idx ); + /** Emitted when an attribute has been renamed + * @param idx attribute index + * @param newName new attribute name + * @note added in QGSI 2.16 + */ + void attributeRenamed( int idx, const QString& newName ); + /** Signals emitted after committing changes */ void committedAttributesDeleted( const QString& layerId, const QgsAttributeList& deletedAttributes ); void committedAttributesAdded( const QString& layerId, const QList& addedAttributes ); void committedFeaturesAdded( const QString& layerId, const QgsFeatureList& addedFeatures ); + + /** Emitted after committing an attribute rename + * @param layerId ID of layer + * @param renamedAttributes map of field index to new name + * @note added in QGIS 2.16 + */ + void committedAttributesRenamed( const QString& layerId, const QgsFieldNameMap& renamedAttributes ); + void committedFeaturesRemoved( const QString& layerId, const QgsFeatureIds& deletedFeatureIds ); void committedAttributeValuesChanges( const QString& layerId, const QgsChangedAttributesMap& changedAttributesValues ); void committedGeometriesChanges( const QString& layerId, const QgsGeometryMap& changedGeometries ); diff --git a/python/core/qgsvectorlayereditpassthrough.sip b/python/core/qgsvectorlayereditpassthrough.sip index 9c37e95b328..c00cc77d15d 100644 --- a/python/core/qgsvectorlayereditpassthrough.sip +++ b/python/core/qgsvectorlayereditpassthrough.sip @@ -14,6 +14,7 @@ class QgsVectorLayerEditPassthrough : QgsVectorLayerEditBuffer bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); bool addAttribute( const QgsField &field ); bool deleteAttribute( int attr ); + bool renameAttribute( int attr, const QString& newName ); bool commitChanges( QStringList& commitErrors ); void rollBack(); diff --git a/python/core/qgsvectorlayerundocommand.sip b/python/core/qgsvectorlayerundocommand.sip index 9e8a637961f..7bd3c5575d1 100644 --- a/python/core/qgsvectorlayerundocommand.sip +++ b/python/core/qgsvectorlayerundocommand.sip @@ -1,10 +1,21 @@ +/** \ingroup core + * \class QgsVectorLayerUndoCommand + * \brief Base class for undo commands within a QgsVectorLayerEditBuffer. + */ + class QgsVectorLayerUndoCommand : QUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommand + * @param buffer associated edit buffer + */ QgsVectorLayerUndoCommand( QgsVectorLayerEditBuffer *buffer /Transfer/ ); + + //! Returns the layer associated with the undo command QgsVectorLayer *layer(); QgsGeometryCache *cache(); @@ -13,12 +24,22 @@ class QgsVectorLayerUndoCommand : QUndoCommand }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandAddFeature + * \brief Undo command for adding a feature to a vector layer. + */ + class QgsVectorLayerUndoCommandAddFeature : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandAddFeature + * @param buffer associated edit buffer + * @param f feature to add to layer + */ QgsVectorLayerUndoCommandAddFeature( QgsVectorLayerEditBuffer* buffer /Transfer/, QgsFeature& f ); virtual void undo(); @@ -26,12 +47,22 @@ class QgsVectorLayerUndoCommandAddFeature : QgsVectorLayerUndoCommand }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandDeleteFeature + * \brief Undo command for deleting a feature from a vector layer. + */ + class QgsVectorLayerUndoCommandDeleteFeature : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandDeleteFeature + * @param buffer associated edit buffer + * @param fid feature ID of feature to delete from layer + */ QgsVectorLayerUndoCommandDeleteFeature( QgsVectorLayerEditBuffer* buffer /Transfer/, QgsFeatureId fid ); virtual void undo(); @@ -39,12 +70,23 @@ class QgsVectorLayerUndoCommandDeleteFeature : QgsVectorLayerUndoCommand }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandChangeGeometry + * \brief Undo command for modifying the geometry of a feature from a vector layer. + */ + class QgsVectorLayerUndoCommandChangeGeometry : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandChangeGeometry + * @param buffer associated edit buffer + * @param fid feature ID of feature to modify geometry of + * @param newGeom new geometry for feature + */ QgsVectorLayerUndoCommandChangeGeometry( QgsVectorLayerEditBuffer* buffer /Transfer/, QgsFeatureId fid, QgsGeometry* newGeom /Transfer/ ); ~QgsVectorLayerUndoCommandChangeGeometry(); @@ -55,24 +97,47 @@ class QgsVectorLayerUndoCommandChangeGeometry : QgsVectorLayerUndoCommand }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandChangeAttribute + * \brief Undo command for modifying an attribute of a feature from a vector layer. + */ + class QgsVectorLayerUndoCommandChangeAttribute : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandChangeAttribute + * @param buffer associated edit buffer + * @param fid feature ID of feature to modify + * @param fieldIndex index of field to modify + * @param newValue new value of attribute + * @param oldValue previous value of attribute + */ QgsVectorLayerUndoCommandChangeAttribute( QgsVectorLayerEditBuffer* buffer /Transfer/, QgsFeatureId fid, int fieldIndex, const QVariant &newValue, const QVariant &oldValue ); virtual void undo(); virtual void redo(); }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandAddAttribute + * \brief Undo command for adding a new attribute to a vector layer. + */ + class QgsVectorLayerUndoCommandAddAttribute : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandAddAttribute + * @param buffer associated edit buffer + * @param field definition of new field to add + */ QgsVectorLayerUndoCommandAddAttribute( QgsVectorLayerEditBuffer* buffer /Transfer/, const QgsField& field ); virtual void undo(); @@ -80,15 +145,51 @@ class QgsVectorLayerUndoCommandAddAttribute : QgsVectorLayerUndoCommand }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandDeleteAttribute + * \brief Undo command for removing an existing attribute from a vector layer. + */ + class QgsVectorLayerUndoCommandDeleteAttribute : QgsVectorLayerUndoCommand { %TypeHeaderCode #include "qgsvectorlayerundocommand.h" %End public: + + /** Constructor for QgsVectorLayerUndoCommandDeleteAttribute + * @param buffer associated edit buffer + * @param fieldIndex index of field to delete + */ QgsVectorLayerUndoCommandDeleteAttribute( QgsVectorLayerEditBuffer* buffer /Transfer/, int fieldIndex ); virtual void undo(); virtual void redo(); }; + + +/** \ingroup core + * \class QgsVectorLayerUndoCommandRenameAttribute + * \brief Undo command for renaming an existing attribute of a vector layer. + * \note added in QGIS 2.16 + */ + +class QgsVectorLayerUndoCommandRenameAttribute : QgsVectorLayerUndoCommand +{ +%TypeHeaderCode +#include "qgsvectorlayerundocommand.h" +%End + public: + + /** Constructor for QgsVectorLayerUndoCommandRenameAttribute + * @param buffer associated edit buffer + * @param fieldIndex index of field to rename + * @param newName new name for field + */ + QgsVectorLayerUndoCommandRenameAttribute( QgsVectorLayerEditBuffer* buffer /Transfer/, int fieldIndex, const QString& newName ); + + virtual void undo(); + virtual void redo(); + +}; diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index b025b4aa5af..c5891e40b64 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2360,6 +2360,14 @@ void QgsVectorLayer::remAttributeAlias( int attIndex ) } } +bool QgsVectorLayer::renameAttribute( int attIndex, const QString& newName ) +{ + if ( !mEditBuffer || !mDataProvider ) + return false; + + return mEditBuffer->renameAttribute( attIndex, newName ); +} + void QgsVectorLayer::addAttributeAlias( int attIndex, const QString& aliasString ) { if ( attIndex < 0 || attIndex >= fields().count() ) diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 3adb15401af..6d21e354059 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1316,6 +1316,13 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Removes an alias (a display name) for attributes to display in dialogs */ void remAttributeAlias( int attIndex ); + /** Renames an attribute field (but does not commit it). + * @param attIndex attribute index + * @param newName new name of field + * @note added in QGIS 2.16 + */ + bool renameAttribute( int attIndex, const QString& newName ); + /** * Adds a tab (for the attribute editor form) holding groups and fields * diff --git a/src/core/qgsvectorlayereditbuffer.cpp b/src/core/qgsvectorlayereditbuffer.cpp index 99d82d30a12..4b393a856a7 100644 --- a/src/core/qgsvectorlayereditbuffer.cpp +++ b/src/core/qgsvectorlayereditbuffer.cpp @@ -72,6 +72,12 @@ void QgsVectorLayerEditBuffer::updateFields( QgsFields& fields ) { fields.append( mAddedAttributes[i], QgsFields::OriginEdit, i ); } + // rename fields + QgsFieldNameMap::const_iterator renameIt = mRenamedAttributes.constBegin(); + for ( ; renameIt != mRenamedAttributes.constEnd(); ++renameIt ) + { + fields[ renameIt.key()].setName( renameIt.value() ); + } } @@ -261,6 +267,27 @@ bool QgsVectorLayerEditBuffer::deleteAttribute( int index ) return true; } +bool QgsVectorLayerEditBuffer::renameAttribute( int index, const QString& newName ) +{ + if ( !( L->dataProvider()->capabilities() & QgsVectorDataProvider::RenameAttributes ) ) + return false; + + if ( newName.isEmpty() ) + return false; + + if ( index < 0 || index >= L->fields().count() ) + return false; + + Q_FOREACH ( const QgsField& updatedField, L->fields() ) + { + if ( updatedField.name() == newName ) + return false; + } + + L->undoStack()->push( new QgsVectorLayerUndoCommandRenameAttribute( this, index, newName ) ); + return true; +} + bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors ) { @@ -355,6 +382,25 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors ) } } + // rename attributes + if ( !mRenamedAttributes.isEmpty() ) + { + if (( cap & QgsVectorDataProvider::RenameAttributes ) && provider->renameAttributes( mRenamedAttributes ) ) + { + commitErrors << tr( "SUCCESS: %n attribute(s) renamed.", "renamed attributes count", mRenamedAttributes.size() ); + + emit committedAttributesRenamed( L->id(), mRenamedAttributes ); + + mRenamedAttributes.clear(); + attributesChanged = true; + } + else + { + commitErrors << tr( "ERROR: %n attribute(s) not renamed", "not renamed attributes count", mRenamedAttributes.size() ); + success = false; + } + } + // // check that addition/removal went as expected // @@ -620,6 +666,20 @@ void QgsVectorLayerEditBuffer::handleAttributeAdded( int index ) attrs.insert( index, QVariant() ); featureIt->setAttributes( attrs ); } + + // go through renamed attributes and adapt + QList< int > sortedRenamedIndices = mRenamedAttributes.keys(); + //sort keys + qSort( sortedRenamedIndices.begin(), sortedRenamedIndices.end(), qGreater< int >() ); + Q_FOREACH ( int renameIndex, sortedRenamedIndices ) + { + if ( renameIndex >= index ) + { + mRenamedAttributes[ renameIndex + 1 ] = mRenamedAttributes.value( renameIndex ); + } + } + //remove last + mRenamedAttributes.remove( index ); } void QgsVectorLayerEditBuffer::handleAttributeDeleted( int index ) @@ -645,6 +705,24 @@ void QgsVectorLayerEditBuffer::handleAttributeDeleted( int index ) attrs.remove( index ); featureIt->setAttributes( attrs ); } + + // go through rename attributes and adapt + QList< int > sortedRenamedIndices = mRenamedAttributes.keys(); + //sort keys + qSort( sortedRenamedIndices.begin(), sortedRenamedIndices.end() ); + int last = -1; + mRenamedAttributes.remove( index ); + Q_FOREACH ( int renameIndex, sortedRenamedIndices ) + { + if ( renameIndex > index ) + { + mRenamedAttributes.insert( renameIndex - 1, mRenamedAttributes.value( renameIndex ) ); + last = renameIndex; + } + } + //remove last + if ( last > -1 ) + mRenamedAttributes.remove( last ); } diff --git a/src/core/qgsvectorlayereditbuffer.h b/src/core/qgsvectorlayereditbuffer.h index 6c95fc24fe3..2da8cf86b8b 100644 --- a/src/core/qgsvectorlayereditbuffer.h +++ b/src/core/qgsvectorlayereditbuffer.h @@ -69,6 +69,12 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject /** Delete an attribute field (but does not commit it) */ virtual bool deleteAttribute( int attr ); + /** Renames an attribute field (but does not commit it) + * @param attr attribute index + * @param newName new name of field + * @note added in QGIS 2.16 + */ + virtual bool renameAttribute( int attr, const QString& newName ); /** Attempts to commit any changes to disk. Returns the result of the attempt. @@ -124,9 +130,23 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject void attributeAdded( int idx ); void attributeDeleted( int idx ); + /** Emitted when an attribute has been renamed + * @param idx attribute index + * @param newName new attribute name + * @note added in QGSI 2.16 + */ + void attributeRenamed( int idx, const QString& newName ); + /** Signals emitted after committing changes */ void committedAttributesDeleted( const QString& layerId, const QgsAttributeList& deletedAttributes ); void committedAttributesAdded( const QString& layerId, const QList& addedAttributes ); + + /** Emitted after committing an attribute rename + * @param layerId ID of layer + * @param renamedAttributes map of field index to new name + * @note added in QGIS 2.16 + */ + void committedAttributesRenamed( const QString& layerId, const QgsFieldNameMap& renamedAttributes ); void committedFeaturesAdded( const QString& layerId, const QgsFeatureList& addedFeatures ); void committedFeaturesRemoved( const QString& layerId, const QgsFeatureIds& deletedFeatureIds ); void committedAttributeValuesChanges( const QString& layerId, const QgsChangedAttributesMap& changedAttributesValues ); @@ -150,7 +170,6 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject /** Update added and changed features after removal of an attribute */ void handleAttributeDeleted( int index ); - /** Updates an index in an attribute map to a new value (for updates of changed attributes) */ void updateAttributeMapIndex( QgsAttributeMap& attrs, int index, int offset ) const; @@ -167,6 +186,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject friend class QgsVectorLayerUndoCommandChangeAttribute; friend class QgsVectorLayerUndoCommandAddAttribute; friend class QgsVectorLayerUndoCommandDeleteAttribute; + friend class QgsVectorLayerUndoCommandRenameAttribute; /** 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 @@ -186,6 +206,9 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject /** Added attributes fields which are not commited */ QList mAddedAttributes; + /** Renamed attributes which are not commited. */ + QgsFieldNameMap mRenamedAttributes; + /** Changed geometries which are not commited. */ QgsGeometryMap mChangedGeometries; }; diff --git a/src/core/qgsvectorlayereditpassthrough.cpp b/src/core/qgsvectorlayereditpassthrough.cpp index 457f3bb539b..fa028eb1da9 100644 --- a/src/core/qgsvectorlayereditpassthrough.cpp +++ b/src/core/qgsvectorlayereditpassthrough.cpp @@ -133,6 +133,19 @@ bool QgsVectorLayerEditPassthrough::deleteAttribute( int attr ) return false; } +bool QgsVectorLayerEditPassthrough::renameAttribute( int attr, const QString& newName ) +{ + QgsFieldNameMap map; + map[ attr ] = newName; + if ( L->dataProvider()->renameAttributes( map ) ) + { + mModified = true; + emit attributeRenamed( attr, newName ); + return true; + } + return false; +} + bool QgsVectorLayerEditPassthrough::commitChanges( QStringList& /*commitErrors*/ ) { mModified = false; diff --git a/src/core/qgsvectorlayereditpassthrough.h b/src/core/qgsvectorlayereditpassthrough.h index e595704b48c..3c439439a42 100644 --- a/src/core/qgsvectorlayereditpassthrough.h +++ b/src/core/qgsvectorlayereditpassthrough.h @@ -33,6 +33,7 @@ class CORE_EXPORT QgsVectorLayerEditPassthrough : public QgsVectorLayerEditBuffe bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) override; bool addAttribute( const QgsField &field ) override; bool deleteAttribute( int attr ) override; + bool renameAttribute( int attr, const QString& newName ) override; bool commitChanges( QStringList& commitErrors ) override; void rollBack() override; diff --git a/src/core/qgsvectorlayerundocommand.cpp b/src/core/qgsvectorlayerundocommand.cpp index 39f8bdb0d1a..046e400973f 100644 --- a/src/core/qgsvectorlayerundocommand.cpp +++ b/src/core/qgsvectorlayerundocommand.cpp @@ -355,6 +355,11 @@ QgsVectorLayerUndoCommandDeleteAttribute::QgsVectorLayerUndoCommandDeleteAttribu mOldField = mBuffer->mAddedAttributes[mOriginIndex]; } + if ( mBuffer->mRenamedAttributes.contains( fieldIndex ) ) + { + mOldName = mBuffer->mRenamedAttributes.value( fieldIndex ); + } + // save values of new features for ( QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.constBegin(); it != mBuffer->mAddedFeatures.constEnd(); ++it ) { @@ -386,6 +391,12 @@ void QgsVectorLayerUndoCommandDeleteAttribute::undo() mBuffer->updateLayerFields(); mBuffer->handleAttributeAdded( mFieldIndex ); // update changed attributes + new features + if ( !mOldName.isEmpty() ) + { + mBuffer->mRenamedAttributes[ mFieldIndex ] = mOldName; + mBuffer->updateLayerFields(); + } + // set previously used attributes of new features for ( QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.begin(); it != mBuffer->mAddedFeatures.end(); ++it ) { @@ -425,3 +436,26 @@ void QgsVectorLayerUndoCommandDeleteAttribute::redo() mBuffer->updateLayerFields(); emit mBuffer->attributeDeleted( mFieldIndex ); } + + +QgsVectorLayerUndoCommandRenameAttribute::QgsVectorLayerUndoCommandRenameAttribute( QgsVectorLayerEditBuffer* buffer, int fieldIndex, const QString& newName ) + : QgsVectorLayerUndoCommand( buffer ) + , mFieldIndex( fieldIndex ) + , mOldName( layer()->fields().at( fieldIndex ).name() ) + , mNewName( newName ) +{ +} + +void QgsVectorLayerUndoCommandRenameAttribute::undo() +{ + mBuffer->mRenamedAttributes[ mFieldIndex ] = mOldName; + mBuffer->updateLayerFields(); + emit mBuffer->attributeRenamed( mFieldIndex, mOldName ); +} + +void QgsVectorLayerUndoCommandRenameAttribute::redo() +{ + mBuffer->mRenamedAttributes[ mFieldIndex ] = mNewName; + mBuffer->updateLayerFields(); + emit mBuffer->attributeRenamed( mFieldIndex, mNewName ); +} diff --git a/src/core/qgsvectorlayerundocommand.h b/src/core/qgsvectorlayerundocommand.h index 53775a46eb6..9c16b9c6bbe 100644 --- a/src/core/qgsvectorlayerundocommand.h +++ b/src/core/qgsvectorlayerundocommand.h @@ -31,14 +31,24 @@ class QgsGeometryCache; #include "qgsvectorlayer.h" #include "qgsvectorlayereditbuffer.h" +/** \ingroup core + * \class QgsVectorLayerUndoCommand + * \brief Base class for undo commands within a QgsVectorLayerEditBuffer. + */ class CORE_EXPORT QgsVectorLayerUndoCommand : public QUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommand + * @param buffer associated edit buffer + */ QgsVectorLayerUndoCommand( QgsVectorLayerEditBuffer *buffer ) : QUndoCommand() , mBuffer( buffer ) {} + + //! Returns the layer associated with the undo command inline QgsVectorLayer *layer() { return mBuffer->L; } inline QgsGeometryCache *cache() { return mBuffer->L->cache(); } @@ -46,13 +56,24 @@ class CORE_EXPORT QgsVectorLayerUndoCommand : public QUndoCommand virtual bool mergeWith( const QUndoCommand * ) override { return false; } protected: + //! Associated edit buffer QgsVectorLayerEditBuffer* mBuffer; }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandAddFeature + * \brief Undo command for adding a feature to a vector layer. + */ + class CORE_EXPORT QgsVectorLayerUndoCommandAddFeature : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandAddFeature + * @param buffer associated edit buffer + * @param f feature to add to layer + */ QgsVectorLayerUndoCommandAddFeature( QgsVectorLayerEditBuffer* buffer, QgsFeature& f ); virtual void undo() override; @@ -63,9 +84,19 @@ class CORE_EXPORT QgsVectorLayerUndoCommandAddFeature : public QgsVectorLayerUnd }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandDeleteFeature + * \brief Undo command for deleting a feature from a vector layer. + */ + class CORE_EXPORT QgsVectorLayerUndoCommandDeleteFeature : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandDeleteFeature + * @param buffer associated edit buffer + * @param fid feature ID of feature to delete from layer + */ QgsVectorLayerUndoCommandDeleteFeature( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid ); virtual void undo() override; @@ -76,10 +107,20 @@ class CORE_EXPORT QgsVectorLayerUndoCommandDeleteFeature : public QgsVectorLayer QgsFeature mOldAddedFeature; }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandChangeGeometry + * \brief Undo command for modifying the geometry of a feature from a vector layer. + */ class CORE_EXPORT QgsVectorLayerUndoCommandChangeGeometry : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandChangeGeometry + * @param buffer associated edit buffer + * @param fid feature ID of feature to modify geometry of + * @param newGeom new geometry for feature + */ QgsVectorLayerUndoCommandChangeGeometry( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid, QgsGeometry* newGeom ); ~QgsVectorLayerUndoCommandChangeGeometry(); @@ -95,9 +136,22 @@ class CORE_EXPORT QgsVectorLayerUndoCommandChangeGeometry : public QgsVectorLaye }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandChangeAttribute + * \brief Undo command for modifying an attribute of a feature from a vector layer. + */ + class CORE_EXPORT QgsVectorLayerUndoCommandChangeAttribute : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandChangeAttribute + * @param buffer associated edit buffer + * @param fid feature ID of feature to modify + * @param fieldIndex index of field to modify + * @param newValue new value of attribute + * @param oldValue previous value of attribute + */ QgsVectorLayerUndoCommandChangeAttribute( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid, int fieldIndex, const QVariant &newValue, const QVariant &oldValue ); virtual void undo() override; virtual void redo() override; @@ -110,10 +164,19 @@ class CORE_EXPORT QgsVectorLayerUndoCommandChangeAttribute : public QgsVectorLay bool mFirstChange; }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandAddAttribute + * \brief Undo command for adding a new attribute to a vector layer. + */ class CORE_EXPORT QgsVectorLayerUndoCommandAddAttribute : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandAddAttribute + * @param buffer associated edit buffer + * @param field definition of new field to add + */ QgsVectorLayerUndoCommandAddAttribute( QgsVectorLayerEditBuffer* buffer, const QgsField& field ); virtual void undo() override; @@ -124,10 +187,19 @@ class CORE_EXPORT QgsVectorLayerUndoCommandAddAttribute : public QgsVectorLayerU int mFieldIndex; }; +/** \ingroup core + * \class QgsVectorLayerUndoCommandDeleteAttribute + * \brief Undo command for removing an existing attribute from a vector layer. + */ class CORE_EXPORT QgsVectorLayerUndoCommandDeleteAttribute : public QgsVectorLayerUndoCommand { public: + + /** Constructor for QgsVectorLayerUndoCommandDeleteAttribute + * @param buffer associated edit buffer + * @param fieldIndex index of field to delete + */ QgsVectorLayerUndoCommandDeleteAttribute( QgsVectorLayerEditBuffer* buffer, int fieldIndex ); virtual void undo() override; @@ -141,6 +213,34 @@ class CORE_EXPORT QgsVectorLayerUndoCommandDeleteAttribute : public QgsVectorLay QgsEditorWidgetConfig mOldEditorWidgetConfig; QMap mDeletedValues; + QString mOldName; +}; + + +/** \ingroup core + * \class QgsVectorLayerUndoCommandRenameAttribute + * \brief Undo command for renaming an existing attribute of a vector layer. + * \note added in QGIS 2.16 + */ + +class CORE_EXPORT QgsVectorLayerUndoCommandRenameAttribute : public QgsVectorLayerUndoCommand +{ + public: + + /** Constructor for QgsVectorLayerUndoCommandRenameAttribute + * @param buffer associated edit buffer + * @param fieldIndex index of field to rename + * @param newName new name for field + */ + QgsVectorLayerUndoCommandRenameAttribute( QgsVectorLayerEditBuffer* buffer, int fieldIndex, const QString& newName ); + + virtual void undo() override; + virtual void redo() override; + + private: + int mFieldIndex; + QString mOldName; + QString mNewName; }; diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 3c70307a806..6de4f74d242 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -832,6 +832,181 @@ class TestQgsVectorLayer(unittest.TestCase): layer.commitChanges() checkAfter2() + # RENAME ATTRIBUTE + + def test_RenameAttribute(self): + layer = createLayerWithOnePoint() + + # without editing mode + self.assertFalse(layer.renameAttribute(0, 'renamed')) + + def checkFieldNames(names): + flds = layer.fields() + f = next(layer.getFeatures()) + self.assertEqual(flds.count(), len(names)) + self.assertEqual(f.fields().count(), len(names)) + + for idx, expected_name in enumerate(names): + self.assertEqual(flds[idx].name(), expected_name) + self.assertEqual(f.fields().at(idx).name(), expected_name) + + layer.startEditing() + + checkFieldNames(['fldtxt', 'fldint']) + + self.assertFalse(layer.renameAttribute(-1, 'fldtxt2')) + self.assertFalse(layer.renameAttribute(10, 'fldtxt2')) + self.assertFalse(layer.renameAttribute(0, 'fldint')) # duplicate name + + self.assertTrue(layer.renameAttribute(0, 'fldtxt2')) + checkFieldNames(['fldtxt2', 'fldint']) + + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint']) + layer.undoStack().redo() + checkFieldNames(['fldtxt2', 'fldint']) + + # change two fields + self.assertTrue(layer.renameAttribute(1, 'fldint2')) + checkFieldNames(['fldtxt2', 'fldint2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt2', 'fldint']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint']) + layer.undoStack().redo() + checkFieldNames(['fldtxt2', 'fldint']) + layer.undoStack().redo() + checkFieldNames(['fldtxt2', 'fldint2']) + + # two renames + self.assertTrue(layer.renameAttribute(0, 'fldtxt3')) + checkFieldNames(['fldtxt3', 'fldint2']) + self.assertTrue(layer.renameAttribute(0, 'fldtxt4')) + checkFieldNames(['fldtxt4', 'fldint2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt3', 'fldint2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt2', 'fldint2']) + layer.undoStack().redo() + checkFieldNames(['fldtxt3', 'fldint2']) + layer.undoStack().redo() + checkFieldNames(['fldtxt4', 'fldint2']) + + def test_RenameAttributeAfterAdd(self): + layer = createLayerWithOnePoint() + + def checkFieldNames(names): + flds = layer.fields() + f = next(layer.getFeatures()) + self.assertEqual(flds.count(), len(names)) + self.assertEqual(f.fields().count(), len(names)) + + for idx, expected_name in enumerate(names): + self.assertEqual(flds[idx].name(), expected_name) + self.assertEqual(f.fields().at(idx).name(), expected_name) + + layer.startEditing() + + checkFieldNames(['fldtxt', 'fldint']) + self.assertTrue(layer.renameAttribute(1, 'fldint2')) + checkFieldNames(['fldtxt', 'fldint2']) + #add an attribute + self.assertTrue(layer.addAttribute(QgsField("flddouble", QVariant.Double, "double"))) + checkFieldNames(['fldtxt', 'fldint2', 'flddouble']) + # rename it + self.assertTrue(layer.renameAttribute(2, 'flddouble2')) + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2']) + self.assertTrue(layer.addAttribute(QgsField("flddate", QVariant.Date, "date"))) + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate']) + self.assertTrue(layer.renameAttribute(2, 'flddouble3')) + checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate']) + self.assertTrue(layer.renameAttribute(3, 'flddate2')) + checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate2']) + + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint']) + + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2']) + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble']) + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2']) + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate']) + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate']) + layer.undoStack().redo() + checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate2']) + + def test_RenameAttributeAndDelete(self): + layer = createLayerWithOnePoint() + layer.dataProvider().addAttributes( + [QgsField("flddouble", QVariant.Double, "double")]) + layer.updateFields() + + def checkFieldNames(names): + flds = layer.fields() + f = next(layer.getFeatures()) + self.assertEqual(flds.count(), len(names)) + self.assertEqual(f.fields().count(), len(names)) + + for idx, expected_name in enumerate(names): + self.assertEqual(flds[idx].name(), expected_name) + self.assertEqual(f.fields().at(idx).name(), expected_name) + + layer.startEditing() + + checkFieldNames(['fldtxt', 'fldint', 'flddouble']) + self.assertTrue(layer.renameAttribute(0, 'fldtxt2')) + checkFieldNames(['fldtxt2', 'fldint', 'flddouble']) + self.assertTrue(layer.renameAttribute(2, 'flddouble2')) + checkFieldNames(['fldtxt2', 'fldint', 'flddouble2']) + + #delete an attribute + self.assertTrue(layer.deleteAttribute(0)) + checkFieldNames(['fldint', 'flddouble2']) + # rename remaining + self.assertTrue(layer.renameAttribute(0, 'fldint2')) + checkFieldNames(['fldint2', 'flddouble2']) + self.assertTrue(layer.renameAttribute(1, 'flddouble3')) + checkFieldNames(['fldint2', 'flddouble3']) + #delete an attribute + self.assertTrue(layer.deleteAttribute(0)) + checkFieldNames(['flddouble3']) + self.assertTrue(layer.renameAttribute(0, 'flddouble4')) + checkFieldNames(['flddouble4']) + + layer.undoStack().undo() + checkFieldNames(['flddouble3']) + layer.undoStack().undo() + checkFieldNames(['fldint2', 'flddouble3']) + layer.undoStack().undo() + checkFieldNames(['fldint2', 'flddouble2']) + layer.undoStack().undo() + checkFieldNames(['fldint', 'flddouble2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt2', 'fldint', 'flddouble2']) + layer.undoStack().undo() + checkFieldNames(['fldtxt2', 'fldint', 'flddouble']) + layer.undoStack().undo() + checkFieldNames(['fldtxt', 'fldint', 'flddouble']) + + #layer.undoStack().redo() + #checkFieldNames(['fldtxt2', 'fldint']) + #layer.undoStack().redo() + #checkFieldNames(['fldint']) + def test_fields(self): layer = createLayerWithOnePoint()