Skip to content

Commit

Permalink
Follow up map layer action changes
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
nyalldawson committed Feb 24, 2018
1 parent 1bada06 commit f35745f
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 69 deletions.
31 changes: 28 additions & 3 deletions python/gui/qgsmaplayeractionregistry.sip.in
Expand Up @@ -30,7 +30,15 @@ An action which can run on map layers
typedef QFlags<QgsMapLayerAction::Target> Targets;


QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon() );
enum Flag
{
EnabledOnlyWhenEditable,
};

typedef QFlags<QgsMapLayerAction::Flag> Flags;


QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
%Docstring
Creates a map layer action which can run on any layer

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

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

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

~QgsMapLayerAction();

QgsMapLayerAction::Flags flags() const;
%Docstring
Layer behavior flags.

.. versionadded:: 3.0
%End

bool canRunUsingLayer( QgsMapLayer *layer ) const;
%Docstring
True if action can run using the specified layer
Expand Down Expand Up @@ -78,6 +93,13 @@ Define the targets of the action
const Targets &targets() const;
%Docstring
Return availibity of action
%End

bool isEnabledOnlyWhenEditable() const;
%Docstring
Returns true if the action is only enabled for layers in editable mode.

.. versionadded:: 3.0
%End

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

};

QFlags<QgsMapLayerAction::Flag> operator|(QgsMapLayerAction::Flag f1, QFlags<QgsMapLayerAction::Flag> f2);


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
4 changes: 0 additions & 4 deletions resources/qgis_global_settings.ini
Expand Up @@ -34,7 +34,3 @@ connections-xyz\OpenStreetMap\zmin=0
# for now this is online version of the User Guide for latest (LTR) release
helpSearchPath=https://docs.qgis.org/$qgis_short_version/$qgis_locale/docs/user_manual/

[app]

# If true, the experimental "duplicate feature" actions will be shown in the QGIS UI
tools\showDuplicateFeatureActions=false
41 changes: 17 additions & 24 deletions src/app/qgisapp.cpp
Expand Up @@ -6488,9 +6488,6 @@ void QgisApp::refreshFeatureActions()

for ( int i = 0; i < registeredActions.size(); i++ )
{
if ( !vlayer->isEditable() && registeredActions.at( i )->isEnabledOnlyWhenEditable() )
continue;

mFeatureActionMenu->addAction( registeredActions.at( i ) );
if ( registeredActions.at( i ) == QgsGui::mapLayerActionRegistry()->defaultActionForLayer( vlayer ) )
{
Expand Down Expand Up @@ -7498,31 +7495,27 @@ void QgisApp::setupLayoutManagerConnections()

void QgisApp::setupDuplicateFeaturesAction()
{
QgsSettings settings;
if ( settings.value( QStringLiteral( "tools/showDuplicateFeatureActions" ), false, QgsSettings::App ).toBool() )
{
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ), true ) );
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );

QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatures( layer, feat );
}
);
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatures( layer, feat );
}
);

mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ), true ) );
mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );

QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatureDigitized( layer, feat );
}
);
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatureDigitized( layer, feat );
}
);
}

