Skip to content

Commit 0559e46

Browse files
committed
Follow up map layer action changes
- Switch to flags instead of boolean argument - Move logic for layer validity to canRunUsingLayer - Add unit test Also remove settings flag to hide duplicate features action
1 parent 2520c28 commit 0559e46

11 files changed

+212
-69
lines changed

python/gui/qgsmaplayeractionregistry.sip.in

+28-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ An action which can run on map layers
3030
typedef QFlags<QgsMapLayerAction::Target> Targets;
3131

3232

33-
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon() );
33+
enum Flag
34+
{
35+
EnabledOnlyWhenEditable,
36+
};
37+
38+
typedef QFlags<QgsMapLayerAction::Flag> Flags;
39+
40+
41+
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
3442
%Docstring
3543
Creates a map layer action which can run on any layer
3644

@@ -39,18 +47,25 @@ Creates a map layer action which can run on any layer
3947
using AllActions as a target probably does not make a lot of sense. This default action was settled for API compatibility reasons.
4048
%End
4149

42-
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon() );
50+
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
4351
%Docstring
4452
Creates a map layer action which can run only on a specific layer
4553
%End
4654

47-
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon() );
55+
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
4856
%Docstring
4957
Creates a map layer action which can run on a specific type of layer
5058
%End
5159

5260
~QgsMapLayerAction();
5361

62+
QgsMapLayerAction::Flags flags() const;
63+
%Docstring
64+
Layer behavior flags.
65+
66+
.. versionadded:: 3.0
67+
%End
68+
5469
bool canRunUsingLayer( QgsMapLayer *layer ) const;
5570
%Docstring
5671
True if action can run using the specified layer
@@ -78,6 +93,13 @@ Define the targets of the action
7893
const Targets &targets() const;
7994
%Docstring
8095
Return availibity of action
96+
%End
97+
98+
bool isEnabledOnlyWhenEditable() const;
99+
%Docstring
100+
Returns true if the action is only enabled for layers in editable mode.
101+
102+
.. versionadded:: 3.0
81103
%End
82104

83105
signals:
@@ -158,6 +180,9 @@ Triggered when an action is added or removed from the registry
158180

159181
};
160182

