diff --git a/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in b/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in index 5b720368f1cc..7b656cc2c25f 100644 --- a/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in +++ b/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in @@ -20,7 +20,7 @@ class QgsTrackedVectorLayerTools : QgsVectorLayerTools Constructor for QgsTrackedVectorLayerTools. %End - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const; + virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = 0 ) const; %Docstring This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools` @@ -32,7 +32,7 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened -:param scope: A context scope to be used to calculate feature expression-based values +:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) :return: ``True`` in case of success, ``False`` if the operation failed/was aborted %End diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayer.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayer.sip.in index 6cb83580ecea..1e9c11afd9e9 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayer.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayer.sip.in @@ -1777,7 +1777,7 @@ be updated. This can be used to override default field value expressions. .. seealso:: :py:func:`updateFeature` %End - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 ); + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 ); %Docstring Changes an attribute value for a feature (but does not immediately commit the changes). The ``fid`` argument specifies the ID of the feature to be changed. @@ -1796,7 +1796,7 @@ so it is more efficient to explicitly pass an ``oldValue`` if it is already avai If ``skipDefaultValues`` is set to ``True``, default field values will not be updated. This can be used to override default field value expressions. -If ``context`` is provided, it will be used when updating default values. +If ``context`` is provided, it will be used when updating default values (since QGIS 3.38). :return: ``True`` if the feature's attribute was successfully changed. @@ -1817,7 +1817,7 @@ If ``context`` is provided, it will be used when updating default values. .. seealso:: :py:func:`updateFeature` %End - bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 ); + bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 ); %Docstring Changes attributes' values for a feature (but does not immediately commit the changes). @@ -1837,7 +1837,7 @@ If ``skipDefaultValues`` is set to ``True``, default field values will not be updated. This can be used to override default field value expressions. -If ``context`` is provided, it will be used when updating default values. +If ``context`` is provided, it will be used when updating default values (since QGIS 3.38). :return: ``True`` if feature's attributes was successfully changed. diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in index c37a098bb3da..a8e1efa82e55 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in @@ -28,7 +28,7 @@ in your application. QgsVectorLayerTools(); - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const; %Docstring This method should/will be called, whenever a new feature will be added to the layer @@ -38,10 +38,35 @@ This method should/will be called, whenever a new feature will be added to the l :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened -:param scope: A context scope to be used to calculate feature expression-based values :return: - ``True`` in case of success, ``False`` if the operation failed/was aborted - feature: Updated feature after adding will be written back to this + +.. note:: + + addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools +%End + + virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = 0 ) const; +%Docstring +This method should/will be called, whenever a new feature will be added to the layer + +:param layer: The layer to which the feature should be added +:param defaultValues: Default values for the feature to add +:param defaultGeometry: A default geometry to add to the feature +:param parentWidget: The widget calling this function to be passed to the used dialog +:param showModal: If the used dialog should be modal or not +:param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) + +:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted + - feature: Updated feature after adding will be written back to this + +.. note:: + + addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools + +.. versionadded:: 3.38 %End diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in new file mode 100644 index 000000000000..d8a9d8817047 --- /dev/null +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in @@ -0,0 +1,83 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vector/qgsvectorlayertoolscontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsVectorLayerToolsContext +{ +%Docstring(signature="appended") +Contains settings which reflect the context in which vector layer tool operations should +consider. + +.. versionadded:: 3.38 +%End + +%TypeHeaderCode +#include "qgsvectorlayertoolscontext.h" +%End + public: + + QgsVectorLayerToolsContext(); +%Docstring +Constructor for QgsVectorLayerToolsContext. +%End + + QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other ); +%Docstring +Copy constructor. + +:param other: source QgsVectorLayerToolsContext +%End + + + void setExpressionContext( QgsExpressionContext *context ); +%Docstring +Sets the optional expression context used by the vector layer tools. + +:param context: expression context pointer. Ownership is not transferred. + +.. seealso:: :py:func:`expressionContext` + +.. seealso:: :py:func:`setAdditionalExpressionContextScope` +%End + + QgsExpressionContext *expressionContext() const; +%Docstring +Returns the optional expression context used by the vector layer tools. + +.. seealso:: :py:func:`setExpressionContext` + +.. seealso:: :py:func:`additionalExpressionContextScope` +%End + + void setAdditionalExpressionContextScope( const QgsExpressionContextScope &scope ); +%Docstring +Sets an additional expression context scope to be made available when calculating expressions. + +:param scope: additional scope + +.. seealso:: :py:func:`additionalExpressionContextScope` +%End + + QgsExpressionContextScope &additionalExpressionContextScope(); +%Docstring +Returns an additional expression context scope to be made available when calculating expressions. + +.. seealso:: :py:func:`setAdditionalExpressionContextScope` +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vector/qgsvectorlayertoolscontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/PyQt6/core/core_auto.sip b/python/PyQt6/core/core_auto.sip index 2e7fc385d389..569b3c1c20a7 100644 --- a/python/PyQt6/core/core_auto.sip +++ b/python/PyQt6/core/core_auto.sip @@ -769,6 +769,7 @@ %Include auto_generated/vector/qgsvectorlayerselectionproperties.sip %Include auto_generated/vector/qgsvectorlayertemporalproperties.sip %Include auto_generated/vector/qgsvectorlayertools.sip +%Include auto_generated/vector/qgsvectorlayertoolscontext.sip %Include auto_generated/vector/qgsvectorlayerundocommand.sip %Include auto_generated/vector/qgsvectorlayerundopassthroughcommand.sip %Include auto_generated/vector/qgsvectorlayerutils.sip diff --git a/python/core/auto_generated/qgstrackedvectorlayertools.sip.in b/python/core/auto_generated/qgstrackedvectorlayertools.sip.in index 5b720368f1cc..7b656cc2c25f 100644 --- a/python/core/auto_generated/qgstrackedvectorlayertools.sip.in +++ b/python/core/auto_generated/qgstrackedvectorlayertools.sip.in @@ -20,7 +20,7 @@ class QgsTrackedVectorLayerTools : QgsVectorLayerTools Constructor for QgsTrackedVectorLayerTools. %End - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const; + virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = 0 ) const; %Docstring This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools` @@ -32,7 +32,7 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened -:param scope: A context scope to be used to calculate feature expression-based values +:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) :return: ``True`` in case of success, ``False`` if the operation failed/was aborted %End diff --git a/python/core/auto_generated/vector/qgsvectorlayer.sip.in b/python/core/auto_generated/vector/qgsvectorlayer.sip.in index 6cb83580ecea..1e9c11afd9e9 100644 --- a/python/core/auto_generated/vector/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayer.sip.in @@ -1777,7 +1777,7 @@ be updated. This can be used to override default field value expressions. .. seealso:: :py:func:`updateFeature` %End - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 ); + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 ); %Docstring Changes an attribute value for a feature (but does not immediately commit the changes). The ``fid`` argument specifies the ID of the feature to be changed. @@ -1796,7 +1796,7 @@ so it is more efficient to explicitly pass an ``oldValue`` if it is already avai If ``skipDefaultValues`` is set to ``True``, default field values will not be updated. This can be used to override default field value expressions. -If ``context`` is provided, it will be used when updating default values. +If ``context`` is provided, it will be used when updating default values (since QGIS 3.38). :return: ``True`` if the feature's attribute was successfully changed. @@ -1817,7 +1817,7 @@ If ``context`` is provided, it will be used when updating default values. .. seealso:: :py:func:`updateFeature` %End - bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 ); + bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 ); %Docstring Changes attributes' values for a feature (but does not immediately commit the changes). @@ -1837,7 +1837,7 @@ If ``skipDefaultValues`` is set to ``True``, default field values will not be updated. This can be used to override default field value expressions. -If ``context`` is provided, it will be used when updating default values. +If ``context`` is provided, it will be used when updating default values (since QGIS 3.38). :return: ``True`` if feature's attributes was successfully changed. diff --git a/python/core/auto_generated/vector/qgsvectorlayertools.sip.in b/python/core/auto_generated/vector/qgsvectorlayertools.sip.in index c37a098bb3da..a8e1efa82e55 100644 --- a/python/core/auto_generated/vector/qgsvectorlayertools.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayertools.sip.in @@ -28,7 +28,7 @@ in your application. QgsVectorLayerTools(); - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const; %Docstring This method should/will be called, whenever a new feature will be added to the layer @@ -38,10 +38,35 @@ This method should/will be called, whenever a new feature will be added to the l :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened -:param scope: A context scope to be used to calculate feature expression-based values :return: - ``True`` in case of success, ``False`` if the operation failed/was aborted - feature: Updated feature after adding will be written back to this + +.. note:: + + addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools +%End + + virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = 0 ) const; +%Docstring +This method should/will be called, whenever a new feature will be added to the layer + +:param layer: The layer to which the feature should be added +:param defaultValues: Default values for the feature to add +:param defaultGeometry: A default geometry to add to the feature +:param parentWidget: The widget calling this function to be passed to the used dialog +:param showModal: If the used dialog should be modal or not +:param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) + +:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted + - feature: Updated feature after adding will be written back to this + +.. note:: + + addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools + +.. versionadded:: 3.38 %End diff --git a/python/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in b/python/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in new file mode 100644 index 000000000000..d8a9d8817047 --- /dev/null +++ b/python/core/auto_generated/vector/qgsvectorlayertoolscontext.sip.in @@ -0,0 +1,83 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vector/qgsvectorlayertoolscontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsVectorLayerToolsContext +{ +%Docstring(signature="appended") +Contains settings which reflect the context in which vector layer tool operations should +consider. + +.. versionadded:: 3.38 +%End + +%TypeHeaderCode +#include "qgsvectorlayertoolscontext.h" +%End + public: + + QgsVectorLayerToolsContext(); +%Docstring +Constructor for QgsVectorLayerToolsContext. +%End + + QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other ); +%Docstring +Copy constructor. + +:param other: source QgsVectorLayerToolsContext +%End + + + void setExpressionContext( QgsExpressionContext *context ); +%Docstring +Sets the optional expression context used by the vector layer tools. + +:param context: expression context pointer. Ownership is not transferred. + +.. seealso:: :py:func:`expressionContext` + +.. seealso:: :py:func:`setAdditionalExpressionContextScope` +%End + + QgsExpressionContext *expressionContext() const; +%Docstring +Returns the optional expression context used by the vector layer tools. + +.. seealso:: :py:func:`setExpressionContext` + +.. seealso:: :py:func:`additionalExpressionContextScope` +%End + + void setAdditionalExpressionContextScope( const QgsExpressionContextScope &scope ); +%Docstring +Sets an additional expression context scope to be made available when calculating expressions. + +:param scope: additional scope + +.. seealso:: :py:func:`additionalExpressionContextScope` +%End + + QgsExpressionContextScope &additionalExpressionContextScope(); +%Docstring +Returns an additional expression context scope to be made available when calculating expressions. + +.. seealso:: :py:func:`setAdditionalExpressionContextScope` +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vector/qgsvectorlayertoolscontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 2e7fc385d389..569b3c1c20a7 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -769,6 +769,7 @@ %Include auto_generated/vector/qgsvectorlayerselectionproperties.sip %Include auto_generated/vector/qgsvectorlayertemporalproperties.sip %Include auto_generated/vector/qgsvectorlayertools.sip +%Include auto_generated/vector/qgsvectorlayertoolscontext.sip %Include auto_generated/vector/qgsvectorlayerundocommand.sip %Include auto_generated/vector/qgsvectorlayerundopassthroughcommand.sip %Include auto_generated/vector/qgsvectorlayerutils.sip diff --git a/src/app/qgsguivectorlayertools.cpp b/src/app/qgsguivectorlayertools.cpp index a3cee42c630a..0ae5cc3886a7 100644 --- a/src/app/qgsguivectorlayertools.cpp +++ b/src/app/qgsguivectorlayertools.cpp @@ -24,8 +24,9 @@ #include "qgsmessagebaritem.h" #include "qgsmessageviewer.h" #include "qgsvectorlayer.h" +#include "qgsvectorlayertoolscontext.h" -bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const +bool QgsGuiVectorLayerTools::addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent, QgsVectorLayerToolsContext *context ) const { QgsFeature *f = feat; if ( !feat ) @@ -35,7 +36,7 @@ bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttribu QgsFeatureAction *a = new QgsFeatureAction( tr( "Add feature" ), *f, layer, QUuid(), -1, parentWidget ); a->setForceSuppressFormPopup( forceSuppressFormPopup() ); connect( a, &QgsFeatureAction::addFeatureFinished, a, &QObject::deleteLater ); - const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, showModal, std::unique_ptr( scope ), hideParent ); + const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, showModal, std::unique_ptr( new QgsExpressionContextScope( context->additionalExpressionContextScope() ) ), hideParent ); if ( !feat ) delete f; diff --git a/src/app/qgsguivectorlayertools.h b/src/app/qgsguivectorlayertools.h index e78414e8ac58..f9fd8077f1d5 100644 --- a/src/app/qgsguivectorlayertools.h +++ b/src/app/qgsguivectorlayertools.h @@ -44,11 +44,11 @@ class QgsGuiVectorLayerTools : public QgsVectorLayerTools * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened - * \param scope A context scope to be used to calculate feature expression-based values + * \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) * * \returns TRUE in case of success, FALSE if the operation failed/was aborted */ - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override; + bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = nullptr ) const override; /** * This should be called, whenever a vector layer should be switched to edit mode. If successful diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a36f9187adc6..1f526ecbdbfd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -952,6 +952,7 @@ set(QGIS_CORE_SRCS vector/qgsvectorlayerselectionproperties.cpp vector/qgsvectorlayertemporalproperties.cpp vector/qgsvectorlayertools.cpp + vector/qgsvectorlayertoolscontext.cpp vector/qgsvectorlayerundocommand.cpp vector/qgsvectorlayerundopassthroughcommand.cpp vector/qgsvectorlayerutils.cpp @@ -2023,6 +2024,7 @@ set(QGIS_CORE_HDRS vector/qgsvectorlayerselectionproperties.h vector/qgsvectorlayertemporalproperties.h vector/qgsvectorlayertools.h + vector/qgsvectorlayertoolscontext.h vector/qgsvectorlayerundocommand.h vector/qgsvectorlayerundopassthroughcommand.h vector/qgsvectorlayerutils.h diff --git a/src/core/qgstrackedvectorlayertools.cpp b/src/core/qgstrackedvectorlayertools.cpp index b6b753b785b4..6903f3a28c5b 100644 --- a/src/core/qgstrackedvectorlayertools.cpp +++ b/src/core/qgstrackedvectorlayertools.cpp @@ -13,18 +13,20 @@ * (at your option) any later version. * * * ***************************************************************************/ + #include "qgstrackedvectorlayertools.h" #include "qgsvectorlayer.h" +#include "qgsvectorlayertoolscontext.h" -bool QgsTrackedVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const +bool QgsTrackedVectorLayerTools::addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget, bool showModal, bool hideParent, QgsVectorLayerToolsContext *context ) const { QgsFeature *f = feature; if ( !feature ) f = new QgsFeature(); const_cast( mBackend )->setForceSuppressFormPopup( forceSuppressFormPopup() ); - if ( mBackend->addFeature( layer, defaultValues, defaultGeometry, f, parentWidget, showModal, hideParent, scope ) ) + if ( mBackend->addFeatureV2( layer, defaultValues, defaultGeometry, f, parentWidget, showModal, hideParent, context ) ) { mAddedFeatures[layer].insert( f->id() ); if ( !feature ) diff --git a/src/core/qgstrackedvectorlayertools.h b/src/core/qgstrackedvectorlayertools.h index 64bb534394a4..f69b284495cf 100644 --- a/src/core/qgstrackedvectorlayertools.h +++ b/src/core/qgstrackedvectorlayertools.h @@ -44,11 +44,11 @@ class CORE_EXPORT QgsTrackedVectorLayerTools : public QgsVectorLayerTools * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened - * \param scope A context scope to be used to calculate feature expression-based values + * \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) * * \returns TRUE in case of success, FALSE if the operation failed/was aborted */ - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override; + bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = nullptr ) const override; bool startEditing( QgsVectorLayer *layer ) const override; bool stopEditing( QgsVectorLayer *layer, bool allowCancel ) const override; bool saveEdits( QgsVectorLayer *layer ) const override; diff --git a/src/core/vector/qgsvectorlayer.cpp b/src/core/vector/qgsvectorlayer.cpp index 7501d4d580c3..97cdcf263a5e 100644 --- a/src/core/vector/qgsvectorlayer.cpp +++ b/src/core/vector/qgsvectorlayer.cpp @@ -3398,7 +3398,7 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, QgsGeometry &geom, bool s } -bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues, QgsExpressionContext *context ) +bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues, QgsVectorLayerToolsContext *context ) { QGIS_PROTECT_QOBJECT_THREAD_ACCESS @@ -3426,12 +3426,12 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV } if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() ) - updateDefaultValues( fid, QgsFeature(), context ); + updateDefaultValues( fid, QgsFeature(), context->expressionContext() ); return result; } -bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues, QgsExpressionContext *context ) +bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues, QgsVectorLayerToolsContext *context ) { QGIS_PROTECT_QOBJECT_THREAD_ACCESS @@ -3488,7 +3488,7 @@ bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttribute if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() ) { - updateDefaultValues( fid, QgsFeature(), context ); + updateDefaultValues( fid, QgsFeature(), context->expressionContext() ); } return result; diff --git a/src/core/vector/qgsvectorlayer.h b/src/core/vector/qgsvectorlayer.h index 12566cd4cac7..496e05370ea8 100644 --- a/src/core/vector/qgsvectorlayer.h +++ b/src/core/vector/qgsvectorlayer.h @@ -33,6 +33,7 @@ #include "qgsfeaturesource.h" #include "qgsfields.h" #include "qgsvectordataprovider.h" +#include "qgsvectorlayertoolscontext.h" #include "qgsvectorsimplifymethod.h" #include "qgseditformconfig.h" #include "qgsattributetableconfig.h" @@ -1735,7 +1736,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * If \a skipDefaultValues is set to TRUE, default field values will not * be updated. This can be used to override default field value expressions. * - * If \a context is provided, it will be used when updating default values. + * If \a context is provided, it will be used when updating default values (since QGIS 3.38). * * \returns TRUE if the feature's attribute was successfully changed. * @@ -1749,7 +1750,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * \see changeGeometry() * \see updateFeature() */ - Q_INVOKABLE bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = nullptr ); + Q_INVOKABLE bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = nullptr ); /** * Changes attributes' values for a feature (but does not immediately @@ -1770,7 +1771,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * be updated. This can be used to override default field value * expressions. * - * If \a context is provided, it will be used when updating default values. + * If \a context is provided, it will be used when updating default values (since QGIS 3.38). * * \returns TRUE if feature's attributes was successfully changed. * @@ -1787,7 +1788,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * \see changeAttributeValue() * */ - Q_INVOKABLE bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = nullptr ); + Q_INVOKABLE bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = nullptr ); /** * Add an attribute field (but does not commit it) diff --git a/src/core/vector/qgsvectorlayertools.h b/src/core/vector/qgsvectorlayertools.h index d8cc9e1386eb..7b048995db99 100644 --- a/src/core/vector/qgsvectorlayertools.h +++ b/src/core/vector/qgsvectorlayertools.h @@ -26,6 +26,7 @@ class QgsFeatureRequest; class QgsVectorLayer; +class QgsVectorLayerToolsContext; class QgsProject; /** @@ -57,11 +58,36 @@ class CORE_EXPORT QgsVectorLayerTools : public QObject * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened - * \param scope A context scope to be used to calculate feature expression-based values - * \returns TRUE in case of success, FALSE if the operation failed/was aborted + * \returns TRUE in case of success, FALSE if the operation failed/was aborted * + * \note addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools */ - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false ) const + { + return addFeatureV2( layer, defaultValues, defaultGeometry, feature, parentWidget, showModal, hideParent, nullptr ); + } + + /** + * This method should/will be called, whenever a new feature will be added to the layer + * + * \param layer The layer to which the feature should be added + * \param defaultValues Default values for the feature to add + * \param defaultGeometry A default geometry to add to the feature + * \param feature Updated feature after adding will be written back to this + * \param parentWidget The widget calling this function to be passed to the used dialog + * \param showModal If the used dialog should be modal or not + * \param hideParent If the parent widget should be hidden, when the used dialog is opened + * \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38) + * \returns TRUE in case of success, FALSE if the operation failed/was aborted + * + * \note addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools + * \since QGIS 3.38 + */ + virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = nullptr ) const + { + Q_UNUSED( context ) + return addFeature( layer, defaultValues, defaultGeometry, feature, parentWidget, showModal, hideParent ); + } // TODO QGIS 4: remove const qualifier diff --git a/src/core/vector/qgsvectorlayertoolscontext.cpp b/src/core/vector/qgsvectorlayertoolscontext.cpp new file mode 100644 index 000000000000..1ce7b656e006 --- /dev/null +++ b/src/core/vector/qgsvectorlayertoolscontext.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + qgsvectorlayertoolscontext.cpp + ------------------------ + begin : May 2024 + copyright : (C) 2024 by Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsvectorlayertoolscontext.h" +#include "qgsexpressioncontextutils.h" + +QgsVectorLayerToolsContext::QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other ) + : mAdditionalExpressionContextScope( other.mAdditionalExpressionContextScope ) +{ + if ( other.mExpressionContext ) + { + mExpressionContext.reset( new QgsExpressionContext( *other.mExpressionContext ) ); + } +} + +QgsVectorLayerToolsContext &QgsVectorLayerToolsContext::operator=( const QgsVectorLayerToolsContext &other ) +{ + mAdditionalExpressionContextScope = other.mAdditionalExpressionContextScope; + if ( other.mExpressionContext ) + { + mExpressionContext.reset( new QgsExpressionContext( *other.mExpressionContext ) ); + } + else + { + mExpressionContext.reset(); + } + return *this; +} + +void QgsVectorLayerToolsContext::setExpressionContext( QgsExpressionContext *context ) +{ + if ( context ) + mExpressionContext.reset( new QgsExpressionContext( *context ) ); + else + mExpressionContext.reset(); +} + +QgsExpressionContext *QgsVectorLayerToolsContext::expressionContext() const +{ + return mExpressionContext.get(); +} + +void QgsVectorLayerToolsContext::setAdditionalExpressionContextScope( const QgsExpressionContextScope &scope ) +{ + mAdditionalExpressionContextScope = scope; +} + +QgsExpressionContextScope &QgsVectorLayerToolsContext::additionalExpressionContextScope() +{ + return mAdditionalExpressionContextScope; +} diff --git a/src/core/vector/qgsvectorlayertoolscontext.h b/src/core/vector/qgsvectorlayertoolscontext.h new file mode 100644 index 000000000000..f80ffbb733cc --- /dev/null +++ b/src/core/vector/qgsvectorlayertoolscontext.h @@ -0,0 +1,83 @@ +/*************************************************************************** + qgsvectorlayertoolscontext.h + ------------------------ + begin : May 2024 + copyright : (C) 2024 by Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSVECTORLAYERTOOLSCONTEXT_H +#define QGSVECTORLAYERTOOLSCONTEXT_H + +#include "qgsexpressioncontext.h" +#include "qgis_core.h" + +#include + +/** + * \ingroup core + * \class QgsVectorLayerToolsContext + * \brief Contains settings which reflect the context in which vector layer tool operations should + * consider. + * \since QGIS 3.38 + */ +class CORE_EXPORT QgsVectorLayerToolsContext +{ + public: + + /** + * Constructor for QgsVectorLayerToolsContext. + */ + QgsVectorLayerToolsContext() = default; + + /** + * Copy constructor. + * \param other source QgsVectorLayerToolsContext + */ + QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other ); + + QgsVectorLayerToolsContext &operator=( const QgsVectorLayerToolsContext &other ); + + /** + * Sets the optional expression context used by the vector layer tools. + * \param context expression context pointer. Ownership is not transferred. + * \see expressionContext() + * \see setAdditionalExpressionContextScope() + */ + void setExpressionContext( QgsExpressionContext *context ); + + /** + * Returns the optional expression context used by the vector layer tools. + * \see setExpressionContext() + * \see additionalExpressionContextScope() + */ + QgsExpressionContext *expressionContext() const; + + /** + * Sets an additional expression context scope to be made available when calculating expressions. + * \param scope additional scope + * \see additionalExpressionContextScope() + */ + void setAdditionalExpressionContextScope( const QgsExpressionContextScope &scope ); + + /** + * Returns an additional expression context scope to be made available when calculating expressions. + * \see setAdditionalExpressionContextScope() + */ + QgsExpressionContextScope &additionalExpressionContextScope(); + + private: + + std::unique_ptr< QgsExpressionContext > mExpressionContext; + QgsExpressionContextScope mAdditionalExpressionContextScope; + +}; + +#endif // QGSVECTORLAYERTOOLSCONTEXT_H diff --git a/src/gui/qgsabstractrelationeditorwidget.cpp b/src/gui/qgsabstractrelationeditorwidget.cpp index b2c2cf6356cb..7153c2537e63 100644 --- a/src/gui/qgsabstractrelationeditorwidget.cpp +++ b/src/gui/qgsabstractrelationeditorwidget.cpp @@ -287,9 +287,11 @@ QgsFeatureIds QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &ge for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeatureList.first().attribute( fieldPair.referencedField() ) ); + std::unique_ptr context = std::make_unique(); QgsExpressionContextScope *scope = QgsExpressionContextUtils::parentFormScope( mFeatureList.first(), mEditorContext.attributeFormModeString() ); + context->setAdditionalExpressionContextScope( *scope ); QgsFeature linkFeature; - if ( !vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, this, true, true, scope ) ) + if ( !vlTools->addFeatureV2( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, this, true, true, context.get() ) ) return QgsFeatureIds(); addedFeatureIds.insert( linkFeature.id() ); diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index e9fc5d332c3c..6edcc09bba69 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -45,6 +45,7 @@ #include "qgssettings.h" #include "qgsscrollarea.h" #include "qgsvectorlayerjoinbuffer.h" +#include "qgsvectorlayertoolscontext.h" #include "qgsvectorlayerutils.h" #include "qgsactionwidgetwrapper.h" #include "qgsqmlwidgetwrapper.h" @@ -464,8 +465,10 @@ bool QgsAttributeForm::saveEdits( QString *error ) n++; } - QgsExpressionContext context = createExpressionContext( updatedFeature ); - success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues, false, &context ); + std::unique_ptr context = std::make_unique(); + QgsExpressionContext expressionContext = createExpressionContext( updatedFeature ); + context->setExpressionContext( &expressionContext ); + success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues, false, context.get() ); if ( success && n > 0 ) { diff --git a/tests/src/gui/testqgsrelationreferencewidget.cpp b/tests/src/gui/testqgsrelationreferencewidget.cpp index 784742a9c24e..82b269473ec2 100644 --- a/tests/src/gui/testqgsrelationreferencewidget.cpp +++ b/tests/src/gui/testqgsrelationreferencewidget.cpp @@ -31,6 +31,7 @@ #include "qgsgui.h" #include "qgsmapcanvas.h" #include "qgsvectorlayertools.h" +#include "qgsvectorlayertoolscontext.h" #include "qgsadvanceddigitizingdockwidget.h" #include "qgsmaptooldigitizefeature.h" @@ -582,12 +583,12 @@ void TestQgsRelationReferenceWidget::testIdentifyOnMap() // referenced layer class DummyVectorLayerTools : public QgsVectorLayerTools // clazy:exclude=missing-qobject-macro { - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &, const QgsGeometry &, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override + bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &, const QgsGeometry &, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsVectorLayerToolsContext *context = nullptr ) const override { Q_UNUSED( parentWidget ); Q_UNUSED( showModal ); Q_UNUSED( hideParent ); - Q_UNUSED( scope ); + Q_UNUSED( context ); feat->setAttribute( QStringLiteral( "pk" ), 13 ); feat->setAttribute( QStringLiteral( "material" ), QStringLiteral( "steel" ) ); feat->setAttribute( QStringLiteral( "diameter" ), 140 ); diff --git a/tests/src/python/test_qgsrelationeditwidget.py b/tests/src/python/test_qgsrelationeditwidget.py index cf69ff3d44be..ece758559334 100644 --- a/tests/src/python/test_qgsrelationeditwidget.py +++ b/tests/src/python/test_qgsrelationeditwidget.py @@ -341,7 +341,7 @@ def test_add_feature_geometry(self): # Mock vector layer tool to just set default value on created feature class DummyVlTools(QgsVectorLayerTools): - def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False, scope=None): + def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False): f = QgsFeature(layer.fields()) for idx, value in defaultValues.items(): f.setAttribute(idx, value) @@ -440,7 +440,7 @@ def setValues(self, values): """ self.values = values - def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False, scope=None): + def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False): """ Overrides the addFeature method :param layer: vector layer