void QgisApp::setupAtlasMapLayerAction( QgsPrintLayout *layout, bool enableAction )
Expand Down
3 changes: 0 additions & 3 deletions src/gui/attributetable/qgsattributetableview.cpp
Expand Up @@ -201,9 +201,6 @@ QWidget *QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
QgsGui::mapLayerActionRegistry()->mapLayerActions( mFilterModel->layer(),
QgsMapLayerAction::SingleFeature ) )
{
if ( !mFilterModel->layer()->isEditable() && mapLayerAction->isEnabledOnlyWhenEditable() )
continue;

QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
action->setData( "map_layer_action" );
action->setToolTip( mapLayerAction->text() );
Expand Down
3 changes: 0 additions & 3 deletions src/gui/attributetable/qgsdualview.cpp
Expand Up @@ -592,9 +592,6 @@ void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atInd

Q_FOREACH ( QgsMapLayerAction *action, registeredActions )
{
if ( !vl->isEditable() && action->isEnabledOnlyWhenEditable() )
continue;

QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, sourceIndex );
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
menu->addAction( action->text(), a, SLOT( execut() ) );
Expand Down
3 changes: 0 additions & 3 deletions src/gui/qgsactionmenu.cpp
Expand Up @@ -154,9 +154,6 @@ void QgsActionMenu::reloadActions()
{
QgsMapLayerAction *qaction = mapLayerActions.at( i );

if ( !mLayer->isEditable() && qaction->isEnabledOnlyWhenEditable() )
continue;

if ( qaction->isEnabledOnlyWhenEditable() && ( mMode == QgsAttributeForm::AddFeatureMode || mMode == QgsAttributeForm::IdentifyMode ) )
continue;

Expand Down
1 change: 1 addition & 0 deletions src/gui/qgsidentifymenu.cpp
Expand Up @@ -346,6 +346,7 @@ void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapT
if ( mShowFeatureActions )
{
featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
featureActionMenu->setMode( QgsAttributeForm::IdentifyMode );
featureActionMenu->setExpressionContextScope( mExpressionContextScope );
}

Expand Down
36 changes: 29 additions & 7 deletions src/gui/qgsmaplayeractionregistry.cpp
Expand Up @@ -15,35 +15,36 @@

#include "qgsmaplayeractionregistry.h"
#include "qgsgui.h"
#include "qgsvectorlayer.h"

QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
: QAction( icon, name, parent )
, mSingleLayer( false )
, mSpecificLayerType( false )
, mLayerType( QgsMapLayer::VectorLayer )
, mTargets( targets )
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
, mFlags( flags )
{
}

QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer *layer, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer *layer, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
: QAction( icon, name, parent )
, mSingleLayer( true )
, mActionLayer( layer )
, mSpecificLayerType( false )
, mLayerType( QgsMapLayer::VectorLayer )
, mTargets( targets )
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
, mFlags( flags )
{
}

QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer::LayerType layerType, Targets targets, const QIcon &icon, const bool enabledOnlyWhenEditable )
QgsMapLayerAction::QgsMapLayerAction( const QString &name, QObject *parent, QgsMapLayer::LayerType layerType, Targets targets, const QIcon &icon, QgsMapLayerAction::Flags flags )
: QAction( icon, name, parent )
, mSingleLayer( false )
, mSpecificLayerType( true )
, mLayerType( layerType )
, mTargets( targets )
, mEnabledOnlyWhenEditable( enabledOnlyWhenEditable )
, mFlags( flags )
{
}

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

QgsMapLayerAction::Flags QgsMapLayerAction::flags() const
{
return mFlags;
}

bool QgsMapLayerAction::canRunUsingLayer( QgsMapLayer *layer ) const
{
if ( mFlags & EnabledOnlyWhenEditable )
{
// action is only enabled for editable layers
if ( !layer )
return false;
if ( layer->type() != QgsMapLayer::VectorLayer )
return false;
if ( !qobject_cast<QgsVectorLayer *>( layer )->isEditable() )
return false;
}

//check layer details
if ( !mSingleLayer && !mSpecificLayerType )
{
Expand All @@ -67,7 +84,7 @@ bool QgsMapLayerAction::canRunUsingLayer( QgsMapLayer *layer ) const
//action is a single layer type and layer matches
return true;
}
else if ( mSpecificLayerType && layer->type() == mLayerType )
else if ( mSpecificLayerType && layer && layer->type() == mLayerType )
{
//action is for a layer type and layer type matches
return true;
Expand All @@ -91,6 +108,11 @@ void QgsMapLayerAction::triggerForLayer( QgsMapLayer *layer )
emit triggeredForLayer( layer );
}

bool QgsMapLayerAction::isEnabledOnlyWhenEditable() const
{
return mFlags & EnabledOnlyWhenEditable;
}

//
// Main class begins now...
//
Expand Down
62 changes: 40 additions & 22 deletions src/gui/qgsmaplayeractionregistry.h
Expand Up @@ -46,29 +46,42 @@ class GUI_EXPORT QgsMapLayerAction : public QAction
Q_DECLARE_FLAGS( Targets, Target )
Q_FLAG( Targets )

/**
* Flags which control action behavior
* /since QGIS 3.0
*/
enum Flag
{
EnabledOnlyWhenEditable = 1 << 1, //!< Action should be shown only for editable layers
};

/**
* Action behavior flags.
* \since QGIS 3.0
*/
Q_DECLARE_FLAGS( Flags, Flag )
Q_FLAG( Flags )

/**
* Creates a map layer action which can run on any layer
* \note using AllActions as a target probably does not make a lot of sense. This default action was settled for API compatibility reasons.
*/
#ifndef SIP_RUN
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
#else
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon() );
#endif
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );

//! Creates a map layer action which can run only on a specific layer
#ifndef SIP_RUN
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
#else
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon() );
#endif
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );

//! Creates a map layer action which can run on a specific type of layer
#ifndef SIP_RUN
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), const bool enabledOnlyWhenEditable = false );
#else
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon() );
#endif
QgsMapLayerAction( const QString &name, QObject *parent SIP_TRANSFERTHIS, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = nullptr );

~QgsMapLayerAction() override;

/**
* Layer behavior flags.
* \since QGIS 3.0
*/
QgsMapLayerAction::Flags flags() const;

//! True if action can run using the specified layer
bool canRunUsingLayer( QgsMapLayer *layer ) const;

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

//! Return whether only enabled in editable mode
bool isEnabledOnlyWhenEditable() const { return mEnabledOnlyWhenEditable; }
/**
* Returns true if the action is only enabled for layers in editable mode.
* \since QGIS 3.0
*/
bool isEnabledOnlyWhenEditable() const;

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

// true if action is only valid for a single layer
bool mSingleLayer;
bool mSingleLayer = false;
// layer if action is only valid for a single layer
QgsMapLayer *mActionLayer = nullptr;

// true if action is only valid for a specific layer type
bool mSpecificLayerType;
bool mSpecificLayerType = false;
// layer type if action is only valid for a specific layer type
QgsMapLayer::LayerType mLayerType;
QgsMapLayer::LayerType mLayerType = QgsMapLayer::VectorLayer;

// determine if the action can be run on layer and/or single feature and/or multiple features
Targets mTargets;
Targets mTargets = nullptr;

bool mEnabledOnlyWhenEditable;
QgsMapLayerAction::Flags mFlags = nullptr;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapLayerAction::Targets )
Expand Down Expand Up @@ -168,4 +184,6 @@ class GUI_EXPORT QgsMapLayerActionRegistry : public QObject

};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapLayerAction::Flags )

#endif // QGSMAPLAYERACTIONREGISTRY_H
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -101,6 +101,7 @@ ADD_PYTHON_TEST(PyQgsLocator test_qgslocator.py)
ADD_PYTHON_TEST(PyQgsMapCanvas test_qgsmapcanvas.py)
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
ADD_PYTHON_TEST(PyQgsMapLayer test_qgsmaplayer.py)
ADD_PYTHON_TEST(PyQgsMapLayerAction test_qgsmaplayeraction.py)
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
ADD_PYTHON_TEST(PyQgsMapLayerStore test_qgsmaplayerstore.py)
ADD_PYTHON_TEST(PyQgsMapRenderer test_qgsmaprenderer.py)
Expand Down

0 comments on commit f35745f

Please sign in to comment.