183+
QFlags<QgsMapLayerAction::Flag> operator|(QgsMapLayerAction::Flag f1, QFlags<QgsMapLayerAction::Flag> f2);
184+
185+
161186
/************************************************************************
162187
* This file has been generated automatically from *
163188
* *

resources/qgis_global_settings.ini

-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,3 @@ connections-xyz\OpenStreetMap\zmin=0
3434
# for now this is online version of the User Guide for latest (LTR) release
3535
helpSearchPath=https://docs.qgis.org/$qgis_short_version/$qgis_locale/docs/user_manual/
3636

37-
[app]
38-
39-
# If true, the experimental "duplicate feature" actions will be shown in the QGIS UI
40-
tools\showDuplicateFeatureActions=false

src/app/qgisapp.cpp

+17-24
Original file line numberDiff line numberDiff line change
@@ -6488,9 +6488,6 @@ void QgisApp::refreshFeatureActions()
64886488

64896489
for ( int i = 0; i < registeredActions.size(); i++ )
64906490
{
6491-
if ( !vlayer->isEditable() && registeredActions.at( i )->isEnabledOnlyWhenEditable() )
6492-
continue;
6493-
64946491
mFeatureActionMenu->addAction( registeredActions.at( i ) );
64956492
if ( registeredActions.at( i ) == QgsGui::mapLayerActionRegistry()->defaultActionForLayer( vlayer ) )
64966493
{
@@ -7498,31 +7495,27 @@ void QgisApp::setupLayoutManagerConnections()
74987495

74997496
void QgisApp::setupDuplicateFeaturesAction()
75007497
{
7501-
QgsSettings settings;
7502-
if ( settings.value( QStringLiteral( "tools/showDuplicateFeatureActions" ), false, QgsSettings::App ).toBool() )
7503-
{
7504-
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
7505-
nullptr, QgsMapLayerAction::SingleFeature,
7506-
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ), true ) );
7498+
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
7499+
nullptr, QgsMapLayerAction::SingleFeature,
7500+
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );
75077501

7508-
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
7509-
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
7510-
{
7511-
duplicateFeatures( layer, feat );
7512-
}
7513-
);
7502+
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
7503+
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
7504+
{
7505+
duplicateFeatures( layer, feat );
7506+
}
7507+
);
75147508

7515-
mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
7516-
nullptr, QgsMapLayerAction::SingleFeature,
7517-
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ), true ) );
7509+
mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
7510+
nullptr, QgsMapLayerAction::SingleFeature,
7511+
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );
75187512

7519-
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
7520-
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
7521-
{
7522-
duplicateFeatureDigitized( layer, feat );
7523-
}
7524-
);
7513+
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
7514+
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
7515+
{
7516+
duplicateFeatureDigitized( layer, feat );
75257517
}
7518+
);
75267519
}
75277520

75287521
void QgisApp::setupAtlasMapLayerAction( QgsPrintLayout *layout, bool enableAction )

src/gui/attributetable/qgsattributetableview.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,6 @@ QWidget *QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
201201
QgsGui::mapLayerActionRegistry()->mapLayerActions( mFilterModel->layer(),
202202
QgsMapLayerAction::SingleFeature ) )
203203
{
204-
if ( !mFilterModel->layer()->isEditable() && mapLayerAction->isEnabledOnlyWhenEditable() )
205-
continue;
206-
207204
QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
208205
action->setData( "map_layer_action" );
209206
action->setToolTip( mapLayerAction->text() );

src/gui/attributetable/qgsdualview.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -592,9 +592,6 @@ void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atInd
592592

593593
Q_FOREACH ( QgsMapLayerAction *action, registeredActions )
594594
{
595-
if ( !vl->isEditable() && action->isEnabledOnlyWhenEditable() )
596-
continue;
597-
598595
QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, sourceIndex );
599596
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
600597
menu->addAction( action->text(), a, SLOT( execut() ) );

src/gui/qgsactionmenu.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,6 @@ void QgsActionMenu::reloadActions()
154154
{
155155
QgsMapLayerAction *qaction = mapLayerActions.at( i );
156156

157-
if ( !mLayer->isEditable() && qaction->isEnabledOnlyWhenEditable() )
158-
continue;
159-
160157
if ( qaction->isEnabledOnlyWhenEditable() && ( mMode == QgsAttributeForm::AddFeatureMode || mMode == QgsAttributeForm::IdentifyMode ) )
161158
continue;
162159

src/gui/qgsidentifymenu.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapT
346346
if ( mShowFeatureActions )
347347
{
348348
featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
349+
featureActionMenu->setMode( QgsAttributeForm::IdentifyMode );
349350
featureActionMenu->setExpressionContextScope( mExpressionContextScope );
350351
}
351352

src/gui/qgsmaplayeractionregistry.cpp

+29-7
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,36 @@
1515

1616
#include "qgsmaplayeractionregistry.h"
1717
#include "qgsgui.h"
18+
#include "qgsvectorlayer.h"
1819

19-
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
20+
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
2021
: QAction( icon, name, parent )
2122
, mSingleLayer( false )
2223
, mSpecificLayerType( false )
2324
, mLayerType( QgsMapLayer::VectorLayer )
2425
, mTargets( targets )
25-
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
26+
, mFlags( flags )
2627
{
2728
}
2829

29-
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer *layer, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
30+
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer *layer, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
3031
: QAction( icon, name, parent )
3132
, mSingleLayer( true )
3233
, mActionLayer( layer )
3334
, mSpecificLayerType( false )
3435
, mLayerType( QgsMapLayer::VectorLayer )
3536
, mTargets( targets )
36-
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
37+
, mFlags( flags )
3738
{
3839
}
3940

40-
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer::LayerType layerType, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
41+
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer::LayerType layerType, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
4142
: QAction( icon, name, parent )
4243
, mSingleLayer( false )
4344
, mSpecificLayerType( true )
4445
, mLayerType( layerType )
4546
, mTargets( targets )
46-
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
47+
, mFlags( flags )
4748
{
4849
}
4950

@@ -53,8 +54,24 @@ QgsMapLayerAction::~QgsMapLayerAction()
5354
QgsGui::mapLayerActionRegistry()->removeMapLayerAction( this );
5455
}
5556

57+
QgsMapLayerAction::Flags QgsMapLayerAction::flags() const
58+
{
59+
return mFlags;
60+
}
61+
5662
bool QgsMapLayerAction::canRunUsingLayer( QgsMapLayer *layer ) const
5763
{
64+
if ( mFlags & EnabledOnlyWhenEditable )
65+
{
66+
// action is only enabled for editable layers
67+
if ( !layer )
68+
return false;
69+
if ( layer->type() != QgsMapLayer::VectorLayer )
70+
return false;
71+
if ( !qobject_cast<QgsVectorLayer *>( layer )->isEditable() )
72+
return false;
73+
}
74+
5875
//check layer details
5976
if ( !mSingleLayer && !mSpecificLayerType )
6077
{
@@ -67,7 +84,7 @@ bool QgsMapLayerAction::canRunUsingLayer( QgsMapLayer *layer ) const
6784
//action is a single layer type and layer matches
6885
return true;
6986
}
70-
else if ( mSpecificLayerType && layer->type() == mLayerType )
87+
else if ( mSpecificLayerType && layer && layer->type() == mLayerType )
7188
{
7289
//action is for a layer type and layer type matches
7390
return true;
@@ -91,6 +108,11 @@ void QgsMapLayerAction::triggerForLayer( QgsMapLayer *layer )
91108
emit triggeredForLayer( layer );
92109
}
93110

111+
bool QgsMapLayerAction::isEnabledOnlyWhenEditable() const
112+
{
113+
return mFlags & EnabledOnlyWhenEditable;
114+
}
115+
94116
//
95117
// Main class begins now...
96118
//

src/gui/qgsmaplayeractionregistry.h

+40-22
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,42 @@ class GUI_EXPORT QgsMapLayerAction : public QAction
4646
Q_DECLARE_FLAGS( Targets, Target )
4747
Q_FLAG( Targets )
4848

49+
/**
50+
* Flags which control action behavior
51+
* /since QGIS 3.0
52+
*/
53+
enum Flag
54+
{
55+
EnabledOnlyWhenEditable = 1 << 1, //!< Action should be shown only for editable layers
56+
};
57+
58+
/**
59+
* Action behavior flags.
60+
* \since QGIS 3.0
61+
*/
62+
Q_DECLARE_FLAGS( Flags, Flag )
63+
Q_FLAG( Flags )
64+
4965
/**
5066
* Creates a map layer action which can run on any layer
5167
* \note using AllActions as a target probably does not make a lot of sense. This default action was settled for API compatibility reasons.
5268
*/
53-
#ifndef SIP_RUN
54-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
55-
#else
56-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon() );
57-
#endif
69+
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );
70+
5871
//! Creates a map layer action which can run only on a specific layer
59-
#ifndef SIP_RUN
60-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
61-
#else
62-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon() );
63-
#endif
72+
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );
73+
6474
//! Creates a map layer action which can run on a specific type of layer
65-
#ifndef SIP_RUN
66-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
67-
#else
68-
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon() );
69-
#endif
75+
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );
76+
7077
~QgsMapLayerAction() override;
7178

