From 51cd712c76e16333d79795e1631f4485606b53b1 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 18 Dec 2020 02:26:59 +0200 Subject: [PATCH 01/23] Add relation widget registry --- .../qgsattributeeditorelement.sip.in | 34 +- .../auto_additions/qgsbasicrelationwidget.py | 4 + .../qgsrelationwidgetwrapper.sip.in | 39 +- .../qgsbasicrelationwidget.sip.in | 110 ++++ python/gui/auto_generated/qgsgui.sip.in | 7 + .../qgsrelationconfigwidget.sip.in | 77 +++ .../auto_generated/qgsrelationwidget.sip.in | 226 +++++++ .../qgsrelationwidgetfactory.sip.in | 75 +++ .../qgsrelationwidgetregistry.sip.in | 81 +++ python/gui/gui_auto.sip | 5 + src/core/qgsattributeeditorelement.cpp | 29 +- src/core/qgsattributeeditorelement.h | 32 +- src/core/qgseditformconfig.cpp | 46 +- src/gui/CMakeLists.txt | 14 + .../qgsattributewidgetedit.cpp | 55 +- .../qgsattributewidgetedit.h | 4 + .../qgsrelationwidgetwrapper.cpp | 69 +- .../editorwidgets/qgsrelationwidgetwrapper.h | 40 +- src/gui/qgsattributeform.cpp | 11 +- src/gui/qgsattributeform.h | 1 + src/gui/qgsbasicrelationconfigwidget.cpp | 57 ++ src/gui/qgsbasicrelationconfigwidget.h | 62 ++ src/gui/qgsbasicrelationwidget.cpp | 558 ++++++++++++++++ src/gui/qgsbasicrelationwidget.h | 172 +++++ src/gui/qgsbasicrelationwidgetfactory.cpp | 46 ++ src/gui/qgsbasicrelationwidgetfactory.h | 48 ++ src/gui/qgsgui.cpp | 8 + src/gui/qgsgui.h | 8 + src/gui/qgsrelationconfigwidget.cpp | 37 ++ src/gui/qgsrelationconfigwidget.h | 87 +++ src/gui/qgsrelationwidget.cpp | 595 ++++++++++++++++++ src/gui/qgsrelationwidget.h | 260 ++++++++ src/gui/qgsrelationwidgetfactory.cpp | 24 + src/gui/qgsrelationwidgetfactory.h | 80 +++ src/gui/qgsrelationwidgetregistry.cpp | 78 +++ src/gui/qgsrelationwidgetregistry.h | 90 +++ .../vector/qgsattributesformproperties.cpp | 25 +- src/gui/vector/qgsattributesformproperties.h | 7 +- .../qgsattributewidgetrelationeditwidget.ui | 130 ++-- src/ui/qgsrelationeditorconfigwidgetbase.ui | 70 +++ 40 files changed, 3240 insertions(+), 161 deletions(-) create mode 100644 python/gui/auto_additions/qgsbasicrelationwidget.py create mode 100644 python/gui/auto_generated/qgsbasicrelationwidget.sip.in create mode 100644 python/gui/auto_generated/qgsrelationconfigwidget.sip.in create mode 100644 python/gui/auto_generated/qgsrelationwidget.sip.in create mode 100644 python/gui/auto_generated/qgsrelationwidgetfactory.sip.in create mode 100644 python/gui/auto_generated/qgsrelationwidgetregistry.sip.in create mode 100644 src/gui/qgsbasicrelationconfigwidget.cpp create mode 100644 src/gui/qgsbasicrelationconfigwidget.h create mode 100644 src/gui/qgsbasicrelationwidget.cpp create mode 100644 src/gui/qgsbasicrelationwidget.h create mode 100644 src/gui/qgsbasicrelationwidgetfactory.cpp create mode 100644 src/gui/qgsbasicrelationwidgetfactory.h create mode 100644 src/gui/qgsrelationconfigwidget.cpp create mode 100644 src/gui/qgsrelationconfigwidget.h create mode 100644 src/gui/qgsrelationwidget.cpp create mode 100644 src/gui/qgsrelationwidget.h create mode 100644 src/gui/qgsrelationwidgetfactory.cpp create mode 100644 src/gui/qgsrelationwidgetfactory.h create mode 100644 src/gui/qgsrelationwidgetregistry.cpp create mode 100644 src/gui/qgsrelationwidgetregistry.h create mode 100644 src/ui/qgsrelationeditorconfigwidgetbase.ui diff --git a/python/core/auto_generated/qgsattributeeditorelement.sip.in b/python/core/auto_generated/qgsattributeeditorelement.sip.in index 7b1f30484458..1bbf26df0537 100644 --- a/python/core/auto_generated/qgsattributeeditorelement.sip.in +++ b/python/core/auto_generated/qgsattributeeditorelement.sip.in @@ -113,6 +113,20 @@ Controls if this element should be labeled with a title (field, relation or grou Controls if this element should be labeled with a title (field, relation or groupname). .. versionadded:: 2.18 +%End + + QVariantMap config() const; +%Docstring +Returns the editor configuration + +.. versionadded:: 3.18 +%End + + void setConfig( const QVariantMap &config ); +%Docstring +Sets the editor configuration + +.. versionadded:: 3.18 %End protected: @@ -411,18 +425,24 @@ Determines if the "Save child layer edits" button should be shown use visibleButtons() instead %End - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); %Docstring Defines the buttons which are shown .. versionadded:: 3.16 + +.. deprecated:: QGIS 3.18 + use setConfig() instead %End - QgsAttributeEditorRelation::Buttons visibleButtons() const; + QgsAttributeEditorRelation::Buttons visibleButtons() const; %Docstring Returns the buttons which are shown .. versionadded:: 3.16 + +.. deprecated:: QGIS 3.18 + use setConfig() instead %End bool forceSuppressFormPopup() const; @@ -469,6 +489,16 @@ Sets ``label`` for this element If it's empty it takes the relation id as label .. versionadded:: 3.16 +%End + + QString relationWidgetTypeId() const; +%Docstring +Returns the current relation widget type id +%End + + void setRelationWidgetTypeId( const QString &relationWidgetTypeId ); +%Docstring +Sets the relation widget type %End }; diff --git a/python/gui/auto_additions/qgsbasicrelationwidget.py b/python/gui/auto_additions/qgsbasicrelationwidget.py new file mode 100644 index 000000000000..ea0714eaf58a --- /dev/null +++ b/python/gui/auto_additions/qgsbasicrelationwidget.py @@ -0,0 +1,4 @@ +# The following has been generated automatically from src/gui/qgsbasicrelationwidget.h +QgsBasicRelationWidget.Button.baseClass = QgsBasicRelationWidget +QgsBasicRelationWidget.Buttons.baseClass = QgsBasicRelationWidget +Buttons = QgsBasicRelationWidget # dirty hack since SIP seems to introduce the flags in module diff --git a/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in b/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in index f6063540a880..9941801e7a56 100644 --- a/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in +++ b/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in @@ -18,7 +18,23 @@ class QgsRelationWidgetWrapper : QgsWidgetWrapper %End public: - explicit QgsRelationWidgetWrapper( QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor = 0, QWidget *parent /TransferThis/ = 0 ); + QgsRelationWidgetWrapper( + QgsVectorLayer *vl, + const QgsRelation &relation, + QWidget *editor /Constrained/ = 0, + QWidget *parent /TransferThis,Constrained/ = 0 + ); +%Docstring +Constructor for QgsRelationWidgetWrapper +%End + + QgsRelationWidgetWrapper( + const QString &relationEditorName, + QgsVectorLayer *vl, + const QgsRelation &relation, + QWidget *editor = 0, + QWidget *parent /TransferThis/ = 0 + ); %Docstring Constructor for QgsRelationWidgetWrapper %End @@ -99,18 +115,35 @@ Determines if the "Save child layer edits" button should be shown use visibleButtons() instead %End - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); %Docstring Defines the buttons which are shown .. versionadded:: 3.16 + +.. deprecated:: QGIS 3.18 %End - QgsAttributeEditorRelation::Buttons visibleButtons() const; + QgsAttributeEditorRelation::Buttons visibleButtons() const; %Docstring Returns the buttons which are shown .. versionadded:: 3.16 + +.. deprecated:: QGIS 3.18 +%End + + + void setWidgetConfig( const QVariantMap &config ); +%Docstring +Will set the config of this widget wrapper to the specified config. + +:param config: The config for this wrapper +%End + + QVariantMap widgetConfig() const; +%Docstring +Returns the whole widget config %End bool forceSuppressFormPopup() const; diff --git a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in new file mode 100644 index 000000000000..3933fffcc97a --- /dev/null +++ b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in @@ -0,0 +1,110 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsbasicrelationwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsBasicRelationWidget : QgsRelationWidget +{ +%Docstring +The default relation widget in QGIS. Successor of the now deprecated {:py:class:`QgsRelationEditorWidget`}. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsbasicrelationwidget.h" +%End + public: + + enum Button + { + Link, + Unlink, + SaveChildEdits, + AddChildFeature, + DuplicateChildFeature, + DeleteChildFeature, + ZoomToChildFeature, + AllButtons + }; + typedef QFlags Buttons; + + + QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); +%Docstring +Constructor + +:param config: widget configuration +:param parent: parent widget +%End + + void setViewMode( QgsDualView::ViewMode mode ); +%Docstring +Define the view mode for the dual view +%End + + QgsDualView::ViewMode viewMode(); +%Docstring +Gets the view mode for the dual view +%End + + void setEditorContext( const QgsAttributeEditorContext &context ); +%Docstring +Sets the editor ``context`` + +.. note:: + + if context cadDockWidget is null, it won't be possible to digitize + the geometry of a referencing feature from this widget +%End + + void setVisibleButtons( const Buttons &buttons ); +%Docstring +Defines the buttons which are shown +%End + + Buttons visibleButtons() const; +%Docstring +Returns the buttons which are shown +%End + + virtual QVariantMap config() const; + +%Docstring +Returns the current configuration +%End + + virtual void setConfig( const QVariantMap &config ); + +%Docstring +Defines the current configuration +%End + + virtual void setTitle( const QString &title ); + +%Docstring +Sets the title of the root groupbox +%End + + public slots: + virtual void parentFormValueChanged( const QString &attribute, const QVariant &newValue ); + + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsbasicrelationwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsgui.sip.in b/python/gui/auto_generated/qgsgui.sip.in index dd661ac36431..c10d8d43460d 100644 --- a/python/gui/auto_generated/qgsgui.sip.in +++ b/python/gui/auto_generated/qgsgui.sip.in @@ -129,6 +129,13 @@ Returns the registry of subset string editors of data providers %Docstring Returns the registry of provider source widget providers. +.. versionadded:: 3.18 +%End + + static QgsRelationWidgetRegistry *relationWidgetRegistry() /KeepReference/; +%Docstring +Returns the global relation widget registry, used for managing all known relation widget factories. + .. versionadded:: 3.18 %End diff --git a/python/gui/auto_generated/qgsrelationconfigwidget.sip.in b/python/gui/auto_generated/qgsrelationconfigwidget.sip.in new file mode 100644 index 000000000000..3783801b82be --- /dev/null +++ b/python/gui/auto_generated/qgsrelationconfigwidget.sip.in @@ -0,0 +1,77 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationconfigwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + + +class QgsRelationConfigWidget : QWidget +{ +%Docstring +This class should be subclassed for every configurable relation widget type. + +It implements the GUI configuration widget and transforms this to/from a configuration. + +It will only be instantiated by {:py:class:`QgsRelationWidgetFactory`} + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsrelationconfigwidget.h" +%End + public: + + explicit QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent /TransferThis/ ); +%Docstring +Create a new configuration widget + +:param relation: The relation for which the configuration dialog will be created +:param parent: A parent widget +%End + + virtual QVariantMap config() = 0; +%Docstring +Create a configuration from the current GUI state + +:return: A widget configuration +%End + + virtual void setConfig( const QVariantMap &config ) = 0; +%Docstring +Update the configuration widget to represent the given configuration. + +:param config: The configuration which should be represented by this widget +%End + + QgsVectorLayer *layer(); +%Docstring +Returns the layer for which this configuration widget applies + +:return: The layer +%End + + QgsRelation relation() const; +%Docstring +Returns the relation for which this configuration widget applies + +:return: The relation +%End + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationconfigwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationwidget.sip.in b/python/gui/auto_generated/qgsrelationwidget.sip.in new file mode 100644 index 000000000000..728988751bdc --- /dev/null +++ b/python/gui/auto_generated/qgsrelationwidget.sip.in @@ -0,0 +1,226 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + + +class QgsRelationWidget : QWidget +{ +%Docstring +Base class to build new relation widgets. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsrelationwidget.h" +%End + public: + + + QgsRelationWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); +%Docstring +Constructor +%End + + void setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ); +%Docstring +Sets the ``relation`` and the ``feature`` +%End + + void setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ); +%Docstring +Set the relation(s) for this widget +If only one relation is set, it will act as a simple 1:N relation widget +If both relations are set, it will act as an N:M relation widget +inserting and deleting entries on the intermediate table as required. + +:param relation: Relation referencing the edited table +:param nmrelation: Optional reference from the referencing table to a 3rd N:M table +%End + + void setFeature( const QgsFeature &feature, bool update = true ); +%Docstring +Sets the ``feature`` being edited and updates the UI unless ``update`` is set to ``False`` +%End + + void setEditorContext( const QgsAttributeEditorContext &context ); +%Docstring +Sets the editor ``context`` + +.. note:: + + if context cadDockWidget is null, it won't be possible to digitize + the geometry of a referencing feature from this widget +%End + + QgsAttributeEditorContext editorContext( ) const; +%Docstring +Returns the attribute editor context. +%End + + QgsIFeatureSelectionManager *featureSelectionManager(); +%Docstring +The feature selection manager is responsible for the selected features +which are currently being edited. +%End + + bool showLabel() const; +%Docstring +Defines if a title label should be shown for this widget. +%End + + void setShowLabel( bool showLabel ); +%Docstring +Defines if a title label should be shown for this widget. +%End + + QVariant nmRelationId() const; +%Docstring +Determines the relation id of the second relation involved in an N:M relation. +%End + + void setNmRelationId( const QVariant &nmRelationId = QVariant() ); +%Docstring +Sets ``nmRelationId`` for the relation id of the second relation involved in an N:M relation. +If it's empty, then it's considered as a 1:M relationship. +%End + + QString label() const; +%Docstring +Determines the label of this element +%End + + void setLabel( const QString &label = QString() ); +%Docstring +Sets ``label`` for this element +If it's empty it takes the relation id as label +%End + + QgsFeature feature() const; +%Docstring +Returns the widget's current feature +%End + + bool forceSuppressFormPopup() const; +%Docstring +Determines the force suppress form popup status that is configured for this widget +%End + + void setForceSuppressFormPopup( bool forceSuppressFormPopup ); +%Docstring +Sets force suppress form popup status with ``forceSuppressFormPopup`` +configured for this widget +%End + + virtual QVariantMap config() const = 0; +%Docstring +Returns the widget configuration +%End + + virtual void setConfig( const QVariantMap &config ) = 0; +%Docstring +Defines the widget configuration +%End + + public slots: + + virtual void parentFormValueChanged( const QString &attribute, const QVariant &newValue ) = 0; +%Docstring +Called when an ``attribute`` value in the parent widget has changed to ``newValue`` +%End + + protected slots: + + void toggleEditing( bool state ); +%Docstring +Toggles editing state of the widget +%End + + void saveEdits(); +%Docstring +Saves the current modifications in the relation +%End + + void addFeature( const QgsGeometry &geometry = QgsGeometry() ); +%Docstring +Adds a new feature with given ``geometry`` +%End + + void deleteFeature( QgsFeatureId fid = QgsFeatureId() ); +%Docstring +Delete a feature with given ``fid`` +%End + + void deleteSelectedFeatures(); +%Docstring +Deletes the currently selected features +%End + + void linkFeature(); +%Docstring +Links a new feature to the relation +%End + + void onLinkFeatureDlgAccepted(); +%Docstring +Called when the link feature dialog is confirmed by the user +%End + + void unlinkFeature( QgsFeatureId fid = QgsFeatureId() ); +%Docstring +Unlinks a feature with given ``fid`` +%End + + void unlinkSelectedFeatures(); +%Docstring +Unlinks the selected features from the relation +%End + + void duplicateFeature(); +%Docstring +Duplicates a feature +%End + + void zoomToSelectedFeatures(); +%Docstring +Zooms to the selected features +%End + + protected: + + + + + void updateTitle(); +%Docstring +Updates the title contents to reflect the current state of the widget +%End + + void deleteFeatures( const QgsFeatureIds &fids ); +%Docstring +Deletes the features with ``fids`` +%End + + void unlinkFeatures( const QgsFeatureIds &fids ); +%Docstring +Unlinks the features with ``fids`` +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in b/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in new file mode 100644 index 000000000000..01bb9b79d1a7 --- /dev/null +++ b/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in @@ -0,0 +1,75 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidgetfactory.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsRelationWidgetFactory +{ +%Docstring +Factory class for creating relation widgets and their corresponding config widgets + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsrelationwidgetfactory.h" +%End + public: + + QgsRelationWidgetFactory(); +%Docstring +Creates a new relation widget factory with given ``name`` +%End + + virtual ~QgsRelationWidgetFactory(); + + virtual QString type() const = 0; +%Docstring +Returns the machine readable identifier name of this widget type +%End + + virtual QString name() const = 0; +%Docstring +Returns the human readable identifier name of this widget type +%End + + virtual QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = 0 ) const = 0 /Factory/; +%Docstring +Override this in your implementation. +Create a new relation widget. Call :py:func:`QgsEditorWidgetRegistry.create()` +instead of calling this method directly. + +:param config: The widget configuration to build the widget with +:param parent: The parent for the wrapper class and any created widget. + +:return: A new widget wrapper +%End + + virtual QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 /Factory/; +%Docstring +Override this in your implementation. +Create a new configuration widget for this widget type. + +:param relation: The relation for which the widget will be created +:param parent: The parent widget of the created config widget + +:return: A configuration widget +%End +}; + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidgetfactory.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in new file mode 100644 index 000000000000..2cca52ef30a1 --- /dev/null +++ b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in @@ -0,0 +1,81 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidgetregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsRelationWidgetRegistry +{ +%Docstring +Keeps track of the registered relations widgets. New widgets can be registered, old ones deleted. +The default {:py:class:`QgsBasicRelationWidget`} is protected from removing. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsrelationwidgetregistry.h" +%End + public: + + QgsRelationWidgetRegistry(); +%Docstring +Constructor +%End + + ~QgsRelationWidgetRegistry(); + + void addRelationWidget( QgsRelationWidgetFactory *widgetFactory /Transfer/ ); +%Docstring +Adds a new registered relation ``widgetFactory`` +%End + + void removeRelationWidget( const QString &widgetType ); +%Docstring +Removes a registered relation widget with given ``widgetType`` +%End + + QStringList relationWidgetNames(); +%Docstring +Returns a list of names of registered relation widgets +%End + + QMap factories() const; +%Docstring +Gets access to all registered factories +%End + + QgsRelationWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = 0 ) const /TransferBack/; +%Docstring +Create a relation widget of a given type for a given field. + +:param widgetType: The widget type to create a relation editor for +:param config: The configuration of the widget +:param parent: +%End + + QgsRelationConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = 0 ) const /TransferBack/; +%Docstring +Creates a configuration widget + +:param widgetType: The widget type to create a configuration widget for +:param relation: The relation for which this widget will be created +:param parent: The parent widget for the created widget +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsrelationwidgetregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index cfff3fec3799..53e3c753a75e 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -181,6 +181,11 @@ %Include auto_generated/qgsrasterpyramidsoptionswidget.sip %Include auto_generated/qgsratiolockbutton.sip %Include auto_generated/qgsrelationeditorwidget.sip +%Include auto_generated/qgsrelationconfigwidget.sip +%Include auto_generated/qgsbasicrelationwidget.sip +%Include auto_generated/qgsrelationwidget.sip +%Include auto_generated/qgsrelationwidgetfactory.sip +%Include auto_generated/qgsrelationwidgetregistry.sip %Include auto_generated/qgsrubberband.sip %Include auto_generated/qgsscalecombobox.sip %Include auto_generated/qgsscalerangewidget.sip diff --git a/src/core/qgsattributeeditorelement.cpp b/src/core/qgsattributeeditorelement.cpp index faf208365643..8a89d432dd00 100644 --- a/src/core/qgsattributeeditorelement.cpp +++ b/src/core/qgsattributeeditorelement.cpp @@ -15,6 +15,7 @@ ***************************************************************************/ #include "qgsattributeeditorelement.h" #include "qgsrelationmanager.h" +#include "qgsxmlutils.h" void QgsAttributeEditorContainer::addChildElement( QgsAttributeEditorElement *widget ) @@ -118,6 +119,11 @@ QDomElement QgsAttributeEditorElement::toDomElement( QDomDocument &doc ) const QDomElement elem = doc.createElement( typeIdentifier() ); elem.setAttribute( QStringLiteral( "name" ), mName ); elem.setAttribute( QStringLiteral( "showLabel" ), mShowLabel ); + + QDomElement elemConfig = QgsXmlUtils::writeVariant( mConfig, doc ); + elemConfig.setTagName( QStringLiteral( "config" ) ); + elem.appendChild( elemConfig ); + saveConfiguration( elem ); return elem; } @@ -132,13 +138,24 @@ void QgsAttributeEditorElement::setShowLabel( bool showLabel ) mShowLabel = showLabel; } +QVariantMap QgsAttributeEditorElement::config() const +{ + return mConfig; +} + +void QgsAttributeEditorElement::setConfig( const QVariantMap &config ) +{ + mConfig = config; +} + void QgsAttributeEditorRelation::saveConfiguration( QDomElement &elem ) const { elem.setAttribute( QStringLiteral( "relation" ), mRelation.id() ); - elem.setAttribute( QStringLiteral( "buttons" ), qgsFlagValueToKeys( mButtons ) ); + elem.setAttribute( QStringLiteral( "buttons" ), mConfig.value( QStringLiteral( "buttons" ) ).toString() ); elem.setAttribute( QStringLiteral( "forceSuppressFormPopup" ), mForceSuppressFormPopup ); elem.setAttribute( QStringLiteral( "nmRelationId" ), mNmRelationId.toString() ); elem.setAttribute( QStringLiteral( "label" ), mLabel ); + elem.setAttribute( QStringLiteral( "relationWidgetTypeId" ), mRelationWidgetTypeId ); } QString QgsAttributeEditorRelation::typeIdentifier() const @@ -211,6 +228,16 @@ QString QgsAttributeEditorRelation::label() const return mLabel; } +QString QgsAttributeEditorRelation::relationWidgetTypeId() const +{ + return mRelationWidgetTypeId; +} + +void QgsAttributeEditorRelation::setRelationWidgetTypeId( const QString &relationWidgetTypeId ) +{ + mRelationWidgetTypeId = relationWidgetTypeId; +} + QgsAttributeEditorElement *QgsAttributeEditorQmlElement::clone( QgsAttributeEditorElement *parent ) const { QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( name(), parent ); diff --git a/src/core/qgsattributeeditorelement.h b/src/core/qgsattributeeditorelement.h index 5c98a7e87609..c02228b9af88 100644 --- a/src/core/qgsattributeeditorelement.h +++ b/src/core/qgsattributeeditorelement.h @@ -135,12 +135,27 @@ class CORE_EXPORT QgsAttributeEditorElement SIP_ABSTRACT */ void setShowLabel( bool showLabel ); + /** + * Returns the editor configuration + * + * \since QGIS 3.18 + */ + QVariantMap config() const; + + /** + * Sets the editor configuration + * + * \since QGIS 3.18 + */ + void setConfig( const QVariantMap &config ); + protected: #ifndef SIP_RUN AttributeEditorType mType; QString mName; QgsAttributeEditorElement *mParent = nullptr; bool mShowLabel; + QVariantMap mConfig; #endif private: @@ -458,14 +473,16 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement /** * Defines the buttons which are shown * \since QGIS 3.16 + * \deprecated since QGIS 3.18 use setConfig() instead */ - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); /** * Returns the buttons which are shown * \since QGIS 3.16 + * \deprecated since QGIS 3.18 use setConfig() instead */ - QgsAttributeEditorRelation::Buttons visibleButtons() const {return mButtons;} + Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const {return mButtons;} /** * Determines the force suppress form popup status. @@ -507,6 +524,16 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement */ void setLabel( const QString &label = QString() ); + /** + * Returns the current relation widget type id + */ + QString relationWidgetTypeId() const; + + /** + * Sets the relation widget type + */ + void setRelationWidgetTypeId( const QString &relationWidgetTypeId ); + private: void saveConfiguration( QDomElement &elem ) const override; QString typeIdentifier() const override; @@ -516,6 +543,7 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement bool mForceSuppressFormPopup = false; QVariant mNmRelationId; QString mLabel; + QString mRelationWidgetTypeId; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsAttributeEditorRelation::Buttons ) diff --git a/src/core/qgseditformconfig.cpp b/src/core/qgseditformconfig.cpp index e2b20c9302fa..16d6cef8a1e3 100644 --- a/src/core/qgseditformconfig.cpp +++ b/src/core/qgseditformconfig.cpp @@ -24,6 +24,7 @@ #include "qgsapplication.h" #include "qgsmessagelog.h" + QgsAttributeEditorContainer::~QgsAttributeEditorContainer() { qDeleteAll( mChildren ); @@ -660,20 +661,36 @@ QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomEleme // At this time, the relations are not loaded // So we only grab the id and delegate the rest to onRelationsLoaded() QgsAttributeEditorRelation *relElement = new QgsAttributeEditorRelation( elem.attribute( QStringLiteral( "relation" ), QStringLiteral( "[None]" ) ), parent ); - if ( elem.hasAttribute( "buttons" ) ) - { - QString buttonString = elem.attribute( QStringLiteral( "buttons" ), qgsFlagValueToKeys( QgsAttributeEditorRelation::Button::AllButtons ) ); - relElement->setVisibleButtons( qgsFlagKeysToValue( buttonString, QgsAttributeEditorRelation::Button::AllButtons ) ); - } - else + QVariantMap config = QgsXmlUtils::readVariant( elem.firstChildElement( "config" ) ).toMap(); + + // load defaults + if ( config.isEmpty() ) + config = relElement->config(); + + // pre QGIS 3.18 compatibility + Q_NOWARN_DEPRECATED_PUSH + if ( ! config.contains( QStringLiteral( "buttons" ) ) ) { - // pre QGIS 3.16 compatibility - QgsAttributeEditorRelation::Buttons buttons = QgsAttributeEditorRelation::Button::AllButtons; - buttons.setFlag( QgsAttributeEditorRelation::Button::Link, elem.attribute( QStringLiteral( "showLinkButton" ), QStringLiteral( "1" ) ).toInt() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::Unlink, elem.attribute( QStringLiteral( "showUnlinkButton" ), QStringLiteral( "1" ) ).toInt() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::SaveChildEdits, elem.attribute( QStringLiteral( "showSaveChildEditsButton" ), QStringLiteral( "1" ) ).toInt() ); - relElement->setVisibleButtons( buttons ); + if ( elem.hasAttribute( "buttons" ) ) + { + QString buttonString = elem.attribute( QStringLiteral( "buttons" ), qgsFlagValueToKeys( QgsAttributeEditorRelation::Button::AllButtons ) ); + relElement->setVisibleButtons( qgsFlagKeysToValue( buttonString, QgsAttributeEditorRelation::Button::AllButtons ) ); + config.insert( "buttons", qgsFlagValueToKeys( relElement->visibleButtons() ) ); + } + else + { + // pre QGIS 3.16 compatibility + QgsAttributeEditorRelation::Buttons buttons = QgsAttributeEditorRelation::Button::AllButtons; + buttons.setFlag( QgsAttributeEditorRelation::Button::Link, elem.attribute( QStringLiteral( "showLinkButton" ), QStringLiteral( "1" ) ).toInt() ); + buttons.setFlag( QgsAttributeEditorRelation::Button::Unlink, elem.attribute( QStringLiteral( "showUnlinkButton" ), QStringLiteral( "1" ) ).toInt() ); + buttons.setFlag( QgsAttributeEditorRelation::Button::SaveChildEdits, elem.attribute( QStringLiteral( "showSaveChildEditsButton" ), QStringLiteral( "1" ) ).toInt() ); + config.insert( "buttons", qgsFlagValueToKeys( relElement->visibleButtons() ) ); + } } + Q_NOWARN_DEPRECATED_POP + + relElement->setConfig( config ); + if ( elem.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) ) { relElement->setForceSuppressFormPopup( elem.attribute( QStringLiteral( "forceSuppressFormPopup" ) ).toInt() ); @@ -698,6 +715,11 @@ QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomEleme QString label = elem.attribute( QStringLiteral( "label" ) ); relElement->setLabel( label ); } + if ( elem.hasAttribute( "relationWidgetTypeId" ) ) + { + QString relationWidgetTypeId = elem.attribute( QStringLiteral( "relationWidgetTypeId" ) ); + relElement->setRelationWidgetTypeId( relationWidgetTypeId ); + } newElement = relElement; } else if ( elem.tagName() == QLatin1String( "attributeEditorQmlElement" ) ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 475389816425..497bf06ffcb6 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -566,6 +566,13 @@ set(QGIS_GUI_SRCS qgsrasterlayersaveasdialog.cpp qgsrasterpyramidsoptionswidget.cpp qgsrelationeditorwidget.cpp + qgsrelationconfigwidget.cpp + qgsbasicrelationwidget.cpp + qgsbasicrelationconfigwidget.cpp + qgsbasicrelationwidgetfactory.cpp + qgsrelationwidget.cpp + qgsrelationwidgetfactory.cpp + qgsrelationwidgetregistry.cpp qgsrubberband.cpp qgsscalecombobox.cpp qgsscalerangewidget.cpp @@ -812,6 +819,13 @@ set(QGIS_GUI_HDRS qgsrasterpyramidsoptionswidget.h qgsratiolockbutton.h qgsrelationeditorwidget.h + qgsrelationconfigwidget.h + qgsbasicrelationwidget.h + qgsbasicrelationconfigwidget.h + qgsbasicrelationwidgetfactory.h + qgsrelationwidget.h + qgsrelationwidgetfactory.h + qgsrelationwidgetregistry.h qgsrubberband.h qgsscalecombobox.h qgsscalerangewidget.h diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp index 5c603ee1efe5..acc69cbb125a 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp @@ -15,6 +15,8 @@ #include "qgsattributewidgetedit.h" #include "qgsattributesformproperties.h" +#include "qgsrelationwidgetregistry.h" +#include "qgsrelationconfigwidget.h" QgsAttributeWidgetEdit::QgsAttributeWidgetEdit( QTreeWidgetItem *item, QWidget *parent ) @@ -29,7 +31,6 @@ QgsAttributeWidgetEdit::QgsAttributeWidgetEdit( QTreeWidgetItem *item, QWidget * // common configs mShowLabelCheckBox->setChecked( itemData.showLabel() ); - switch ( itemData.type() ) { case QgsAttributesFormProperties::DnDTreeItemData::Relation: @@ -92,18 +93,18 @@ QgsAttributeWidgetRelationEditWidget::QgsAttributeWidgetRelationEditWidget( QWid : QWidget( parent ) { setupUi( this ); + + QMapIterator it( QgsGui::relationWidgetRegistry()->factories() ); + + while ( it.hasNext() ) + { + it.next(); + mWidgetTypeComboBox->addItem( it.value()->name(), it.key() ); + } } void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const QgsAttributesFormProperties::RelationEditorConfiguration &config, const QString &relationId ) { - mRelationShowLinkCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::Link ) ); - mRelationShowUnlinkCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::Unlink ) ); - mRelationShowAddChildCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) ); - mRelationShowDuplicateChildFeatureCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature ) ); - mRelationShowZoomToFeatureCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature ) ); - mRelationDeleteChildFeatureCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature ) ); - mRelationShowSaveChildEditsCheckBox->setChecked( config.buttons.testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits ) ); - //load the combo mRelationCardinalityCombo setCardinalityCombo( tr( "Many to one relation" ) ); @@ -115,7 +116,30 @@ void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const setCardinalityCombo( QStringLiteral( "%1 (%2)" ).arg( nmrel.referencedLayer()->name(), nmrel.fieldPairs().at( 0 ).referencedField() ), nmrel.id() ); } - mRelationCardinalityCombo->setToolTip( tr( "For a many to many (N:M) relation, the direct link has to be selected. The in-between table will be hidden." ) ); + int widgetTypeIdx = mWidgetTypeComboBox->findText( config.mRelationWidgetType ); + mWidgetTypeComboBox->setCurrentIndex( widgetTypeIdx >= 0 ? widgetTypeIdx : 0 ); + + const QString widgetType = mWidgetTypeComboBox->currentData().toString(); + mConfigWidget = QgsGui::relationWidgetRegistry()->createConfigWidget( widgetType, relation, this ); + mConfigWidget->setConfig( config.mRelationWidgetConfig ); + mWidgetTypePlaceholderLayout->addWidget( mConfigWidget ); + + disconnect( mWidgetTypeComboBoxConnection ); + + mWidgetTypeComboBoxConnection = connect( mWidgetTypeComboBox, &QComboBox::currentTextChanged, this, [ = ]() + { + const QString widgetId = mWidgetTypeComboBox->currentData().toString(); + + mConfigWidget->deleteLater(); + mConfigWidget = QgsGui::relationWidgetRegistry()->createConfigWidget( widgetId, relation, this ); + mConfigWidget->setConfig( config.mRelationWidgetConfig ); + mWidgetTypePlaceholderLayout->addWidget( mConfigWidget ); + update(); + } ); + + mRelationCardinalityCombo->setToolTip( tr( "This is being changed" ) ); + mRelationCardinalityCombo->addItem( "CHECKED" ); + setNmRelationId( config.nmRelationId ); mRelationLabelEdit->setText( config.label ); @@ -126,15 +150,8 @@ void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const QgsAttributesFormProperties::RelationEditorConfiguration QgsAttributeWidgetRelationEditWidget::relationEditorConfiguration() const { QgsAttributesFormProperties::RelationEditorConfiguration relEdCfg; - QgsAttributeEditorRelation::Buttons buttons; - buttons.setFlag( QgsAttributeEditorRelation::Button::Link, mRelationShowLinkCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsAttributeEditorRelation::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); - relEdCfg.buttons = buttons; + relEdCfg.mRelationWidgetType = mWidgetTypeComboBox->currentData().toString(); + relEdCfg.mRelationWidgetConfig = mConfigWidget->config(); relEdCfg.nmRelationId = mRelationCardinalityCombo->currentData(); relEdCfg.forceSuppressFormPopup = mRelationForceSuppressFormPopupCheckBox->isChecked(); relEdCfg.label = mRelationLabelEdit->text(); diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.h b/src/gui/attributeformconfig/qgsattributewidgetedit.h index d5ef591b135c..d5dba30671d8 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.h +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.h @@ -29,6 +29,7 @@ #include "qgis_gui.h" class QTreeWidgetItem; +class QgsRelationConfigWidget; /** * Widget to edit the configuration (tab or group box, any field or relation, QML, …) of a form item @@ -72,6 +73,9 @@ class GUI_EXPORT QgsAttributeWidgetRelationEditWidget : public QWidget, private private: void setCardinalityCombo( const QString &cardinalityComboItem, const QVariant &auserData = QVariant() ); void setNmRelationId( const QVariant &auserData = QVariant() ); + + QMetaObject::Connection mWidgetTypeComboBoxConnection; + QgsRelationConfigWidget *mConfigWidget = nullptr; }; #endif // QGSATTRIBUTEWIDGETEDIT_H diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp index 4b75988457f7..65ed9ae1ad37 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp @@ -19,12 +19,19 @@ #include "qgsattributeeditorcontext.h" #include "qgsproject.h" #include "qgsrelationmanager.h" +#include "qgsrelationwidgetregistry.h" +#include "qgsgui.h" #include QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent ) + : QgsRelationWidgetWrapper( QStringLiteral( "basic" ), vl, relation, editor, parent ) +{ +} + +QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( const QString &relationEditorName, QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent ) : QgsWidgetWrapper( vl, editor, parent ) , mRelation( relation ) - + , mRelationEditorName( relationEditorName ) { } @@ -34,7 +41,15 @@ QWidget *QgsRelationWidgetWrapper::createWidget( QWidget *parent ) if ( form ) connect( form, &QgsAttributeForm::widgetValueChanged, this, &QgsRelationWidgetWrapper::widgetValueChanged ); - return new QgsRelationEditorWidget( parent ); + QWidget *widget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorName, widgetConfig(), parent ); + + if ( !widget ) + { + QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorName ) ); + widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "basic" ), widgetConfig(), parent ); + } + + return widget; } void QgsRelationWidgetWrapper::setFeature( const QgsFeature &feature ) @@ -101,25 +116,17 @@ void QgsRelationWidgetWrapper::widgetValueChanged( const QString &attribute, con bool QgsRelationWidgetWrapper::showUnlinkButton() const { - Q_NOWARN_DEPRECATED_PUSH - return mWidget->showUnlinkButton(); - Q_NOWARN_DEPRECATED_POP + return visibleButtons().testFlag( QgsAttributeEditorRelation::Button::Unlink ); } void QgsRelationWidgetWrapper::setShowUnlinkButton( bool showUnlinkButton ) { - Q_NOWARN_DEPRECATED_PUSH - if ( mWidget ) - mWidget->setShowUnlinkButton( showUnlinkButton ); - Q_NOWARN_DEPRECATED_POP + setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::Unlink, showUnlinkButton ) ); } void QgsRelationWidgetWrapper::setShowSaveChildEditsButton( bool showSaveChildEditsButton ) { - Q_NOWARN_DEPRECATED_PUSH - if ( mWidget ) - mWidget->setShowSaveChildEditsButton( showSaveChildEditsButton ); - Q_NOWARN_DEPRECATED_POP + setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::SaveChildEdits, showSaveChildEditsButton ) ); } bool QgsRelationWidgetWrapper::showLabel() const @@ -139,17 +146,12 @@ void QgsRelationWidgetWrapper::setShowLabel( bool showLabel ) void QgsRelationWidgetWrapper::initWidget( QWidget *editor ) { - QgsRelationEditorWidget *w = qobject_cast( editor ); + QgsRelationWidget *w = qobject_cast( editor ); // if the editor cannot be cast to relation editor, insert a new one if ( !w ) { - w = new QgsRelationEditorWidget( editor ); - w->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - if ( ! editor->layout() ) - { - editor->setLayout( new QGridLayout() ); - } + w = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorName, widgetConfig(), editor ); editor->layout()->addWidget( w ); } @@ -206,10 +208,7 @@ bool QgsRelationWidgetWrapper::showLinkButton() const void QgsRelationWidgetWrapper::setShowLinkButton( bool showLinkButton ) { - Q_NOWARN_DEPRECATED_PUSH - if ( mWidget ) - mWidget->setShowLinkButton( showLinkButton ); - Q_NOWARN_DEPRECATED_POP + setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::Link, showLinkButton ) ); } bool QgsRelationWidgetWrapper::showSaveChildEditsButton() const @@ -219,13 +218,17 @@ bool QgsRelationWidgetWrapper::showSaveChildEditsButton() const void QgsRelationWidgetWrapper::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) { - if ( mWidget ) - mWidget->setVisibleButtons( buttons ); + if ( ! mWidget ) + return; + QVariantMap config = mWidget->config(); + config.insert( "buttons", qgsFlagValueToKeys( buttons ) ); + + mWidget->setConfig( config ); } QgsAttributeEditorRelation::Buttons QgsRelationWidgetWrapper::visibleButtons() const { - return mWidget->visibleButtons(); + return qgsFlagKeysToValue( mWidget->config().value( QStringLiteral( "buttons" ) ).toString(), QgsAttributeEditorRelation::AllButtons ); } void QgsRelationWidgetWrapper::setForceSuppressFormPopup( bool forceSuppressFormPopup ) @@ -245,6 +248,7 @@ bool QgsRelationWidgetWrapper::forceSuppressFormPopup() const { if ( mWidget ) return mWidget->forceSuppressFormPopup(); + return false; } @@ -294,3 +298,14 @@ QString QgsRelationWidgetWrapper::label() const return mWidget->label(); return QString(); } + +void QgsRelationWidgetWrapper::setWidgetConfig( const QVariantMap &config ) +{ + if ( mWidget ) + mWidget->setConfig( config ); +} + +QVariantMap QgsRelationWidgetWrapper::widgetConfig() const +{ + return mWidget ? mWidget->config() : QVariantMap(); +} diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h index 6c3c48fc603c..32e984e01c06 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h @@ -20,7 +20,7 @@ #include "qgis_sip.h" #include "qgis_gui.h" -class QgsRelationEditorWidget; +class QgsRelationWidget; /** * \ingroup gui @@ -34,7 +34,21 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper public: //! Constructor for QgsRelationWidgetWrapper - explicit QgsRelationWidgetWrapper( QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor = nullptr, QWidget *parent SIP_TRANSFERTHIS = nullptr ); + QgsRelationWidgetWrapper( + QgsVectorLayer *vl, + const QgsRelation &relation, + QWidget *editor SIP_CONSTRAINED = nullptr, + QWidget *parent SIP_TRANSFERTHIS SIP_CONSTRAINED = nullptr + ); + + //! Constructor for QgsRelationWidgetWrapper + QgsRelationWidgetWrapper( + const QString &relationEditorName, + QgsVectorLayer *vl, + const QgsRelation &relation, + QWidget *editor = nullptr, + QWidget *parent SIP_TRANSFERTHIS = nullptr + ); /** * Defines if a title label should be shown for this widget. @@ -97,14 +111,29 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper /** * Defines the buttons which are shown * \since QGIS 3.16 + * \deprecated since QGIS 3.18 */ - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); /** * Returns the buttons which are shown * \since QGIS 3.16 + * \deprecated since QGIS 3.18 + */ + Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const; + + + /** + * Will set the config of this widget wrapper to the specified config. + * + * \param config The config for this wrapper + */ + void setWidgetConfig( const QVariantMap &config ); + + /** + * Returns the whole widget config */ - QgsAttributeEditorRelation::Buttons visibleButtons() const; + QVariantMap widgetConfig() const; /** * Determines the force suppress form popup status that is configured for this widget @@ -187,7 +216,8 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper void aboutToSave() override; QgsRelation mRelation; QgsRelation mNmRelation; - QgsRelationEditorWidget *mWidget = nullptr; + QString mRelationEditorName; + QgsRelationWidget *mWidget = nullptr; }; #endif // QGSRELATIONWIDGETWRAPPER_H diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 76752e4ad612..00e071ded396 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -1254,7 +1254,12 @@ QList QgsAttributeForm::constraintDependencies( QgsEdi QgsRelationWidgetWrapper *QgsAttributeForm::setupRelationWidgetWrapper( const QgsRelation &rel, const QgsAttributeEditorContext &context ) { - QgsRelationWidgetWrapper *rww = new QgsRelationWidgetWrapper( mLayer, rel, nullptr, this ); + return setupRelationWidgetWrapper( QString(), rel, context ); +} + +QgsRelationWidgetWrapper *QgsAttributeForm::setupRelationWidgetWrapper( const QString &relationWidgetTypeId, const QgsRelation &rel, const QgsAttributeEditorContext &context ) +{ + QgsRelationWidgetWrapper *rww = new QgsRelationWidgetWrapper( relationWidgetTypeId, mLayer, rel, nullptr, this ); const QVariantMap config = mLayer->editFormConfig().widgetConfig( rel.id() ); rww->setConfig( config ); rww->setContext( context ); @@ -1943,7 +1948,7 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt { const QgsAttributeEditorRelation *relDef = static_cast( widgetDef ); - QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( relDef->relation(), context ); + QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( relDef->relationWidgetTypeId(), relDef->relation(), context ); QgsAttributeFormRelationEditorWidget *formWidget = new QgsAttributeFormRelationEditorWidget( rww, this ); formWidget->createSearchWidgetWrappers( mContext ); @@ -1951,7 +1956,7 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt // This needs to be after QgsAttributeFormRelationEditorWidget creation, because the widget // does not exists yet until QgsAttributeFormRelationEditorWidget is created and the setters // below directly alter the widget and check for it. - rww->setVisibleButtons( relDef->visibleButtons() ); + rww->setWidgetConfig( relDef->config() ); rww->setShowLabel( relDef->showLabel() ); rww->setNmRelationId( relDef->nmRelationId() ); rww->setForceSuppressFormPopup( relDef->forceSuppressFormPopup() ); diff --git a/src/gui/qgsattributeform.h b/src/gui/qgsattributeform.h index 65e69311e092..9b35a02c0a4a 100644 --- a/src/gui/qgsattributeform.h +++ b/src/gui/qgsattributeform.h @@ -415,6 +415,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget QList constraintDependencies( QgsEditorWidgetWrapper *w ); QgsRelationWidgetWrapper *setupRelationWidgetWrapper( const QgsRelation &rel, const QgsAttributeEditorContext &context ); + QgsRelationWidgetWrapper *setupRelationWidgetWrapper( const QString &relationWidgetTypeId, const QgsRelation &rel, const QgsAttributeEditorContext &context ); QgsVectorLayer *mLayer = nullptr; QgsFeature mFeature; diff --git a/src/gui/qgsbasicrelationconfigwidget.cpp b/src/gui/qgsbasicrelationconfigwidget.cpp new file mode 100644 index 000000000000..c8204ec645cd --- /dev/null +++ b/src/gui/qgsbasicrelationconfigwidget.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** + qgsbasicrelationconfigwidget.cpp + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsbasicrelationconfigwidget.h" +#include "qgsbasicrelationwidget.h" + + +QgsBasicRelationConfigWidget::QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) + : QgsRelationConfigWidget( relation, parent ) +{ + setupUi( this ); +} + +QVariantMap QgsBasicRelationConfigWidget::config() +{ + QgsBasicRelationWidget::Buttons buttons; + buttons.setFlag( QgsBasicRelationWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); + + return QVariantMap( + { + {"buttons", qgsFlagValueToKeys( buttons )}, + } ); +} + +void QgsBasicRelationConfigWidget::setConfig( const QVariantMap &config ) +{ + const QgsBasicRelationWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); + + mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Link ) ); + mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); + mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); + mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); + mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) ); + mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); + mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) ); +} diff --git a/src/gui/qgsbasicrelationconfigwidget.h b/src/gui/qgsbasicrelationconfigwidget.h new file mode 100644 index 000000000000..236700371734 --- /dev/null +++ b/src/gui/qgsbasicrelationconfigwidget.h @@ -0,0 +1,62 @@ +/*************************************************************************** + qgsbasicrelationconfigwidget.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSBASICRELATIONCONFIGWIDGET_H +#define QGSBASICRELATIONCONFIGWIDGET_H + + +#include "ui_qgsrelationeditorconfigwidgetbase.h" +#include "qgsrelationconfigwidget.h" + +#define SIP_NO_FILE + +/** + * \ingroup gui + * \class QgsBasicRelationConfigWidget + * Creates a new configuration widget for the basic relation widget + * \since QGIS 3.18 + */ +class QgsBasicRelationConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase +{ + public: + + /** + * Create a new configuration widget + * + * \param relation The relation for which the configuration dialog will be created + * \param parent A parent widget + */ + explicit QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); + + /** + * \brief Create a configuration from the current GUI state + * + * \returns A widget configuration + */ + QVariantMap config(); + + /** + * \brief Update the configuration widget to represent the given configuration. + * + * \param config The configuration which should be represented by this widget + */ + void setConfig( const QVariantMap &config ); + +}; + +#endif // QGSBASICRELATIONCONFIGWIDGET_H diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp new file mode 100644 index 000000000000..3083e705f204 --- /dev/null +++ b/src/gui/qgsbasicrelationwidget.cpp @@ -0,0 +1,558 @@ +/*************************************************************************** + qgsbasicrelationwidget.cpp + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsbasicrelationwidget.h" + +#include "qgsapplication.h" +#include "qgsdistancearea.h" +#include "qgsfeatureiterator.h" +#include "qgsvectordataprovider.h" +#include "qgsexpression.h" +#include "qgsfeature.h" +#include "qgsfeatureselectiondlg.h" +#include "qgsgenericfeatureselectionmanager.h" +#include "qgsrelation.h" +#include "qgsvectorlayertools.h" +#include "qgsproject.h" +#include "qgstransactiongroup.h" +#include "qgslogger.h" +#include "qgsvectorlayerutils.h" +#include "qgsmapcanvas.h" +#include "qgsvectorlayerselectionmanager.h" +#include "qgsmaptooldigitizefeature.h" +#include "qgsexpressioncontextutils.h" +#include "qgsmessagebar.h" +#include "qgsmessagebaritem.h" + +#include +#include +#include +#include + + +QgsBasicRelationWidget::QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent ) + : QgsRelationWidget( config, parent ) + , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ) ) +{ + QVBoxLayout *rootLayout = new QVBoxLayout( this ); + rootLayout->setContentsMargins( 0, 0, 0, 0 ); + + mRootCollapsibleGroupBox = new QgsCollapsibleGroupBox( QString(), this ); + rootLayout->addWidget( mRootCollapsibleGroupBox ); + + QVBoxLayout *topLayout = new QVBoxLayout( mRootCollapsibleGroupBox ); + topLayout->setContentsMargins( 0, 9, 0, 0 ); + + // buttons + QHBoxLayout *buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins( 0, 0, 0, 0 ); + // toggle editing + mToggleEditingButton = new QToolButton( this ); + mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) ); + mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) ); + mToggleEditingButton->setText( tr( "Toggle Editing" ) ); + mToggleEditingButton->setEnabled( false ); + mToggleEditingButton->setCheckable( true ); + mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) ); + buttonLayout->addWidget( mToggleEditingButton ); + // save Edits + mSaveEditsButton = new QToolButton( this ); + mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) ); + mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) ); + mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) ); + mSaveEditsButton->setEnabled( true ); + buttonLayout->addWidget( mSaveEditsButton ); + // add feature with geometry + mAddFeatureGeometryButton = new QToolButton( this ); + mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) ); + buttonLayout->addWidget( mAddFeatureGeometryButton ); + // add feature + mAddFeatureButton = new QToolButton( this ); + mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) ); + mAddFeatureButton->setText( tr( "Add Child Feature" ) ); + mAddFeatureButton->setToolTip( tr( "Add child feature" ) ); + mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) ); + buttonLayout->addWidget( mAddFeatureButton ); + // duplicate feature + mDuplicateFeatureButton = new QToolButton( this ); + mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) ); + mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) ); + mDuplicateFeatureButton->setToolTip( tr( "Duplicate child feature" ) ); + mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) ); + buttonLayout->addWidget( mDuplicateFeatureButton ); + // delete feature + mDeleteFeatureButton = new QToolButton( this ); + mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) ); + mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) ); + mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) ); + mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) ); + buttonLayout->addWidget( mDeleteFeatureButton ); + // link feature + mLinkFeatureButton = new QToolButton( this ); + mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) ); + mLinkFeatureButton->setText( tr( "Link Existing Features" ) ); + mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) ); + mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) ); + buttonLayout->addWidget( mLinkFeatureButton ); + // unlink feature + mUnlinkFeatureButton = new QToolButton( this ); + mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) ); + mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) ); + mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) ); + mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) ); + buttonLayout->addWidget( mUnlinkFeatureButton ); + // zoom to linked feature + mZoomToFeatureButton = new QToolButton( this ); + mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) ); + mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) ); + mZoomToFeatureButton->setToolTip( tr( "Zoom to child feature" ) ); + mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) ); + buttonLayout->addWidget( mZoomToFeatureButton ); + // spacer + buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) ); + // form view + mFormViewButton = new QToolButton( this ); + mFormViewButton->setText( tr( "Form View" ) ); + mFormViewButton->setToolTip( tr( "Switch to form view" ) ); + mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) ); + mFormViewButton->setCheckable( true ); + mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor ); + buttonLayout->addWidget( mFormViewButton ); + // table view + mTableViewButton = new QToolButton( this ); + mTableViewButton->setText( tr( "Table View" ) ); + mTableViewButton->setToolTip( tr( "Switch to table view" ) ); + mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) ); + mTableViewButton->setCheckable( true ); + mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable ); + buttonLayout->addWidget( mTableViewButton ); + // button group + mViewModeButtonGroup = new QButtonGroup( this ); + mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor ); + mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable ); + + // add buttons layout + topLayout->addLayout( buttonLayout ); + + mRelationLayout = new QGridLayout(); + mRelationLayout->setContentsMargins( 0, 0, 0, 0 ); + topLayout->addLayout( mRelationLayout ); + + mDualView = new QgsDualView( this ); + mDualView->setView( mViewMode ); + + mRelationLayout->addWidget( mDualView ); + +// connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget2::onCollapsedStateChanged ); + connect( mViewModeButtonGroup, static_cast( &QButtonGroup::buttonClicked ), + this, static_cast( &QgsBasicRelationWidget::setViewMode ) ); + connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::toggleEditing ); + connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::saveEdits ); + connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } ); + connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeatureGeometry ); + connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::duplicateFeature ); + connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::deleteSelectedFeatures ); + connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::linkFeature ); + connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::unlinkSelectedFeatures ); + connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::zoomToSelectedFeatures ); + + connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsBasicRelationWidget::showContextMenu ); + + // Set initial state for add/remove etc. buttons + updateButtons(); +} + +void QgsBasicRelationWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ) +{ + QgsAttributeEditorContext ctx { mEditorContext }; + ctx.setParentFormFeature( mFeature ); + mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx ); + mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView ); + mDualView->setFeatureSelectionManager( mFeatureSelectionMgr ); + + connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsBasicRelationWidget::updateButtons ); + + QIcon icon; + QString text; + if ( layer->geometryType() == QgsWkbTypes::PointGeometry ) + { + icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) ); + text = tr( "Add Point child Feature" ); + } + else if ( layer->geometryType() == QgsWkbTypes::LineGeometry ) + { + icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) ); + text = tr( "Add Line child Feature" ); + } + else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry ) + { + icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) ); + text = tr( "Add Polygon Feature" ); + } + + mAddFeatureGeometryButton->setIcon( icon ); + mAddFeatureGeometryButton->setText( text ); + mAddFeatureGeometryButton->setToolTip( text ); + + updateButtons(); +} + +void QgsBasicRelationWidget::setEditorContext( const QgsAttributeEditorContext &context ) +{ + mEditorContext = context; + + if ( context.mapCanvas() && context.cadDockWidget() ) + { + mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) ); + mMapToolDigitize->setButton( mAddFeatureGeometryButton ); + } + + updateButtons(); +} + +void QgsBasicRelationWidget::setViewMode( QgsDualView::ViewMode mode ) +{ + mDualView->setView( mode ); + mViewMode = mode; +} + +void QgsBasicRelationWidget::updateButtons() +{ + bool editable = false; + bool linkable = false; + bool spatial = false; + bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false; + + if ( mRelation.isValid() ) + { + editable = mRelation.referencingLayer()->isEditable(); + linkable = mRelation.referencingLayer()->isEditable(); + spatial = mRelation.referencingLayer()->isSpatial(); + } + + if ( mNmRelation.isValid() ) + { + editable = mNmRelation.referencedLayer()->isEditable(); + spatial = mNmRelation.referencedLayer()->isSpatial(); + } + + mAddFeatureButton->setEnabled( editable ); + mAddFeatureGeometryButton->setEnabled( editable ); + mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty ); + mLinkFeatureButton->setEnabled( linkable ); + mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty ); + mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty ); + mZoomToFeatureButton->setEnabled( selectionNotEmpty ); + mToggleEditingButton->setChecked( editable ); + mSaveEditsButton->setEnabled( editable ); + + mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup ); + + mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::Link ) ); + mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); + mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup ); + mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); + mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial ); + mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); + mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); + mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial ); +} + +void QgsBasicRelationWidget::addFeatureGeometry() +{ + QgsVectorLayer *layer = nullptr; + if ( mNmRelation.isValid() ) + layer = mNmRelation.referencedLayer(); + else + layer = mRelation.referencingLayer(); + + mMapToolDigitize->setLayer( layer ); + + // window is always on top, so we hide it to digitize without seeing it + window()->setVisible( false ); + setMapTool( mMapToolDigitize ); + + connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsBasicRelationWidget::onDigitizingCompleted ); + connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsBasicRelationWidget::onKeyPressed ); + + if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() ) + { + QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature ); + + QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ); + QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." ) + .arg( layer->name() ); + mMessageBarItem = QgsMessageBar::createMessage( title, msg, this ); + lMainMessageBar->pushItem( mMessageBarItem ); + } + +} + +void QgsBasicRelationWidget::onDigitizingCompleted( const QgsFeature &feature ) +{ + addFeature( feature.geometry() ); + + unsetMapTool(); +} + +void QgsBasicRelationWidget::toggleEditing( bool state ) +{ + QgsRelationWidget::toggleEditing( state ); + + updateButtons(); +} + +void QgsBasicRelationWidget::onCollapsedStateChanged( bool collapsed ) +{ + if ( !collapsed ) + { + mVisible = true; + updateUi(); + } +} + +void QgsBasicRelationWidget::updateUi() +{ + // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) + // If it is already initialized, it has been set visible before and the currently shown feature is changing + // and the widget needs updating + + if ( mVisible ) + { + QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); + + if ( mNmRelation.isValid() ) + { + QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( myRequest ); + + QgsFeature fet; + + QStringList filters; + + while ( it.nextFeature( fet ) ) + { + QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression(); + filters << filter.prepend( '(' ).append( ')' ); + } + + QgsFeatureRequest nmRequest; + + nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) ); + + initDualView( mNmRelation.referencedLayer(), nmRequest ); + } + else if ( mRelation.referencingLayer() ) + { + initDualView( mRelation.referencingLayer(), myRequest ); + } + } +} + +void QgsBasicRelationWidget::setVisibleButtons( const Buttons &buttons ) +{ + mButtonsVisibility = buttons; + updateButtons(); +} + +QgsBasicRelationWidget::Buttons QgsBasicRelationWidget::visibleButtons() const +{ + Buttons buttons; + if ( mLinkFeatureButton->isVisible() ) + buttons |= Button::Link; + if ( mUnlinkFeatureButton->isVisible() ) + buttons |= Button::Unlink; + if ( mSaveEditsButton->isVisible() ) + buttons |= Button::SaveChildEdits; + if ( mAddFeatureButton->isVisible() ) + buttons |= Button::AddChildFeature; + if ( mDuplicateFeatureButton->isVisible() ) + buttons |= Button::DuplicateChildFeature; + if ( mDeleteFeatureButton->isVisible() ) + buttons |= Button::DeleteChildFeature; + if ( mZoomToFeatureButton->isVisible() ) + buttons |= Button::ZoomToChildFeature; + return buttons; +} + +void QgsBasicRelationWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue ) +{ + mDualView->parentFormValueChanged( attribute, newValue ); +} + +void QgsBasicRelationWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid ) +{ + if ( mRelation.referencingLayer()->isEditable() ) + { + QAction *qAction = nullptr; + + qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) ); + connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } ); + + qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) ); + connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } ); + } +} + +void QgsBasicRelationWidget::setMapTool( QgsMapTool *mapTool ) +{ + QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + + mapCanvas->setMapTool( mapTool ); + mapCanvas->window()->raise(); + mapCanvas->activateWindow(); + mapCanvas->setFocus(); + connect( mapTool, &QgsMapTool::deactivated, this, &QgsBasicRelationWidget::mapToolDeactivated ); +} + +void QgsBasicRelationWidget::unsetMapTool() +{ + QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + + // this will call mapToolDeactivated + mapCanvas->unsetMapTool( mMapToolDigitize ); + + disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsBasicRelationWidget::onKeyPressed ); + disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsBasicRelationWidget::onDigitizingCompleted ); +} + +void QgsBasicRelationWidget::onKeyPressed( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Escape ) + { + unsetMapTool(); + } +} + +void QgsBasicRelationWidget::mapToolDeactivated() +{ + window()->setVisible( true ); + window()->raise(); + window()->activateWindow(); + + if ( mEditorContext.mainMessageBar() && mMessageBarItem ) + { + mEditorContext.mainMessageBar()->popWidget( mMessageBarItem ); + } + mMessageBarItem = nullptr; +} + +QVariantMap QgsBasicRelationWidget::config() const +{ + return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )}} ); +} + +void QgsBasicRelationWidget::setConfig( const QVariantMap &config ) +{ + mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); + updateButtons(); +} + +void QgsBasicRelationWidget::setTitle( const QString &title ) +{ + mRootCollapsibleGroupBox->setTitle( title ); +} + +void QgsBasicRelationWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) +{ + Q_UNUSED( newRelation ); + Q_UNUSED( newFeature ); + + if ( ! mRelation.isValid() ) + return; + + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); +} + +void QgsBasicRelationWidget::afterSetRelationFeature() +{ + mToggleEditingButton->setEnabled( false ); + + if ( ! mRelation.isValid() ) + return; + + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); + + QgsVectorLayer *vl = mRelation.referencingLayer(); + bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; + if ( canChangeAttributes && !vl->readOnly() ) + { + mToggleEditingButton->setEnabled( true ); + updateButtons(); + } + else + { + mToggleEditingButton->setEnabled( false ); + } + + // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) + // If it is already initialized, it has been set visible before and the currently shown feature is changing + // and the widget needs updating + + if ( mVisible ) + { + QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); + initDualView( mRelation.referencingLayer(), myRequest ); + } +} + +void QgsBasicRelationWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) +{ + Q_UNUSED( newRelation ); + Q_UNUSED( newNmRelation ); + + if ( mRelation.isValid() ) + { + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); + } + + if ( mNmRelation.isValid() ) + { + disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); + } +} + +void QgsBasicRelationWidget::afterSetRelations() +{ + if ( !mRelation.isValid() ) + return; + + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); + + if ( mNmRelation.isValid() ) + { + connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); + connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); + } + + QgsVectorLayer *vl = mRelation.referencingLayer(); + bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; + if ( canChangeAttributes && !vl->readOnly() ) + { + mToggleEditingButton->setEnabled( true ); + } + else + { + mToggleEditingButton->setEnabled( false ); + } + + updateButtons(); +} diff --git a/src/gui/qgsbasicrelationwidget.h b/src/gui/qgsbasicrelationwidget.h new file mode 100644 index 000000000000..8e711999dd29 --- /dev/null +++ b/src/gui/qgsbasicrelationwidget.h @@ -0,0 +1,172 @@ +/*************************************************************************** + qgsbasicrelationwidget.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSBASICRELATIONWIDGET_H +#define QGSBASICRELATIONWIDGET_H + +#include +#include +#include +#include +#include "qobjectuniqueptr.h" + +#include "qgsrelationeditorwidget.h" +#include "qgsrelationwidget.h" +#include "qobjectuniqueptr.h" +#include "qgsattributeeditorcontext.h" +#include "qgscollapsiblegroupbox.h" +#include "qgsdualview.h" +#include "qgsrelation.h" +#include "qgsvectorlayerselectionmanager.h" +#include "qgis_gui.h" + +class QgsFeature; +class QgsVectorLayer; +class QgsVectorLayerTools; +class QgsMapTool; +class QgsMapToolDigitizeFeature; + +/** + * The default relation widget in QGIS. Successor of the now deprecated {\see QgsRelationEditorWidget}. + * \ingroup gui + * \class QgsBasicRelationWidget + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsBasicRelationWidget : public QgsRelationWidget +{ + + Q_OBJECT + Q_PROPERTY( QgsDualView::ViewMode viewMode READ viewMode WRITE setViewMode ) + Q_PROPERTY( Buttons visibleButtons READ visibleButtons WRITE setVisibleButtons ) + + public: + + /** + * Possible buttons shown in the relation editor + */ + enum Button + { + Link = 1 << 1, //!< Link button + Unlink = 1 << 2, //!< Unlink button + SaveChildEdits = 1 << 3, //!< Save child edits button + AddChildFeature = 1 << 4, //!< Add child feature (as in some projects we only want to allow linking/unlinking existing features) + DuplicateChildFeature = 1 << 5, //!< Duplicate child feature + DeleteChildFeature = 1 << 6, //!< Delete child feature button + ZoomToChildFeature = 1 << 7, //!< Zoom to child feature + AllButtons = Link | Unlink | SaveChildEdits | AddChildFeature | DuplicateChildFeature | DeleteChildFeature | ZoomToChildFeature //!< All buttons + }; + Q_ENUM( Button ) + Q_DECLARE_FLAGS( Buttons, Button ) + Q_FLAG( Buttons ) + + /** + * Constructor + * \param config widget configuration + * \param parent parent widget + */ + QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); + + //! Define the view mode for the dual view + void setViewMode( QgsDualView::ViewMode mode ); + + //! Gets the view mode for the dual view + QgsDualView::ViewMode viewMode() {return mViewMode;} + + /** + * Sets the editor \a context + * \note if context cadDockWidget is null, it won't be possible to digitize + * the geometry of a referencing feature from this widget + */ + void setEditorContext( const QgsAttributeEditorContext &context ); + + /** + * Defines the buttons which are shown + */ + void setVisibleButtons( const Buttons &buttons ); + + /** + * Returns the buttons which are shown + */ + Buttons visibleButtons() const; + + /** + * Returns the current configuration + */ + QVariantMap config() const override; + + /** + * Defines the current configuration + */ + void setConfig( const QVariantMap &config ) override; + + /** + * Sets the title of the root groupbox + */ + void setTitle( const QString &title ) override; + + public slots: + void parentFormValueChanged( const QString &attribute, const QVariant &newValue ) override; + + private slots: + void setViewMode( int mode ) {setViewMode( static_cast( mode ) );} + void updateButtons(); + + void addFeatureGeometry(); + void toggleEditing( bool state ); + void onCollapsedStateChanged( bool collapsed ); + void showContextMenu( QgsActionMenu *menu, QgsFeatureId fid ); + void mapToolDeactivated(); + void onKeyPressed( QKeyEvent *e ); + void onDigitizingCompleted( const QgsFeature &feature ); + + private: + void updateUi() override; + void initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ); + void setMapTool( QgsMapTool *mapTool ); + void unsetMapTool(); + + QgsCollapsibleGroupBox *mRootCollapsibleGroupBox = nullptr; + QgsDualView *mDualView = nullptr; + QPointer mMessageBarItem; + QgsDualView::ViewMode mViewMode = QgsDualView::AttributeEditor; + + QToolButton *mToggleEditingButton = nullptr; + QToolButton *mSaveEditsButton = nullptr; + QToolButton *mAddFeatureButton = nullptr; + QToolButton *mDuplicateFeatureButton = nullptr; + QToolButton *mDeleteFeatureButton = nullptr; + QToolButton *mLinkFeatureButton = nullptr; + QToolButton *mUnlinkFeatureButton = nullptr; + QToolButton *mZoomToFeatureButton = nullptr; + QToolButton *mFormViewButton = nullptr; + QToolButton *mTableViewButton = nullptr; + QToolButton *mAddFeatureGeometryButton = nullptr; + QGridLayout *mRelationLayout = nullptr; + QObjectUniquePtr mMapToolDigitize; + QButtonGroup *mViewModeButtonGroup = nullptr; + + Buttons mButtonsVisibility = Button::AllButtons; + bool mVisible = true; + + void beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) override; + void afterSetRelationFeature() override; + void beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) override; + void afterSetRelations() override; +}; + + +#endif // QGSBASICRELATIONWIDGET_H diff --git a/src/gui/qgsbasicrelationwidgetfactory.cpp b/src/gui/qgsbasicrelationwidgetfactory.cpp new file mode 100644 index 000000000000..acf8de03b089 --- /dev/null +++ b/src/gui/qgsbasicrelationwidgetfactory.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + qgsbasicrelationwidgetfactory.cpp + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsbasicrelationwidgetfactory.h" +#include "qgsbasicrelationconfigwidget.h" + +QgsBasicRelationWidgetFactory::QgsBasicRelationWidgetFactory() +{ + +} + +QString QgsBasicRelationWidgetFactory::type() const +{ + return QStringLiteral( "basic" ); +} + +QString QgsBasicRelationWidgetFactory::name() const +{ + return QStringLiteral( "Relation Editor" ); +} + +QgsRelationWidget *QgsBasicRelationWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const +{ + return new QgsBasicRelationWidget( config, parent ); +} + + +QgsRelationConfigWidget *QgsBasicRelationWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const +{ + return static_cast( new QgsBasicRelationConfigWidget( relation, parent ) ); +} + diff --git a/src/gui/qgsbasicrelationwidgetfactory.h b/src/gui/qgsbasicrelationwidgetfactory.h new file mode 100644 index 000000000000..e5824ac4a402 --- /dev/null +++ b/src/gui/qgsbasicrelationwidgetfactory.h @@ -0,0 +1,48 @@ +/*************************************************************************** + qgsbasicrelationwidgetfactory.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSBASICRELATIONWIDGETFACTORY_H +#define QGSBASICRELATIONWIDGETFACTORY_H + +#include "qgsrelationwidgetfactory.h" +#include "qgsbasicrelationwidget.h" + +#define SIP_NO_FILE + +/** + * Factory class for creating a basic relation widget and the respective config widget. + * \ingroup gui + * \class QgsBasicRelationWidgetFactory + * \note not available in Python bindings + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsBasicRelationWidgetFactory : public QgsRelationWidgetFactory +{ + public: + QgsBasicRelationWidgetFactory(); + + QString type() const override; + + QString name() const override; + + QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; + + QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; + +}; + +#endif // QGSBASICRELATIONWIDGETFACTORY_H diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index c48e1c565a7d..5db19edc2fad 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -60,6 +60,7 @@ #include "qgscodeeditorcolorschemeregistry.h" #include "qgssubsetstringeditorproviderregistry.h" #include "qgsprovidersourcewidgetproviderregistry.h" +#include "qgsrelationwidgetregistry.h" QgsGui *QgsGui::instance() { @@ -77,6 +78,11 @@ QgsEditorWidgetRegistry *QgsGui::editorWidgetRegistry() return instance()->mEditorWidgetRegistry; } +QgsRelationWidgetRegistry *QgsGui::relationWidgetRegistry() +{ + return instance()->mRelationEditorRegistry; +} + QgsSourceSelectProviderRegistry *QgsGui::sourceSelectProviderRegistry() { return instance()->mSourceSelectProviderRegistry; @@ -198,6 +204,7 @@ QgsGui::~QgsGui() delete mCodeEditorColorSchemeRegistry; delete mSubsetStringEditorProviderRegistry; delete mProviderSourceWidgetProviderRegistry; + delete mRelationEditorRegistry; } QColor QgsGui::sampleColor( QPoint point ) @@ -261,6 +268,7 @@ QgsGui::QgsGui() mProviderSourceWidgetProviderRegistry->initializeFromProviderGuiRegistry( mProviderGuiRegistry ); mEditorWidgetRegistry = new QgsEditorWidgetRegistry(); + mRelationEditorRegistry = new QgsRelationWidgetRegistry(); mShortcutsManager = new QgsShortcutsManager(); mLayerTreeEmbeddedWidgetRegistry = new QgsLayerTreeEmbeddedWidgetRegistry(); mMapLayerActionRegistry = new QgsMapLayerActionRegistry(); diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index ce9c55285bb3..e52b426b7f99 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -42,6 +42,7 @@ class QgsCodeEditorColorSchemeRegistry; class QgsMessageBar; class QgsSubsetStringEditorProviderRegistry; class QgsProviderSourceWidgetProviderRegistry; +class QgsRelationWidgetRegistry; /** * \ingroup gui @@ -168,6 +169,12 @@ class GUI_EXPORT QgsGui : public QObject */ static QgsProviderSourceWidgetProviderRegistry *sourceWidgetProviderRegistry() SIP_KEEPREFERENCE; + /** + * Returns the global relation widget registry, used for managing all known relation widget factories. + * \since QGIS 3.18 + */ + static QgsRelationWidgetRegistry *relationWidgetRegistry() SIP_KEEPREFERENCE; + /** * Register the widget to allow its position to be automatically saved and restored when open and closed. * Use this to avoid needing to call saveGeometry() and restoreGeometry() on your widget. @@ -271,6 +278,7 @@ class GUI_EXPORT QgsGui : public QObject QgsProjectStorageGuiRegistry *mProjectStorageGuiRegistry = nullptr; QgsSubsetStringEditorProviderRegistry *mSubsetStringEditorProviderRegistry = nullptr; QgsProviderSourceWidgetProviderRegistry *mProviderSourceWidgetProviderRegistry = nullptr; + QgsRelationWidgetRegistry *mRelationEditorRegistry = nullptr; std::unique_ptr< QgsWindowManagerInterface > mWindowManager; #ifdef SIP_RUN diff --git a/src/gui/qgsrelationconfigwidget.cpp b/src/gui/qgsrelationconfigwidget.cpp new file mode 100644 index 000000000000..2ad5dc823cbf --- /dev/null +++ b/src/gui/qgsrelationconfigwidget.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + qgsrelationconfigbasewidget.cpp + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsrelationconfigwidget.h" +#include "qgsexpressioncontextutils.h" + +QgsRelationConfigWidget::QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) + : QWidget( parent ) + , mRelation( relation ) +{ +} + +QgsVectorLayer *QgsRelationConfigWidget::layer() +{ + return mLayer; +} + +QgsRelation QgsRelationConfigWidget::relation() const +{ + return mRelation; +} + + diff --git a/src/gui/qgsrelationconfigwidget.h b/src/gui/qgsrelationconfigwidget.h new file mode 100644 index 000000000000..2d82b2fe0df0 --- /dev/null +++ b/src/gui/qgsrelationconfigwidget.h @@ -0,0 +1,87 @@ +/*************************************************************************** + qgsrelationconfigwidget.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSRELATIONCONFIGBASEWIDGET_H +#define QGSRELATIONCONFIGBASEWIDGET_H + +#include +#include "qgis_sip.h" +#include "qgis_gui.h" + +#include "qgseditorwidgetwrapper.h" + +class QgsRelation; +class QgsPropertyOverrideButton; + +/** + * \ingroup gui + * This class should be subclassed for every configurable relation widget type. + * + * It implements the GUI configuration widget and transforms this to/from a configuration. + * + * It will only be instantiated by {\see QgsRelationWidgetFactory} + * \since QGIS 3.18 + */ + +class GUI_EXPORT QgsRelationConfigWidget : public QWidget +{ + Q_OBJECT + public: + + /** + * Create a new configuration widget + * + * \param relation The relation for which the configuration dialog will be created + * \param parent A parent widget + */ + explicit QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); + + /** + * \brief Create a configuration from the current GUI state + * + * \returns A widget configuration + */ + virtual QVariantMap config() = 0; + + /** + * \brief Update the configuration widget to represent the given configuration. + * + * \param config The configuration which should be represented by this widget + */ + virtual void setConfig( const QVariantMap &config ) = 0; + + /** + * Returns the layer for which this configuration widget applies + * + * \returns The layer + */ + QgsVectorLayer *layer(); + + /** + * Returns the relation for which this configuration widget applies + * + * \returns The relation + */ + QgsRelation relation() const; + + + private: + QgsVectorLayer *mLayer = nullptr; + QgsRelation mRelation; +}; + +#endif // QGSRELATIONCONFIGBASEWIDGET_H diff --git a/src/gui/qgsrelationwidget.cpp b/src/gui/qgsrelationwidget.cpp new file mode 100644 index 000000000000..aba4a11dc06e --- /dev/null +++ b/src/gui/qgsrelationwidget.cpp @@ -0,0 +1,595 @@ +/*************************************************************************** + qgsrelationwidget.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsrelationwidget.h" + +#include "qgsapplication.h" +#include "qgsdistancearea.h" +#include "qgsfeatureiterator.h" +#include "qgsvectordataprovider.h" +#include "qgsexpression.h" +#include "qgsfeature.h" +#include "qgsfeatureselectiondlg.h" +#include "qgsgenericfeatureselectionmanager.h" +#include "qgsrelation.h" +#include "qgsvectorlayertools.h" +#include "qgsproject.h" +#include "qgstransactiongroup.h" +#include "qgslogger.h" +#include "qgsvectorlayerutils.h" +#include "qgsmapcanvas.h" +#include "qgsvectorlayerselectionmanager.h" +#include "qgsmaptooldigitizefeature.h" +#include "qgsexpressioncontextutils.h" +#include "qgsmessagebar.h" +#include "qgsmessagebaritem.h" + +#include +#include +#include +#include + + +QgsRelationWidget::QgsRelationWidget( const QVariantMap &config, QWidget *parent ) + : QWidget( parent ) +{ + Q_UNUSED( config ); +} + +void QgsRelationWidget::setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ) +{ + beforeSetRelationFeature( relation, feature ); + + mRelation = relation; + mFeature = feature; + + setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() ); + + updateTitle(); + afterSetRelationFeature(); + updateUi(); +} + +void QgsRelationWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ) +{ + + beforeSetRelations( relation, nmrelation ); + + mRelation = relation; + mNmRelation = nmrelation; + + if ( !mRelation.isValid() ) + { + afterSetRelations(); + return; + } + + mLayerInSameTransactionGroup = false; + + const auto transactionGroups = QgsProject::instance()->transactionGroups(); + for ( auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it ) + { + if ( mNmRelation.isValid() ) + { + if ( it.value()->layers().contains( mRelation.referencedLayer() ) && + it.value()->layers().contains( mRelation.referencingLayer() ) && + it.value()->layers().contains( mNmRelation.referencedLayer() ) ) + mLayerInSameTransactionGroup = true; + } + else + { + if ( it.value()->layers().contains( mRelation.referencedLayer() ) && + it.value()->layers().contains( mRelation.referencingLayer() ) ) + mLayerInSameTransactionGroup = true; + } + } + + updateTitle(); + + setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() ); + + afterSetRelations(); + updateUi(); +} + +void QgsRelationWidget::setEditorContext( const QgsAttributeEditorContext &context ) +{ + mEditorContext = context; +} + +QgsAttributeEditorContext QgsRelationWidget::editorContext() const +{ + return mEditorContext; +} + +QgsIFeatureSelectionManager *QgsRelationWidget::featureSelectionManager() +{ + return mFeatureSelectionMgr; +} + +void QgsRelationWidget::setFeature( const QgsFeature &feature, bool update ) +{ + mFeature = feature; + + mEditorContext.setFormFeature( feature ); + + if ( update ) + updateUi(); +} + +void QgsRelationWidget::setNmRelationId( const QVariant &nmRelationId ) +{ + mNmRelationId = nmRelationId; +} + +QVariant QgsRelationWidget::nmRelationId() const +{ + return mNmRelationId; +} + +QString QgsRelationWidget::label() const +{ + return mLabel; +} + +void QgsRelationWidget::setLabel( const QString &label ) +{ + mLabel = label; + + updateTitle(); +} + +bool QgsRelationWidget::showLabel() const +{ + return mShowLabel; +} + +void QgsRelationWidget::setShowLabel( bool showLabel ) +{ + mShowLabel = showLabel; + + updateTitle(); +} + +void QgsRelationWidget::setForceSuppressFormPopup( bool forceSuppressFormPopup ) +{ + mForceSuppressFormPopup = forceSuppressFormPopup; +} + +bool QgsRelationWidget::forceSuppressFormPopup() const +{ + return mForceSuppressFormPopup; +} + +void QgsRelationWidget::updateTitle() +{ + if ( mShowLabel && !mLabel.isEmpty() ) + { + setTitle( mLabel ); + } + else if ( mShowLabel && mRelation.isValid() ) + { + setTitle( mRelation.name() ); + } + else + { + setTitle( QString() ); + } +} + +QgsFeature QgsRelationWidget::feature() const +{ + return mFeature; +} + +void QgsRelationWidget::toggleEditing( bool state ) +{ + if ( state ) + { + mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() ); + if ( mNmRelation.isValid() ) + mEditorContext.vectorLayerTools()->startEditing( mNmRelation.referencedLayer() ); + } + else + { + mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() ); + if ( mNmRelation.isValid() ) + mEditorContext.vectorLayerTools()->stopEditing( mNmRelation.referencedLayer() ); + } +} + +void QgsRelationWidget::saveEdits() +{ + mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() ); + if ( mNmRelation.isValid() ) + mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() ); +} + +void QgsRelationWidget::addFeature( const QgsGeometry &geometry ) +{ + QgsAttributeMap keyAttrs; + + const QgsVectorLayerTools *vlTools = mEditorContext.vectorLayerTools(); + + if ( mNmRelation.isValid() ) + { + // n:m Relation: first let the user create a new feature on the other table + // and autocreate a new linking feature. + QgsFeature f; + if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), geometry, &f ) ) + { + // Fields of the linking table + const QgsFields fields = mRelation.referencingLayer()->fields(); + + // Expression context for the linking table + QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext(); + + QgsAttributeMap linkAttributes; + const auto constFieldPairs = mRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + int index = fields.indexOf( fieldPair.first ); + linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) ); + } + + const auto constNmFieldPairs = mNmRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constNmFieldPairs ) + { + int index = fields.indexOf( fieldPair.first ); + linkAttributes.insert( index, f.attribute( fieldPair.second ) ); + } + QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context ); + + mRelation.referencingLayer()->addFeature( linkFeature ); + + updateUi(); + } + } + else + { + QgsFields fields = mRelation.referencingLayer()->fields(); + + const auto constFieldPairs = mRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) ); + + // TODO FINISH!!!! +// vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry ); + } + } +} + +void QgsRelationWidget::deleteFeature( const QgsFeatureId fid ) +{ + deleteFeatures( QgsFeatureIds() << fid ); +} + +void QgsRelationWidget::deleteSelectedFeatures() +{ + QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds(); + deleteFeatures( selectedFids ); +} + +void QgsRelationWidget::deleteFeatures( const QgsFeatureIds &fids ) +{ + bool deleteFeatures = true; + + QgsVectorLayer *layer; + if ( mNmRelation.isValid() ) + { + layer = mNmRelation.referencedLayer(); + + // When deleting a linked feature within an N:M relation, + // check if the feature is linked to more than just one feature. + // In case it is linked more than just once, ask the user for confirmation + // as it is likely he was not aware of the implications and might delete + // there may be several linking entries deleted along. + + QgsFeatureRequest deletedFeaturesRequest; + deletedFeaturesRequest.setFilterFids( fids ); + deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry ); + deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() ); + + QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest ); + QStringList deletedFeaturesPks; + QgsFeature feature; + while ( deletedFeatures.nextFeature( feature ) ) + { + deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) ); + } + + QgsFeatureRequest linkingFeaturesRequest; + linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry ); + linkingFeaturesRequest.setNoAttributes(); + + QString linkingFeaturesRequestExpression; + if ( !deletedFeaturesPks.empty() ) + { + linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) ); + linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression ); + + QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest ); + + int relatedLinkingFeaturesCount = 0; + while ( relatedLinkingFeatures.nextFeature( feature ) ) + { + relatedLinkingFeaturesCount++; + } + + if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 ) + { + QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entry?" ), tr( "The entry on %1 is still linked to %2 features on %3. Do you want to delete it?" ).arg( mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this ); + messageBox.addButton( QMessageBox::Cancel ); + QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole ); + + messageBox.exec(); + if ( messageBox.clickedButton() != deleteButton ) + deleteFeatures = false; + } + else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() ) + { + QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entries?" ), tr( "The %1 entries on %2 are still linked to %3 features on %4. Do you want to delete them?" ).arg( QString::number( deletedFeaturesPks.size() ), mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this ); + messageBox.addButton( QMessageBox::Cancel ); + QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole ); + + messageBox.exec(); + if ( messageBox.clickedButton() != deleteButton ) + deleteFeatures = false; + } + } + } + else + { + layer = mRelation.referencingLayer(); + } + + QgsVectorLayerUtils::QgsDuplicateFeatureContext infoContext; + if ( QgsVectorLayerUtils::impactsCascadeFeatures( layer, fids, QgsProject::instance(), infoContext ) ) + { + QString childrenInfo; + int childrenCount = 0; + const auto infoContextLayers = infoContext.layers(); + for ( QgsVectorLayer *chl : infoContextLayers ) + { + childrenCount += infoContext.duplicatedFeatures( chl ).size(); + childrenInfo += ( tr( "%1 feature(s) on layer \"%2\", " ).arg( infoContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) ); + } + + // for extra safety to make sure we know that the delete can have impact on children and joins + int res = QMessageBox::question( this, tr( "Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ), + tr( "Delete %1 feature(s) on layer \"%2\", %3 as well\nand all of its other descendants.\nDelete these features?" ).arg( fids.count() ).arg( layer->name() ).arg( childrenInfo ), + QMessageBox::Yes | QMessageBox::No ); + if ( res != QMessageBox::Yes ) + deleteFeatures = false; + } + + if ( deleteFeatures ) + { + QgsVectorLayer::DeleteContext context( true, QgsProject::instance() ); + layer->deleteFeatures( fids, &context ); + const auto contextLayers = context.handledLayers(); + if ( contextLayers.size() > 1 ) + { + int deletedCount = 0; + QString feedbackMessage; + for ( QgsVectorLayer *contextLayer : contextLayers ) + { + feedbackMessage += tr( "%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() ); + deletedCount += context.handledFeatures( contextLayer ).size(); + } + mEditorContext.mainMessageBar()->pushMessage( tr( "%1 features deleted: %2" ).arg( deletedCount ).arg( feedbackMessage ), Qgis::Success ); + } + + updateUi(); + } +} + +void QgsRelationWidget::linkFeature() +{ + QgsVectorLayer *layer = nullptr; + + if ( mNmRelation.isValid() ) + layer = mNmRelation.referencedLayer(); + else + layer = mRelation.referencingLayer(); + + QgsFeatureSelectionDlg *selectionDlg = new QgsFeatureSelectionDlg( layer, mEditorContext, this ); + selectionDlg->setAttribute( Qt::WA_DeleteOnClose ); + + const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), mFeature ); + selectionDlg->setWindowTitle( tr( "Link existing child features for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ) ); + + connect( selectionDlg, &QDialog::accepted, this, &QgsRelationWidget::onLinkFeatureDlgAccepted ); + selectionDlg->show(); +} + +void QgsRelationWidget::onLinkFeatureDlgAccepted() +{ + QgsFeatureSelectionDlg *selectionDlg = qobject_cast( sender() ); + if ( mNmRelation.isValid() ) + { + QgsFeatureIterator it = mNmRelation.referencedLayer()->getFeatures( + QgsFeatureRequest() + .setFilterFids( selectionDlg->selectedFeatures() ) + .setSubsetOfAttributes( mNmRelation.referencedFields() ) ); + + QgsFeature relatedFeature; + + QgsFeatureList newFeatures; + + // Fields of the linking table + const QgsFields fields = mRelation.referencingLayer()->fields(); + + // Expression context for the linking table + QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext(); + + QgsAttributeMap linkAttributes; + const auto constFieldPairs = mRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + int index = fields.indexOf( fieldPair.first ); + linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) ); + } + + while ( it.nextFeature( relatedFeature ) ) + { + const auto constFieldPairs = mNmRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + int index = fields.indexOf( fieldPair.first ); + linkAttributes.insert( index, relatedFeature.attribute( fieldPair.second ) ); + } + const QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context ); + + newFeatures << linkFeature; + } + + mRelation.referencingLayer()->addFeatures( newFeatures ); + QgsFeatureIds ids; + const auto constNewFeatures = newFeatures; + for ( const QgsFeature &f : constNewFeatures ) + ids << f.id(); + mRelation.referencingLayer()->selectByIds( ids ); + } + else + { + QMap keys; + const auto constFieldPairs = mRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() ); + QVariant val = mFeature.attribute( fieldPair.referencedField() ); + keys.insert( idx, val ); + } + + const auto constSelectedFeatures = selectionDlg->selectedFeatures(); + for ( QgsFeatureId fid : constSelectedFeatures ) + { + QMapIterator it( keys ); + while ( it.hasNext() ) + { + it.next(); + mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() ); + } + } + } + + updateUi(); +} + +void QgsRelationWidget::unlinkFeature( const QgsFeatureId fid ) +{ + unlinkFeatures( QgsFeatureIds() << fid ); +} + +void QgsRelationWidget::unlinkSelectedFeatures() +{ + unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); +} + +void QgsRelationWidget::unlinkFeatures( const QgsFeatureIds &fids ) +{ + if ( mNmRelation.isValid() ) + { + QgsFeatureIterator selectedIterator = mNmRelation.referencedLayer()->getFeatures( + QgsFeatureRequest() + .setFilterFids( fids ) + .setSubsetOfAttributes( mNmRelation.referencedFields() ) ); + + QgsFeature f; + + QStringList filters; + + while ( selectedIterator.nextFeature( f ) ) + { + filters << '(' + mNmRelation.getRelatedFeaturesRequest( f ).filterExpression()->expression() + ')'; + } + + QString filter = QStringLiteral( "(%1) AND (%2)" ).arg( + mRelation.getRelatedFeaturesRequest( mFeature ).filterExpression()->expression(), + filters.join( QLatin1String( " OR " ) ) ); + + QgsFeatureIterator linkedIterator = mRelation.referencingLayer()->getFeatures( QgsFeatureRequest() + .setNoAttributes() + .setFilterExpression( filter ) ); + + QgsFeatureIds fids; + + while ( linkedIterator.nextFeature( f ) ) + { + fids << f.id(); + QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 ); + } + + mRelation.referencingLayer()->deleteFeatures( fids ); + + updateUi(); + } + else + { + QMap keyFields; + const auto constFieldPairs = mRelation.fieldPairs(); + for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) + { + int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() ); + if ( idx < 0 ) + { + QgsDebugMsg( QStringLiteral( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) ); + return; + } + QgsField fld = mRelation.referencingLayer()->fields().at( idx ); + keyFields.insert( idx, fld ); + } + + const auto constFeatureids = fids; + for ( QgsFeatureId fid : constFeatureids ) + { + QMapIterator it( keyFields ); + while ( it.hasNext() ) + { + it.next(); + mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) ); + } + } + } +} + +void QgsRelationWidget::duplicateFeature() +{ + QgsVectorLayer *layer = mRelation.referencingLayer(); + + QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) ); + QgsFeature f; + while ( fit.nextFeature( f ) ) + { + QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext; + QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext ); + } +} + +void QgsRelationWidget::zoomToSelectedFeatures() +{ + QgsMapCanvas *c = mEditorContext.mapCanvas(); + if ( !c ) + return; + + c->zoomToFeatureIds( + mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(), + mFeatureSelectionMgr->selectedFeatureIds() + ); +} diff --git a/src/gui/qgsrelationwidget.h b/src/gui/qgsrelationwidget.h new file mode 100644 index 000000000000..914381233d45 --- /dev/null +++ b/src/gui/qgsrelationwidget.h @@ -0,0 +1,260 @@ +/*************************************************************************** + qgsrelationwidget.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSRELATIONWIDGET_H +#define QGSRELATIONWIDGET_H + +#include +#include +#include +#include +#include "qobjectuniqueptr.h" + +#include "qobjectuniqueptr.h" +#include "qgsattributeeditorcontext.h" +#include "qgscollapsiblegroupbox.h" +#include "qgsdualview.h" +#include "qgsrelation.h" +#include "qgsvectorlayerselectionmanager.h" +#include "qgis_gui.h" + +class QgsFeature; +class QgsVectorLayer; +class QgsVectorLayerTools; +class QgsMapTool; +class QgsMapToolDigitizeFeature; + + +/** + * Base class to build new relation widgets. + * \ingroup gui + * \class QgsRelationWidget + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsRelationWidget : public QWidget +{ + Q_OBJECT + + public: + + + /** + * Constructor + */ + QgsRelationWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); + + /** + * Sets the \a relation and the \a feature + */ + void setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ); + + /** + * Set the relation(s) for this widget + * If only one relation is set, it will act as a simple 1:N relation widget + * If both relations are set, it will act as an N:M relation widget + * inserting and deleting entries on the intermediate table as required. + * + * \param relation Relation referencing the edited table + * \param nmrelation Optional reference from the referencing table to a 3rd N:M table + */ + void setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ); + + /** + * Sets the \a feature being edited and updates the UI unless \a update is set to FALSE + */ + void setFeature( const QgsFeature &feature, bool update = true ); + + /** + * Sets the editor \a context + * \note if context cadDockWidget is null, it won't be possible to digitize + * the geometry of a referencing feature from this widget + */ + void setEditorContext( const QgsAttributeEditorContext &context ); + + /** + * Returns the attribute editor context. + */ + QgsAttributeEditorContext editorContext( ) const; + + /** + * The feature selection manager is responsible for the selected features + * which are currently being edited. + */ + QgsIFeatureSelectionManager *featureSelectionManager(); + + /** + * Defines if a title label should be shown for this widget. + */ + bool showLabel() const; + + /** + * Defines if a title label should be shown for this widget. + */ + void setShowLabel( bool showLabel ); + + /** + * Determines the relation id of the second relation involved in an N:M relation. + */ + QVariant nmRelationId() const; + + /** + * Sets \a nmRelationId for the relation id of the second relation involved in an N:M relation. + * If it's empty, then it's considered as a 1:M relationship. + */ + void setNmRelationId( const QVariant &nmRelationId = QVariant() ); + + /** + * Determines the label of this element + */ + QString label() const; + + /** + * Sets \a label for this element + * If it's empty it takes the relation id as label + */ + void setLabel( const QString &label = QString() ); + + /** + * Returns the widget's current feature + */ + QgsFeature feature() const; + + /** + * Determines the force suppress form popup status that is configured for this widget + */ + bool forceSuppressFormPopup() const; + + /** + * Sets force suppress form popup status with \a forceSuppressFormPopup + * configured for this widget + */ + void setForceSuppressFormPopup( bool forceSuppressFormPopup ); + + /** + * Returns the widget configuration + */ + virtual QVariantMap config() const = 0; + + /** + * Defines the widget configuration + */ + virtual void setConfig( const QVariantMap &config ) = 0; + + public slots: + + /** + * Called when an \a attribute value in the parent widget has changed to \a newValue + */ + virtual void parentFormValueChanged( const QString &attribute, const QVariant &newValue ) = 0; + + protected slots: + + /** + * Toggles editing state of the widget + */ + void toggleEditing( bool state ); + + /** + * Saves the current modifications in the relation + */ + void saveEdits(); + + /** + * Adds a new feature with given \a geometry + */ + void addFeature( const QgsGeometry &geometry = QgsGeometry() ); + + /** + * Delete a feature with given \a fid + */ + void deleteFeature( QgsFeatureId fid = QgsFeatureId() ); + + /** + * Deletes the currently selected features + */ + void deleteSelectedFeatures(); + + /** + * Links a new feature to the relation + */ + void linkFeature(); + + /** + * Called when the link feature dialog is confirmed by the user + */ + void onLinkFeatureDlgAccepted(); + + /** + * Unlinks a feature with given \a fid + */ + void unlinkFeature( QgsFeatureId fid = QgsFeatureId() ); + + /** + * Unlinks the selected features from the relation + */ + void unlinkSelectedFeatures(); + + /** + * Duplicates a feature + */ + void duplicateFeature(); + + /** + * Zooms to the selected features + */ + void zoomToSelectedFeatures(); + + protected: + + QgsVectorLayerSelectionManager *mFeatureSelectionMgr = nullptr; + QgsAttributeEditorContext mEditorContext; + QgsRelation mRelation; + QgsRelation mNmRelation; + QgsFeature mFeature; + + bool mShowLabel = true; + bool mLayerInSameTransactionGroup = false; + + bool mForceSuppressFormPopup = false; + QVariant mNmRelationId; + QString mLabel; + + /** + * Updates the title contents to reflect the current state of the widget + */ + void updateTitle(); + + /** + * Deletes the features with \a fids + */ + void deleteFeatures( const QgsFeatureIds &fids ); + + /** + * Unlinks the features with \a fids + */ + void unlinkFeatures( const QgsFeatureIds &fids ); + + private: + virtual void updateUi() {}; + virtual void setTitle( const QString &title ) { Q_UNUSED( title ); }; + virtual void beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) { Q_UNUSED( newRelation ); Q_UNUSED( newFeature ); }; + virtual void afterSetRelationFeature() {}; + virtual void beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) { Q_UNUSED( newRelation ); Q_UNUSED( newNmRelation ); }; + virtual void afterSetRelations() {}; +}; + +#endif // QGSRELATIONWIDGET_H diff --git a/src/gui/qgsrelationwidgetfactory.cpp b/src/gui/qgsrelationwidgetfactory.cpp new file mode 100644 index 000000000000..4a2267db483d --- /dev/null +++ b/src/gui/qgsrelationwidgetfactory.cpp @@ -0,0 +1,24 @@ +/*************************************************************************** + qgsrelationwidgetfactory.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsrelationwidgetfactory.h" + + +QgsRelationWidgetFactory::QgsRelationWidgetFactory() +{ +} + diff --git a/src/gui/qgsrelationwidgetfactory.h b/src/gui/qgsrelationwidgetfactory.h new file mode 100644 index 000000000000..92c7b2449f02 --- /dev/null +++ b/src/gui/qgsrelationwidgetfactory.h @@ -0,0 +1,80 @@ +/*************************************************************************** + qgsrelationwidgetfactory.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 QGSRELATIONEDITORBASEWIDGETFACTORY_H +#define QGSRELATIONEDITORBASEWIDGETFACTORY_H + +#include +#include "qgsrelationwidget.h" +#include "qgis_gui.h" + +class QgsRelationConfigWidget; + +/** + * Factory class for creating relation widgets and their corresponding config widgets + * \ingroup gui + * \class QgsRelationWidgetFactory + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsRelationWidgetFactory +{ + public: + + /** + * Creates a new relation widget factory with given \a name + */ + QgsRelationWidgetFactory(); + + virtual ~QgsRelationWidgetFactory() = default; + + /** + * Returns the machine readable identifier name of this widget type + */ + virtual QString type() const = 0; + + /** + * Returns the human readable identifier name of this widget type + */ + virtual QString name() const = 0; + + /** + * Override this in your implementation. + * Create a new relation widget. Call QgsEditorWidgetRegistry::create() + * instead of calling this method directly. + * + * \param config The widget configuration to build the widget with + * \param parent The parent for the wrapper class and any created widget. + * + * \returns A new widget wrapper + */ + virtual QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const = 0 SIP_FACTORY; + + /** + * Override this in your implementation. + * Create a new configuration widget for this widget type. + * + * \param relation The relation for which the widget will be created + * \param parent The parent widget of the created config widget + * + * \returns A configuration widget + */ + virtual QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 SIP_FACTORY; +}; + + + +#endif // QGSRELATIONEDITORBASEWIDGETFACTORY_H diff --git a/src/gui/qgsrelationwidgetregistry.cpp b/src/gui/qgsrelationwidgetregistry.cpp new file mode 100644 index 000000000000..65d5ed123b22 --- /dev/null +++ b/src/gui/qgsrelationwidgetregistry.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + qgsrelationwidgetregistry.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgsrelationwidgetregistry.h" +#include "qgsrelationwidget.h" +#include "qgsrelationconfigwidget.h" +#include "qgsbasicrelationwidgetfactory.h" + +QgsRelationWidgetRegistry::QgsRelationWidgetRegistry() +{ + addRelationWidget( new QgsBasicRelationWidgetFactory() ); +} + +QgsRelationWidgetRegistry::~QgsRelationWidgetRegistry() +{ + qDeleteAll( mRelationWidgetFactories ); + mRelationWidgetFactories.clear(); +} + +void QgsRelationWidgetRegistry::addRelationWidget( QgsRelationWidgetFactory *widgetFactory ) +{ + if ( !widgetFactory ) + return; + + if ( mRelationWidgetFactories.contains( widgetFactory->type() ) ) + return; + + mRelationWidgetFactories.insert( widgetFactory->type(), widgetFactory ); +} + +void QgsRelationWidgetRegistry::removeRelationWidget( const QString &widgetType ) +{ + // protect the basic widget from removing, so the user has at least one relation widget type + if ( dynamic_cast( mRelationWidgetFactories.value( widgetType ) ) ) + return; + + mRelationWidgetFactories.remove( widgetType ); +} + +QStringList QgsRelationWidgetRegistry::relationWidgetNames() +{ + return mRelationWidgetFactories.keys(); +} + +QMap QgsRelationWidgetRegistry::factories() const +{ + return mRelationWidgetFactories; +} + +QgsRelationWidget *QgsRelationWidgetRegistry::create( const QString &widgetType, const QVariantMap &config, QWidget *parent ) const +{ + if ( ! mRelationWidgetFactories.contains( widgetType ) ) + return nullptr; + + return mRelationWidgetFactories.value( widgetType )->create( config, parent ); +} + +QgsRelationConfigWidget *QgsRelationWidgetRegistry::createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent ) const +{ + if ( ! mRelationWidgetFactories.contains( widgetType ) ) + return nullptr; + + return mRelationWidgetFactories.value( widgetType )->configWidget( relation, parent ); +} diff --git a/src/gui/qgsrelationwidgetregistry.h b/src/gui/qgsrelationwidgetregistry.h new file mode 100644 index 000000000000..0dff6ed2e890 --- /dev/null +++ b/src/gui/qgsrelationwidgetregistry.h @@ -0,0 +1,90 @@ +/*************************************************************************** + qgsrelationwidgetregistry.h + ---------------------- + begin : October 2020 + copyright : (C) 2020 by Ivan Ivanov + email : ivan@opengis.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 "qgseditorconfigwidget.h" +#include "qgsrelationwidget.h" +#include "qgsrelationwidgetfactory.h" +#include "qgis_gui.h" + + +#ifndef QGSRELATIONWIDGETREGISTRY_H +#define QGSRELATIONWIDGETREGISTRY_H + +class QgsRelationConfigWidget; + +/** + * Keeps track of the registered relations widgets. New widgets can be registered, old ones deleted. + * The default {\see QgsBasicRelationWidget} is protected from removing. + * \ingroup gui + * \class QgsRelationWidgetRegistry + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsRelationWidgetRegistry +{ + public: + + /** + * Constructor + */ + QgsRelationWidgetRegistry(); + + ~QgsRelationWidgetRegistry(); + + /** + * Adds a new registered relation \a widgetFactory + */ + void addRelationWidget( QgsRelationWidgetFactory *widgetFactory SIP_TRANSFER ); + + /** + * Removes a registered relation widget with given \a widgetType + */ + void removeRelationWidget( const QString &widgetType ); + + /** + * Returns a list of names of registered relation widgets + */ + QStringList relationWidgetNames(); + + /** + * Gets access to all registered factories + */ + QMap factories() const; + + /** + * Create a relation widget of a given type for a given field. + * + * \param widgetType The widget type to create a relation editor for + * \param config The configuration of the widget + * \param parent + */ + QgsRelationWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; + + /** + * Creates a configuration widget + * + * \param widgetType The widget type to create a configuration widget for + * \param relation The relation for which this widget will be created + * \param parent The parent widget for the created widget + */ + QgsRelationConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; + + private: + + QMap mRelationWidgetFactories; +}; + +#endif // QGSRELATIONWIDGETREGISTRY_H diff --git a/src/gui/vector/qgsattributesformproperties.cpp b/src/gui/vector/qgsattributesformproperties.cpp index 2f264f8e3cf0..dda433cdfa8c 100644 --- a/src/gui/vector/qgsattributesformproperties.cpp +++ b/src/gui/vector/qgsattributesformproperties.cpp @@ -409,8 +409,11 @@ QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAt const QgsAttributeEditorRelation *relationEditor = static_cast( widgetDef ); DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() ); itemData.setShowLabel( widgetDef->showLabel() ); + RelationEditorConfiguration relEdConfig; - relEdConfig.buttons = relationEditor->visibleButtons(); +// relEdConfig.buttons = relationEditor->visibleButtons(); + relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId(); + relEdConfig.mRelationWidgetConfig = relationEditor->config(); relEdConfig.nmRelationId = relationEditor->nmRelationId(); relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup(); relEdConfig.label = relationEditor->label(); @@ -657,10 +660,12 @@ QgsAttributeEditorElement *QgsAttributesFormProperties::createAttributeEditorWid { QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() ); QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent ); - relDef->setVisibleButtons( itemData.relationEditorConfiguration().buttons ); - relDef->setNmRelationId( itemData.relationEditorConfiguration().nmRelationId ); - relDef->setForceSuppressFormPopup( itemData.relationEditorConfiguration().forceSuppressFormPopup ); - relDef->setLabel( itemData.relationEditorConfiguration().label ); + QgsAttributesFormProperties::RelationEditorConfiguration relationEditorConfig = itemData.relationEditorConfiguration(); + relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType ); + relDef->setConfig( relationEditorConfig.mRelationWidgetConfig ); + relDef->setNmRelationId( relationEditorConfig.nmRelationId ); + relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup ); + relDef->setLabel( relationEditorConfig.label ); widgetDef = relDef; break; } @@ -892,7 +897,6 @@ void QgsAttributesFormProperties::apply() /* * FieldConfig implementation */ - QgsAttributesFormProperties::FieldConfig::FieldConfig( QgsVectorLayer *layer, int idx ) { mAlias = layer->fields().at( idx ).alias(); @@ -913,6 +917,15 @@ QgsAttributesFormProperties::FieldConfig::operator QVariant() return QVariant::fromValue( *this ); } +/* + * RelationEditorConfiguration implementation + */ + +QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant() +{ + return QVariant::fromValue( *this ); +} + /* * DnDTree implementation */ diff --git a/src/gui/vector/qgsattributesformproperties.h b/src/gui/vector/qgsattributesformproperties.h index 428127a4b888..d7ba428d6833 100644 --- a/src/gui/vector/qgsattributesformproperties.h +++ b/src/gui/vector/qgsattributesformproperties.h @@ -62,12 +62,16 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress { DnDTreeRole = Qt::UserRole, FieldConfigRole, - FieldNameRole + FieldNameRole, }; struct RelationEditorConfiguration { + operator QVariant(); + QgsAttributeEditorRelation::Buttons buttons = QgsAttributeEditorRelation::Button::AllButtons; + QString mRelationWidgetType; + QVariantMap mRelationWidgetConfig; QVariant nmRelationId; bool forceSuppressFormPopup = false; QString label; @@ -310,6 +314,7 @@ class GUI_EXPORT QgsAttributesDnDTree : public QTreeWidget }; +Q_DECLARE_METATYPE( QgsAttributesFormProperties::RelationEditorConfiguration ) Q_DECLARE_METATYPE( QgsAttributesFormProperties::FieldConfig ) Q_DECLARE_METATYPE( QgsAttributesFormProperties::DnDTreeItemData ) diff --git a/src/ui/attributeformconfig/qgsattributewidgetrelationeditwidget.ui b/src/ui/attributeformconfig/qgsattributewidgetrelationeditwidget.ui index 9f427252d6e5..fa1bffe8a6aa 100644 --- a/src/ui/attributeformconfig/qgsattributewidgetrelationeditwidget.ui +++ b/src/ui/attributeformconfig/qgsattributewidgetrelationeditwidget.ui @@ -7,101 +7,41 @@ 0 0 340 - 361 + 316 Attribute Widget Relation Edit Widget - - - - - QLayout::SetDefaultConstraint - - - - - Label - - - - - - - - 0 - 0 - - - - - - - - - - Show link button - - - - - - - Show unlink button - - - - - + + + - Show save child layer edits button + Label - - - - Add child feature + + + + + 0 + 0 + - - + + - Duplicate child feature + Cardinality - - - - Delete child feature - - + + - - - - Zoom to child feature - - - - - - - - - Cardinality - - - - - - - - - + Do not open a new attribute form after digitizing a new feature, overrides all other options @@ -111,8 +51,20 @@ - - + + + + Widget Configuration + + + + + + + + + + Qt::Vertical @@ -124,8 +76,26 @@ + + + + + + + Widget Type + + + + + + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
+
diff --git a/src/ui/qgsrelationeditorconfigwidgetbase.ui b/src/ui/qgsrelationeditorconfigwidgetbase.ui new file mode 100644 index 000000000000..84160718444c --- /dev/null +++ b/src/ui/qgsrelationeditorconfigwidgetbase.ui @@ -0,0 +1,70 @@ + + + QgsRelationEditorConfigWidgetBase + + + + 0 + 0 + 274 + 215 + + + + Attribute Widget Relation Edit Widget + + + + + + Show link button + + + + + + + Show unlink button + + + + + + + Show save child layer edits button + + + + + + + Add child feature + + + + + + + Duplicate child feature + + + + + + + Delete child feature + + + + + + + Zoom to child feature + + + + + + + + From b1dad1d3ef0feab309ceb9eb67a1a4a9c09c0afb Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 23 Dec 2020 01:30:23 +0200 Subject: [PATCH 02/23] Added SIP_DEPRECATED and added forgotten \since docs --- .../auto_generated/qgsattributeeditorelement.sip.in | 10 +++++++--- .../editorwidgets/qgsrelationwidgetwrapper.sip.in | 10 ++++++++-- src/core/qgsattributeeditorelement.h | 8 +++++--- src/gui/editorwidgets/qgsrelationwidgetwrapper.h | 10 ++++++---- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/python/core/auto_generated/qgsattributeeditorelement.sip.in b/python/core/auto_generated/qgsattributeeditorelement.sip.in index 1bbf26df0537..525beadf547b 100644 --- a/python/core/auto_generated/qgsattributeeditorelement.sip.in +++ b/python/core/auto_generated/qgsattributeeditorelement.sip.in @@ -425,7 +425,7 @@ Determines if the "Save child layer edits" button should be shown use visibleButtons() instead %End - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) /Deprecated/; %Docstring Defines the buttons which are shown @@ -435,14 +435,14 @@ Defines the buttons which are shown use setConfig() instead %End - QgsAttributeEditorRelation::Buttons visibleButtons() const; + QgsAttributeEditorRelation::Buttons visibleButtons() const /Deprecated/; %Docstring Returns the buttons which are shown .. versionadded:: 3.16 .. deprecated:: QGIS 3.18 - use setConfig() instead + use config() instead %End bool forceSuppressFormPopup() const; @@ -494,11 +494,15 @@ If it's empty it takes the relation id as label QString relationWidgetTypeId() const; %Docstring Returns the current relation widget type id + +.. versionadded:: 3.18 %End void setRelationWidgetTypeId( const QString &relationWidgetTypeId ); %Docstring Sets the relation widget type + +.. versionadded:: 3.18 %End }; diff --git a/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in b/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in index 9941801e7a56..737c9d33bd81 100644 --- a/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in +++ b/python/gui/auto_generated/editorwidgets/qgsrelationwidgetwrapper.sip.in @@ -115,22 +115,24 @@ Determines if the "Save child layer edits" button should be shown use visibleButtons() instead %End - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) /Deprecated/; %Docstring Defines the buttons which are shown .. versionadded:: 3.16 .. deprecated:: QGIS 3.18 + use setWidgetConfig() instead %End - QgsAttributeEditorRelation::Buttons visibleButtons() const; + QgsAttributeEditorRelation::Buttons visibleButtons() const /Deprecated/; %Docstring Returns the buttons which are shown .. versionadded:: 3.16 .. deprecated:: QGIS 3.18 + use widgetConfig() instead %End @@ -139,11 +141,15 @@ Returns the buttons which are shown Will set the config of this widget wrapper to the specified config. :param config: The config for this wrapper + +.. versionadded:: 3.18 %End QVariantMap widgetConfig() const; %Docstring Returns the whole widget config + +.. versionadded:: 3.18 %End bool forceSuppressFormPopup() const; diff --git a/src/core/qgsattributeeditorelement.h b/src/core/qgsattributeeditorelement.h index c02228b9af88..e7411aefd6c2 100644 --- a/src/core/qgsattributeeditorelement.h +++ b/src/core/qgsattributeeditorelement.h @@ -475,14 +475,14 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement * \since QGIS 3.16 * \deprecated since QGIS 3.18 use setConfig() instead */ - Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) SIP_DEPRECATED; /** * Returns the buttons which are shown * \since QGIS 3.16 - * \deprecated since QGIS 3.18 use setConfig() instead + * \deprecated since QGIS 3.18 use config() instead */ - Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const {return mButtons;} + Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const SIP_DEPRECATED {return mButtons;} /** * Determines the force suppress form popup status. @@ -526,11 +526,13 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement /** * Returns the current relation widget type id + * \since QGIS 3.18 */ QString relationWidgetTypeId() const; /** * Sets the relation widget type + * \since QGIS 3.18 */ void setRelationWidgetTypeId( const QString &relationWidgetTypeId ); diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h index 32e984e01c06..bf1ff308575b 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h @@ -111,27 +111,29 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper /** * Defines the buttons which are shown * \since QGIS 3.16 - * \deprecated since QGIS 3.18 + * \deprecated since QGIS 3.18 use setWidgetConfig() instead */ - Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) SIP_DEPRECATED; /** * Returns the buttons which are shown * \since QGIS 3.16 - * \deprecated since QGIS 3.18 + * \deprecated since QGIS 3.18 use widgetConfig() instead */ - Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const; + Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const SIP_DEPRECATED; /** * Will set the config of this widget wrapper to the specified config. * * \param config The config for this wrapper + * \since QGIS 3.18 */ void setWidgetConfig( const QVariantMap &config ); /** * Returns the whole widget config + * \since QGIS 3.18 */ QVariantMap widgetConfig() const; From 19d398e3bde76a2b8d0d66df34bea0dfb0bfd528 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 23 Dec 2020 01:40:30 +0200 Subject: [PATCH 03/23] Restore a tooltip replaced during debugging --- src/gui/attributeformconfig/qgsattributewidgetedit.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp index acc69cbb125a..f899f51e408d 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp @@ -137,9 +137,7 @@ void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const update(); } ); - mRelationCardinalityCombo->setToolTip( tr( "This is being changed" ) ); - mRelationCardinalityCombo->addItem( "CHECKED" ); - + mRelationCardinalityCombo->setToolTip( tr( "For a many to many (N:M) relation, the direct link has to be selected. The in-between table will be hidden." ) ); setNmRelationId( config.nmRelationId ); mRelationLabelEdit->setText( config.label ); From 1151a8f0fb0f13e688254f6c972290f3fccbb76d Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 30 Dec 2020 04:17:32 +0200 Subject: [PATCH 04/23] Open the correct configuration widget --- src/gui/attributeformconfig/qgsattributewidgetedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp index f899f51e408d..89d4a3d9f1a9 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp @@ -116,7 +116,7 @@ void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const setCardinalityCombo( QStringLiteral( "%1 (%2)" ).arg( nmrel.referencedLayer()->name(), nmrel.fieldPairs().at( 0 ).referencedField() ), nmrel.id() ); } - int widgetTypeIdx = mWidgetTypeComboBox->findText( config.mRelationWidgetType ); + int widgetTypeIdx = mWidgetTypeComboBox->findData( config.mRelationWidgetType ); mWidgetTypeComboBox->setCurrentIndex( widgetTypeIdx >= 0 ? widgetTypeIdx : 0 ); const QString widgetType = mWidgetTypeComboBox->currentData().toString(); From c3411b98c0b6a12c222f187f1ea89d88f248f6e6 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 31 Dec 2020 02:05:09 +0200 Subject: [PATCH 05/23] Not sure why this was like this in the original QgsRelationEditorWidget --- src/gui/qgsbasicrelationwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp index 3083e705f204..111d2970efb5 100644 --- a/src/gui/qgsbasicrelationwidget.cpp +++ b/src/gui/qgsbasicrelationwidget.cpp @@ -162,7 +162,7 @@ QgsBasicRelationWidget::QgsBasicRelationWidget( const QVariantMap &config, QWidg this, static_cast( &QgsBasicRelationWidget::setViewMode ) ); connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::toggleEditing ); connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::saveEdits ); - connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } ); + connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeature ); connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeatureGeometry ); connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::duplicateFeature ); connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::deleteSelectedFeatures ); From 01fd98355b0e39658e98bce8248b3b369c64740a Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 31 Dec 2020 02:09:13 +0200 Subject: [PATCH 06/23] Fix adding new features --- src/gui/qgsrelationwidget.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gui/qgsrelationwidget.cpp b/src/gui/qgsrelationwidget.cpp index aba4a11dc06e..0c5c4976bcf1 100644 --- a/src/gui/qgsrelationwidget.cpp +++ b/src/gui/qgsrelationwidget.cpp @@ -267,9 +267,7 @@ void QgsRelationWidget::addFeature( const QgsGeometry &geometry ) for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) { keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) ); - - // TODO FINISH!!!! -// vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry ); + vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry ); } } } From 4c463d352b2b76d66b7646cd7e40d92f1896e8cf Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Mon, 4 Jan 2021 15:18:29 +0200 Subject: [PATCH 07/23] Revert "Not sure why this was like this in the original QgsRelationEditorWidget" This reverts commit c3411b98c0b6a12c222f187f1ea89d88f248f6e6. Since `addFeature()` has an argument, this should not be called directly --- src/gui/qgsbasicrelationwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp index 111d2970efb5..3083e705f204 100644 --- a/src/gui/qgsbasicrelationwidget.cpp +++ b/src/gui/qgsbasicrelationwidget.cpp @@ -162,7 +162,7 @@ QgsBasicRelationWidget::QgsBasicRelationWidget( const QVariantMap &config, QWidg this, static_cast( &QgsBasicRelationWidget::setViewMode ) ); connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::toggleEditing ); connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::saveEdits ); - connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeature ); + connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } ); connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeatureGeometry ); connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::duplicateFeature ); connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::deleteSelectedFeatures ); From e892bee38ec28e6f7f25178afa4f8263f4725908 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Mon, 4 Jan 2021 15:25:28 +0200 Subject: [PATCH 08/23] Update the UI when the root collapsible box is opened --- src/gui/qgsbasicrelationwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp index 3083e705f204..e973984f1040 100644 --- a/src/gui/qgsbasicrelationwidget.cpp +++ b/src/gui/qgsbasicrelationwidget.cpp @@ -157,7 +157,7 @@ QgsBasicRelationWidget::QgsBasicRelationWidget( const QVariantMap &config, QWidg mRelationLayout->addWidget( mDualView ); -// connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget2::onCollapsedStateChanged ); + connect( mRootCollapsibleGroupBox, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsBasicRelationWidget::onCollapsedStateChanged ); connect( mViewModeButtonGroup, static_cast( &QButtonGroup::buttonClicked ), this, static_cast( &QgsBasicRelationWidget::setViewMode ) ); connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::toggleEditing ); From 95300c9800b94680bb2d7555b9f348479f6b4097 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Mon, 4 Jan 2021 15:32:25 +0200 Subject: [PATCH 09/23] declare setupRelationWidgetWrapper with 2 args as deprecated --- src/gui/qgsattributeform.cpp | 2 +- src/gui/qgsattributeform.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 00e071ded396..8122e5c41023 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -1644,7 +1644,7 @@ void QgsAttributeForm::init() const QList relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer ); for ( const QgsRelation &rel : relations ) { - QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( rel, mContext ); + QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( QStringLiteral( "basic" ), rel, mContext ); QgsAttributeFormRelationEditorWidget *formWidget = new QgsAttributeFormRelationEditorWidget( rww, this ); formWidget->createSearchWidgetWrappers( mContext ); diff --git a/src/gui/qgsattributeform.h b/src/gui/qgsattributeform.h index 9b35a02c0a4a..446aff63dd8a 100644 --- a/src/gui/qgsattributeform.h +++ b/src/gui/qgsattributeform.h @@ -414,7 +414,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget bool currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions ); QList constraintDependencies( QgsEditorWidgetWrapper *w ); - QgsRelationWidgetWrapper *setupRelationWidgetWrapper( const QgsRelation &rel, const QgsAttributeEditorContext &context ); + Q_DECL_DEPRECATED QgsRelationWidgetWrapper *setupRelationWidgetWrapper( const QgsRelation &rel, const QgsAttributeEditorContext &context ) SIP_DEPRECATED; QgsRelationWidgetWrapper *setupRelationWidgetWrapper( const QString &relationWidgetTypeId, const QgsRelation &rel, const QgsAttributeEditorContext &context ); QgsVectorLayer *mLayer = nullptr; From 10b2b3b18e3796f5313648dc6ee8235a6589c569 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 14:05:11 +0200 Subject: [PATCH 10/23] mRelationEditorName -> mRelationEditorId --- src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp | 8 ++++---- src/gui/editorwidgets/qgsrelationwidgetwrapper.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp index 65ed9ae1ad37..51aec6f6322e 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp @@ -31,7 +31,7 @@ QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( QgsVectorLayer *vl, const Qg QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( const QString &relationEditorName, QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent ) : QgsWidgetWrapper( vl, editor, parent ) , mRelation( relation ) - , mRelationEditorName( relationEditorName ) + , mRelationEditorId( relationEditorName ) { } @@ -41,11 +41,11 @@ QWidget *QgsRelationWidgetWrapper::createWidget( QWidget *parent ) if ( form ) connect( form, &QgsAttributeForm::widgetValueChanged, this, &QgsRelationWidgetWrapper::widgetValueChanged ); - QWidget *widget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorName, widgetConfig(), parent ); + QWidget *widget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), parent ); if ( !widget ) { - QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorName ) ); + QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorId ) ); widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "basic" ), widgetConfig(), parent ); } @@ -151,7 +151,7 @@ void QgsRelationWidgetWrapper::initWidget( QWidget *editor ) // if the editor cannot be cast to relation editor, insert a new one if ( !w ) { - w = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorName, widgetConfig(), editor ); + w = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), editor ); editor->layout()->addWidget( w ); } diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h index bf1ff308575b..8416157ace69 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h @@ -218,7 +218,7 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper void aboutToSave() override; QgsRelation mRelation; QgsRelation mNmRelation; - QString mRelationEditorName; + QString mRelationEditorId; QgsRelationWidget *mWidget = nullptr; }; From ca56fe6d5284bed7e866f81ca94e7f91d29120a0 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 14:10:28 +0200 Subject: [PATCH 11/23] Remove from the layout, just in case --- src/gui/attributeformconfig/qgsattributewidgetedit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp index 89d4a3d9f1a9..c9a5b8571e73 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp @@ -130,6 +130,7 @@ void QgsAttributeWidgetRelationEditWidget::setRelationEditorConfiguration( const { const QString widgetId = mWidgetTypeComboBox->currentData().toString(); + mWidgetTypePlaceholderLayout->removeWidget( mConfigWidget ); mConfigWidget->deleteLater(); mConfigWidget = QgsGui::relationWidgetRegistry()->createConfigWidget( widgetId, relation, this ); mConfigWidget->setConfig( config.mRelationWidgetConfig ); From 415ecc4e7f0f79c09a96c08515288ceab60706ff Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 14:10:48 +0200 Subject: [PATCH 12/23] Rename the "basic" widget to "relation_editor" widget --- src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp | 4 ++-- src/gui/qgsattributeform.cpp | 2 +- src/gui/qgsbasicrelationwidgetfactory.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp index 51aec6f6322e..8bb1bda9d215 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp @@ -24,7 +24,7 @@ #include QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent ) - : QgsRelationWidgetWrapper( QStringLiteral( "basic" ), vl, relation, editor, parent ) + : QgsRelationWidgetWrapper( QStringLiteral( "relation_editor" ), vl, relation, editor, parent ) { } @@ -46,7 +46,7 @@ QWidget *QgsRelationWidgetWrapper::createWidget( QWidget *parent ) if ( !widget ) { QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorId ) ); - widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "basic" ), widgetConfig(), parent ); + widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "relation_editor" ), widgetConfig(), parent ); } return widget; diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 8122e5c41023..02fbeb046679 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -1644,7 +1644,7 @@ void QgsAttributeForm::init() const QList relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer ); for ( const QgsRelation &rel : relations ) { - QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( QStringLiteral( "basic" ), rel, mContext ); + QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( QStringLiteral( "relation_editor" ), rel, mContext ); QgsAttributeFormRelationEditorWidget *formWidget = new QgsAttributeFormRelationEditorWidget( rww, this ); formWidget->createSearchWidgetWrappers( mContext ); diff --git a/src/gui/qgsbasicrelationwidgetfactory.cpp b/src/gui/qgsbasicrelationwidgetfactory.cpp index acf8de03b089..d38f34964fe3 100644 --- a/src/gui/qgsbasicrelationwidgetfactory.cpp +++ b/src/gui/qgsbasicrelationwidgetfactory.cpp @@ -25,7 +25,7 @@ QgsBasicRelationWidgetFactory::QgsBasicRelationWidgetFactory() QString QgsBasicRelationWidgetFactory::type() const { - return QStringLiteral( "basic" ); + return QStringLiteral( "relation_editor" ); } QString QgsBasicRelationWidgetFactory::name() const From 8f4bf3e32598dff3388358c8c8bb18dd5d2d8345 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 14:21:36 +0200 Subject: [PATCH 13/23] SIP convert --- .../qgsbasicrelationwidget.sip.in | 2 ++ .../auto_generated/qgsrelationwidget.sip.in | 11 +++++++++++ src/gui/qgsrelationwidget.h | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in index 3933fffcc97a..bdc654ef2f3b 100644 --- a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in +++ b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in @@ -11,6 +11,8 @@ + + class QgsBasicRelationWidget : QgsRelationWidget { %Docstring diff --git a/python/gui/auto_generated/qgsrelationwidget.sip.in b/python/gui/auto_generated/qgsrelationwidget.sip.in index 728988751bdc..345186265420 100644 --- a/python/gui/auto_generated/qgsrelationwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationwidget.sip.in @@ -10,6 +10,11 @@ +// this is needed for the "convert to subclass" code below to compile +%ModuleHeaderCode +#include "qgsbasicrelationwidget.h" +%End + class QgsRelationWidget : QWidget @@ -22,6 +27,12 @@ Base class to build new relation widgets. %TypeHeaderCode #include "qgsrelationwidget.h" +%End +%ConvertToSubClassCode + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsBasicRelationWidget; + else + sipType = 0; %End public: diff --git a/src/gui/qgsrelationwidget.h b/src/gui/qgsrelationwidget.h index 914381233d45..184ab6fd56c7 100644 --- a/src/gui/qgsrelationwidget.h +++ b/src/gui/qgsrelationwidget.h @@ -32,11 +32,19 @@ #include "qgsvectorlayerselectionmanager.h" #include "qgis_gui.h" +#ifdef SIP_RUN +// this is needed for the "convert to subclass" code below to compile +% ModuleHeaderCode +#include "qgsbasicrelationwidget.h" +% End +#endif + class QgsFeature; class QgsVectorLayer; class QgsVectorLayerTools; class QgsMapTool; class QgsMapToolDigitizeFeature; +class QgsBaseRelationWidget; /** @@ -47,6 +55,16 @@ class QgsMapToolDigitizeFeature; */ class GUI_EXPORT QgsRelationWidget : public QWidget { + +#ifdef SIP_RUN + SIP_CONVERT_TO_SUBCLASS_CODE + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsBasicRelationWidget; + else + sipType = 0; + SIP_END +#endif + Q_OBJECT public: From 472698583c903f04a488c3f9154fc812130b22da Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 15:24:56 +0200 Subject: [PATCH 14/23] Single QgsBasicRelationWidget file --- .../qgsbasicrelationwidget.sip.in | 1 + src/gui/CMakeLists.txt | 4 - src/gui/qgsbasicrelationconfigwidget.cpp | 57 -------------- src/gui/qgsbasicrelationconfigwidget.h | 62 ---------------- src/gui/qgsbasicrelationwidget.cpp | 74 +++++++++++++++++++ src/gui/qgsbasicrelationwidget.h | 66 ++++++++++++++++- src/gui/qgsbasicrelationwidgetfactory.cpp | 46 ------------ src/gui/qgsbasicrelationwidgetfactory.h | 48 ------------ src/gui/qgsrelationwidgetregistry.cpp | 2 +- 9 files changed, 141 insertions(+), 219 deletions(-) delete mode 100644 src/gui/qgsbasicrelationconfigwidget.cpp delete mode 100644 src/gui/qgsbasicrelationconfigwidget.h delete mode 100644 src/gui/qgsbasicrelationwidgetfactory.cpp delete mode 100644 src/gui/qgsbasicrelationwidgetfactory.h diff --git a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in index bdc654ef2f3b..9f0907d87251 100644 --- a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in +++ b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in @@ -103,6 +103,7 @@ Sets the title of the root groupbox }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 497bf06ffcb6..931a9a626859 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -568,8 +568,6 @@ set(QGIS_GUI_SRCS qgsrelationeditorwidget.cpp qgsrelationconfigwidget.cpp qgsbasicrelationwidget.cpp - qgsbasicrelationconfigwidget.cpp - qgsbasicrelationwidgetfactory.cpp qgsrelationwidget.cpp qgsrelationwidgetfactory.cpp qgsrelationwidgetregistry.cpp @@ -821,8 +819,6 @@ set(QGIS_GUI_HDRS qgsrelationeditorwidget.h qgsrelationconfigwidget.h qgsbasicrelationwidget.h - qgsbasicrelationconfigwidget.h - qgsbasicrelationwidgetfactory.h qgsrelationwidget.h qgsrelationwidgetfactory.h qgsrelationwidgetregistry.h diff --git a/src/gui/qgsbasicrelationconfigwidget.cpp b/src/gui/qgsbasicrelationconfigwidget.cpp deleted file mode 100644 index c8204ec645cd..000000000000 --- a/src/gui/qgsbasicrelationconfigwidget.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************** - qgsbasicrelationconfigwidget.cpp - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 "qgsbasicrelationconfigwidget.h" -#include "qgsbasicrelationwidget.h" - - -QgsBasicRelationConfigWidget::QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) - : QgsRelationConfigWidget( relation, parent ) -{ - setupUi( this ); -} - -QVariantMap QgsBasicRelationConfigWidget::config() -{ - QgsBasicRelationWidget::Buttons buttons; - buttons.setFlag( QgsBasicRelationWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); - - return QVariantMap( - { - {"buttons", qgsFlagValueToKeys( buttons )}, - } ); -} - -void QgsBasicRelationConfigWidget::setConfig( const QVariantMap &config ) -{ - const QgsBasicRelationWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); - - mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Link ) ); - mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); - mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); - mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); - mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) ); - mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); - mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) ); -} diff --git a/src/gui/qgsbasicrelationconfigwidget.h b/src/gui/qgsbasicrelationconfigwidget.h deleted file mode 100644 index 236700371734..000000000000 --- a/src/gui/qgsbasicrelationconfigwidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************** - qgsbasicrelationconfigwidget.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 QGSBASICRELATIONCONFIGWIDGET_H -#define QGSBASICRELATIONCONFIGWIDGET_H - - -#include "ui_qgsrelationeditorconfigwidgetbase.h" -#include "qgsrelationconfigwidget.h" - -#define SIP_NO_FILE - -/** - * \ingroup gui - * \class QgsBasicRelationConfigWidget - * Creates a new configuration widget for the basic relation widget - * \since QGIS 3.18 - */ -class QgsBasicRelationConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase -{ - public: - - /** - * Create a new configuration widget - * - * \param relation The relation for which the configuration dialog will be created - * \param parent A parent widget - */ - explicit QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); - - /** - * \brief Create a configuration from the current GUI state - * - * \returns A widget configuration - */ - QVariantMap config(); - - /** - * \brief Update the configuration widget to represent the given configuration. - * - * \param config The configuration which should be represented by this widget - */ - void setConfig( const QVariantMap &config ); - -}; - -#endif // QGSBASICRELATIONCONFIGWIDGET_H diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp index e973984f1040..6148081a8398 100644 --- a/src/gui/qgsbasicrelationwidget.cpp +++ b/src/gui/qgsbasicrelationwidget.cpp @@ -556,3 +556,77 @@ void QgsBasicRelationWidget::afterSetRelations() updateButtons(); } + + +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SIP_RUN +QgsBasicRelationWidgetFactory::QgsBasicRelationWidgetFactory() +{ + +} + +QString QgsBasicRelationWidgetFactory::type() const +{ + return QStringLiteral( "relation_editor" ); +} + +QString QgsBasicRelationWidgetFactory::name() const +{ + return QStringLiteral( "Relation Editor" ); +} + +QgsRelationWidget *QgsBasicRelationWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const +{ + return new QgsBasicRelationWidget( config, parent ); +} + + +QgsRelationConfigWidget *QgsBasicRelationWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const +{ + return static_cast( new QgsBasicRelationConfigWidget( relation, parent ) ); +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SIP_RUN +QgsBasicRelationConfigWidget::QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) + : QgsRelationConfigWidget( relation, parent ) +{ + setupUi( this ); +} + +QVariantMap QgsBasicRelationConfigWidget::config() +{ + QgsBasicRelationWidget::Buttons buttons; + buttons.setFlag( QgsBasicRelationWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsBasicRelationWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); + + return QVariantMap( + { + {"buttons", qgsFlagValueToKeys( buttons )}, + } ); +} + +void QgsBasicRelationConfigWidget::setConfig( const QVariantMap &config ) +{ + const QgsBasicRelationWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); + + mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Link ) ); + mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); + mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); + mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); + mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) ); + mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); + mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) ); +} +#endif diff --git a/src/gui/qgsbasicrelationwidget.h b/src/gui/qgsbasicrelationwidget.h index 8e711999dd29..67757d6c87d3 100644 --- a/src/gui/qgsbasicrelationwidget.h +++ b/src/gui/qgsbasicrelationwidget.h @@ -24,8 +24,12 @@ #include #include "qobjectuniqueptr.h" -#include "qgsrelationeditorwidget.h" +#include "ui_qgsrelationeditorconfigwidgetbase.h" + #include "qgsrelationwidget.h" +#include "qgsrelationconfigwidget.h" +#include "qgsrelationwidgetfactory.h" +#include "qgsrelationeditorwidget.h" #include "qobjectuniqueptr.h" #include "qgsattributeeditorcontext.h" #include "qgscollapsiblegroupbox.h" @@ -40,6 +44,7 @@ class QgsVectorLayerTools; class QgsMapTool; class QgsMapToolDigitizeFeature; + /** * The default relation widget in QGIS. Successor of the now deprecated {\see QgsRelationEditorWidget}. * \ingroup gui @@ -169,4 +174,63 @@ class GUI_EXPORT QgsBasicRelationWidget : public QgsRelationWidget }; +#ifndef SIP_RUN + +/** + * Factory class for creating a basic relation widget and the respective config widget. + * \ingroup gui + * \class QgsBasicRelationWidgetFactory + * \note not available in Python bindings + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsBasicRelationWidgetFactory : public QgsRelationWidgetFactory +{ + public: + QgsBasicRelationWidgetFactory(); + + QString type() const override; + + QString name() const override; + + QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; + + QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; + +}; + +/** + * \ingroup gui + * \class QgsBasicRelationConfigWidget + * Creates a new configuration widget for the basic relation widget + * \since QGIS 3.18 + */ +class QgsBasicRelationConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase +{ + public: + + /** + * Create a new configuration widget + * + * \param relation The relation for which the configuration dialog will be created + * \param parent A parent widget + */ + explicit QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); + + /** + * \brief Create a configuration from the current GUI state + * + * \returns A widget configuration + */ + QVariantMap config(); + + /** + * \brief Update the configuration widget to represent the given configuration. + * + * \param config The configuration which should be represented by this widget + */ + void setConfig( const QVariantMap &config ); + +}; +#endif + #endif // QGSBASICRELATIONWIDGET_H diff --git a/src/gui/qgsbasicrelationwidgetfactory.cpp b/src/gui/qgsbasicrelationwidgetfactory.cpp deleted file mode 100644 index d38f34964fe3..000000000000 --- a/src/gui/qgsbasicrelationwidgetfactory.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/*************************************************************************** - qgsbasicrelationwidgetfactory.cpp - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 "qgsbasicrelationwidgetfactory.h" -#include "qgsbasicrelationconfigwidget.h" - -QgsBasicRelationWidgetFactory::QgsBasicRelationWidgetFactory() -{ - -} - -QString QgsBasicRelationWidgetFactory::type() const -{ - return QStringLiteral( "relation_editor" ); -} - -QString QgsBasicRelationWidgetFactory::name() const -{ - return QStringLiteral( "Relation Editor" ); -} - -QgsRelationWidget *QgsBasicRelationWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const -{ - return new QgsBasicRelationWidget( config, parent ); -} - - -QgsRelationConfigWidget *QgsBasicRelationWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const -{ - return static_cast( new QgsBasicRelationConfigWidget( relation, parent ) ); -} - diff --git a/src/gui/qgsbasicrelationwidgetfactory.h b/src/gui/qgsbasicrelationwidgetfactory.h deleted file mode 100644 index e5824ac4a402..000000000000 --- a/src/gui/qgsbasicrelationwidgetfactory.h +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************** - qgsbasicrelationwidgetfactory.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 QGSBASICRELATIONWIDGETFACTORY_H -#define QGSBASICRELATIONWIDGETFACTORY_H - -#include "qgsrelationwidgetfactory.h" -#include "qgsbasicrelationwidget.h" - -#define SIP_NO_FILE - -/** - * Factory class for creating a basic relation widget and the respective config widget. - * \ingroup gui - * \class QgsBasicRelationWidgetFactory - * \note not available in Python bindings - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsBasicRelationWidgetFactory : public QgsRelationWidgetFactory -{ - public: - QgsBasicRelationWidgetFactory(); - - QString type() const override; - - QString name() const override; - - QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; - - QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; - -}; - -#endif // QGSBASICRELATIONWIDGETFACTORY_H diff --git a/src/gui/qgsrelationwidgetregistry.cpp b/src/gui/qgsrelationwidgetregistry.cpp index 65d5ed123b22..d7fa988b2d96 100644 --- a/src/gui/qgsrelationwidgetregistry.cpp +++ b/src/gui/qgsrelationwidgetregistry.cpp @@ -18,7 +18,7 @@ #include "qgsrelationwidgetregistry.h" #include "qgsrelationwidget.h" #include "qgsrelationconfigwidget.h" -#include "qgsbasicrelationwidgetfactory.h" +#include "qgsbasicrelationwidget.h" QgsRelationWidgetRegistry::QgsRelationWidgetRegistry() { From 891c89ae2aeeb76dd04cf96b007b5984f63e5ac6 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 6 Jan 2021 16:01:20 +0200 Subject: [PATCH 15/23] rename QgsBasicRelationWidget--> QgsRelationEditorWidget --- .../auto_additions/qgsbasicrelationwidget.py | 4 - .../auto_additions/qgsrelationeditorwidget.py | 4 + .../qgsbasicrelationwidget.sip.in | 113 --- .../qgsrelationeditorwidget.sip.in | 201 +---- .../auto_generated/qgsrelationwidget.sip.in | 6 +- .../qgsrelationwidgetregistry.sip.in | 2 +- python/gui/gui_auto.sip | 1 - src/gui/CMakeLists.txt | 2 - src/gui/qgsbasicrelationwidget.cpp | 632 ------------- src/gui/qgsbasicrelationwidget.h | 236 ----- src/gui/qgsrelationeditorwidget.cpp | 851 +++++------------- src/gui/qgsrelationeditorwidget.h | 294 +++--- src/gui/qgsrelationwidget.h | 6 +- src/gui/qgsrelationwidgetregistry.cpp | 8 +- src/gui/qgsrelationwidgetregistry.h | 2 +- 15 files changed, 365 insertions(+), 1997 deletions(-) delete mode 100644 python/gui/auto_additions/qgsbasicrelationwidget.py create mode 100644 python/gui/auto_additions/qgsrelationeditorwidget.py delete mode 100644 python/gui/auto_generated/qgsbasicrelationwidget.sip.in delete mode 100644 src/gui/qgsbasicrelationwidget.cpp delete mode 100644 src/gui/qgsbasicrelationwidget.h diff --git a/python/gui/auto_additions/qgsbasicrelationwidget.py b/python/gui/auto_additions/qgsbasicrelationwidget.py deleted file mode 100644 index ea0714eaf58a..000000000000 --- a/python/gui/auto_additions/qgsbasicrelationwidget.py +++ /dev/null @@ -1,4 +0,0 @@ -# The following has been generated automatically from src/gui/qgsbasicrelationwidget.h -QgsBasicRelationWidget.Button.baseClass = QgsBasicRelationWidget -QgsBasicRelationWidget.Buttons.baseClass = QgsBasicRelationWidget -Buttons = QgsBasicRelationWidget # dirty hack since SIP seems to introduce the flags in module diff --git a/python/gui/auto_additions/qgsrelationeditorwidget.py b/python/gui/auto_additions/qgsrelationeditorwidget.py new file mode 100644 index 000000000000..3bb37c58e85a --- /dev/null +++ b/python/gui/auto_additions/qgsrelationeditorwidget.py @@ -0,0 +1,4 @@ +# The following has been generated automatically from src/gui/qgsrelationeditorwidget.h +QgsRelationEditorWidget.Button.baseClass = QgsRelationEditorWidget +QgsRelationEditorWidget.Buttons.baseClass = QgsRelationEditorWidget +Buttons = QgsRelationEditorWidget # dirty hack since SIP seems to introduce the flags in module diff --git a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in b/python/gui/auto_generated/qgsbasicrelationwidget.sip.in deleted file mode 100644 index 9f0907d87251..000000000000 --- a/python/gui/auto_generated/qgsbasicrelationwidget.sip.in +++ /dev/null @@ -1,113 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsbasicrelationwidget.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - - - - - - -class QgsBasicRelationWidget : QgsRelationWidget -{ -%Docstring -The default relation widget in QGIS. Successor of the now deprecated {:py:class:`QgsRelationEditorWidget`}. - -.. versionadded:: 3.18 -%End - -%TypeHeaderCode -#include "qgsbasicrelationwidget.h" -%End - public: - - enum Button - { - Link, - Unlink, - SaveChildEdits, - AddChildFeature, - DuplicateChildFeature, - DeleteChildFeature, - ZoomToChildFeature, - AllButtons - }; - typedef QFlags Buttons; - - - QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); -%Docstring -Constructor - -:param config: widget configuration -:param parent: parent widget -%End - - void setViewMode( QgsDualView::ViewMode mode ); -%Docstring -Define the view mode for the dual view -%End - - QgsDualView::ViewMode viewMode(); -%Docstring -Gets the view mode for the dual view -%End - - void setEditorContext( const QgsAttributeEditorContext &context ); -%Docstring -Sets the editor ``context`` - -.. note:: - - if context cadDockWidget is null, it won't be possible to digitize - the geometry of a referencing feature from this widget -%End - - void setVisibleButtons( const Buttons &buttons ); -%Docstring -Defines the buttons which are shown -%End - - Buttons visibleButtons() const; -%Docstring -Returns the buttons which are shown -%End - - virtual QVariantMap config() const; - -%Docstring -Returns the current configuration -%End - - virtual void setConfig( const QVariantMap &config ); - -%Docstring -Defines the current configuration -%End - - virtual void setTitle( const QString &title ); - -%Docstring -Sets the title of the root groupbox -%End - - public slots: - virtual void parentFormValueChanged( const QString &attribute, const QVariant &newValue ); - - -}; - - - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsbasicrelationwidget.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in index 44dc64031cbb..64eac5bcd43a 100644 --- a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in @@ -10,6 +10,7 @@ + %ModuleHeaderCode // fix to allow compilation with sip that for some reason // doesn't add this include to the file where the code from @@ -20,24 +21,38 @@ -class QgsRelationEditorWidget : QgsCollapsibleGroupBox +class QgsRelationEditorWidget : QgsRelationWidget { +%Docstring +The default relation widget in QGIS. Successor of the now deprecated {:py:class:`QgsRelationEditorWidget`}. + +.. versionadded:: 3.18 +%End %TypeHeaderCode #include "qgsrelationeditorwidget.h" -%End -%ConvertToSubClassCode - if ( qobject_cast( sipCpp ) ) - sipType = sipType_QgsRelationEditorWidget; - else - sipType = NULL; %End public: + enum Button + { + Link, + Unlink, + SaveChildEdits, + AddChildFeature, + DuplicateChildFeature, + DeleteChildFeature, + ZoomToChildFeature, + AllButtons + }; + typedef QFlags Buttons; + - QgsRelationEditorWidget( QWidget *parent /TransferThis/ = 0 ); + QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); %Docstring +Constructor +:param config: widget configuration :param parent: parent widget %End @@ -49,27 +64,6 @@ Define the view mode for the dual view QgsDualView::ViewMode viewMode(); %Docstring Gets the view mode for the dual view -%End - - void setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ); -%Docstring -Sets the ``relation`` and the ``feature`` -%End - - void setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ); -%Docstring -Set the relation(s) for this widget -If only one relation is set, it will act as a simple 1:N relation widget -If both relations are set, it will act as an N:M relation widget -inserting and deleting entries on the intermediate table as required. - -:param relation: Relation referencing the edited table -:param nmrelation: Optional reference from the referencing table to a 3rd N:M table -%End - - void setFeature( const QgsFeature &feature, bool update = true ); -%Docstring -Sets the ``feature`` being edited and updates the UI unless ``update`` is set to ``False`` %End void setEditorContext( const QgsAttributeEditorContext &context ); @@ -82,171 +76,42 @@ Sets the editor ``context`` the geometry of a referencing feature from this widget %End - QgsAttributeEditorContext editorContext( ) const; -%Docstring -Returns the attribute editor context. - -.. versionadded:: 3.14 -%End - - QgsIFeatureSelectionManager *featureSelectionManager(); -%Docstring -The feature selection manager is responsible for the selected features -which are currently being edited. -%End - - bool showLabel() const; -%Docstring -Defines if a title label should be shown for this widget. - -.. versionadded:: 2.18 -%End - - void setShowLabel( bool showLabel ); -%Docstring -Defines if a title label should be shown for this widget. - -.. versionadded:: 2.18 -%End - - bool showLinkButton() const /Deprecated/; -%Docstring -Determines if the "link feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setShowLinkButton( bool showLinkButton ) /Deprecated/; -%Docstring -Determines if the "link feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - bool showUnlinkButton() const /Deprecated/; -%Docstring -Determines if the "unlink feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setShowUnlinkButton( bool showUnlinkButton ) /Deprecated/; -%Docstring -Determines if the "unlink feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - void setShowSaveChildEditsButton( bool showChildEdits ) /Deprecated/; -%Docstring -Determines if the "Save child layer edits" button should be shown - -.. versionadded:: 3.14 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - bool showSaveChildEditsButton() const /Deprecated/; -%Docstring -Determines if the "Save child layer edits" button should be shown - -.. versionadded:: 3.14 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const Buttons &buttons ); %Docstring Defines the buttons which are shown - -.. versionadded:: 3.16 %End - QgsAttributeEditorRelation::Buttons visibleButtons() const; + Buttons visibleButtons() const; %Docstring Returns the buttons which are shown - -.. versionadded:: 3.16 %End - bool forceSuppressFormPopup() const; -%Docstring -Determines the force suppress form popup status that is configured for this widget - -.. versionadded:: 3.16 -%End - - void setForceSuppressFormPopup( bool forceSuppressFormPopup ); -%Docstring -Sets force suppress form popup status with ``forceSuppressFormPopup`` -configured for this widget + virtual QVariantMap config() const; -.. versionadded:: 3.16 -%End - - QVariant nmRelationId() const; %Docstring -Determines the relation id of the second relation involved in an N:M relation. - -.. versionadded:: 3.16 +Returns the current configuration %End - void setNmRelationId( const QVariant &nmRelationId = QVariant() ); -%Docstring -Sets ``nmRelationId`` for the relation id of the second relation involved in an N:M relation. -If it's empty, then it's considered as a 1:M relationship. - -.. versionadded:: 3.16 -%End + virtual void setConfig( const QVariantMap &config ); - QString label() const; %Docstring -Determines the label of this element - -.. versionadded:: 3.16 +Defines the current configuration %End - void setLabel( const QString &label = QString() ); -%Docstring -Sets ``label`` for this element -If it's empty it takes the relation id as label - -.. versionadded:: 3.16 -%End + virtual void setTitle( const QString &title ); - QgsFeature feature() const; %Docstring -Returns the widget's current feature - -.. versionadded:: 3.14 +Sets the title of the root groupbox %End public slots: + virtual void parentFormValueChanged( const QString &attribute, const QVariant &newValue ); - void parentFormValueChanged( const QString &attribute, const QVariant &newValue ); -%Docstring -Called when an ``attribute`` value in the parent widget has changed to ``newValue`` - -.. versionadded:: 3.14 -%End }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/auto_generated/qgsrelationwidget.sip.in b/python/gui/auto_generated/qgsrelationwidget.sip.in index 345186265420..2194e4fe00da 100644 --- a/python/gui/auto_generated/qgsrelationwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationwidget.sip.in @@ -12,7 +12,7 @@ // this is needed for the "convert to subclass" code below to compile %ModuleHeaderCode -#include "qgsbasicrelationwidget.h" +#include "qgsrelationeditorwidget.h" %End @@ -29,8 +29,8 @@ Base class to build new relation widgets. #include "qgsrelationwidget.h" %End %ConvertToSubClassCode - if ( qobject_cast( sipCpp ) ) - sipType = sipType_QgsBasicRelationWidget; + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsRelationEditorWidget; else sipType = 0; %End diff --git a/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in index 2cca52ef30a1..297afc7a10de 100644 --- a/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in +++ b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in @@ -15,7 +15,7 @@ class QgsRelationWidgetRegistry { %Docstring Keeps track of the registered relations widgets. New widgets can be registered, old ones deleted. -The default {:py:class:`QgsBasicRelationWidget`} is protected from removing. +The default {:py:class:`QgsRelationEditorWidget`} is protected from removing. .. versionadded:: 3.18 %End diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 53e3c753a75e..17fd9faeea7d 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -182,7 +182,6 @@ %Include auto_generated/qgsratiolockbutton.sip %Include auto_generated/qgsrelationeditorwidget.sip %Include auto_generated/qgsrelationconfigwidget.sip -%Include auto_generated/qgsbasicrelationwidget.sip %Include auto_generated/qgsrelationwidget.sip %Include auto_generated/qgsrelationwidgetfactory.sip %Include auto_generated/qgsrelationwidgetregistry.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 931a9a626859..be34a31e7d10 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -567,7 +567,6 @@ set(QGIS_GUI_SRCS qgsrasterpyramidsoptionswidget.cpp qgsrelationeditorwidget.cpp qgsrelationconfigwidget.cpp - qgsbasicrelationwidget.cpp qgsrelationwidget.cpp qgsrelationwidgetfactory.cpp qgsrelationwidgetregistry.cpp @@ -818,7 +817,6 @@ set(QGIS_GUI_HDRS qgsratiolockbutton.h qgsrelationeditorwidget.h qgsrelationconfigwidget.h - qgsbasicrelationwidget.h qgsrelationwidget.h qgsrelationwidgetfactory.h qgsrelationwidgetregistry.h diff --git a/src/gui/qgsbasicrelationwidget.cpp b/src/gui/qgsbasicrelationwidget.cpp deleted file mode 100644 index 6148081a8398..000000000000 --- a/src/gui/qgsbasicrelationwidget.cpp +++ /dev/null @@ -1,632 +0,0 @@ -/*************************************************************************** - qgsbasicrelationwidget.cpp - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 "qgsbasicrelationwidget.h" - -#include "qgsapplication.h" -#include "qgsdistancearea.h" -#include "qgsfeatureiterator.h" -#include "qgsvectordataprovider.h" -#include "qgsexpression.h" -#include "qgsfeature.h" -#include "qgsfeatureselectiondlg.h" -#include "qgsgenericfeatureselectionmanager.h" -#include "qgsrelation.h" -#include "qgsvectorlayertools.h" -#include "qgsproject.h" -#include "qgstransactiongroup.h" -#include "qgslogger.h" -#include "qgsvectorlayerutils.h" -#include "qgsmapcanvas.h" -#include "qgsvectorlayerselectionmanager.h" -#include "qgsmaptooldigitizefeature.h" -#include "qgsexpressioncontextutils.h" -#include "qgsmessagebar.h" -#include "qgsmessagebaritem.h" - -#include -#include -#include -#include - - -QgsBasicRelationWidget::QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent ) - : QgsRelationWidget( config, parent ) - , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ) ) -{ - QVBoxLayout *rootLayout = new QVBoxLayout( this ); - rootLayout->setContentsMargins( 0, 0, 0, 0 ); - - mRootCollapsibleGroupBox = new QgsCollapsibleGroupBox( QString(), this ); - rootLayout->addWidget( mRootCollapsibleGroupBox ); - - QVBoxLayout *topLayout = new QVBoxLayout( mRootCollapsibleGroupBox ); - topLayout->setContentsMargins( 0, 9, 0, 0 ); - - // buttons - QHBoxLayout *buttonLayout = new QHBoxLayout(); - buttonLayout->setContentsMargins( 0, 0, 0, 0 ); - // toggle editing - mToggleEditingButton = new QToolButton( this ); - mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) ); - mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) ); - mToggleEditingButton->setText( tr( "Toggle Editing" ) ); - mToggleEditingButton->setEnabled( false ); - mToggleEditingButton->setCheckable( true ); - mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) ); - buttonLayout->addWidget( mToggleEditingButton ); - // save Edits - mSaveEditsButton = new QToolButton( this ); - mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) ); - mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) ); - mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) ); - mSaveEditsButton->setEnabled( true ); - buttonLayout->addWidget( mSaveEditsButton ); - // add feature with geometry - mAddFeatureGeometryButton = new QToolButton( this ); - mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) ); - buttonLayout->addWidget( mAddFeatureGeometryButton ); - // add feature - mAddFeatureButton = new QToolButton( this ); - mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) ); - mAddFeatureButton->setText( tr( "Add Child Feature" ) ); - mAddFeatureButton->setToolTip( tr( "Add child feature" ) ); - mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) ); - buttonLayout->addWidget( mAddFeatureButton ); - // duplicate feature - mDuplicateFeatureButton = new QToolButton( this ); - mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) ); - mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) ); - mDuplicateFeatureButton->setToolTip( tr( "Duplicate child feature" ) ); - mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) ); - buttonLayout->addWidget( mDuplicateFeatureButton ); - // delete feature - mDeleteFeatureButton = new QToolButton( this ); - mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) ); - mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) ); - mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) ); - mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) ); - buttonLayout->addWidget( mDeleteFeatureButton ); - // link feature - mLinkFeatureButton = new QToolButton( this ); - mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) ); - mLinkFeatureButton->setText( tr( "Link Existing Features" ) ); - mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) ); - mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) ); - buttonLayout->addWidget( mLinkFeatureButton ); - // unlink feature - mUnlinkFeatureButton = new QToolButton( this ); - mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) ); - mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) ); - mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) ); - mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) ); - buttonLayout->addWidget( mUnlinkFeatureButton ); - // zoom to linked feature - mZoomToFeatureButton = new QToolButton( this ); - mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) ); - mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) ); - mZoomToFeatureButton->setToolTip( tr( "Zoom to child feature" ) ); - mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) ); - buttonLayout->addWidget( mZoomToFeatureButton ); - // spacer - buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) ); - // form view - mFormViewButton = new QToolButton( this ); - mFormViewButton->setText( tr( "Form View" ) ); - mFormViewButton->setToolTip( tr( "Switch to form view" ) ); - mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) ); - mFormViewButton->setCheckable( true ); - mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor ); - buttonLayout->addWidget( mFormViewButton ); - // table view - mTableViewButton = new QToolButton( this ); - mTableViewButton->setText( tr( "Table View" ) ); - mTableViewButton->setToolTip( tr( "Switch to table view" ) ); - mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) ); - mTableViewButton->setCheckable( true ); - mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable ); - buttonLayout->addWidget( mTableViewButton ); - // button group - mViewModeButtonGroup = new QButtonGroup( this ); - mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor ); - mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable ); - - // add buttons layout - topLayout->addLayout( buttonLayout ); - - mRelationLayout = new QGridLayout(); - mRelationLayout->setContentsMargins( 0, 0, 0, 0 ); - topLayout->addLayout( mRelationLayout ); - - mDualView = new QgsDualView( this ); - mDualView->setView( mViewMode ); - - mRelationLayout->addWidget( mDualView ); - - connect( mRootCollapsibleGroupBox, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsBasicRelationWidget::onCollapsedStateChanged ); - connect( mViewModeButtonGroup, static_cast( &QButtonGroup::buttonClicked ), - this, static_cast( &QgsBasicRelationWidget::setViewMode ) ); - connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::toggleEditing ); - connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::saveEdits ); - connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } ); - connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::addFeatureGeometry ); - connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::duplicateFeature ); - connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::deleteSelectedFeatures ); - connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::linkFeature ); - connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::unlinkSelectedFeatures ); - connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsBasicRelationWidget::zoomToSelectedFeatures ); - - connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsBasicRelationWidget::showContextMenu ); - - // Set initial state for add/remove etc. buttons - updateButtons(); -} - -void QgsBasicRelationWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ) -{ - QgsAttributeEditorContext ctx { mEditorContext }; - ctx.setParentFormFeature( mFeature ); - mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx ); - mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView ); - mDualView->setFeatureSelectionManager( mFeatureSelectionMgr ); - - connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsBasicRelationWidget::updateButtons ); - - QIcon icon; - QString text; - if ( layer->geometryType() == QgsWkbTypes::PointGeometry ) - { - icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) ); - text = tr( "Add Point child Feature" ); - } - else if ( layer->geometryType() == QgsWkbTypes::LineGeometry ) - { - icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) ); - text = tr( "Add Line child Feature" ); - } - else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry ) - { - icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) ); - text = tr( "Add Polygon Feature" ); - } - - mAddFeatureGeometryButton->setIcon( icon ); - mAddFeatureGeometryButton->setText( text ); - mAddFeatureGeometryButton->setToolTip( text ); - - updateButtons(); -} - -void QgsBasicRelationWidget::setEditorContext( const QgsAttributeEditorContext &context ) -{ - mEditorContext = context; - - if ( context.mapCanvas() && context.cadDockWidget() ) - { - mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) ); - mMapToolDigitize->setButton( mAddFeatureGeometryButton ); - } - - updateButtons(); -} - -void QgsBasicRelationWidget::setViewMode( QgsDualView::ViewMode mode ) -{ - mDualView->setView( mode ); - mViewMode = mode; -} - -void QgsBasicRelationWidget::updateButtons() -{ - bool editable = false; - bool linkable = false; - bool spatial = false; - bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false; - - if ( mRelation.isValid() ) - { - editable = mRelation.referencingLayer()->isEditable(); - linkable = mRelation.referencingLayer()->isEditable(); - spatial = mRelation.referencingLayer()->isSpatial(); - } - - if ( mNmRelation.isValid() ) - { - editable = mNmRelation.referencedLayer()->isEditable(); - spatial = mNmRelation.referencedLayer()->isSpatial(); - } - - mAddFeatureButton->setEnabled( editable ); - mAddFeatureGeometryButton->setEnabled( editable ); - mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty ); - mLinkFeatureButton->setEnabled( linkable ); - mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty ); - mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty ); - mZoomToFeatureButton->setEnabled( selectionNotEmpty ); - mToggleEditingButton->setChecked( editable ); - mSaveEditsButton->setEnabled( editable ); - - mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup ); - - mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::Link ) ); - mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); - mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup ); - mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); - mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial ); - mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); - mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); - mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial ); -} - -void QgsBasicRelationWidget::addFeatureGeometry() -{ - QgsVectorLayer *layer = nullptr; - if ( mNmRelation.isValid() ) - layer = mNmRelation.referencedLayer(); - else - layer = mRelation.referencingLayer(); - - mMapToolDigitize->setLayer( layer ); - - // window is always on top, so we hide it to digitize without seeing it - window()->setVisible( false ); - setMapTool( mMapToolDigitize ); - - connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsBasicRelationWidget::onDigitizingCompleted ); - connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsBasicRelationWidget::onKeyPressed ); - - if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() ) - { - QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature ); - - QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ); - QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." ) - .arg( layer->name() ); - mMessageBarItem = QgsMessageBar::createMessage( title, msg, this ); - lMainMessageBar->pushItem( mMessageBarItem ); - } - -} - -void QgsBasicRelationWidget::onDigitizingCompleted( const QgsFeature &feature ) -{ - addFeature( feature.geometry() ); - - unsetMapTool(); -} - -void QgsBasicRelationWidget::toggleEditing( bool state ) -{ - QgsRelationWidget::toggleEditing( state ); - - updateButtons(); -} - -void QgsBasicRelationWidget::onCollapsedStateChanged( bool collapsed ) -{ - if ( !collapsed ) - { - mVisible = true; - updateUi(); - } -} - -void QgsBasicRelationWidget::updateUi() -{ - // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) - // If it is already initialized, it has been set visible before and the currently shown feature is changing - // and the widget needs updating - - if ( mVisible ) - { - QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); - - if ( mNmRelation.isValid() ) - { - QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( myRequest ); - - QgsFeature fet; - - QStringList filters; - - while ( it.nextFeature( fet ) ) - { - QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression(); - filters << filter.prepend( '(' ).append( ')' ); - } - - QgsFeatureRequest nmRequest; - - nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) ); - - initDualView( mNmRelation.referencedLayer(), nmRequest ); - } - else if ( mRelation.referencingLayer() ) - { - initDualView( mRelation.referencingLayer(), myRequest ); - } - } -} - -void QgsBasicRelationWidget::setVisibleButtons( const Buttons &buttons ) -{ - mButtonsVisibility = buttons; - updateButtons(); -} - -QgsBasicRelationWidget::Buttons QgsBasicRelationWidget::visibleButtons() const -{ - Buttons buttons; - if ( mLinkFeatureButton->isVisible() ) - buttons |= Button::Link; - if ( mUnlinkFeatureButton->isVisible() ) - buttons |= Button::Unlink; - if ( mSaveEditsButton->isVisible() ) - buttons |= Button::SaveChildEdits; - if ( mAddFeatureButton->isVisible() ) - buttons |= Button::AddChildFeature; - if ( mDuplicateFeatureButton->isVisible() ) - buttons |= Button::DuplicateChildFeature; - if ( mDeleteFeatureButton->isVisible() ) - buttons |= Button::DeleteChildFeature; - if ( mZoomToFeatureButton->isVisible() ) - buttons |= Button::ZoomToChildFeature; - return buttons; -} - -void QgsBasicRelationWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue ) -{ - mDualView->parentFormValueChanged( attribute, newValue ); -} - -void QgsBasicRelationWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid ) -{ - if ( mRelation.referencingLayer()->isEditable() ) - { - QAction *qAction = nullptr; - - qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) ); - connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } ); - - qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) ); - connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } ); - } -} - -void QgsBasicRelationWidget::setMapTool( QgsMapTool *mapTool ) -{ - QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); - - mapCanvas->setMapTool( mapTool ); - mapCanvas->window()->raise(); - mapCanvas->activateWindow(); - mapCanvas->setFocus(); - connect( mapTool, &QgsMapTool::deactivated, this, &QgsBasicRelationWidget::mapToolDeactivated ); -} - -void QgsBasicRelationWidget::unsetMapTool() -{ - QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); - - // this will call mapToolDeactivated - mapCanvas->unsetMapTool( mMapToolDigitize ); - - disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsBasicRelationWidget::onKeyPressed ); - disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsBasicRelationWidget::onDigitizingCompleted ); -} - -void QgsBasicRelationWidget::onKeyPressed( QKeyEvent *e ) -{ - if ( e->key() == Qt::Key_Escape ) - { - unsetMapTool(); - } -} - -void QgsBasicRelationWidget::mapToolDeactivated() -{ - window()->setVisible( true ); - window()->raise(); - window()->activateWindow(); - - if ( mEditorContext.mainMessageBar() && mMessageBarItem ) - { - mEditorContext.mainMessageBar()->popWidget( mMessageBarItem ); - } - mMessageBarItem = nullptr; -} - -QVariantMap QgsBasicRelationWidget::config() const -{ - return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )}} ); -} - -void QgsBasicRelationWidget::setConfig( const QVariantMap &config ) -{ - mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); - updateButtons(); -} - -void QgsBasicRelationWidget::setTitle( const QString &title ) -{ - mRootCollapsibleGroupBox->setTitle( title ); -} - -void QgsBasicRelationWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) -{ - Q_UNUSED( newRelation ); - Q_UNUSED( newFeature ); - - if ( ! mRelation.isValid() ) - return; - - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); -} - -void QgsBasicRelationWidget::afterSetRelationFeature() -{ - mToggleEditingButton->setEnabled( false ); - - if ( ! mRelation.isValid() ) - return; - - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); - - QgsVectorLayer *vl = mRelation.referencingLayer(); - bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; - if ( canChangeAttributes && !vl->readOnly() ) - { - mToggleEditingButton->setEnabled( true ); - updateButtons(); - } - else - { - mToggleEditingButton->setEnabled( false ); - } - - // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) - // If it is already initialized, it has been set visible before and the currently shown feature is changing - // and the widget needs updating - - if ( mVisible ) - { - QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); - initDualView( mRelation.referencingLayer(), myRequest ); - } -} - -void QgsBasicRelationWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) -{ - Q_UNUSED( newRelation ); - Q_UNUSED( newNmRelation ); - - if ( mRelation.isValid() ) - { - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); - } - - if ( mNmRelation.isValid() ) - { - disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); - } -} - -void QgsBasicRelationWidget::afterSetRelations() -{ - if ( !mRelation.isValid() ) - return; - - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); - - if ( mNmRelation.isValid() ) - { - connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsBasicRelationWidget::updateButtons ); - connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsBasicRelationWidget::updateButtons ); - } - - QgsVectorLayer *vl = mRelation.referencingLayer(); - bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; - if ( canChangeAttributes && !vl->readOnly() ) - { - mToggleEditingButton->setEnabled( true ); - } - else - { - mToggleEditingButton->setEnabled( false ); - } - - updateButtons(); -} - - -/////////////////////////////////////////////////////////////////////////////// - - -#ifndef SIP_RUN -QgsBasicRelationWidgetFactory::QgsBasicRelationWidgetFactory() -{ - -} - -QString QgsBasicRelationWidgetFactory::type() const -{ - return QStringLiteral( "relation_editor" ); -} - -QString QgsBasicRelationWidgetFactory::name() const -{ - return QStringLiteral( "Relation Editor" ); -} - -QgsRelationWidget *QgsBasicRelationWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const -{ - return new QgsBasicRelationWidget( config, parent ); -} - - -QgsRelationConfigWidget *QgsBasicRelationWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const -{ - return static_cast( new QgsBasicRelationConfigWidget( relation, parent ) ); -} -#endif - - -/////////////////////////////////////////////////////////////////////////////// - - -#ifndef SIP_RUN -QgsBasicRelationConfigWidget::QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) - : QgsRelationConfigWidget( relation, parent ) -{ - setupUi( this ); -} - -QVariantMap QgsBasicRelationConfigWidget::config() -{ - QgsBasicRelationWidget::Buttons buttons; - buttons.setFlag( QgsBasicRelationWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); - buttons.setFlag( QgsBasicRelationWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); - - return QVariantMap( - { - {"buttons", qgsFlagValueToKeys( buttons )}, - } ); -} - -void QgsBasicRelationConfigWidget::setConfig( const QVariantMap &config ) -{ - const QgsBasicRelationWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsBasicRelationWidget::Button::AllButtons ); - - mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Link ) ); - mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::Unlink ) ); - mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::AddChildFeature ) ); - mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DuplicateChildFeature ) ); - mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::ZoomToChildFeature ) ); - mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::DeleteChildFeature ) ); - mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsBasicRelationWidget::Button::SaveChildEdits ) ); -} -#endif diff --git a/src/gui/qgsbasicrelationwidget.h b/src/gui/qgsbasicrelationwidget.h deleted file mode 100644 index 67757d6c87d3..000000000000 --- a/src/gui/qgsbasicrelationwidget.h +++ /dev/null @@ -1,236 +0,0 @@ -/*************************************************************************** - qgsbasicrelationwidget.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 QGSBASICRELATIONWIDGET_H -#define QGSBASICRELATIONWIDGET_H - -#include -#include -#include -#include -#include "qobjectuniqueptr.h" - -#include "ui_qgsrelationeditorconfigwidgetbase.h" - -#include "qgsrelationwidget.h" -#include "qgsrelationconfigwidget.h" -#include "qgsrelationwidgetfactory.h" -#include "qgsrelationeditorwidget.h" -#include "qobjectuniqueptr.h" -#include "qgsattributeeditorcontext.h" -#include "qgscollapsiblegroupbox.h" -#include "qgsdualview.h" -#include "qgsrelation.h" -#include "qgsvectorlayerselectionmanager.h" -#include "qgis_gui.h" - -class QgsFeature; -class QgsVectorLayer; -class QgsVectorLayerTools; -class QgsMapTool; -class QgsMapToolDigitizeFeature; - - -/** - * The default relation widget in QGIS. Successor of the now deprecated {\see QgsRelationEditorWidget}. - * \ingroup gui - * \class QgsBasicRelationWidget - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsBasicRelationWidget : public QgsRelationWidget -{ - - Q_OBJECT - Q_PROPERTY( QgsDualView::ViewMode viewMode READ viewMode WRITE setViewMode ) - Q_PROPERTY( Buttons visibleButtons READ visibleButtons WRITE setVisibleButtons ) - - public: - - /** - * Possible buttons shown in the relation editor - */ - enum Button - { - Link = 1 << 1, //!< Link button - Unlink = 1 << 2, //!< Unlink button - SaveChildEdits = 1 << 3, //!< Save child edits button - AddChildFeature = 1 << 4, //!< Add child feature (as in some projects we only want to allow linking/unlinking existing features) - DuplicateChildFeature = 1 << 5, //!< Duplicate child feature - DeleteChildFeature = 1 << 6, //!< Delete child feature button - ZoomToChildFeature = 1 << 7, //!< Zoom to child feature - AllButtons = Link | Unlink | SaveChildEdits | AddChildFeature | DuplicateChildFeature | DeleteChildFeature | ZoomToChildFeature //!< All buttons - }; - Q_ENUM( Button ) - Q_DECLARE_FLAGS( Buttons, Button ) - Q_FLAG( Buttons ) - - /** - * Constructor - * \param config widget configuration - * \param parent parent widget - */ - QgsBasicRelationWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); - - //! Define the view mode for the dual view - void setViewMode( QgsDualView::ViewMode mode ); - - //! Gets the view mode for the dual view - QgsDualView::ViewMode viewMode() {return mViewMode;} - - /** - * Sets the editor \a context - * \note if context cadDockWidget is null, it won't be possible to digitize - * the geometry of a referencing feature from this widget - */ - void setEditorContext( const QgsAttributeEditorContext &context ); - - /** - * Defines the buttons which are shown - */ - void setVisibleButtons( const Buttons &buttons ); - - /** - * Returns the buttons which are shown - */ - Buttons visibleButtons() const; - - /** - * Returns the current configuration - */ - QVariantMap config() const override; - - /** - * Defines the current configuration - */ - void setConfig( const QVariantMap &config ) override; - - /** - * Sets the title of the root groupbox - */ - void setTitle( const QString &title ) override; - - public slots: - void parentFormValueChanged( const QString &attribute, const QVariant &newValue ) override; - - private slots: - void setViewMode( int mode ) {setViewMode( static_cast( mode ) );} - void updateButtons(); - - void addFeatureGeometry(); - void toggleEditing( bool state ); - void onCollapsedStateChanged( bool collapsed ); - void showContextMenu( QgsActionMenu *menu, QgsFeatureId fid ); - void mapToolDeactivated(); - void onKeyPressed( QKeyEvent *e ); - void onDigitizingCompleted( const QgsFeature &feature ); - - private: - void updateUi() override; - void initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ); - void setMapTool( QgsMapTool *mapTool ); - void unsetMapTool(); - - QgsCollapsibleGroupBox *mRootCollapsibleGroupBox = nullptr; - QgsDualView *mDualView = nullptr; - QPointer mMessageBarItem; - QgsDualView::ViewMode mViewMode = QgsDualView::AttributeEditor; - - QToolButton *mToggleEditingButton = nullptr; - QToolButton *mSaveEditsButton = nullptr; - QToolButton *mAddFeatureButton = nullptr; - QToolButton *mDuplicateFeatureButton = nullptr; - QToolButton *mDeleteFeatureButton = nullptr; - QToolButton *mLinkFeatureButton = nullptr; - QToolButton *mUnlinkFeatureButton = nullptr; - QToolButton *mZoomToFeatureButton = nullptr; - QToolButton *mFormViewButton = nullptr; - QToolButton *mTableViewButton = nullptr; - QToolButton *mAddFeatureGeometryButton = nullptr; - QGridLayout *mRelationLayout = nullptr; - QObjectUniquePtr mMapToolDigitize; - QButtonGroup *mViewModeButtonGroup = nullptr; - - Buttons mButtonsVisibility = Button::AllButtons; - bool mVisible = true; - - void beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) override; - void afterSetRelationFeature() override; - void beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) override; - void afterSetRelations() override; -}; - - -#ifndef SIP_RUN - -/** - * Factory class for creating a basic relation widget and the respective config widget. - * \ingroup gui - * \class QgsBasicRelationWidgetFactory - * \note not available in Python bindings - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsBasicRelationWidgetFactory : public QgsRelationWidgetFactory -{ - public: - QgsBasicRelationWidgetFactory(); - - QString type() const override; - - QString name() const override; - - QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; - - QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; - -}; - -/** - * \ingroup gui - * \class QgsBasicRelationConfigWidget - * Creates a new configuration widget for the basic relation widget - * \since QGIS 3.18 - */ -class QgsBasicRelationConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase -{ - public: - - /** - * Create a new configuration widget - * - * \param relation The relation for which the configuration dialog will be created - * \param parent A parent widget - */ - explicit QgsBasicRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); - - /** - * \brief Create a configuration from the current GUI state - * - * \returns A widget configuration - */ - QVariantMap config(); - - /** - * \brief Update the configuration widget to represent the given configuration. - * - * \param config The configuration which should be represented by this widget - */ - void setConfig( const QVariantMap &config ); - -}; -#endif - -#endif // QGSBASICRELATIONWIDGET_H diff --git a/src/gui/qgsrelationeditorwidget.cpp b/src/gui/qgsrelationeditorwidget.cpp index acc8743a7e52..95a71b8332cd 100644 --- a/src/gui/qgsrelationeditorwidget.cpp +++ b/src/gui/qgsrelationeditorwidget.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - qgsrelationeditor.cpp + qgsrelationeditorwidget.cpp -------------------------------------- Date : 17.5.2013 Copyright : (C) 2013 Matthias Kuhn @@ -58,6 +58,8 @@ QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, } const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const + + { return mSelectedFeatureIds; } @@ -91,12 +93,18 @@ void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selec /// @endcond -QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent ) - : QgsCollapsibleGroupBox( parent ) +QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent ) + : QgsRelationWidget( config, parent ) + , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) ) { - QVBoxLayout *topLayout = new QVBoxLayout( this ); + QVBoxLayout *rootLayout = new QVBoxLayout( this ); + rootLayout->setContentsMargins( 0, 0, 0, 0 ); + + mRootCollapsibleGroupBox = new QgsCollapsibleGroupBox( QString(), this ); + rootLayout->addWidget( mRootCollapsibleGroupBox ); + + QVBoxLayout *topLayout = new QVBoxLayout( mRootCollapsibleGroupBox ); topLayout->setContentsMargins( 0, 9, 0, 0 ); - setLayout( topLayout ); // buttons QHBoxLayout *buttonLayout = new QHBoxLayout(); @@ -198,7 +206,7 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent ) mRelationLayout->addWidget( mDualView ); - connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged ); + connect( mRootCollapsibleGroupBox, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged ); connect( mViewModeButtonGroup, static_cast( &QButtonGroup::buttonClicked ), this, static_cast( &QgsRelationEditorWidget::setViewMode ) ); connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing ); @@ -217,48 +225,6 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent ) updateButtons(); } -void QgsRelationEditorWidget::setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ) -{ - if ( mRelation.isValid() ) - { - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - } - - mRelation = relation; - mFeature = feature; - - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - - updateTitle(); - - QgsVectorLayer *lyr = relation.referencingLayer(); - - bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; - if ( canChangeAttributes && !lyr->readOnly() ) - { - mToggleEditingButton->setEnabled( true ); - updateButtons(); - } - else - { - mToggleEditingButton->setEnabled( false ); - } - - setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() ); - - // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) - // If it is already initialized, it has been set visible before and the currently shown feature is changing - // and the widget needs updating - - if ( mVisible ) - { - QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); - initDualView( mRelation.referencingLayer(), myRequest ); - } -} - void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ) { QgsAttributeEditorContext ctx { mEditorContext }; @@ -294,76 +260,6 @@ void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeat updateButtons(); } -void QgsRelationEditorWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ) -{ - if ( mRelation.isValid() ) - { - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - } - - if ( mNmRelation.isValid() ) - { - disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - } - - mRelation = relation; - mNmRelation = nmrelation; - - if ( !mRelation.isValid() ) - return; - - mLayerInSameTransactionGroup = false; - - const auto transactionGroups = QgsProject::instance()->transactionGroups(); - for ( auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it ) - { - if ( mNmRelation.isValid() ) - { - if ( it.value()->layers().contains( mRelation.referencedLayer() ) && - it.value()->layers().contains( mRelation.referencingLayer() ) && - it.value()->layers().contains( mNmRelation.referencedLayer() ) ) - mLayerInSameTransactionGroup = true; - } - else - { - if ( it.value()->layers().contains( mRelation.referencedLayer() ) && - it.value()->layers().contains( mRelation.referencingLayer() ) ) - mLayerInSameTransactionGroup = true; - } - } - - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - - if ( mNmRelation.isValid() ) - { - connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); - connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); - } - - updateTitle(); - - QgsVectorLayer *lyr = relation.referencingLayer(); - - bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; - if ( canChangeAttributes && !lyr->readOnly() ) - { - mToggleEditingButton->setEnabled( true ); - } - else - { - mToggleEditingButton->setEnabled( false ); - } - - updateButtons(); - - setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() ); - - updateUi(); -} - void QgsRelationEditorWidget::setEditorContext( const QgsAttributeEditorContext &context ) { mEditorContext = context; @@ -377,32 +273,12 @@ void QgsRelationEditorWidget::setEditorContext( const QgsAttributeEditorContext updateButtons(); } -QgsAttributeEditorContext QgsRelationEditorWidget::editorContext() const -{ - return mEditorContext; -} - -QgsIFeatureSelectionManager *QgsRelationEditorWidget::featureSelectionManager() -{ - return mFeatureSelectionMgr; -} - void QgsRelationEditorWidget::setViewMode( QgsDualView::ViewMode mode ) { mDualView->setView( mode ); mViewMode = mode; } -void QgsRelationEditorWidget::setFeature( const QgsFeature &feature, bool update ) -{ - mFeature = feature; - - mEditorContext.setFormFeature( feature ); - - if ( update ) - updateUi(); -} - void QgsRelationEditorWidget::updateButtons() { bool editable = false; @@ -434,14 +310,15 @@ void QgsRelationEditorWidget::updateButtons() mSaveEditsButton->setEnabled( editable ); mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup ); - mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Link ) ); - mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Unlink ) ); - mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup ); - mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) ); - mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial ); - mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature ) ); - mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature ) ); - mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial ); + + mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) ); + mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) ); + mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup ); + mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) ); + mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial ); + mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) ); + mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) ); + mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial ); } void QgsRelationEditorWidget::addFeatureGeometry() @@ -474,60 +351,6 @@ void QgsRelationEditorWidget::addFeatureGeometry() } -void QgsRelationEditorWidget::addFeature( const QgsGeometry &geometry ) -{ - QgsAttributeMap keyAttrs; - - const QgsVectorLayerTools *vlTools = mEditorContext.vectorLayerTools(); - - if ( mNmRelation.isValid() ) - { - // n:m Relation: first let the user create a new feature on the other table - // and autocreate a new linking feature. - QgsFeature f; - if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), geometry, &f ) ) - { - // Fields of the linking table - const QgsFields fields = mRelation.referencingLayer()->fields(); - - // Expression context for the linking table - QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext(); - - QgsAttributeMap linkAttributes; - const auto constFieldPairs = mRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - int index = fields.indexOf( fieldPair.first ); - linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) ); - } - - const auto constNmFieldPairs = mNmRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constNmFieldPairs ) - { - int index = fields.indexOf( fieldPair.first ); - linkAttributes.insert( index, f.attribute( fieldPair.second ) ); - } - QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context ); - - mRelation.referencingLayer()->addFeature( linkFeature ); - - updateUi(); - } - } - else - { - QgsFields fields = mRelation.referencingLayer()->fields(); - - const auto constFieldPairs = mRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) ); - } - - vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry ); - } -} - void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature ) { addFeature( feature.geometry() ); @@ -535,351 +358,13 @@ void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature ) unsetMapTool(); } -void QgsRelationEditorWidget::linkFeature() -{ - QgsVectorLayer *layer = nullptr; - - if ( mNmRelation.isValid() ) - layer = mNmRelation.referencedLayer(); - else - layer = mRelation.referencingLayer(); - - QgsFeatureSelectionDlg *selectionDlg = new QgsFeatureSelectionDlg( layer, mEditorContext, this ); - selectionDlg->setAttribute( Qt::WA_DeleteOnClose ); - - const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), mFeature ); - selectionDlg->setWindowTitle( tr( "Link existing child features for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ) ); - - connect( selectionDlg, &QDialog::accepted, this, &QgsRelationEditorWidget::onLinkFeatureDlgAccepted ); - selectionDlg->show(); -} - -void QgsRelationEditorWidget::onLinkFeatureDlgAccepted() -{ - QgsFeatureSelectionDlg *selectionDlg = qobject_cast( sender() ); - if ( mNmRelation.isValid() ) - { - QgsFeatureIterator it = mNmRelation.referencedLayer()->getFeatures( - QgsFeatureRequest() - .setFilterFids( selectionDlg->selectedFeatures() ) - .setSubsetOfAttributes( mNmRelation.referencedFields() ) ); - - QgsFeature relatedFeature; - - QgsFeatureList newFeatures; - - // Fields of the linking table - const QgsFields fields = mRelation.referencingLayer()->fields(); - - // Expression context for the linking table - QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext(); - - QgsAttributeMap linkAttributes; - const auto constFieldPairs = mRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - int index = fields.indexOf( fieldPair.first ); - linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) ); - } - - while ( it.nextFeature( relatedFeature ) ) - { - const auto constFieldPairs = mNmRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - int index = fields.indexOf( fieldPair.first ); - linkAttributes.insert( index, relatedFeature.attribute( fieldPair.second ) ); - } - const QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context ); - - newFeatures << linkFeature; - } - - mRelation.referencingLayer()->addFeatures( newFeatures ); - QgsFeatureIds ids; - const auto constNewFeatures = newFeatures; - for ( const QgsFeature &f : constNewFeatures ) - ids << f.id(); - mRelation.referencingLayer()->selectByIds( ids ); - } - else - { - QMap keys; - const auto constFieldPairs = mRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() ); - QVariant val = mFeature.attribute( fieldPair.referencedField() ); - keys.insert( idx, val ); - } - - const auto constSelectedFeatures = selectionDlg->selectedFeatures(); - for ( QgsFeatureId fid : constSelectedFeatures ) - { - QMapIterator it( keys ); - while ( it.hasNext() ) - { - it.next(); - mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() ); - } - } - } - - updateUi(); -} - -void QgsRelationEditorWidget::duplicateFeature() -{ - QgsVectorLayer *layer = mRelation.referencingLayer(); - - QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) ); - QgsFeature f; - while ( fit.nextFeature( f ) ) - { - QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext; - QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext ); - } -} - -void QgsRelationEditorWidget::deleteFeature( const QgsFeatureId featureid ) -{ - deleteFeatures( QgsFeatureIds() << featureid ); -} - -void QgsRelationEditorWidget::deleteSelectedFeatures() -{ - QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds(); - deleteFeatures( selectedFids ); -} - -void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids ) -{ - bool deleteFeatures = true; - - QgsVectorLayer *layer; - if ( mNmRelation.isValid() ) - { - layer = mNmRelation.referencedLayer(); - - // When deleting a linked feature within an N:M relation, - // check if the feature is linked to more than just one feature. - // In case it is linked more than just once, ask the user for confirmation - // as it is likely he was not aware of the implications and might delete - // there may be several linking entries deleted along. - - QgsFeatureRequest deletedFeaturesRequest; - deletedFeaturesRequest.setFilterFids( featureids ); - deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry ); - deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() ); - - QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest ); - QStringList deletedFeaturesPks; - QgsFeature feature; - while ( deletedFeatures.nextFeature( feature ) ) - { - deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) ); - } - - QgsFeatureRequest linkingFeaturesRequest; - linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry ); - linkingFeaturesRequest.setNoAttributes(); - - QString linkingFeaturesRequestExpression; - if ( !deletedFeaturesPks.empty() ) - { - linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) ); - linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression ); - - QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest ); - - int relatedLinkingFeaturesCount = 0; - while ( relatedLinkingFeatures.nextFeature( feature ) ) - { - relatedLinkingFeaturesCount++; - } - - if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 ) - { - QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entry?" ), tr( "The entry on %1 is still linked to %2 features on %3. Do you want to delete it?" ).arg( mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this ); - messageBox.addButton( QMessageBox::Cancel ); - QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole ); - - messageBox.exec(); - if ( messageBox.clickedButton() != deleteButton ) - deleteFeatures = false; - } - else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() ) - { - QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entries?" ), tr( "The %1 entries on %2 are still linked to %3 features on %4. Do you want to delete them?" ).arg( QString::number( deletedFeaturesPks.size() ), mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this ); - messageBox.addButton( QMessageBox::Cancel ); - QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole ); - - messageBox.exec(); - if ( messageBox.clickedButton() != deleteButton ) - deleteFeatures = false; - } - } - } - else - { - layer = mRelation.referencingLayer(); - } - - QgsVectorLayerUtils::QgsDuplicateFeatureContext infoContext; - if ( QgsVectorLayerUtils::impactsCascadeFeatures( layer, featureids, QgsProject::instance(), infoContext ) ) - { - QString childrenInfo; - int childrenCount = 0; - const auto infoContextLayers = infoContext.layers(); - for ( QgsVectorLayer *chl : infoContextLayers ) - { - childrenCount += infoContext.duplicatedFeatures( chl ).size(); - childrenInfo += ( tr( "%1 feature(s) on layer \"%2\", " ).arg( infoContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) ); - } - - // for extra safety to make sure we know that the delete can have impact on children and joins - int res = QMessageBox::question( this, tr( "Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ), - tr( "Delete %1 feature(s) on layer \"%2\", %3 as well\nand all of its other descendants.\nDelete these features?" ).arg( featureids.count() ).arg( layer->name() ).arg( childrenInfo ), - QMessageBox::Yes | QMessageBox::No ); - if ( res != QMessageBox::Yes ) - deleteFeatures = false; - } - - if ( deleteFeatures ) - { - QgsVectorLayer::DeleteContext context( true, QgsProject::instance() ); - layer->deleteFeatures( featureids, &context ); - const auto contextLayers = context.handledLayers(); - if ( contextLayers.size() > 1 ) - { - int deletedCount = 0; - QString feedbackMessage; - for ( QgsVectorLayer *contextLayer : contextLayers ) - { - feedbackMessage += tr( "%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() ); - deletedCount += context.handledFeatures( contextLayer ).size(); - } - mEditorContext.mainMessageBar()->pushMessage( tr( "%1 features deleted: %2" ).arg( deletedCount ).arg( feedbackMessage ), Qgis::Success ); - } - - updateUi(); - } -} - -void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid ) -{ - unlinkFeatures( QgsFeatureIds() << featureid ); -} - -void QgsRelationEditorWidget::unlinkSelectedFeatures() -{ - unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); -} - -void QgsRelationEditorWidget::zoomToSelectedFeatures() -{ - QgsMapCanvas *c = mEditorContext.mapCanvas(); - if ( !c ) - return; - - c->zoomToFeatureIds( - mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(), - mFeatureSelectionMgr->selectedFeatureIds() - ); -} - -void QgsRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &featureids ) -{ - if ( mNmRelation.isValid() ) - { - QgsFeatureIterator selectedIterator = mNmRelation.referencedLayer()->getFeatures( - QgsFeatureRequest() - .setFilterFids( featureids ) - .setSubsetOfAttributes( mNmRelation.referencedFields() ) ); - - QgsFeature f; - - QStringList filters; - - while ( selectedIterator.nextFeature( f ) ) - { - filters << '(' + mNmRelation.getRelatedFeaturesRequest( f ).filterExpression()->expression() + ')'; - } - - QString filter = QStringLiteral( "(%1) AND (%2)" ).arg( - mRelation.getRelatedFeaturesRequest( mFeature ).filterExpression()->expression(), - filters.join( QLatin1String( " OR " ) ) ); - - QgsFeatureIterator linkedIterator = mRelation.referencingLayer()->getFeatures( QgsFeatureRequest() - .setNoAttributes() - .setFilterExpression( filter ) ); - - QgsFeatureIds fids; - - while ( linkedIterator.nextFeature( f ) ) - { - fids << f.id(); - QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 ); - } - - mRelation.referencingLayer()->deleteFeatures( fids ); - - updateUi(); - } - else - { - QMap keyFields; - const auto constFieldPairs = mRelation.fieldPairs(); - for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) - { - int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() ); - if ( idx < 0 ) - { - QgsDebugMsg( QStringLiteral( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) ); - return; - } - QgsField fld = mRelation.referencingLayer()->fields().at( idx ); - keyFields.insert( idx, fld ); - } - - const auto constFeatureids = featureids; - for ( QgsFeatureId fid : constFeatureids ) - { - QMapIterator it( keyFields ); - while ( it.hasNext() ) - { - it.next(); - mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) ); - } - } - } -} - void QgsRelationEditorWidget::toggleEditing( bool state ) { - if ( state ) - { - mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() ); - if ( mNmRelation.isValid() ) - mEditorContext.vectorLayerTools()->startEditing( mNmRelation.referencedLayer() ); - } - else - { - mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() ); - if ( mNmRelation.isValid() ) - mEditorContext.vectorLayerTools()->stopEditing( mNmRelation.referencedLayer() ); - } + QgsRelationWidget::toggleEditing( state ); updateButtons(); } -void QgsRelationEditorWidget::saveEdits() -{ - mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() ); - if ( mNmRelation.isValid() ) - mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() ); -} - void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed ) { if ( !collapsed ) @@ -926,185 +411,271 @@ void QgsRelationEditorWidget::updateUi() } } -bool QgsRelationEditorWidget::showLinkButton() const -{ - return mLinkFeatureButton->isVisible(); -} - -void QgsRelationEditorWidget::setShowLinkButton( bool showLinkButton ) -{ - mLinkFeatureButton->setVisible( showLinkButton ); -} - -bool QgsRelationEditorWidget::showUnlinkButton() const -{ - return mUnlinkFeatureButton->isVisible(); -} - -void QgsRelationEditorWidget::setShowSaveChildEditsButton( bool showChildEdits ) -{ - mSaveEditsButton->setVisible( showChildEdits ); -} - -bool QgsRelationEditorWidget::showSaveChildEditsButton() const -{ - return mSaveEditsButton->isVisible(); -} - -void QgsRelationEditorWidget::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) +void QgsRelationEditorWidget::setVisibleButtons( const Buttons &buttons ) { mButtonsVisibility = buttons; updateButtons(); } -QgsAttributeEditorRelation::Buttons QgsRelationEditorWidget::visibleButtons() const +QgsRelationEditorWidget::Buttons QgsRelationEditorWidget::visibleButtons() const { - QgsAttributeEditorRelation::Buttons buttons; + Buttons buttons; if ( mLinkFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::Link; + buttons |= Button::Link; if ( mUnlinkFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::Unlink; + buttons |= Button::Unlink; if ( mSaveEditsButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::SaveChildEdits; + buttons |= Button::SaveChildEdits; if ( mAddFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::AddChildFeature; + buttons |= Button::AddChildFeature; if ( mDuplicateFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::DuplicateChildFeature; + buttons |= Button::DuplicateChildFeature; if ( mDeleteFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::DeleteChildFeature; + buttons |= Button::DeleteChildFeature; if ( mZoomToFeatureButton->isVisible() ) - buttons |= QgsAttributeEditorRelation::Button::ZoomToChildFeature; + buttons |= Button::ZoomToChildFeature; return buttons; } -void QgsRelationEditorWidget::setForceSuppressFormPopup( bool forceSuppressFormPopup ) +void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue ) { - mForceSuppressFormPopup = forceSuppressFormPopup; + mDualView->parentFormValueChanged( attribute, newValue ); } -bool QgsRelationEditorWidget::forceSuppressFormPopup() const +void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid ) { - return mForceSuppressFormPopup; + if ( mRelation.referencingLayer()->isEditable() ) + { + QAction *qAction = nullptr; + + qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) ); + connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } ); + + qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) ); + connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } ); + } } -void QgsRelationEditorWidget::setNmRelationId( const QVariant &nmRelationId ) +void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool ) { - mNmRelationId = nmRelationId; + QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + + mapCanvas->setMapTool( mapTool ); + mapCanvas->window()->raise(); + mapCanvas->activateWindow(); + mapCanvas->setFocus(); + connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated ); } -QVariant QgsRelationEditorWidget::nmRelationId() const +void QgsRelationEditorWidget::unsetMapTool() { - return mNmRelationId; + QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + + // this will call mapToolDeactivated + mapCanvas->unsetMapTool( mMapToolDigitize ); + + disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed ); + disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted ); } -QString QgsRelationEditorWidget::label() const +void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e ) { - return mLabel; + if ( e->key() == Qt::Key_Escape ) + { + unsetMapTool(); + } } -void QgsRelationEditorWidget::setLabel( const QString &label ) +void QgsRelationEditorWidget::mapToolDeactivated() { - mLabel = label; + window()->setVisible( true ); + window()->raise(); + window()->activateWindow(); - updateTitle(); + if ( mEditorContext.mainMessageBar() && mMessageBarItem ) + { + mEditorContext.mainMessageBar()->popWidget( mMessageBarItem ); + } + mMessageBarItem = nullptr; } -void QgsRelationEditorWidget::setShowUnlinkButton( bool showUnlinkButton ) +QVariantMap QgsRelationEditorWidget::config() const { - mUnlinkFeatureButton->setVisible( showUnlinkButton ); + return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )}} ); } -void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue ) +void QgsRelationEditorWidget::setConfig( const QVariantMap &config ) { - mDualView->parentFormValueChanged( attribute, newValue ); + mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ); + updateButtons(); } -bool QgsRelationEditorWidget::showLabel() const +void QgsRelationEditorWidget::setTitle( const QString &title ) { - return mShowLabel; + mRootCollapsibleGroupBox->setTitle( title ); } -void QgsRelationEditorWidget::setShowLabel( bool showLabel ) +void QgsRelationEditorWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) { - mShowLabel = showLabel; + Q_UNUSED( newRelation ); + Q_UNUSED( newFeature ); + + if ( ! mRelation.isValid() ) + return; - updateTitle(); + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); } -void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid ) +void QgsRelationEditorWidget::afterSetRelationFeature() { - if ( mRelation.referencingLayer()->isEditable() ) - { - QAction *qAction = nullptr; + mToggleEditingButton->setEnabled( false ); - qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) ); - connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } ); + if ( ! mRelation.isValid() ) + return; - qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) ); - connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } ); + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); + + QgsVectorLayer *vl = mRelation.referencingLayer(); + bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; + if ( canChangeAttributes && !vl->readOnly() ) + { + mToggleEditingButton->setEnabled( true ); + updateButtons(); + } + else + { + mToggleEditingButton->setEnabled( false ); } -} -void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool ) -{ - QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading) + // If it is already initialized, it has been set visible before and the currently shown feature is changing + // and the widget needs updating - mapCanvas->setMapTool( mapTool ); - mapCanvas->window()->raise(); - mapCanvas->activateWindow(); - mapCanvas->setFocus(); - connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated ); + if ( mVisible ) + { + QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature ); + initDualView( mRelation.referencingLayer(), myRequest ); + } } -void QgsRelationEditorWidget::unsetMapTool() +void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) { - QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas(); + Q_UNUSED( newRelation ); + Q_UNUSED( newNmRelation ); - // this will call mapToolDeactivated - mapCanvas->unsetMapTool( mMapToolDigitize ); + if ( mRelation.isValid() ) + { + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); + } - disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed ); - disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted ); + if ( mNmRelation.isValid() ) + { + disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); + } } -void QgsRelationEditorWidget::updateTitle() +void QgsRelationEditorWidget::afterSetRelations() { - if ( mShowLabel && !mLabel.isEmpty() ) + if ( !mRelation.isValid() ) + return; + + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); + + if ( mNmRelation.isValid() ) { - setTitle( mLabel ); + connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons ); + connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons ); } - else if ( mShowLabel && mRelation.isValid() ) + + QgsVectorLayer *vl = mRelation.referencingLayer(); + bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; + if ( canChangeAttributes && !vl->readOnly() ) { - setTitle( mRelation.name() ); + mToggleEditingButton->setEnabled( true ); } else { - setTitle( QString() ); + mToggleEditingButton->setEnabled( false ); } + + updateButtons(); } -QgsFeature QgsRelationEditorWidget::feature() const + +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SIP_RUN +QgsRelationEditorWidgetFactory::QgsRelationEditorWidgetFactory() { - return mFeature; + } -void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e ) +QString QgsRelationEditorWidgetFactory::type() const { - if ( e->key() == Qt::Key_Escape ) - { - unsetMapTool(); - } + return QStringLiteral( "relation_editor" ); } -void QgsRelationEditorWidget::mapToolDeactivated() +QString QgsRelationEditorWidgetFactory::name() const { - window()->setVisible( true ); - window()->raise(); - window()->activateWindow(); + return QStringLiteral( "Relation Editor" ); +} - if ( mEditorContext.mainMessageBar() && mMessageBarItem ) +QgsRelationWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const +{ + return new QgsRelationEditorWidget( config, parent ); +} + + +QgsRelationConfigWidget *QgsRelationEditorWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const +{ + return static_cast( new QgsRelationEditorConfigWidget( relation, parent ) ); +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SIP_RUN +QgsRelationEditorConfigWidget::QgsRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent ) + : QgsRelationConfigWidget( relation, parent ) +{ + setupUi( this ); +} + +QVariantMap QgsRelationEditorConfigWidget::config() +{ + QgsRelationEditorWidget::Buttons buttons; + buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() ); + buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() ); + + return QVariantMap( { - mEditorContext.mainMessageBar()->popWidget( mMessageBarItem ); - } - mMessageBarItem = nullptr; + {"buttons", qgsFlagValueToKeys( buttons )}, + } ); +} + +void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config ) +{ + const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ); + + mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) ); + mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) ); + mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) ); + mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) ); + mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) ); + mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) ); + mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) ); } +#endif diff --git a/src/gui/qgsrelationeditorwidget.h b/src/gui/qgsrelationeditorwidget.h index c172d033b1bd..90ca849595bf 100644 --- a/src/gui/qgsrelationeditorwidget.h +++ b/src/gui/qgsrelationeditorwidget.h @@ -13,8 +13,8 @@ * * ***************************************************************************/ -#ifndef QGSRELATIONEDITOR_H -#define QGSRELATIONEDITOR_H +#ifndef QGSRELATIONEDITORWIDGET_H +#define QGSRELATIONEDITORWIDGET_H #include #include @@ -22,6 +22,12 @@ #include #include "qobjectuniqueptr.h" +#include "ui_qgsrelationeditorconfigwidgetbase.h" + +#include "qgsrelationwidget.h" +#include "qgsrelationconfigwidget.h" +#include "qgsrelationwidgetfactory.h" +#include "qgsrelationeditorwidget.h" #include "qobjectuniqueptr.h" #include "qgsattributeeditorcontext.h" #include "qgscollapsiblegroupbox.h" @@ -77,35 +83,44 @@ class QgsFilteredSelectionManager : public QgsVectorLayerSelectionManager /** + * The default relation widget in QGIS. Successor of the now deprecated {\see QgsRelationEditorWidget}. * \ingroup gui * \class QgsRelationEditorWidget + * \since QGIS 3.18 */ -class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox +class GUI_EXPORT QgsRelationEditorWidget : public QgsRelationWidget { -#ifdef SIP_RUN - SIP_CONVERT_TO_SUBCLASS_CODE - if ( qobject_cast( sipCpp ) ) - sipType = sipType_QgsRelationEditorWidget; - else - sipType = NULL; - SIP_END -#endif - - - Q_OBJECT Q_PROPERTY( QgsDualView::ViewMode viewMode READ viewMode WRITE setViewMode ) - Q_PROPERTY( bool showLabel READ showLabel WRITE setShowLabel ) - Q_PROPERTY( QgsAttributeEditorRelation::Buttons visibleButtons READ visibleButtons WRITE setVisibleButtons ) + Q_PROPERTY( Buttons visibleButtons READ visibleButtons WRITE setVisibleButtons ) public: + /** + * Possible buttons shown in the relation editor + */ + enum Button + { + Link = 1 << 1, //!< Link button + Unlink = 1 << 2, //!< Unlink button + SaveChildEdits = 1 << 3, //!< Save child edits button + AddChildFeature = 1 << 4, //!< Add child feature (as in some projects we only want to allow linking/unlinking existing features) + DuplicateChildFeature = 1 << 5, //!< Duplicate child feature + DeleteChildFeature = 1 << 6, //!< Delete child feature button + ZoomToChildFeature = 1 << 7, //!< Zoom to child feature + AllButtons = Link | Unlink | SaveChildEdits | AddChildFeature | DuplicateChildFeature | DeleteChildFeature | ZoomToChildFeature //!< All buttons + }; + Q_ENUM( Button ) + Q_DECLARE_FLAGS( Buttons, Button ) + Q_FLAG( Buttons ) /** + * Constructor + * \param config widget configuration * \param parent parent widget */ - QgsRelationEditorWidget( QWidget *parent SIP_TRANSFERTHIS = nullptr ); + QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); //! Define the view mode for the dual view void setViewMode( QgsDualView::ViewMode mode ); @@ -113,27 +128,6 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox //! Gets the view mode for the dual view QgsDualView::ViewMode viewMode() {return mViewMode;} - /** - * Sets the \a relation and the \a feature - */ - void setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ); - - /** - * Set the relation(s) for this widget - * If only one relation is set, it will act as a simple 1:N relation widget - * If both relations are set, it will act as an N:M relation widget - * inserting and deleting entries on the intermediate table as required. - * - * \param relation Relation referencing the edited table - * \param nmrelation Optional reference from the referencing table to a 3rd N:M table - */ - void setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ); - - /** - * Sets the \a feature being edited and updates the UI unless \a update is set to FALSE - */ - void setFeature( const QgsFeature &feature, bool update = true ); - /** * Sets the editor \a context * \note if context cadDockWidget is null, it won't be possible to digitize @@ -141,178 +135,56 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox */ void setEditorContext( const QgsAttributeEditorContext &context ); - /** - * Returns the attribute editor context. - * \since QGIS 3.14 - */ - QgsAttributeEditorContext editorContext( ) const; - - /** - * The feature selection manager is responsible for the selected features - * which are currently being edited. - */ - QgsIFeatureSelectionManager *featureSelectionManager(); - - /** - * Defines if a title label should be shown for this widget. - * - * \since QGIS 2.18 - */ - bool showLabel() const; - - /** - * Defines if a title label should be shown for this widget. - * - * \since QGIS 2.18 - */ - void setShowLabel( bool showLabel ); - - /** - * Determines if the "link feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showLinkButton() const SIP_DEPRECATED; - - /** - * Determines if the "link feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowLinkButton( bool showLinkButton ) SIP_DEPRECATED; - - /** - * Determines if the "unlink feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showUnlinkButton() const SIP_DEPRECATED; - - /** - * Determines if the "unlink feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowUnlinkButton( bool showUnlinkButton ) SIP_DEPRECATED; - - /** - * Determines if the "Save child layer edits" button should be shown - * \since QGIS 3.14 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowSaveChildEditsButton( bool showChildEdits ) SIP_DEPRECATED; - - /** - * Determines if the "Save child layer edits" button should be shown - * \since QGIS 3.14 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showSaveChildEditsButton() const SIP_DEPRECATED; - /** * Defines the buttons which are shown - * \since QGIS 3.16 */ - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ); + void setVisibleButtons( const Buttons &buttons ); /** * Returns the buttons which are shown - * \since QGIS 3.16 - */ - QgsAttributeEditorRelation::Buttons visibleButtons() const; - - /** - * Determines the force suppress form popup status that is configured for this widget - * \since QGIS 3.16 - */ - bool forceSuppressFormPopup() const; - - /** - * Sets force suppress form popup status with \a forceSuppressFormPopup - * configured for this widget - * \since QGIS 3.16 */ - void setForceSuppressFormPopup( bool forceSuppressFormPopup ); + Buttons visibleButtons() const; /** - * Determines the relation id of the second relation involved in an N:M relation. - * \since QGIS 3.16 - */ - QVariant nmRelationId() const; - - /** - * Sets \a nmRelationId for the relation id of the second relation involved in an N:M relation. - * If it's empty, then it's considered as a 1:M relationship. - * \since QGIS 3.16 - */ - void setNmRelationId( const QVariant &nmRelationId = QVariant() ); - - /** - * Determines the label of this element - * \since QGIS 3.16 + * Returns the current configuration */ - QString label() const; + QVariantMap config() const override; /** - * Sets \a label for this element - * If it's empty it takes the relation id as label - * \since QGIS 3.16 + * Defines the current configuration */ - void setLabel( const QString &label = QString() ); + void setConfig( const QVariantMap &config ) override; /** - * Returns the widget's current feature - * - * \since QGIS 3.14 - */ - QgsFeature feature() const; + * Sets the title of the root groupbox + */ + void setTitle( const QString &title ) override; public slots: - - /** - * Called when an \a attribute value in the parent widget has changed to \a newValue - * - * \since QGIS 3.14 - */ - void parentFormValueChanged( const QString &attribute, const QVariant &newValue ); + void parentFormValueChanged( const QString &attribute, const QVariant &newValue ) override; private slots: void setViewMode( int mode ) {setViewMode( static_cast( mode ) );} void updateButtons(); - void addFeature( const QgsGeometry &geometry = QgsGeometry() ); void addFeatureGeometry(); - void duplicateFeature(); - void linkFeature(); - void deleteFeature( QgsFeatureId featureid = QgsFeatureId() ); - void deleteSelectedFeatures(); - void unlinkFeature( QgsFeatureId featureid = QgsFeatureId() ); - void unlinkSelectedFeatures(); - void zoomToSelectedFeatures(); - void saveEdits(); void toggleEditing( bool state ); void onCollapsedStateChanged( bool collapsed ); void showContextMenu( QgsActionMenu *menu, QgsFeatureId fid ); void mapToolDeactivated(); void onKeyPressed( QKeyEvent *e ); void onDigitizingCompleted( const QgsFeature &feature ); - void onLinkFeatureDlgAccepted(); private: - void updateUi(); + void updateUi() override; void initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request ); void setMapTool( QgsMapTool *mapTool ); void unsetMapTool(); - void updateTitle(); + QgsCollapsibleGroupBox *mRootCollapsibleGroupBox = nullptr; QgsDualView *mDualView = nullptr; QPointer mMessageBarItem; QgsDualView::ViewMode mViewMode = QgsDualView::AttributeEditor; - QgsVectorLayerSelectionManager *mFeatureSelectionMgr = nullptr; - QgsAttributeEditorContext mEditorContext; - QgsRelation mRelation; - QgsRelation mNmRelation; - QgsFeature mFeature; QToolButton *mToggleEditingButton = nullptr; QToolButton *mSaveEditsButton = nullptr; @@ -329,29 +201,73 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox QObjectUniquePtr mMapToolDigitize; QButtonGroup *mViewModeButtonGroup = nullptr; - QgsAttributeEditorRelation::Buttons mButtonsVisibility = QgsAttributeEditorRelation::Button::AllButtons; - bool mShowLabel = true; - bool mVisible = false; - bool mLayerInSameTransactionGroup = false; + Buttons mButtonsVisibility = Button::AllButtons; + bool mVisible = true; + + void beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature ) override; + void afterSetRelationFeature() override; + void beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation ) override; + void afterSetRelations() override; +}; + + +#ifndef SIP_RUN + +/** + * Factory class for creating a relation editor widget and the respective config widget. + * \ingroup gui + * \class QgsRelationEditorWidgetFactory + * \note not available in Python bindings + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsRelationEditorWidgetFactory : public QgsRelationWidgetFactory +{ + public: + QgsRelationEditorWidgetFactory(); + + QString type() const override; + + QString name() const override; + + QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; + + QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; + +}; + +/** + * \ingroup gui + * \class QgsRelationEditorConfigWidget + * Creates a new configuration widget for the relation editor widget + * \since QGIS 3.18 + */ +class QgsRelationEditorConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase +{ + public: - bool mForceSuppressFormPopup = false; - QVariant mNmRelationId; - QString mLabel; + /** + * Create a new configuration widget + * + * \param relation The relation for which the configuration dialog will be created + * \param parent A parent widget + */ + explicit QgsRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); /** - * Deletes the features - * \param featureids features to delete - * \since QGIS 3.00 + * \brief Create a configuration from the current GUI state + * + * \returns A widget configuration */ - void deleteFeatures( const QgsFeatureIds &featureids ); + QVariantMap config(); /** - * Unlinks the features - * \param featureids features to unlink - * \since QGIS 3.00 + * \brief Update the configuration widget to represent the given configuration. + * + * \param config The configuration which should be represented by this widget */ - void unlinkFeatures( const QgsFeatureIds &featureids ); -}; + void setConfig( const QVariantMap &config ); +}; +#endif -#endif // QGSRELATIONEDITOR_H +#endif // QGSRELATIONEDITORWIDGET_H diff --git a/src/gui/qgsrelationwidget.h b/src/gui/qgsrelationwidget.h index 184ab6fd56c7..c92f16656be9 100644 --- a/src/gui/qgsrelationwidget.h +++ b/src/gui/qgsrelationwidget.h @@ -35,7 +35,7 @@ #ifdef SIP_RUN // this is needed for the "convert to subclass" code below to compile % ModuleHeaderCode -#include "qgsbasicrelationwidget.h" +#include "qgsrelationeditorwidget.h" % End #endif @@ -58,8 +58,8 @@ class GUI_EXPORT QgsRelationWidget : public QWidget #ifdef SIP_RUN SIP_CONVERT_TO_SUBCLASS_CODE - if ( qobject_cast( sipCpp ) ) - sipType = sipType_QgsBasicRelationWidget; + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsRelationEditorWidget; else sipType = 0; SIP_END diff --git a/src/gui/qgsrelationwidgetregistry.cpp b/src/gui/qgsrelationwidgetregistry.cpp index d7fa988b2d96..187379cfea83 100644 --- a/src/gui/qgsrelationwidgetregistry.cpp +++ b/src/gui/qgsrelationwidgetregistry.cpp @@ -18,11 +18,11 @@ #include "qgsrelationwidgetregistry.h" #include "qgsrelationwidget.h" #include "qgsrelationconfigwidget.h" -#include "qgsbasicrelationwidget.h" +#include "qgsrelationeditorwidget.h" QgsRelationWidgetRegistry::QgsRelationWidgetRegistry() { - addRelationWidget( new QgsBasicRelationWidgetFactory() ); + addRelationWidget( new QgsRelationEditorWidgetFactory() ); } QgsRelationWidgetRegistry::~QgsRelationWidgetRegistry() @@ -44,8 +44,8 @@ void QgsRelationWidgetRegistry::addRelationWidget( QgsRelationWidgetFactory *wid void QgsRelationWidgetRegistry::removeRelationWidget( const QString &widgetType ) { - // protect the basic widget from removing, so the user has at least one relation widget type - if ( dynamic_cast( mRelationWidgetFactories.value( widgetType ) ) ) + // protect the relation editor widget from removing, so the user has at least one relation widget type + if ( dynamic_cast( mRelationWidgetFactories.value( widgetType ) ) ) return; mRelationWidgetFactories.remove( widgetType ); diff --git a/src/gui/qgsrelationwidgetregistry.h b/src/gui/qgsrelationwidgetregistry.h index 0dff6ed2e890..1614a824abec 100644 --- a/src/gui/qgsrelationwidgetregistry.h +++ b/src/gui/qgsrelationwidgetregistry.h @@ -28,7 +28,7 @@ class QgsRelationConfigWidget; /** * Keeps track of the registered relations widgets. New widgets can be registered, old ones deleted. - * The default {\see QgsBasicRelationWidget} is protected from removing. + * The default {\see QgsRelationEditorWidget} is protected from removing. * \ingroup gui * \class QgsRelationWidgetRegistry * \since QGIS 3.18 From bb7f411304178752c15029a15c0249fedf702b8a Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 7 Jan 2021 15:42:21 +0200 Subject: [PATCH 16/23] Rename to Abstract prefix --- ...=> qgsabstractrelationeditorwidget.sip.in} | 125 ++++++++++++++++- .../qgsrelationconfigwidget.sip.in | 77 ----------- .../qgsrelationeditorwidget.sip.in | 41 +++++- .../qgsrelationwidgetfactory.sip.in | 75 ---------- .../qgsrelationwidgetregistry.sip.in | 8 +- python/gui/gui_auto.sip | 4 +- src/gui/CMakeLists.txt | 8 +- .../qgsattributewidgetedit.cpp | 3 +- .../qgsattributewidgetedit.h | 4 +- .../qgsrelationwidgetwrapper.cpp | 3 +- .../editorwidgets/qgsrelationwidgetwrapper.h | 4 +- ...pp => qgsabstractrelationeditorwidget.cpp} | 94 ++++++++----- ...et.h => qgsabstractrelationeditorwidget.h} | 129 ++++++++++++++++-- src/gui/qgsrelationconfigwidget.cpp | 37 ----- src/gui/qgsrelationconfigwidget.h | 87 ------------ src/gui/qgsrelationeditorwidget.cpp | 69 +++++----- src/gui/qgsrelationeditorwidget.h | 59 ++++---- src/gui/qgsrelationwidgetfactory.cpp | 24 ---- src/gui/qgsrelationwidgetfactory.h | 80 ----------- src/gui/qgsrelationwidgetregistry.cpp | 10 +- src/gui/qgsrelationwidgetregistry.h | 15 +- 21 files changed, 427 insertions(+), 529 deletions(-) rename python/gui/auto_generated/{qgsrelationwidget.sip.in => qgsabstractrelationeditorwidget.sip.in} (64%) delete mode 100644 python/gui/auto_generated/qgsrelationconfigwidget.sip.in delete mode 100644 python/gui/auto_generated/qgsrelationwidgetfactory.sip.in rename src/gui/{qgsrelationwidget.cpp => qgsabstractrelationeditorwidget.cpp} (85%) rename src/gui/{qgsrelationwidget.h => qgsabstractrelationeditorwidget.h} (67%) delete mode 100644 src/gui/qgsrelationconfigwidget.cpp delete mode 100644 src/gui/qgsrelationconfigwidget.h delete mode 100644 src/gui/qgsrelationwidgetfactory.cpp delete mode 100644 src/gui/qgsrelationwidgetfactory.h diff --git a/python/gui/auto_generated/qgsrelationwidget.sip.in b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in similarity index 64% rename from python/gui/auto_generated/qgsrelationwidget.sip.in rename to python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in index 2194e4fe00da..5e7a4b07219c 100644 --- a/python/gui/auto_generated/qgsrelationwidget.sip.in +++ b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in @@ -1,7 +1,7 @@ /************************************************************************ * This file has been generated automatically from * * * - * src/gui/qgsrelationwidget.h * + * src/gui/qgsabstractrelationeditorwidget.h * * * * Do not edit manually ! Edit header and run scripts/sipify.pl again * ************************************************************************/ @@ -16,8 +16,7 @@ %End - -class QgsRelationWidget : QWidget +class QgsAbstractRelationEditorWidget : QWidget { %Docstring Base class to build new relation widgets. @@ -26,7 +25,7 @@ Base class to build new relation widgets. %End %TypeHeaderCode -#include "qgsrelationwidget.h" +#include "qgsabstractrelationeditorwidget.h" %End %ConvertToSubClassCode if ( qobject_cast( sipCpp ) ) @@ -37,7 +36,7 @@ Base class to build new relation widgets. public: - QgsRelationWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); + QgsAbstractRelationEditorWidget( const QVariantMap &config, QWidget *parent /TransferThis/ = 0 ); %Docstring Constructor %End @@ -228,10 +227,124 @@ Unlinks the features with ``fids`` }; + +class QgsAbstractRelationEditorConfigWidget : QWidget +{ +%Docstring +This class should be subclassed for every configurable relation widget type. + +It implements the GUI configuration widget and transforms this to/from a configuration. + +It will only be instantiated by {:py:class:`QgsAbstractRelationEditorWidgetFactory`} + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsabstractrelationeditorwidget.h" +%End + public: + + explicit QgsAbstractRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent /TransferThis/ ); +%Docstring +Create a new configuration widget + +:param relation: The relation for which the configuration dialog will be created +:param parent: A parent widget +%End + + virtual QVariantMap config() = 0; +%Docstring +Create a configuration from the current GUI state + +:return: A widget configuration +%End + + virtual void setConfig( const QVariantMap &config ) = 0; +%Docstring +Update the configuration widget to represent the given configuration. + +:param config: The configuration which should be represented by this widget +%End + + QgsVectorLayer *layer(); +%Docstring +Returns the layer for which this configuration widget applies + +:return: The layer +%End + + QgsRelation relation() const; +%Docstring +Returns the relation for which this configuration widget applies + +:return: The relation +%End + + +}; + + + + +class QgsAbstractRelationEditorWidgetFactory +{ +%Docstring +Factory class for creating relation widgets and their corresponding config widgets + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsabstractrelationeditorwidget.h" +%End + public: + + QgsAbstractRelationEditorWidgetFactory(); +%Docstring +Creates a new relation widget factory with given ``name`` +%End + + virtual ~QgsAbstractRelationEditorWidgetFactory(); + + virtual QString type() const = 0; +%Docstring +Returns the machine readable identifier name of this widget type +%End + + virtual QString name() const = 0; +%Docstring +Returns the human readable identifier name of this widget type +%End + + virtual QgsAbstractRelationEditorWidget *create( const QVariantMap &config, QWidget *parent = 0 ) const = 0 /Factory/; +%Docstring +Override this in your implementation. +Create a new relation widget. Call :py:func:`QgsEditorWidgetRegistry.create()` +instead of calling this method directly. + +:param config: The widget configuration to build the widget with +:param parent: The parent for the wrapper class and any created widget. + +:return: A new widget wrapper +%End + + virtual QgsAbstractRelationEditorConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 /Factory/; +%Docstring +Override this in your implementation. +Create a new configuration widget for this widget type. + +:param relation: The relation for which the widget will be created +:param parent: The parent widget of the created config widget + +:return: A configuration widget +%End +}; + /************************************************************************ * This file has been generated automatically from * * * - * src/gui/qgsrelationwidget.h * + * src/gui/qgsabstractrelationeditorwidget.h * * * * Do not edit manually ! Edit header and run scripts/sipify.pl again * ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationconfigwidget.sip.in b/python/gui/auto_generated/qgsrelationconfigwidget.sip.in deleted file mode 100644 index 3783801b82be..000000000000 --- a/python/gui/auto_generated/qgsrelationconfigwidget.sip.in +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsrelationconfigwidget.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - - - - - -class QgsRelationConfigWidget : QWidget -{ -%Docstring -This class should be subclassed for every configurable relation widget type. - -It implements the GUI configuration widget and transforms this to/from a configuration. - -It will only be instantiated by {:py:class:`QgsRelationWidgetFactory`} - -.. versionadded:: 3.18 -%End - -%TypeHeaderCode -#include "qgsrelationconfigwidget.h" -%End - public: - - explicit QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent /TransferThis/ ); -%Docstring -Create a new configuration widget - -:param relation: The relation for which the configuration dialog will be created -:param parent: A parent widget -%End - - virtual QVariantMap config() = 0; -%Docstring -Create a configuration from the current GUI state - -:return: A widget configuration -%End - - virtual void setConfig( const QVariantMap &config ) = 0; -%Docstring -Update the configuration widget to represent the given configuration. - -:param config: The configuration which should be represented by this widget -%End - - QgsVectorLayer *layer(); -%Docstring -Returns the layer for which this configuration widget applies - -:return: The layer -%End - - QgsRelation relation() const; -%Docstring -Returns the relation for which this configuration widget applies - -:return: The relation -%End - - -}; - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsrelationconfigwidget.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in index 64eac5bcd43a..51fad5aa5ca3 100644 --- a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in @@ -21,7 +21,7 @@ -class QgsRelationEditorWidget : QgsRelationWidget +class QgsRelationEditorWidget : QgsAbstractRelationEditorWidget { %Docstring The default relation widget in QGIS. Successor of the now deprecated {:py:class:`QgsRelationEditorWidget`}. @@ -111,6 +111,45 @@ Sets the title of the root groupbox }; +class QgsRelationEditorConfigWidget : QgsAbstractRelationEditorConfigWidget +{ +%Docstring +Creates a new configuration widget for the relation editor widget + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgsrelationeditorwidget.h" +%End + public: + + explicit QgsRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent /TransferThis/ ); +%Docstring +Create a new configuration widget + +:param relation: The relation for which the configuration dialog will be created +:param parent: A parent widget +%End + + QVariantMap config(); +%Docstring +Create a configuration from the current GUI state + +:return: A widget configuration +%End + + void setConfig( const QVariantMap &config ); +%Docstring +Update the configuration widget to represent the given configuration. + +:param config: The configuration which should be represented by this widget +%End + +}; + + + /************************************************************************ * This file has been generated automatically from * diff --git a/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in b/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in deleted file mode 100644 index 01bb9b79d1a7..000000000000 --- a/python/gui/auto_generated/qgsrelationwidgetfactory.sip.in +++ /dev/null @@ -1,75 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsrelationwidgetfactory.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - - - -class QgsRelationWidgetFactory -{ -%Docstring -Factory class for creating relation widgets and their corresponding config widgets - -.. versionadded:: 3.18 -%End - -%TypeHeaderCode -#include "qgsrelationwidgetfactory.h" -%End - public: - - QgsRelationWidgetFactory(); -%Docstring -Creates a new relation widget factory with given ``name`` -%End - - virtual ~QgsRelationWidgetFactory(); - - virtual QString type() const = 0; -%Docstring -Returns the machine readable identifier name of this widget type -%End - - virtual QString name() const = 0; -%Docstring -Returns the human readable identifier name of this widget type -%End - - virtual QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = 0 ) const = 0 /Factory/; -%Docstring -Override this in your implementation. -Create a new relation widget. Call :py:func:`QgsEditorWidgetRegistry.create()` -instead of calling this method directly. - -:param config: The widget configuration to build the widget with -:param parent: The parent for the wrapper class and any created widget. - -:return: A new widget wrapper -%End - - virtual QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 /Factory/; -%Docstring -Override this in your implementation. -Create a new configuration widget for this widget type. - -:param relation: The relation for which the widget will be created -:param parent: The parent widget of the created config widget - -:return: A configuration widget -%End -}; - - - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/gui/qgsrelationwidgetfactory.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in index 297afc7a10de..88636f7548d5 100644 --- a/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in +++ b/python/gui/auto_generated/qgsrelationwidgetregistry.sip.in @@ -32,7 +32,7 @@ Constructor ~QgsRelationWidgetRegistry(); - void addRelationWidget( QgsRelationWidgetFactory *widgetFactory /Transfer/ ); + void addRelationWidget( QgsAbstractRelationEditorWidgetFactory *widgetFactory /Transfer/ ); %Docstring Adds a new registered relation ``widgetFactory`` %End @@ -47,12 +47,12 @@ Removes a registered relation widget with given ``widgetType`` Returns a list of names of registered relation widgets %End - QMap factories() const; + QMap factories() const; %Docstring Gets access to all registered factories %End - QgsRelationWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = 0 ) const /TransferBack/; + QgsAbstractRelationEditorWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = 0 ) const /TransferBack/; %Docstring Create a relation widget of a given type for a given field. @@ -61,7 +61,7 @@ Create a relation widget of a given type for a given field. :param parent: %End - QgsRelationConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = 0 ) const /TransferBack/; + QgsAbstractRelationEditorConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = 0 ) const /TransferBack/; %Docstring Creates a configuration widget diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 17fd9faeea7d..ba2f91b969b2 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -181,9 +181,7 @@ %Include auto_generated/qgsrasterpyramidsoptionswidget.sip %Include auto_generated/qgsratiolockbutton.sip %Include auto_generated/qgsrelationeditorwidget.sip -%Include auto_generated/qgsrelationconfigwidget.sip -%Include auto_generated/qgsrelationwidget.sip -%Include auto_generated/qgsrelationwidgetfactory.sip +%Include auto_generated/qgsabstractrelationeditorwidget.sip %Include auto_generated/qgsrelationwidgetregistry.sip %Include auto_generated/qgsrubberband.sip %Include auto_generated/qgsscalecombobox.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index be34a31e7d10..41e7b8248525 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -566,9 +566,7 @@ set(QGIS_GUI_SRCS qgsrasterlayersaveasdialog.cpp qgsrasterpyramidsoptionswidget.cpp qgsrelationeditorwidget.cpp - qgsrelationconfigwidget.cpp - qgsrelationwidget.cpp - qgsrelationwidgetfactory.cpp + qgsabstractrelationeditorwidget.cpp qgsrelationwidgetregistry.cpp qgsrubberband.cpp qgsscalecombobox.cpp @@ -816,9 +814,7 @@ set(QGIS_GUI_HDRS qgsrasterpyramidsoptionswidget.h qgsratiolockbutton.h qgsrelationeditorwidget.h - qgsrelationconfigwidget.h - qgsrelationwidget.h - qgsrelationwidgetfactory.h + qgsabstractrelationeditorwidget.h qgsrelationwidgetregistry.h qgsrubberband.h qgsscalecombobox.h diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp index c9a5b8571e73..4ae2e7a5e642 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.cpp +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.cpp @@ -16,7 +16,6 @@ #include "qgsattributewidgetedit.h" #include "qgsattributesformproperties.h" #include "qgsrelationwidgetregistry.h" -#include "qgsrelationconfigwidget.h" QgsAttributeWidgetEdit::QgsAttributeWidgetEdit( QTreeWidgetItem *item, QWidget *parent ) @@ -94,7 +93,7 @@ QgsAttributeWidgetRelationEditWidget::QgsAttributeWidgetRelationEditWidget( QWid { setupUi( this ); - QMapIterator it( QgsGui::relationWidgetRegistry()->factories() ); + QMapIterator it( QgsGui::relationWidgetRegistry()->factories() ); while ( it.hasNext() ) { diff --git a/src/gui/attributeformconfig/qgsattributewidgetedit.h b/src/gui/attributeformconfig/qgsattributewidgetedit.h index d5dba30671d8..b93dcda3175c 100644 --- a/src/gui/attributeformconfig/qgsattributewidgetedit.h +++ b/src/gui/attributeformconfig/qgsattributewidgetedit.h @@ -29,7 +29,7 @@ #include "qgis_gui.h" class QTreeWidgetItem; -class QgsRelationConfigWidget; +class QgsAbstractRelationEditorConfigWidget; /** * Widget to edit the configuration (tab or group box, any field or relation, QML, …) of a form item @@ -75,7 +75,7 @@ class GUI_EXPORT QgsAttributeWidgetRelationEditWidget : public QWidget, private void setNmRelationId( const QVariant &auserData = QVariant() ); QMetaObject::Connection mWidgetTypeComboBoxConnection; - QgsRelationConfigWidget *mConfigWidget = nullptr; + QgsAbstractRelationEditorConfigWidget *mConfigWidget = nullptr; }; #endif // QGSATTRIBUTEWIDGETEDIT_H diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp index 8bb1bda9d215..a75fb8ce2c7b 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp @@ -19,6 +19,7 @@ #include "qgsattributeeditorcontext.h" #include "qgsproject.h" #include "qgsrelationmanager.h" +#include "qgsabstractrelationeditorwidget.h" #include "qgsrelationwidgetregistry.h" #include "qgsgui.h" #include @@ -146,7 +147,7 @@ void QgsRelationWidgetWrapper::setShowLabel( bool showLabel ) void QgsRelationWidgetWrapper::initWidget( QWidget *editor ) { - QgsRelationWidget *w = qobject_cast( editor ); + QgsAbstractRelationEditorWidget *w = qobject_cast( editor ); // if the editor cannot be cast to relation editor, insert a new one if ( !w ) diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h index 8416157ace69..21f0e4609d71 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.h +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.h @@ -20,7 +20,7 @@ #include "qgis_sip.h" #include "qgis_gui.h" -class QgsRelationWidget; +class QgsAbstractRelationEditorWidget; /** * \ingroup gui @@ -219,7 +219,7 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper QgsRelation mRelation; QgsRelation mNmRelation; QString mRelationEditorId; - QgsRelationWidget *mWidget = nullptr; + QgsAbstractRelationEditorWidget *mWidget = nullptr; }; #endif // QGSRELATIONWIDGETWRAPPER_H diff --git a/src/gui/qgsrelationwidget.cpp b/src/gui/qgsabstractrelationeditorwidget.cpp similarity index 85% rename from src/gui/qgsrelationwidget.cpp rename to src/gui/qgsabstractrelationeditorwidget.cpp index 0c5c4976bcf1..7772ada94303 100644 --- a/src/gui/qgsrelationwidget.cpp +++ b/src/gui/qgsabstractrelationeditorwidget.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - qgsrelationwidget.h + qgsabstractrelationeditorwidget.cpp ---------------------- begin : October 2020 copyright : (C) 2020 by Ivan Ivanov @@ -15,7 +15,7 @@ * * ***************************************************************************/ -#include "qgsrelationwidget.h" +#include "qgsabstractrelationeditorwidget.h" #include "qgsapplication.h" #include "qgsdistancearea.h" @@ -44,13 +44,13 @@ #include -QgsRelationWidget::QgsRelationWidget( const QVariantMap &config, QWidget *parent ) +QgsAbstractRelationEditorWidget::QgsAbstractRelationEditorWidget( const QVariantMap &config, QWidget *parent ) : QWidget( parent ) { Q_UNUSED( config ); } -void QgsRelationWidget::setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ) +void QgsAbstractRelationEditorWidget::setRelationFeature( const QgsRelation &relation, const QgsFeature &feature ) { beforeSetRelationFeature( relation, feature ); @@ -64,7 +64,7 @@ void QgsRelationWidget::setRelationFeature( const QgsRelation &relation, const Q updateUi(); } -void QgsRelationWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ) +void QgsAbstractRelationEditorWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation ) { beforeSetRelations( relation, nmrelation ); @@ -106,22 +106,22 @@ void QgsRelationWidget::setRelations( const QgsRelation &relation, const QgsRela updateUi(); } -void QgsRelationWidget::setEditorContext( const QgsAttributeEditorContext &context ) +void QgsAbstractRelationEditorWidget::setEditorContext( const QgsAttributeEditorContext &context ) { mEditorContext = context; } -QgsAttributeEditorContext QgsRelationWidget::editorContext() const +QgsAttributeEditorContext QgsAbstractRelationEditorWidget::editorContext() const { return mEditorContext; } -QgsIFeatureSelectionManager *QgsRelationWidget::featureSelectionManager() +QgsIFeatureSelectionManager *QgsAbstractRelationEditorWidget::featureSelectionManager() { return mFeatureSelectionMgr; } -void QgsRelationWidget::setFeature( const QgsFeature &feature, bool update ) +void QgsAbstractRelationEditorWidget::setFeature( const QgsFeature &feature, bool update ) { mFeature = feature; @@ -131,51 +131,51 @@ void QgsRelationWidget::setFeature( const QgsFeature &feature, bool update ) updateUi(); } -void QgsRelationWidget::setNmRelationId( const QVariant &nmRelationId ) +void QgsAbstractRelationEditorWidget::setNmRelationId( const QVariant &nmRelationId ) { mNmRelationId = nmRelationId; } -QVariant QgsRelationWidget::nmRelationId() const +QVariant QgsAbstractRelationEditorWidget::nmRelationId() const { return mNmRelationId; } -QString QgsRelationWidget::label() const +QString QgsAbstractRelationEditorWidget::label() const { return mLabel; } -void QgsRelationWidget::setLabel( const QString &label ) +void QgsAbstractRelationEditorWidget::setLabel( const QString &label ) { mLabel = label; updateTitle(); } -bool QgsRelationWidget::showLabel() const +bool QgsAbstractRelationEditorWidget::showLabel() const { return mShowLabel; } -void QgsRelationWidget::setShowLabel( bool showLabel ) +void QgsAbstractRelationEditorWidget::setShowLabel( bool showLabel ) { mShowLabel = showLabel; updateTitle(); } -void QgsRelationWidget::setForceSuppressFormPopup( bool forceSuppressFormPopup ) +void QgsAbstractRelationEditorWidget::setForceSuppressFormPopup( bool forceSuppressFormPopup ) { mForceSuppressFormPopup = forceSuppressFormPopup; } -bool QgsRelationWidget::forceSuppressFormPopup() const +bool QgsAbstractRelationEditorWidget::forceSuppressFormPopup() const { return mForceSuppressFormPopup; } -void QgsRelationWidget::updateTitle() +void QgsAbstractRelationEditorWidget::updateTitle() { if ( mShowLabel && !mLabel.isEmpty() ) { @@ -191,12 +191,12 @@ void QgsRelationWidget::updateTitle() } } -QgsFeature QgsRelationWidget::feature() const +QgsFeature QgsAbstractRelationEditorWidget::feature() const { return mFeature; } -void QgsRelationWidget::toggleEditing( bool state ) +void QgsAbstractRelationEditorWidget::toggleEditing( bool state ) { if ( state ) { @@ -212,14 +212,14 @@ void QgsRelationWidget::toggleEditing( bool state ) } } -void QgsRelationWidget::saveEdits() +void QgsAbstractRelationEditorWidget::saveEdits() { mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() ); if ( mNmRelation.isValid() ) mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() ); } -void QgsRelationWidget::addFeature( const QgsGeometry &geometry ) +void QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &geometry ) { QgsAttributeMap keyAttrs; @@ -272,18 +272,18 @@ void QgsRelationWidget::addFeature( const QgsGeometry &geometry ) } } -void QgsRelationWidget::deleteFeature( const QgsFeatureId fid ) +void QgsAbstractRelationEditorWidget::deleteFeature( const QgsFeatureId fid ) { deleteFeatures( QgsFeatureIds() << fid ); } -void QgsRelationWidget::deleteSelectedFeatures() +void QgsAbstractRelationEditorWidget::deleteSelectedFeatures() { QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds(); deleteFeatures( selectedFids ); } -void QgsRelationWidget::deleteFeatures( const QgsFeatureIds &fids ) +void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids ) { bool deleteFeatures = true; @@ -397,7 +397,7 @@ void QgsRelationWidget::deleteFeatures( const QgsFeatureIds &fids ) } } -void QgsRelationWidget::linkFeature() +void QgsAbstractRelationEditorWidget::linkFeature() { QgsVectorLayer *layer = nullptr; @@ -412,11 +412,11 @@ void QgsRelationWidget::linkFeature() const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), mFeature ); selectionDlg->setWindowTitle( tr( "Link existing child features for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ) ); - connect( selectionDlg, &QDialog::accepted, this, &QgsRelationWidget::onLinkFeatureDlgAccepted ); + connect( selectionDlg, &QDialog::accepted, this, &QgsAbstractRelationEditorWidget::onLinkFeatureDlgAccepted ); selectionDlg->show(); } -void QgsRelationWidget::onLinkFeatureDlgAccepted() +void QgsAbstractRelationEditorWidget::onLinkFeatureDlgAccepted() { QgsFeatureSelectionDlg *selectionDlg = qobject_cast( sender() ); if ( mNmRelation.isValid() ) @@ -490,17 +490,17 @@ void QgsRelationWidget::onLinkFeatureDlgAccepted() updateUi(); } -void QgsRelationWidget::unlinkFeature( const QgsFeatureId fid ) +void QgsAbstractRelationEditorWidget::unlinkFeature( const QgsFeatureId fid ) { unlinkFeatures( QgsFeatureIds() << fid ); } -void QgsRelationWidget::unlinkSelectedFeatures() +void QgsAbstractRelationEditorWidget::unlinkSelectedFeatures() { unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); } -void QgsRelationWidget::unlinkFeatures( const QgsFeatureIds &fids ) +void QgsAbstractRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &fids ) { if ( mNmRelation.isValid() ) { @@ -567,7 +567,7 @@ void QgsRelationWidget::unlinkFeatures( const QgsFeatureIds &fids ) } } -void QgsRelationWidget::duplicateFeature() +void QgsAbstractRelationEditorWidget::duplicateFeature() { QgsVectorLayer *layer = mRelation.referencingLayer(); @@ -580,7 +580,7 @@ void QgsRelationWidget::duplicateFeature() } } -void QgsRelationWidget::zoomToSelectedFeatures() +void QgsAbstractRelationEditorWidget::zoomToSelectedFeatures() { QgsMapCanvas *c = mEditorContext.mapCanvas(); if ( !c ) @@ -591,3 +591,31 @@ void QgsRelationWidget::zoomToSelectedFeatures() mFeatureSelectionMgr->selectedFeatureIds() ); } + + +/////////////////////////////////////////////////////////////////////////////// + + +QgsAbstractRelationEditorConfigWidget::QgsAbstractRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent ) + : QWidget( parent ) + , mRelation( relation ) +{ +} + +QgsVectorLayer *QgsAbstractRelationEditorConfigWidget::layer() +{ + return mLayer; +} + +QgsRelation QgsAbstractRelationEditorConfigWidget::relation() const +{ + return mRelation; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +QgsAbstractRelationEditorWidgetFactory::QgsAbstractRelationEditorWidgetFactory() +{ +} diff --git a/src/gui/qgsrelationwidget.h b/src/gui/qgsabstractrelationeditorwidget.h similarity index 67% rename from src/gui/qgsrelationwidget.h rename to src/gui/qgsabstractrelationeditorwidget.h index c92f16656be9..df16741e7027 100644 --- a/src/gui/qgsrelationwidget.h +++ b/src/gui/qgsabstractrelationeditorwidget.h @@ -1,5 +1,5 @@ /*************************************************************************** - qgsrelationwidget.h + qgsabstractrelationeditorwidget.h ---------------------- begin : October 2020 copyright : (C) 2020 by Ivan Ivanov @@ -15,8 +15,8 @@ * * ***************************************************************************/ -#ifndef QGSRELATIONWIDGET_H -#define QGSRELATIONWIDGET_H +#ifndef QGSABSTRACTRELATIONEDITORWIDGET_H +#define QGSABSTRACTRELATIONEDITORWIDGET_H #include #include @@ -30,6 +30,7 @@ #include "qgsdualview.h" #include "qgsrelation.h" #include "qgsvectorlayerselectionmanager.h" +#include "qgis_sip.h" #include "qgis_gui.h" #ifdef SIP_RUN @@ -45,15 +46,15 @@ class QgsVectorLayerTools; class QgsMapTool; class QgsMapToolDigitizeFeature; class QgsBaseRelationWidget; - +class QgsPropertyOverrideButton; /** * Base class to build new relation widgets. * \ingroup gui - * \class QgsRelationWidget + * \class QgsAbstractRelationEditorWidget * \since QGIS 3.18 */ -class GUI_EXPORT QgsRelationWidget : public QWidget +class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget { #ifdef SIP_RUN @@ -73,7 +74,7 @@ class GUI_EXPORT QgsRelationWidget : public QWidget /** * Constructor */ - QgsRelationWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); + QgsAbstractRelationEditorWidget( const QVariantMap &config, QWidget *parent SIP_TRANSFERTHIS = nullptr ); /** * Sets the \a relation and the \a feature @@ -275,4 +276,116 @@ class GUI_EXPORT QgsRelationWidget : public QWidget virtual void afterSetRelations() {}; }; -#endif // QGSRELATIONWIDGET_H + +/** + * \ingroup gui + * This class should be subclassed for every configurable relation widget type. + * + * It implements the GUI configuration widget and transforms this to/from a configuration. + * + * It will only be instantiated by {\see QgsAbstractRelationEditorWidgetFactory} + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsAbstractRelationEditorConfigWidget : public QWidget +{ + Q_OBJECT + public: + + /** + * Create a new configuration widget + * + * \param relation The relation for which the configuration dialog will be created + * \param parent A parent widget + */ + explicit QgsAbstractRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); + + /** + * \brief Create a configuration from the current GUI state + * + * \returns A widget configuration + */ + virtual QVariantMap config() = 0; + + /** + * \brief Update the configuration widget to represent the given configuration. + * + * \param config The configuration which should be represented by this widget + */ + virtual void setConfig( const QVariantMap &config ) = 0; + + /** + * Returns the layer for which this configuration widget applies + * + * \returns The layer + */ + QgsVectorLayer *layer(); + + /** + * Returns the relation for which this configuration widget applies + * + * \returns The relation + */ + QgsRelation relation() const; + + + private: + QgsVectorLayer *mLayer = nullptr; + QgsRelation mRelation; +}; + + +/////////////////////////////// + + +/** + * Factory class for creating relation widgets and their corresponding config widgets + * \ingroup gui + * \class QgsAbstractRelationEditorWidgetFactory + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsAbstractRelationEditorWidgetFactory +{ + public: + + /** + * Creates a new relation widget factory with given \a name + */ + QgsAbstractRelationEditorWidgetFactory(); + + virtual ~QgsAbstractRelationEditorWidgetFactory() = default; + + /** + * Returns the machine readable identifier name of this widget type + */ + virtual QString type() const = 0; + + /** + * Returns the human readable identifier name of this widget type + */ + virtual QString name() const = 0; + + /** + * Override this in your implementation. + * Create a new relation widget. Call QgsEditorWidgetRegistry::create() + * instead of calling this method directly. + * + * \param config The widget configuration to build the widget with + * \param parent The parent for the wrapper class and any created widget. + * + * \returns A new widget wrapper + */ + virtual QgsAbstractRelationEditorWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const = 0 SIP_FACTORY; + + /** + * Override this in your implementation. + * Create a new configuration widget for this widget type. + * + * \param relation The relation for which the widget will be created + * \param parent The parent widget of the created config widget + * + * \returns A configuration widget + */ + virtual QgsAbstractRelationEditorConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 SIP_FACTORY; +}; + +#endif // QGSABSTRACTRELATIONEDITORWIDGET_H diff --git a/src/gui/qgsrelationconfigwidget.cpp b/src/gui/qgsrelationconfigwidget.cpp deleted file mode 100644 index 2ad5dc823cbf..000000000000 --- a/src/gui/qgsrelationconfigwidget.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************** - qgsrelationconfigbasewidget.cpp - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 "qgsrelationconfigwidget.h" -#include "qgsexpressioncontextutils.h" - -QgsRelationConfigWidget::QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent ) - : QWidget( parent ) - , mRelation( relation ) -{ -} - -QgsVectorLayer *QgsRelationConfigWidget::layer() -{ - return mLayer; -} - -QgsRelation QgsRelationConfigWidget::relation() const -{ - return mRelation; -} - - diff --git a/src/gui/qgsrelationconfigwidget.h b/src/gui/qgsrelationconfigwidget.h deleted file mode 100644 index 2d82b2fe0df0..000000000000 --- a/src/gui/qgsrelationconfigwidget.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - qgsrelationconfigwidget.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 QGSRELATIONCONFIGBASEWIDGET_H -#define QGSRELATIONCONFIGBASEWIDGET_H - -#include -#include "qgis_sip.h" -#include "qgis_gui.h" - -#include "qgseditorwidgetwrapper.h" - -class QgsRelation; -class QgsPropertyOverrideButton; - -/** - * \ingroup gui - * This class should be subclassed for every configurable relation widget type. - * - * It implements the GUI configuration widget and transforms this to/from a configuration. - * - * It will only be instantiated by {\see QgsRelationWidgetFactory} - * \since QGIS 3.18 - */ - -class GUI_EXPORT QgsRelationConfigWidget : public QWidget -{ - Q_OBJECT - public: - - /** - * Create a new configuration widget - * - * \param relation The relation for which the configuration dialog will be created - * \param parent A parent widget - */ - explicit QgsRelationConfigWidget( const QgsRelation &relation, QWidget *parent SIP_TRANSFERTHIS ); - - /** - * \brief Create a configuration from the current GUI state - * - * \returns A widget configuration - */ - virtual QVariantMap config() = 0; - - /** - * \brief Update the configuration widget to represent the given configuration. - * - * \param config The configuration which should be represented by this widget - */ - virtual void setConfig( const QVariantMap &config ) = 0; - - /** - * Returns the layer for which this configuration widget applies - * - * \returns The layer - */ - QgsVectorLayer *layer(); - - /** - * Returns the relation for which this configuration widget applies - * - * \returns The relation - */ - QgsRelation relation() const; - - - private: - QgsVectorLayer *mLayer = nullptr; - QgsRelation mRelation; -}; - -#endif // QGSRELATIONCONFIGBASEWIDGET_H diff --git a/src/gui/qgsrelationeditorwidget.cpp b/src/gui/qgsrelationeditorwidget.cpp index 95a71b8332cd..c47656f08266 100644 --- a/src/gui/qgsrelationeditorwidget.cpp +++ b/src/gui/qgsrelationeditorwidget.cpp @@ -94,7 +94,7 @@ void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selec /// @endcond QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent ) - : QgsRelationWidget( config, parent ) + : QgsAbstractRelationEditorWidget( config, parent ) , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) ) { QVBoxLayout *rootLayout = new QVBoxLayout( this ); @@ -360,7 +360,7 @@ void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature ) void QgsRelationEditorWidget::toggleEditing( bool state ) { - QgsRelationWidget::toggleEditing( state ); + QgsAbstractRelationEditorWidget::toggleEditing( state ); updateButtons(); } @@ -610,41 +610,8 @@ void QgsRelationEditorWidget::afterSetRelations() /////////////////////////////////////////////////////////////////////////////// -#ifndef SIP_RUN -QgsRelationEditorWidgetFactory::QgsRelationEditorWidgetFactory() -{ - -} - -QString QgsRelationEditorWidgetFactory::type() const -{ - return QStringLiteral( "relation_editor" ); -} - -QString QgsRelationEditorWidgetFactory::name() const -{ - return QStringLiteral( "Relation Editor" ); -} - -QgsRelationWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const -{ - return new QgsRelationEditorWidget( config, parent ); -} - - -QgsRelationConfigWidget *QgsRelationEditorWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const -{ - return static_cast( new QgsRelationEditorConfigWidget( relation, parent ) ); -} -#endif - - -/////////////////////////////////////////////////////////////////////////////// - - -#ifndef SIP_RUN QgsRelationEditorConfigWidget::QgsRelationEditorConfigWidget( const QgsRelation &relation, QWidget *parent ) - : QgsRelationConfigWidget( relation, parent ) + : QgsAbstractRelationEditorConfigWidget( relation, parent ) { setupUi( this ); } @@ -678,4 +645,34 @@ void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config ) mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) ); mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) ); } + + +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SIP_RUN +QgsRelationEditorWidgetFactory::QgsRelationEditorWidgetFactory() +{ + +} + +QString QgsRelationEditorWidgetFactory::type() const +{ + return QStringLiteral( "relation_editor" ); +} + +QString QgsRelationEditorWidgetFactory::name() const +{ + return QStringLiteral( "Relation Editor" ); +} + +QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const +{ + return new QgsRelationEditorWidget( config, parent ); +} + +QgsAbstractRelationEditorConfigWidget *QgsRelationEditorWidgetFactory::configWidget( const QgsRelation &relation, QWidget *parent ) const +{ + return static_cast( new QgsRelationEditorConfigWidget( relation, parent ) ); +} #endif diff --git a/src/gui/qgsrelationeditorwidget.h b/src/gui/qgsrelationeditorwidget.h index 90ca849595bf..b47e3701a7e6 100644 --- a/src/gui/qgsrelationeditorwidget.h +++ b/src/gui/qgsrelationeditorwidget.h @@ -24,10 +24,7 @@ #include "ui_qgsrelationeditorconfigwidgetbase.h" -#include "qgsrelationwidget.h" -#include "qgsrelationconfigwidget.h" -#include "qgsrelationwidgetfactory.h" -#include "qgsrelationeditorwidget.h" +#include "qgsabstractrelationeditorwidget.h" #include "qobjectuniqueptr.h" #include "qgsattributeeditorcontext.h" #include "qgscollapsiblegroupbox.h" @@ -88,7 +85,7 @@ class QgsFilteredSelectionManager : public QgsVectorLayerSelectionManager * \class QgsRelationEditorWidget * \since QGIS 3.18 */ -class GUI_EXPORT QgsRelationEditorWidget : public QgsRelationWidget +class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidget { Q_OBJECT @@ -211,37 +208,13 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsRelationWidget }; -#ifndef SIP_RUN - -/** - * Factory class for creating a relation editor widget and the respective config widget. - * \ingroup gui - * \class QgsRelationEditorWidgetFactory - * \note not available in Python bindings - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsRelationEditorWidgetFactory : public QgsRelationWidgetFactory -{ - public: - QgsRelationEditorWidgetFactory(); - - QString type() const override; - - QString name() const override; - - QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; - - QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; - -}; - /** * \ingroup gui * \class QgsRelationEditorConfigWidget * Creates a new configuration widget for the relation editor widget * \since QGIS 3.18 */ -class QgsRelationEditorConfigWidget : public QgsRelationConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase +class GUI_EXPORT QgsRelationEditorConfigWidget : public QgsAbstractRelationEditorConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase { public: @@ -268,6 +241,32 @@ class QgsRelationEditorConfigWidget : public QgsRelationConfigWidget, private Ui void setConfig( const QVariantMap &config ); }; + + +#ifndef SIP_RUN + +/** + * Factory class for creating a relation editor widget and the respective config widget. + * \ingroup gui + * \class QgsRelationEditorWidgetFactory + * \note not available in Python bindings + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsRelationEditorWidgetFactory : public QgsAbstractRelationEditorWidgetFactory +{ + public: + QgsRelationEditorWidgetFactory(); + + QString type() const override; + + QString name() const override; + + QgsAbstractRelationEditorWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const override; + + QgsAbstractRelationEditorConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const override; + +}; #endif + #endif // QGSRELATIONEDITORWIDGET_H diff --git a/src/gui/qgsrelationwidgetfactory.cpp b/src/gui/qgsrelationwidgetfactory.cpp deleted file mode 100644 index 4a2267db483d..000000000000 --- a/src/gui/qgsrelationwidgetfactory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*************************************************************************** - qgsrelationwidgetfactory.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 "qgsrelationwidgetfactory.h" - - -QgsRelationWidgetFactory::QgsRelationWidgetFactory() -{ -} - diff --git a/src/gui/qgsrelationwidgetfactory.h b/src/gui/qgsrelationwidgetfactory.h deleted file mode 100644 index 92c7b2449f02..000000000000 --- a/src/gui/qgsrelationwidgetfactory.h +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************** - qgsrelationwidgetfactory.h - ---------------------- - begin : October 2020 - copyright : (C) 2020 by Ivan Ivanov - email : ivan@opengis.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 QGSRELATIONEDITORBASEWIDGETFACTORY_H -#define QGSRELATIONEDITORBASEWIDGETFACTORY_H - -#include -#include "qgsrelationwidget.h" -#include "qgis_gui.h" - -class QgsRelationConfigWidget; - -/** - * Factory class for creating relation widgets and their corresponding config widgets - * \ingroup gui - * \class QgsRelationWidgetFactory - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsRelationWidgetFactory -{ - public: - - /** - * Creates a new relation widget factory with given \a name - */ - QgsRelationWidgetFactory(); - - virtual ~QgsRelationWidgetFactory() = default; - - /** - * Returns the machine readable identifier name of this widget type - */ - virtual QString type() const = 0; - - /** - * Returns the human readable identifier name of this widget type - */ - virtual QString name() const = 0; - - /** - * Override this in your implementation. - * Create a new relation widget. Call QgsEditorWidgetRegistry::create() - * instead of calling this method directly. - * - * \param config The widget configuration to build the widget with - * \param parent The parent for the wrapper class and any created widget. - * - * \returns A new widget wrapper - */ - virtual QgsRelationWidget *create( const QVariantMap &config, QWidget *parent = nullptr ) const = 0 SIP_FACTORY; - - /** - * Override this in your implementation. - * Create a new configuration widget for this widget type. - * - * \param relation The relation for which the widget will be created - * \param parent The parent widget of the created config widget - * - * \returns A configuration widget - */ - virtual QgsRelationConfigWidget *configWidget( const QgsRelation &relation, QWidget *parent ) const = 0 SIP_FACTORY; -}; - - - -#endif // QGSRELATIONEDITORBASEWIDGETFACTORY_H diff --git a/src/gui/qgsrelationwidgetregistry.cpp b/src/gui/qgsrelationwidgetregistry.cpp index 187379cfea83..79310ad29ff5 100644 --- a/src/gui/qgsrelationwidgetregistry.cpp +++ b/src/gui/qgsrelationwidgetregistry.cpp @@ -16,8 +16,6 @@ ***************************************************************************/ #include "qgsrelationwidgetregistry.h" -#include "qgsrelationwidget.h" -#include "qgsrelationconfigwidget.h" #include "qgsrelationeditorwidget.h" QgsRelationWidgetRegistry::QgsRelationWidgetRegistry() @@ -31,7 +29,7 @@ QgsRelationWidgetRegistry::~QgsRelationWidgetRegistry() mRelationWidgetFactories.clear(); } -void QgsRelationWidgetRegistry::addRelationWidget( QgsRelationWidgetFactory *widgetFactory ) +void QgsRelationWidgetRegistry::addRelationWidget( QgsAbstractRelationEditorWidgetFactory *widgetFactory ) { if ( !widgetFactory ) return; @@ -56,12 +54,12 @@ QStringList QgsRelationWidgetRegistry::relationWidgetNames() return mRelationWidgetFactories.keys(); } -QMap QgsRelationWidgetRegistry::factories() const +QMap QgsRelationWidgetRegistry::factories() const { return mRelationWidgetFactories; } -QgsRelationWidget *QgsRelationWidgetRegistry::create( const QString &widgetType, const QVariantMap &config, QWidget *parent ) const +QgsAbstractRelationEditorWidget *QgsRelationWidgetRegistry::create( const QString &widgetType, const QVariantMap &config, QWidget *parent ) const { if ( ! mRelationWidgetFactories.contains( widgetType ) ) return nullptr; @@ -69,7 +67,7 @@ QgsRelationWidget *QgsRelationWidgetRegistry::create( const QString &widgetType, return mRelationWidgetFactories.value( widgetType )->create( config, parent ); } -QgsRelationConfigWidget *QgsRelationWidgetRegistry::createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent ) const +QgsAbstractRelationEditorConfigWidget *QgsRelationWidgetRegistry::createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent ) const { if ( ! mRelationWidgetFactories.contains( widgetType ) ) return nullptr; diff --git a/src/gui/qgsrelationwidgetregistry.h b/src/gui/qgsrelationwidgetregistry.h index 1614a824abec..0ba25f807516 100644 --- a/src/gui/qgsrelationwidgetregistry.h +++ b/src/gui/qgsrelationwidgetregistry.h @@ -15,16 +15,13 @@ * * ***************************************************************************/ -#include "qgseditorconfigwidget.h" -#include "qgsrelationwidget.h" -#include "qgsrelationwidgetfactory.h" +#include "qgsabstractrelationeditorwidget.h" #include "qgis_gui.h" #ifndef QGSRELATIONWIDGETREGISTRY_H #define QGSRELATIONWIDGETREGISTRY_H -class QgsRelationConfigWidget; /** * Keeps track of the registered relations widgets. New widgets can be registered, old ones deleted. @@ -47,7 +44,7 @@ class GUI_EXPORT QgsRelationWidgetRegistry /** * Adds a new registered relation \a widgetFactory */ - void addRelationWidget( QgsRelationWidgetFactory *widgetFactory SIP_TRANSFER ); + void addRelationWidget( QgsAbstractRelationEditorWidgetFactory *widgetFactory SIP_TRANSFER ); /** * Removes a registered relation widget with given \a widgetType @@ -62,7 +59,7 @@ class GUI_EXPORT QgsRelationWidgetRegistry /** * Gets access to all registered factories */ - QMap factories() const; + QMap factories() const; /** * Create a relation widget of a given type for a given field. @@ -71,7 +68,7 @@ class GUI_EXPORT QgsRelationWidgetRegistry * \param config The configuration of the widget * \param parent */ - QgsRelationWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; + QgsAbstractRelationEditorWidget *create( const QString &widgetType, const QVariantMap &config, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; /** * Creates a configuration widget @@ -80,11 +77,11 @@ class GUI_EXPORT QgsRelationWidgetRegistry * \param relation The relation for which this widget will be created * \param parent The parent widget for the created widget */ - QgsRelationConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; + QgsAbstractRelationEditorConfigWidget *createConfigWidget( const QString &widgetType, const QgsRelation &relation, QWidget *parent = nullptr ) const SIP_TRANSFERBACK; private: - QMap mRelationWidgetFactories; + QMap mRelationWidgetFactories; }; #endif // QGSRELATIONWIDGETREGISTRY_H From e786da89c968f0c935b7897fcb7a43f05448e095 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 7 Jan 2021 15:56:39 +0200 Subject: [PATCH 17/23] Add test for the relation widget registry --- tests/src/python/CMakeLists.txt | 1 + .../test_qgsrelationeditorwidgetregistry.py | 134 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/src/python/test_qgsrelationeditorwidgetregistry.py diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 0a10bdd66fef..694877907c23 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -341,6 +341,7 @@ ADD_PYTHON_TEST(PyQgsFieldValidator test_qgsfieldvalidator.py) ADD_PYTHON_TEST(PyQgsPluginDependencies test_plugindependencies.py) ADD_PYTHON_TEST(PyQgsDBManagerSQLWindow test_db_manager_sql_window.py) ADD_PYTHON_TEST(PyQgsSelectiveMasking test_selective_masking.py) +ADD_PYTHON_TEST(PyQgsRelationEditorWidgetRegistry test_qgsrelationeditorwidgetregistry.py) if (NOT WIN32) ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py) diff --git a/tests/src/python/test_qgsrelationeditorwidgetregistry.py b/tests/src/python/test_qgsrelationeditorwidgetregistry.py new file mode 100644 index 000000000000..0f3f55f3d3a2 --- /dev/null +++ b/tests/src/python/test_qgsrelationeditorwidgetregistry.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for edit widgets. + +.. note:: 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. +""" +__author__ = 'Matthias Kuhn' +__date__ = '28/11/2015' +__copyright__ = 'Copyright 2015, The QGIS Project' + +import qgis # NOQA + +import os + +from qgis.core import ( + QgsRelation, + QgsVectorLayerTools, +) + +from qgis.gui import ( + QgsGui, + QgsAbstractRelationEditorWidget, + QgsAbstractRelationEditorConfigWidget, + QgsAbstractRelationEditorWidgetFactory, + QgsRelationEditorWidget, + QgsRelationEditorConfigWidget, + QgsAttributeEditorContext, + QgsAdvancedDigitizingDockWidget +) + +from qgis.PyQt.QtCore import QTimer +from qgis.PyQt.QtWidgets import ( + QToolButton, + QMessageBox, + QDialogButtonBox, + QTableView, + QDialog, + QLabel, + QGridLayout, + QCheckBox, +) +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsRelationEditorWidgetRegistry(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """ + Setup the involved layers and relations for a n:m relation + :return: + """ + cls.registry = QgsGui.relationWidgetRegistry() + + def test_cannot_delete_relation_editor(self): + count_before = len(self.registry.relationWidgetNames()) + self.registry.removeRelationWidget('relation_editor') + count_after = len(self.registry.relationWidgetNames()) + + self.assertEqual(count_before, count_after) + self.assertIsInstance(self.registry.create('relation_editor', {}), QgsRelationEditorWidget) + self.assertIsInstance(self.registry.createConfigWidget('relation_editor', QgsRelation()), QgsRelationEditorConfigWidget) + + def test_returns_none_when_unknown_widget_id(self): + self.assertIsNone(self.registry.create('babinatatitrunkina', {})) + self.assertIsNone(self.registry.createConfigWidget('babinatatitrunkina', QgsRelation())) + + def test_creates_new_widget(self): + # define the widget + class QgsExampleRelationEditorWidget(QgsAbstractRelationEditorWidget): + + def __init__(self, config, parent): + super().__init__(config, parent) + + self.setLayout(QGridLayout()) + self.label = QLabel() + self.label.setText(f'According to the configuration, the checkboxin state was {str(config.get("checkboxin", "Unknown"))}') + self.layout().addWidget(self.label) + + def config(self): + return { + + } + + def setConfig(self, config): + self.label.setText(f'According to the configuration, the checkboxin state was {str(config.get("checkboxin", "Unknown"))}') + + # define the config widget + class QgsExampleRelationEditorConfigWidget(QgsAbstractRelationEditorConfigWidget): + + def __init__(self, relation, parent): + super().__init__(relation, parent) + + self.setLayout(QGridLayout()) + self.checkbox = QCheckBox('Is this checkbox checkboxin?') + self.layout().addWidget(self.checkbox) + + def config(self): + return { + "checkboxin": self.checkbox.isChecked() + } + + def setConfig(self, config): + self.checkbox.setChecked(config.get('checkboxin', False)) + + # define the widget factory + class QgsExampleRelationEditorWidgetFactory(QgsAbstractRelationEditorWidgetFactory): + def type(self): + return "example" + + def name(self): + return "Example Relation Widget" + + def create(self, config, parent): + return QgsExampleRelationEditorWidget(config, parent) + + def configWidget(self, relation, parent): + return QgsExampleRelationEditorConfigWidget(relation, parent) + + self.assertIsNone(self.registry.create('example', {})) + self.assertIsNone(self.registry.createConfigWidget('example', QgsRelation())) + + self.registry.addRelationWidget(QgsExampleRelationEditorWidgetFactory()) + + self.assertIsInstance(self.registry.create('example', {}), QgsExampleRelationEditorWidget) + self.assertIsInstance(self.registry.createConfigWidget('example', QgsRelation()), QgsExampleRelationEditorConfigWidget) + + +if __name__ == '__main__': + unittest.main() From 5d47b5d8f99571212552fae7a2f42621329c6bf4 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 7 Jan 2021 17:06:57 +0200 Subject: [PATCH 18/23] Convert cpp type to python type --- .../qgsabstractrelationeditorwidget.sip.in | 6 ++++++ src/gui/qgsabstractrelationeditorwidget.h | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in index 5e7a4b07219c..2d524f4a47d0 100644 --- a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in @@ -242,6 +242,12 @@ It will only be instantiated by {:py:class:`QgsAbstractRelationEditorWidgetFacto %TypeHeaderCode #include "qgsabstractrelationeditorwidget.h" +%End +%ConvertToSubClassCode + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsRelationEditorConfigWidget; + else + sipType = 0; %End public: diff --git a/src/gui/qgsabstractrelationeditorwidget.h b/src/gui/qgsabstractrelationeditorwidget.h index df16741e7027..4f6474eb2126 100644 --- a/src/gui/qgsabstractrelationeditorwidget.h +++ b/src/gui/qgsabstractrelationeditorwidget.h @@ -288,6 +288,16 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget */ class GUI_EXPORT QgsAbstractRelationEditorConfigWidget : public QWidget { + +#ifdef SIP_RUN + SIP_CONVERT_TO_SUBCLASS_CODE + if ( qobject_cast( sipCpp ) ) + sipType = sipType_QgsRelationEditorConfigWidget; + else + sipType = 0; + SIP_END +#endif + Q_OBJECT public: From 71baf2bed75077aee7fea2de54985f090ae9633b Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 8 Jan 2021 10:56:11 +0200 Subject: [PATCH 19/23] Make it Q_OBJECT --- src/gui/qgsrelationeditorwidget.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/qgsrelationeditorwidget.h b/src/gui/qgsrelationeditorwidget.h index b47e3701a7e6..51fee94fa628 100644 --- a/src/gui/qgsrelationeditorwidget.h +++ b/src/gui/qgsrelationeditorwidget.h @@ -216,6 +216,8 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge */ class GUI_EXPORT QgsRelationEditorConfigWidget : public QgsAbstractRelationEditorConfigWidget, private Ui::QgsRelationEditorConfigWidgetBase { + Q_OBJECT + public: /** From a54483457c4ff280d19a57d95931be08dadd06b2 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 8 Jan 2021 11:32:16 +0200 Subject: [PATCH 20/23] No more Q_NOWARN_DEPRECATED_POP --- src/core/qgseditformconfig.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/qgseditformconfig.cpp b/src/core/qgseditformconfig.cpp index 16d6cef8a1e3..85aab8c571d5 100644 --- a/src/core/qgseditformconfig.cpp +++ b/src/core/qgseditformconfig.cpp @@ -668,14 +668,12 @@ QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomEleme config = relElement->config(); // pre QGIS 3.18 compatibility - Q_NOWARN_DEPRECATED_PUSH if ( ! config.contains( QStringLiteral( "buttons" ) ) ) { if ( elem.hasAttribute( "buttons" ) ) { QString buttonString = elem.attribute( QStringLiteral( "buttons" ), qgsFlagValueToKeys( QgsAttributeEditorRelation::Button::AllButtons ) ); - relElement->setVisibleButtons( qgsFlagKeysToValue( buttonString, QgsAttributeEditorRelation::Button::AllButtons ) ); - config.insert( "buttons", qgsFlagValueToKeys( relElement->visibleButtons() ) ); + config.insert( "buttons", qgsFlagValueToKeys( qgsFlagKeysToValue( buttonString, QgsAttributeEditorRelation::Button::AllButtons ) ) ); } else { @@ -684,10 +682,9 @@ QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomEleme buttons.setFlag( QgsAttributeEditorRelation::Button::Link, elem.attribute( QStringLiteral( "showLinkButton" ), QStringLiteral( "1" ) ).toInt() ); buttons.setFlag( QgsAttributeEditorRelation::Button::Unlink, elem.attribute( QStringLiteral( "showUnlinkButton" ), QStringLiteral( "1" ) ).toInt() ); buttons.setFlag( QgsAttributeEditorRelation::Button::SaveChildEdits, elem.attribute( QStringLiteral( "showSaveChildEditsButton" ), QStringLiteral( "1" ) ).toInt() ); - config.insert( "buttons", qgsFlagValueToKeys( relElement->visibleButtons() ) ); + config.insert( "buttons", qgsFlagValueToKeys( buttons ) ); } } - Q_NOWARN_DEPRECATED_POP relElement->setConfig( config ); From ebddedd955e5acb1548adbc07ec71404339eeba1 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 8 Jan 2021 13:58:11 +0200 Subject: [PATCH 21/23] Fix wrong usage of mFeatureSelectionMgr in the abstract class --- .../qgsabstractrelationeditorwidget.sip.in | 22 ++--------- .../qgsrelationeditorwidget.sip.in | 36 +++++++++++++++++ src/gui/qgsabstractrelationeditorwidget.cpp | 37 ++++-------------- src/gui/qgsabstractrelationeditorwidget.h | 24 ++---------- src/gui/qgsrelationeditorwidget.cpp | 39 ++++++++++++++++++- src/gui/qgsrelationeditorwidget.h | 34 ++++++++++++++++ 6 files changed, 121 insertions(+), 71 deletions(-) diff --git a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in index 2d524f4a47d0..51d8de45880f 100644 --- a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in @@ -75,12 +75,6 @@ Sets the editor ``context`` QgsAttributeEditorContext editorContext( ) const; %Docstring Returns the attribute editor context. -%End - - QgsIFeatureSelectionManager *featureSelectionManager(); -%Docstring -The feature selection manager is responsible for the selected features -which are currently being edited. %End bool showLabel() const; @@ -168,11 +162,6 @@ Adds a new feature with given ``geometry`` void deleteFeature( QgsFeatureId fid = QgsFeatureId() ); %Docstring Delete a feature with given ``fid`` -%End - - void deleteSelectedFeatures(); -%Docstring -Deletes the currently selected features %End void linkFeature(); @@ -190,19 +179,14 @@ Called when the link feature dialog is confirmed by the user Unlinks a feature with given ``fid`` %End - void unlinkSelectedFeatures(); -%Docstring -Unlinks the selected features from the relation -%End - - void duplicateFeature(); + void duplicateFeature( const QgsFeatureId &fid ); %Docstring Duplicates a feature %End - void zoomToSelectedFeatures(); + void duplicateFeatures( const QgsFeatureIds &fids ); %Docstring -Zooms to the selected features +Duplicates features %End protected: diff --git a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in index 51fad5aa5ca3..19fd4871dce3 100644 --- a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in @@ -64,6 +64,12 @@ Define the view mode for the dual view QgsDualView::ViewMode viewMode(); %Docstring Gets the view mode for the dual view +%End + + QgsIFeatureSelectionManager *featureSelectionManager(); +%Docstring +The feature selection manager is responsible for the selected features +which are currently being edited. %End void setEditorContext( const QgsAttributeEditorContext &context ); @@ -84,6 +90,36 @@ Defines the buttons which are shown Buttons visibleButtons() const; %Docstring Returns the buttons which are shown +%End + + void duplicateFeature() /Deprecated/; +%Docstring +Duplicates a feature + +.. deprecated:: QGIS 3.18 + use duplicateSelectedFeatures() instead +%End + + void duplicateSelectedFeatures(); +%Docstring +Duplicates the selected features + +.. versionadded:: 3.18 +%End + + void unlinkSelectedFeatures(); +%Docstring +Unlinks the selected features from the relation +%End + + void deleteSelectedFeatures(); +%Docstring +Deletes the currently selected features +%End + + void zoomToSelectedFeatures(); +%Docstring +Zooms to the selected features %End virtual QVariantMap config() const; diff --git a/src/gui/qgsabstractrelationeditorwidget.cpp b/src/gui/qgsabstractrelationeditorwidget.cpp index 7772ada94303..3c4048406a4b 100644 --- a/src/gui/qgsabstractrelationeditorwidget.cpp +++ b/src/gui/qgsabstractrelationeditorwidget.cpp @@ -116,11 +116,6 @@ QgsAttributeEditorContext QgsAbstractRelationEditorWidget::editorContext() const return mEditorContext; } -QgsIFeatureSelectionManager *QgsAbstractRelationEditorWidget::featureSelectionManager() -{ - return mFeatureSelectionMgr; -} - void QgsAbstractRelationEditorWidget::setFeature( const QgsFeature &feature, bool update ) { mFeature = feature; @@ -277,12 +272,6 @@ void QgsAbstractRelationEditorWidget::deleteFeature( const QgsFeatureId fid ) deleteFeatures( QgsFeatureIds() << fid ); } -void QgsAbstractRelationEditorWidget::deleteSelectedFeatures() -{ - QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds(); - deleteFeatures( selectedFids ); -} - void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids ) { bool deleteFeatures = true; @@ -495,11 +484,6 @@ void QgsAbstractRelationEditorWidget::unlinkFeature( const QgsFeatureId fid ) unlinkFeatures( QgsFeatureIds() << fid ); } -void QgsAbstractRelationEditorWidget::unlinkSelectedFeatures() -{ - unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); -} - void QgsAbstractRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &fids ) { if ( mNmRelation.isValid() ) @@ -567,11 +551,16 @@ void QgsAbstractRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &fids } } -void QgsAbstractRelationEditorWidget::duplicateFeature() +void QgsAbstractRelationEditorWidget::duplicateFeature( const QgsFeatureId &fid ) +{ + duplicateFeatures( QgsFeatureIds() << fid ); +} + +void QgsAbstractRelationEditorWidget::duplicateFeatures( const QgsFeatureIds &fids ) { QgsVectorLayer *layer = mRelation.referencingLayer(); - QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) ); + QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( fids ) ); QgsFeature f; while ( fit.nextFeature( f ) ) { @@ -580,18 +569,6 @@ void QgsAbstractRelationEditorWidget::duplicateFeature() } } -void QgsAbstractRelationEditorWidget::zoomToSelectedFeatures() -{ - QgsMapCanvas *c = mEditorContext.mapCanvas(); - if ( !c ) - return; - - c->zoomToFeatureIds( - mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(), - mFeatureSelectionMgr->selectedFeatureIds() - ); -} - /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gui/qgsabstractrelationeditorwidget.h b/src/gui/qgsabstractrelationeditorwidget.h index 4f6474eb2126..71d58c326088 100644 --- a/src/gui/qgsabstractrelationeditorwidget.h +++ b/src/gui/qgsabstractrelationeditorwidget.h @@ -29,7 +29,6 @@ #include "qgscollapsiblegroupbox.h" #include "qgsdualview.h" #include "qgsrelation.h" -#include "qgsvectorlayerselectionmanager.h" #include "qgis_sip.h" #include "qgis_gui.h" @@ -109,12 +108,6 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget */ QgsAttributeEditorContext editorContext( ) const; - /** - * The feature selection manager is responsible for the selected features - * which are currently being edited. - */ - QgsIFeatureSelectionManager *featureSelectionManager(); - /** * Defines if a title label should be shown for this widget. */ @@ -202,11 +195,6 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget */ void deleteFeature( QgsFeatureId fid = QgsFeatureId() ); - /** - * Deletes the currently selected features - */ - void deleteSelectedFeatures(); - /** * Links a new feature to the relation */ @@ -222,24 +210,18 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget */ void unlinkFeature( QgsFeatureId fid = QgsFeatureId() ); - /** - * Unlinks the selected features from the relation - */ - void unlinkSelectedFeatures(); - /** * Duplicates a feature */ - void duplicateFeature(); + void duplicateFeature( const QgsFeatureId &fid ); /** - * Zooms to the selected features + * Duplicates features */ - void zoomToSelectedFeatures(); + void duplicateFeatures( const QgsFeatureIds &fids ); protected: - QgsVectorLayerSelectionManager *mFeatureSelectionMgr = nullptr; QgsAttributeEditorContext mEditorContext; QgsRelation mRelation; QgsRelation mNmRelation; diff --git a/src/gui/qgsrelationeditorwidget.cpp b/src/gui/qgsrelationeditorwidget.cpp index c47656f08266..7ed3103cadc9 100644 --- a/src/gui/qgsrelationeditorwidget.cpp +++ b/src/gui/qgsrelationeditorwidget.cpp @@ -213,7 +213,7 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWi connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits ); connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } ); connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry ); - connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateFeature ); + connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures ); connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures ); connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature ); connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures ); @@ -606,6 +606,43 @@ void QgsRelationEditorWidget::afterSetRelations() updateButtons(); } +QgsIFeatureSelectionManager *QgsRelationEditorWidget::featureSelectionManager() +{ + return mFeatureSelectionMgr; +} + +void QgsRelationEditorWidget::unlinkSelectedFeatures() +{ + unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); +} + +void QgsRelationEditorWidget::duplicateFeature() +{ + duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); +} + +void QgsRelationEditorWidget::duplicateSelectedFeatures() +{ + duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() ); +} + +void QgsRelationEditorWidget::deleteSelectedFeatures() +{ + QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds(); + deleteFeatures( selectedFids ); +} + +void QgsRelationEditorWidget::zoomToSelectedFeatures() +{ + QgsMapCanvas *c = mEditorContext.mapCanvas(); + if ( !c ) + return; + + c->zoomToFeatureIds( + mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(), + mFeatureSelectionMgr->selectedFeatureIds() + ); +} /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gui/qgsrelationeditorwidget.h b/src/gui/qgsrelationeditorwidget.h index 51fee94fa628..88d288a5fd64 100644 --- a/src/gui/qgsrelationeditorwidget.h +++ b/src/gui/qgsrelationeditorwidget.h @@ -125,6 +125,12 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge //! Gets the view mode for the dual view QgsDualView::ViewMode viewMode() {return mViewMode;} + /** + * The feature selection manager is responsible for the selected features + * which are currently being edited. + */ + QgsIFeatureSelectionManager *featureSelectionManager(); + /** * Sets the editor \a context * \note if context cadDockWidget is null, it won't be possible to digitize @@ -142,6 +148,33 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge */ Buttons visibleButtons() const; + /** + * Duplicates a feature + * \deprecated since QGIS 3.18, use duplicateSelectedFeatures() instead + */ + Q_DECL_DEPRECATED void duplicateFeature() SIP_DEPRECATED; + + /** + * Duplicates the selected features + * \since QGIS 3.18 + */ + void duplicateSelectedFeatures(); + + /** + * Unlinks the selected features from the relation + */ + void unlinkSelectedFeatures(); + + /** + * Deletes the currently selected features + */ + void deleteSelectedFeatures(); + + /** + * Zooms to the selected features + */ + void zoomToSelectedFeatures(); + /** * Returns the current configuration */ @@ -197,6 +230,7 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge QGridLayout *mRelationLayout = nullptr; QObjectUniquePtr mMapToolDigitize; QButtonGroup *mViewModeButtonGroup = nullptr; + QgsVectorLayerSelectionManager *mFeatureSelectionMgr = nullptr; Buttons mButtonsVisibility = Button::AllButtons; bool mVisible = true; From 770856959b4f063044901a7be377f95ced47ca29 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 8 Jan 2021 14:12:08 +0200 Subject: [PATCH 22/23] Fix breaking tests --- .../gui/auto_generated/qgsabstractrelationeditorwidget.sip.in | 2 +- python/gui/auto_generated/qgsrelationeditorwidget.sip.in | 3 ++- src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp | 3 +-- src/gui/qgsabstractrelationeditorwidget.h | 2 +- src/gui/qgsrelationeditorwidget.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in index 51d8de45880f..16513a642ea8 100644 --- a/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in @@ -62,7 +62,7 @@ inserting and deleting entries on the intermediate table as required. Sets the ``feature`` being edited and updates the UI unless ``update`` is set to ``False`` %End - void setEditorContext( const QgsAttributeEditorContext &context ); + virtual void setEditorContext( const QgsAttributeEditorContext &context ); %Docstring Sets the editor ``context`` diff --git a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in index 19fd4871dce3..82b59803e3b7 100644 --- a/python/gui/auto_generated/qgsrelationeditorwidget.sip.in +++ b/python/gui/auto_generated/qgsrelationeditorwidget.sip.in @@ -72,7 +72,8 @@ The feature selection manager is responsible for the selected features which are currently being edited. %End - void setEditorContext( const QgsAttributeEditorContext &context ); + virtual void setEditorContext( const QgsAttributeEditorContext &context ); + %Docstring Sets the editor ``context`` diff --git a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp index a75fb8ce2c7b..e2a97ae0cbc7 100644 --- a/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp @@ -190,9 +190,8 @@ void QgsRelationWidgetWrapper::initWidget( QWidget *editor ) } while ( ctx ); - w->setRelations( mRelation, mNmRelation ); - w->setEditorContext( myContext ); + w->setRelations( mRelation, mNmRelation ); mWidget = w; } diff --git a/src/gui/qgsabstractrelationeditorwidget.h b/src/gui/qgsabstractrelationeditorwidget.h index 71d58c326088..27049459cbb8 100644 --- a/src/gui/qgsabstractrelationeditorwidget.h +++ b/src/gui/qgsabstractrelationeditorwidget.h @@ -101,7 +101,7 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget * \note if context cadDockWidget is null, it won't be possible to digitize * the geometry of a referencing feature from this widget */ - void setEditorContext( const QgsAttributeEditorContext &context ); + virtual void setEditorContext( const QgsAttributeEditorContext &context ); /** * Returns the attribute editor context. diff --git a/src/gui/qgsrelationeditorwidget.h b/src/gui/qgsrelationeditorwidget.h index 88d288a5fd64..4bddc730c362 100644 --- a/src/gui/qgsrelationeditorwidget.h +++ b/src/gui/qgsrelationeditorwidget.h @@ -136,7 +136,7 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge * \note if context cadDockWidget is null, it won't be possible to digitize * the geometry of a referencing feature from this widget */ - void setEditorContext( const QgsAttributeEditorContext &context ); + void setEditorContext( const QgsAttributeEditorContext &context ) override; /** * Defines the buttons which are shown From 6620bb900e68a3acd9315b8d387a45bd92b269a9 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 8 Jan 2021 16:03:50 +0200 Subject: [PATCH 23/23] Removed the deprecated methods, will be implemented in Python only --- .../qgsattributeeditorelement.sip.in | 80 ------------------- src/core/qgsattributeeditorelement.cpp | 35 -------- src/core/qgsattributeeditorelement.h | 56 ------------- 3 files changed, 171 deletions(-) diff --git a/python/core/auto_generated/qgsattributeeditorelement.sip.in b/python/core/auto_generated/qgsattributeeditorelement.sip.in index 525beadf547b..067bc063e6ce 100644 --- a/python/core/auto_generated/qgsattributeeditorelement.sip.in +++ b/python/core/auto_generated/qgsattributeeditorelement.sip.in @@ -365,86 +365,6 @@ Initializes the relation from the id virtual QgsAttributeEditorElement *clone( QgsAttributeEditorElement *parent ) const /Factory/; - bool showLinkButton() const /Deprecated/; -%Docstring -Determines if the "link feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setShowLinkButton( bool showLinkButton ) /Deprecated/; -%Docstring -Determines if the "link feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - bool showUnlinkButton() const /Deprecated/; -%Docstring -Determines if the "unlink feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setShowUnlinkButton( bool showUnlinkButton ) /Deprecated/; -%Docstring -Determines if the "unlink feature" button should be shown - -.. versionadded:: 2.18 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - void setShowSaveChildEditsButton( bool showChildEdits ) /Deprecated/; -%Docstring -Determines if the "Save child layer edits" button should be shown - -.. versionadded:: 3.14 - -.. deprecated:: QGIS 3.16 - use setVisibleButtons() instead -%End - - bool showSaveChildEditsButton() const /Deprecated/; -%Docstring -Determines if the "Save child layer edits" button should be shown - -.. versionadded:: 3.14 - -.. deprecated:: QGIS 3.16 - use visibleButtons() instead -%End - - void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) /Deprecated/; -%Docstring -Defines the buttons which are shown - -.. versionadded:: 3.16 - -.. deprecated:: QGIS 3.18 - use setConfig() instead -%End - - QgsAttributeEditorRelation::Buttons visibleButtons() const /Deprecated/; -%Docstring -Returns the buttons which are shown - -.. versionadded:: 3.16 - -.. deprecated:: QGIS 3.18 - use config() instead -%End - bool forceSuppressFormPopup() const; %Docstring Determines the force suppress form popup status. diff --git a/src/core/qgsattributeeditorelement.cpp b/src/core/qgsattributeeditorelement.cpp index 8a89d432dd00..911c43fe9708 100644 --- a/src/core/qgsattributeeditorelement.cpp +++ b/src/core/qgsattributeeditorelement.cpp @@ -163,41 +163,6 @@ QString QgsAttributeEditorRelation::typeIdentifier() const return QStringLiteral( "attributeEditorRelation" ); } -bool QgsAttributeEditorRelation::showLinkButton() const -{ - return mButtons.testFlag( Button::Link ); -} - -void QgsAttributeEditorRelation::setShowLinkButton( bool showLinkButton ) -{ - mButtons.setFlag( Button::Link, showLinkButton ); -} - -bool QgsAttributeEditorRelation::showUnlinkButton() const -{ - return mButtons.testFlag( Button::Unlink ); -} - -void QgsAttributeEditorRelation::setShowUnlinkButton( bool showUnlinkButton ) -{ - mButtons.setFlag( Button::Unlink, showUnlinkButton ); -} - -void QgsAttributeEditorRelation::setShowSaveChildEditsButton( bool showSaveChildEdits ) -{ - mButtons.setFlag( Button::SaveChildEdits, showSaveChildEdits ); -} - -bool QgsAttributeEditorRelation::showSaveChildEditsButton() const -{ - return mButtons.testFlag( Button::SaveChildEdits ); -} - -void QgsAttributeEditorRelation::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) -{ - mButtons = buttons; -} - void QgsAttributeEditorRelation::setForceSuppressFormPopup( bool forceSuppressFormPopup ) { mForceSuppressFormPopup = forceSuppressFormPopup; diff --git a/src/core/qgsattributeeditorelement.h b/src/core/qgsattributeeditorelement.h index e7411aefd6c2..5c0bc48300c9 100644 --- a/src/core/qgsattributeeditorelement.h +++ b/src/core/qgsattributeeditorelement.h @@ -428,62 +428,6 @@ class CORE_EXPORT QgsAttributeEditorRelation : public QgsAttributeEditorElement QgsAttributeEditorElement *clone( QgsAttributeEditorElement *parent ) const override SIP_FACTORY; - /** - * Determines if the "link feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showLinkButton() const SIP_DEPRECATED; - - /** - * Determines if the "link feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowLinkButton( bool showLinkButton ) SIP_DEPRECATED; - - /** - * Determines if the "unlink feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showUnlinkButton() const SIP_DEPRECATED; - - /** - * Determines if the "unlink feature" button should be shown - * \since QGIS 2.18 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowUnlinkButton( bool showUnlinkButton ) SIP_DEPRECATED; - - /** - * Determines if the "Save child layer edits" button should be shown - * \since QGIS 3.14 - * \deprecated since QGIS 3.16 use setVisibleButtons() instead - */ - Q_DECL_DEPRECATED void setShowSaveChildEditsButton( bool showChildEdits ) SIP_DEPRECATED; - - /** - * Determines if the "Save child layer edits" button should be shown - * \since QGIS 3.14 - * \deprecated since QGIS 3.16 use visibleButtons() instead - */ - Q_DECL_DEPRECATED bool showSaveChildEditsButton() const SIP_DEPRECATED; - - /** - * Defines the buttons which are shown - * \since QGIS 3.16 - * \deprecated since QGIS 3.18 use setConfig() instead - */ - Q_DECL_DEPRECATED void setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons ) SIP_DEPRECATED; - - /** - * Returns the buttons which are shown - * \since QGIS 3.16 - * \deprecated since QGIS 3.18 use config() instead - */ - Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const SIP_DEPRECATED {return mButtons;} - /** * Determines the force suppress form popup status. * \since QGIS 3.16