diff --git a/python/core/qgsdefaultvalue.sip b/python/core/qgsdefaultvalue.sip index e80dc1aaeca..b0c28095877 100644 --- a/python/core/qgsdefaultvalue.sip +++ b/python/core/qgsdefaultvalue.sip @@ -72,11 +72,17 @@ class QgsDefaultValue bool isValid() const; %Docstring - Returns if this default value is should be applied. + Returns if this default value should be applied. :return: false if the expression is a null string. :rtype: bool %End + operator bool() const; +%Docstring + Checks if a default value is set. Alias for isValid(). + :return: false if the expression is a null string. +%End + }; /************************************************************************ diff --git a/python/core/qgsvectorlayer.sip b/python/core/qgsvectorlayer.sip index aaaac7031de..6571f50165c 100644 --- a/python/core/qgsvectorlayer.sip +++ b/python/core/qgsvectorlayer.sip @@ -925,7 +925,7 @@ Return the provider type for this layer virtual bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = 0 ); - bool updateFeature( QgsFeature &f ); + bool updateFeature( const QgsFeature &f ); %Docstring Updates an existing feature. This method needs to query the datasource on every call. Consider using changeAttributeValue() or @@ -1172,13 +1172,13 @@ Returns list of attributes making up the primary key :rtype: bool %End - bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ); + bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom, bool skipDefaultValue = false ); %Docstring Change feature's geometry :rtype: bool %End - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false ); %Docstring Changes an attribute value (but does not commit it) diff --git a/src/core/qgsdefaultvalue.cpp b/src/core/qgsdefaultvalue.cpp index 7e44bf004ca..01d6e52aed6 100644 --- a/src/core/qgsdefaultvalue.cpp +++ b/src/core/qgsdefaultvalue.cpp @@ -50,5 +50,10 @@ void QgsDefaultValue::setApplyOnUpdate( bool applyOnUpdate ) bool QgsDefaultValue::isValid() const { - return mExpression.isEmpty(); + return !mExpression.isEmpty(); +} + +QgsDefaultValue::operator bool() const +{ + return !mExpression.isEmpty(); } diff --git a/src/core/qgsdefaultvalue.h b/src/core/qgsdefaultvalue.h index c4e55ba3067..96e09c2bc65 100644 --- a/src/core/qgsdefaultvalue.h +++ b/src/core/qgsdefaultvalue.h @@ -84,11 +84,17 @@ class CORE_EXPORT QgsDefaultValue void setApplyOnUpdate( bool applyOnUpdate ); /** - * Returns if this default value is should be applied. + * Returns if this default value should be applied. * \returns false if the expression is a null string. */ bool isValid() const; + /** + * Checks if a default value is set. Alias for isValid(). + * \returns false if the expression is a null string. + */ + operator bool() const; + private: QString mExpression; bool mApplyOnUpdate = false; diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 315b5ff01ad..98590171ab1 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -766,6 +766,25 @@ void QgsVectorLayer::setExtent( const QgsRectangle &r ) mValidExtent = true; } +void QgsVectorLayer::updateDefaultValues( QgsFeatureId fid, QgsFeature feature ) +{ + if ( !mDefaultValueOnUpdateFields.isEmpty() ) + { + if ( !feature.isValid() ) + feature = getFeature( fid ); + + const QgsFields fields = mFields; + int size = fields.size(); + for ( int idx : qgsAsConst( mDefaultValueOnUpdateFields ) ) + { + if ( idx < 0 || idx >= size ) + continue; + + defaultValue( idx, feature ); + } + } +} + QgsRectangle QgsVectorLayer::extent() const { QgsRectangle rect; @@ -941,47 +960,52 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags ) return success; } -bool QgsVectorLayer::updateFeature( QgsFeature &f ) +bool QgsVectorLayer::updateFeature( const QgsFeature &updatedFeature ) { - QgsFeatureRequest req; - req.setFilterFid( f.id() ); - if ( !f.hasGeometry() ) - req.setFlags( QgsFeatureRequest::NoGeometry ); - if ( f.attributes().isEmpty() ) - req.setSubsetOfAttributes( QgsAttributeList() ); + bool hasChanged = false; + bool hasError = false; - QgsFeature current; - if ( !getFeatures( req ).nextFeature( current ) ) + QgsFeature currentFeature = getFeature( updatedFeature.id() ); + if ( currentFeature.isValid() ) { - QgsDebugMsg( QString( "feature %1 could not be retrieved" ).arg( f.id() ) ); - return false; - } + QgsDebugMsg( QString( "feature %1 could not be retrieved" ).arg( updatedFeature.id() ) ); - if ( f.hasGeometry() && current.hasGeometry() && !f.geometry().isGeosEqual( current.geometry() ) ) - { - if ( !changeGeometry( f.id(), f.geometry() ) ) + if ( updatedFeature.hasGeometry() && currentFeature.hasGeometry() && !updatedFeature.geometry().isGeosEqual( currentFeature.geometry() ) ) { - QgsDebugMsg( QString( "geometry of feature %1 could not be changed." ).arg( f.id() ) ); - return false; - } - } - - QgsAttributes fa = f.attributes(); - QgsAttributes ca = current.attributes(); - - for ( int attr = 0; attr < fa.count(); ++attr ) - { - if ( fa.at( attr ) != ca.at( attr ) ) - { - if ( !changeAttributeValue( f.id(), attr, fa.at( attr ), ca.at( attr ) ) ) + if ( changeGeometry( updatedFeature.id(), updatedFeature.geometry(), true ) ) { - QgsDebugMsg( QString( "attribute %1 of feature %2 could not be changed." ).arg( attr ).arg( f.id() ) ); - return false; + hasChanged = true; + } + else + { + QgsDebugMsg( QString( "geometry of feature %1 could not be changed." ).arg( updatedFeature.id() ) ); + } + } + + QgsAttributes fa = updatedFeature.attributes(); + QgsAttributes ca = currentFeature.attributes(); + + for ( int attr = 0; attr < fa.count(); ++attr ) + { + if ( fa.at( attr ) != ca.at( attr ) ) + { + if ( changeAttributeValue( updatedFeature.id(), attr, fa.at( attr ), ca.at( attr ), true ) ) + { + hasChanged = true; + } + else + { + QgsDebugMsg( QString( "attribute %1 of feature %2 could not be changed." ).arg( attr ).arg( updatedFeature.id() ) ); + hasError = true; + } } } } - return true; + if ( hasChanged && !mDefaultValueOnUpdateFields.isEmpty() ) + updateDefaultValues( updatedFeature.id(), updatedFeature ); + + return !hasError; } @@ -2260,7 +2284,7 @@ bool QgsVectorLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &error } -bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) +bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom, bool skipDefaultValue ) { if ( !mEditBuffer || !mDataProvider ) { @@ -2272,33 +2296,40 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) bool result = mEditBuffer->changeGeometry( fid, geom ); if ( result ) + { updateExtents(); + if ( !skipDefaultValue && !mDefaultValueOnUpdateFields.isEmpty() ) + updateDefaultValues( fid ); + } return result; } -bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) +bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues ) { + bool result = false; + switch ( fields().fieldOrigin( field ) ) { case QgsFields::OriginJoin: - return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + result = mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); case QgsFields::OriginProvider: case QgsFields::OriginEdit: case QgsFields::OriginExpression: { - if ( !mEditBuffer || !mDataProvider ) - return false; - else - return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + if ( mEditBuffer && mDataProvider ) + result = mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); } case QgsFields::OriginUnknown: - return false; + break; } - return false; + if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() ) + updateDefaultValues( fid ); + + return result; } bool QgsVectorLayer::addAttribute( const QgsField &field ) @@ -2920,6 +2951,9 @@ void QgsVectorLayer::updateFields() mFields[ index ].setAlias( aliasIt.value() ); } + + // Update default values + mDefaultValueOnUpdateFields.clear(); QMap< QString, QgsDefaultValue >::const_iterator defaultIt = mDefaultExpressionMap.constBegin(); for ( ; defaultIt != mDefaultExpressionMap.constEnd(); ++defaultIt ) { @@ -2928,6 +2962,8 @@ void QgsVectorLayer::updateFields() continue; mFields[ index ].setDefaultValue( defaultIt.value() ); + if ( defaultIt.value().applyOnUpdate() ) + mDefaultValueOnUpdateFields.insert( index ); } QMap< QString, QgsFieldConstraints::Constraints >::const_iterator constraintIt = mFieldConstraints.constBegin(); diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 6fdc198332e..13b519236c7 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -913,7 +913,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte \param f Feature to update \returns True in case of success and False in case of error */ - bool updateFeature( QgsFeature &f ); + bool updateFeature( const QgsFeature &f ); /** Insert a new vertex before the given vertex number, * in the given ring, item (first number is index 0), and feature @@ -1142,7 +1142,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte bool setReadOnly( bool readonly = true ); //! Change feature's geometry - bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ); + bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom, bool skipDefaultValue = false ); /** * Changes an attribute value (but does not commit it) @@ -1154,7 +1154,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * * \returns true in case of success */ - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false ); /** Add an attribute field (but does not commit it) * returns true if the field was added @@ -1958,6 +1958,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte private: // Private methods + void updateDefaultValues( QgsFeatureId fid, QgsFeature feature = QgsFeature() ); + /** * Returns true if the provider is in read-only mode */ @@ -2014,6 +2016,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte //! Map which stores default value expressions for fields QMap mDefaultExpressionMap; + //! An internal structure to keep track of fields that have a defaultValueOnUpdate + QSet mDefaultValueOnUpdateFields; + //! Map which stores constraints for fields QMap< QString, QgsFieldConstraints::Constraints > mFieldConstraints;