79+
/**
80+
* Layer behavior flags.
81+
* \since QGIS 3.0
82+
*/
83+
QgsMapLayerAction::Flags flags() const;
84+
7285
//! True if action can run using the specified layer
7386
bool canRunUsingLayer( QgsMapLayer *layer ) const;
7487

@@ -86,8 +99,11 @@ class GUI_EXPORT QgsMapLayerAction : public QAction
8699
//! Return availibity of action
87100
const Targets &targets() const {return mTargets;}
88101

89-
//! Return whether only enabled in editable mode
90-
bool isEnabledOnlyWhenEditable() const { return mEnabledOnlyWhenEditable; }
102+
/**
103+
* Returns true if the action is only enabled for layers in editable mode.
104+
* \since QGIS 3.0
105+
*/
106+
bool isEnabledOnlyWhenEditable() const;
91107

92108
signals:
93109
//! Triggered when action has been run for a specific list of features
@@ -102,19 +118,19 @@ class GUI_EXPORT QgsMapLayerAction : public QAction
102118
private:
103119

104120
// true if action is only valid for a single layer
105-
bool mSingleLayer;
121+
bool mSingleLayer = false;
106122
// layer if action is only valid for a single layer
107123
QgsMapLayer *mActionLayer = nullptr;
108124

109125
// true if action is only valid for a specific layer type
110-
bool mSpecificLayerType;
126+
bool mSpecificLayerType = false;
111127
// layer type if action is only valid for a specific layer type
112-
QgsMapLayer::LayerType mLayerType;
128+
QgsMapLayer::LayerType mLayerType = QgsMapLayer::VectorLayer;
113129

114130
// determine if the action can be run on layer and/or single feature and/or multiple features
115-
Targets mTargets;
131+
Targets mTargets = nullptr;
116132

117-
bool mEnabledOnlyWhenEditable;
133+
QgsMapLayerAction::Flags mFlags = nullptr;
118134
};
119135

120136
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapLayerAction::Targets )
@@ -168,4 +184,6 @@ class GUI_EXPORT QgsMapLayerActionRegistry : public QObject
168184

169185
};
170186

187+
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapLayerAction::Flags )
188+
171189
#endif // QGSMAPLAYERACTIONREGISTRY_H

tests/src/python/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ ADD_PYTHON_TEST(PyQgsLocator test_qgslocator.py)
101101
ADD_PYTHON_TEST(PyQgsMapCanvas test_qgsmapcanvas.py)
102102
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
103103
ADD_PYTHON_TEST(PyQgsMapLayer test_qgsmaplayer.py)
104+
ADD_PYTHON_TEST(PyQgsMapLayerAction test_qgsmaplayeraction.py)
104105
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
105106
ADD_PYTHON_TEST(PyQgsMapLayerStore test_qgsmaplayerstore.py)
106107
ADD_PYTHON_TEST(PyQgsMapRenderer test_qgsmaprenderer.py)

0 commit comments

Comments
 (0)