Skip to content

Commit

Permalink
add possibility to add custom actions for the group-nodes in the laye…
Browse files Browse the repository at this point in the history
…r tree
  • Loading branch information
notguiltyspark committed Jan 1, 2025
1 parent 340861d commit 1b99119
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 49 deletions.
13 changes: 13 additions & 0 deletions python/PyQt6/gui/auto_generated/qgisinterface.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,26 @@ It is necessary to first call :py:func:`~QgisInterface.addCustomActionForLayerTy
in order for this method to have any effect.

.. seealso:: :py:func:`addCustomActionForLayerType`
%End

virtual void addCustomActionForGroups( QAction *action, const QString &menu ) = 0;
%Docstring
Add action to context menu for all group-nodes in the layer tree.
%End

virtual bool removeCustomActionForLayerType( QAction *action ) = 0;
%Docstring
Remove action for layers in the layer tree previously added with :py:func:`~QgisInterface.addCustomActionForLayerType`

.. seealso:: :py:func:`addCustomActionForLayerType`
%End

virtual bool removeCustomActionForGroups( QAction *action ) = 0;
%Docstring
Remove action from the context menu for all group-nodes in the layer tree
previously added with :py:func:`~QgisInterface.addCustomActionForGroups`.

.. seealso:: :py:func:`addCustomActionForGroups`
%End

virtual QList<QgsMapCanvas *> mapCanvases() = 0;
Expand Down
13 changes: 13 additions & 0 deletions python/gui/auto_generated/qgisinterface.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,26 @@ It is necessary to first call :py:func:`~QgisInterface.addCustomActionForLayerTy
in order for this method to have any effect.

.. seealso:: :py:func:`addCustomActionForLayerType`
%End

virtual void addCustomActionForGroups( QAction *action, const QString &menu ) = 0;
%Docstring
Add action to context menu for all group-nodes in the layer tree.
%End

virtual bool removeCustomActionForLayerType( QAction *action ) = 0;
%Docstring
Remove action for layers in the layer tree previously added with :py:func:`~QgisInterface.addCustomActionForLayerType`

.. seealso:: :py:func:`addCustomActionForLayerType`
%End

virtual bool removeCustomActionForGroups( QAction *action ) = 0;
%Docstring
Remove action from the context menu for all group-nodes in the layer tree
previously added with :py:func:`~QgisInterface.addCustomActionForGroups`.

.. seealso:: :py:func:`addCustomActionForGroups`
%End

virtual QList<QgsMapCanvas *> mapCanvases() = 0;
Expand Down
18 changes: 18 additions & 0 deletions src/app/qgisappinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ void QgisAppInterface::addCustomActionForLayer( QAction *action, QgsMapLayer *la
menuProvider->addLegendLayerActionForLayer( action, layer );
}

void QgisAppInterface::addCustomActionForGroups( QAction *action, const QString &menu )
{
QgsAppLayerTreeViewMenuProvider *menuProvider = dynamic_cast<QgsAppLayerTreeViewMenuProvider *>( qgis->layerTreeView()->menuProvider() );
if ( !menuProvider )
return;

menuProvider->addLegendLayerActionForGroup( action, menu );
}

bool QgisAppInterface::removeCustomActionForLayerType( QAction *action )
{
QgsAppLayerTreeViewMenuProvider *menuProvider = dynamic_cast<QgsAppLayerTreeViewMenuProvider *>( qgis->layerTreeView()->menuProvider() );
Expand All @@ -103,6 +112,15 @@ bool QgisAppInterface::removeCustomActionForLayerType( QAction *action )
return menuProvider->removeLegendLayerAction( action );
}

bool QgisAppInterface::removeCustomActionForGroups( QAction *action )
{
QgsAppLayerTreeViewMenuProvider *menuProvider = dynamic_cast<QgsAppLayerTreeViewMenuProvider *>( qgis->layerTreeView()->menuProvider() );
if ( !menuProvider )
return false;

return menuProvider->removeLegendLayerActionForGroup( action );
}

void QgisAppInterface::zoomFull()
{
qgis->zoomFull();
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgisappinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class APP_EXPORT QgisAppInterface : public QgisInterface

void addCustomActionForLayerType( QAction *action, QString menu, Qgis::LayerType type, bool allLayers ) override;
void addCustomActionForLayer( QAction *action, QgsMapLayer *layer ) override;
void addCustomActionForGroups( QAction *action, const QString &menu ) override;
bool removeCustomActionForLayerType( QAction *action ) override;
bool removeCustomActionForGroups( QAction *action ) override;

/* Exposed functions */

Expand Down
156 changes: 108 additions & 48 deletions src/app/qgsapplayertreeviewmenuprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()

menu->addAction( actions->actionRenameGroupOrLayer( menu ) );

addCustomGroupActions( menu );

menu->addSeparator();
menu->addAction( actions->actionAddGroup( menu ) );
QAction *removeAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ), tr( "&Remove Group…" ), QgisApp::instance(), &QgisApp::removeLayer );
Expand Down Expand Up @@ -1017,6 +1019,33 @@ void QgsAppLayerTreeViewMenuProvider::removeLegendLayerActionsForLayer( QgsMapLa
}
}

void QgsAppLayerTreeViewMenuProvider::addLegendLayerActionForGroup( QAction *action, const QString &menu )
{
mGroupLegendLayerActionList.append( LegendLayerAction( action, menu, false ) );
}

bool QgsAppLayerTreeViewMenuProvider::removeLegendLayerActionForGroup( QAction *action )
{
size_t beforeRemoveSize = mGroupLegendLayerActionList.size();
mGroupLegendLayerActionList.erase( std::remove_if( mGroupLegendLayerActionList.begin(), mGroupLegendLayerActionList.end(), [&]( const LegendLayerAction &lla ) {
return lla.action == action;
} ),
mGroupLegendLayerActionList.end() );
size_t afterRemoveSize = mGroupLegendLayerActionList.size();
return afterRemoveSize < beforeRemoveSize;
}

bool QgsAppLayerTreeViewMenuProvider::removeLegendLayerActionsForGroup( const QString &menu )
{
size_t beforeRemoveSize = mGroupLegendLayerActionList.size();
mGroupLegendLayerActionList.erase( std::remove_if( mGroupLegendLayerActionList.begin(), mGroupLegendLayerActionList.end(), [&]( const LegendLayerAction &lla ) {
return lla.menu == menu;
} ),
mGroupLegendLayerActionList.end() );
size_t afterRemoveSize = mGroupLegendLayerActionList.size();
return afterRemoveSize < beforeRemoveSize;
}

QList<LegendLayerAction> QgsAppLayerTreeViewMenuProvider::legendLayerActions( Qgis::LayerType type ) const
{
#ifdef QGISDEBUG
Expand All @@ -1036,6 +1065,24 @@ QList<LegendLayerAction> QgsAppLayerTreeViewMenuProvider::legendLayerActions( Qg
return mLegendLayerActionMap.contains( type ) ? mLegendLayerActionMap.value( type ) : QList<LegendLayerAction>();
}

QList<LegendLayerAction> QgsAppLayerTreeViewMenuProvider::groupLegendLayerActions() const
{
return mGroupLegendLayerActionList;
}

QList<QAction *> QgsAppLayerTreeViewMenuProvider::groupMenuActions( const QString &menu ) const
{
QList<QAction *> actionForGroupMenuList;
for ( const auto &groupLegendLayerAction : mGroupLegendLayerActionList )
{
if ( groupLegendLayerAction.menu == menu )
{
actionForGroupMenuList.push_back( groupLegendLayerAction.action );
}
}
return actionForGroupMenuList;
}

void QgsAppLayerTreeViewMenuProvider::addCustomLayerActions( QMenu *menu, QgsMapLayer *layer )
{
if ( !layer )
Expand All @@ -1046,63 +1093,76 @@ void QgsAppLayerTreeViewMenuProvider::addCustomLayerActions( QMenu *menu, QgsMap

if ( !lyrActions.isEmpty() )
{
menu->addSeparator();
QList<QMenu *> menus;
for ( int i = 0; i < lyrActions.count(); i++ )
addCustomActionsToMenu( menu, lyrActions );
}
}

void QgsAppLayerTreeViewMenuProvider::addCustomGroupActions( QMenu *menu )
{
// add custom group actions - should this go at end?
QList<LegendLayerAction> groupActions = groupLegendLayerActions();

if ( !groupActions.isEmpty() )
{
addCustomActionsToMenu( menu, groupActions );
}
}

void QgsAppLayerTreeViewMenuProvider::addCustomActionsToMenu( QMenu *menu, const QList<LegendLayerAction> &customActions )
{
menu->addSeparator();
QList<QMenu *> menus;
for ( int i = 0; i < customActions.count(); i++ )
{
if ( customActions[i].menu.isEmpty() )
{
menu->addAction( customActions[i].action );
}
else
{
if ( lyrActions[i].allLayers || lyrActions[i].layers.contains( layer ) )
// find or create menu for given menu name
// adapted from QgisApp::getPluginMenu( QString menuName )
QString menuName = customActions[i].menu;
#ifdef Q_OS_MAC
// Mac doesn't have '&' keyboard shortcuts.
menuName.remove( QChar( '&' ) );
#endif
QAction *before = nullptr;
QMenu *newMenu = nullptr;
QString dst = menuName;
dst.remove( QChar( '&' ) );
const auto constMenus = menus;
for ( QMenu *menu : constMenus )
{
if ( lyrActions[i].menu.isEmpty() )
QString src = menu->title();
src.remove( QChar( '&' ) );
const int comp = dst.localeAwareCompare( src );
if ( comp < 0 )
{
menu->addAction( lyrActions[i].action );
// Add item before this one
before = menu->menuAction();
break;
}
else
else if ( comp == 0 )
{
// find or create menu for given menu name
// adapted from QgisApp::getPluginMenu( QString menuName )
QString menuName = lyrActions[i].menu;
#ifdef Q_OS_MAC
// Mac doesn't have '&' keyboard shortcuts.
menuName.remove( QChar( '&' ) );
#endif
QAction *before = nullptr;
QMenu *newMenu = nullptr;
QString dst = menuName;
dst.remove( QChar( '&' ) );
const auto constMenus = menus;
for ( QMenu *menu : constMenus )
{
QString src = menu->title();
src.remove( QChar( '&' ) );
const int comp = dst.localeAwareCompare( src );
if ( comp < 0 )
{
// Add item before this one
before = menu->menuAction();
break;
}
else if ( comp == 0 )
{
// Plugin menu item already exists
newMenu = menu;
break;
}
}
if ( !newMenu )
{
// It doesn't exist, so create
newMenu = new QMenu( menuName );
menus.append( newMenu );
// Where to put it? - we worked that out above...
menu->insertMenu( before, newMenu );
}
// QMenu* menu = getMenu( lyrActions[i].menu, &beforeSep, &afterSep, &menu );
newMenu->addAction( lyrActions[i].action );
// Plugin menu item already exists
newMenu = menu;
break;
}
}
if ( !newMenu )
{
// It doesn't exist, so create
newMenu = new QMenu( menuName );
menus.append( newMenu );
// Where to put it? - we worked that out above...
menu->insertMenu( before, newMenu );
}
// QMenu* menu = getMenu( lyrActions[i].menu, &beforeSep, &afterSep, &menu );
newMenu->addAction( customActions[i].action );
}
menu->addSeparator();
}
menu->addSeparator();
}

void QgsAppLayerTreeViewMenuProvider::editVectorSymbol( const QString &layerId )
Expand Down
9 changes: 8 additions & 1 deletion src/app/qgsapplayertreeviewmenuprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,23 @@ class QgsAppLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewM
bool removeLegendLayerAction( QAction *action );
void addLegendLayerActionForLayer( QAction *action, QgsMapLayer *layer );
void removeLegendLayerActionsForLayer( QgsMapLayer *layer );
void addLegendLayerActionForGroup( QAction *action, const QString &menu );
bool removeLegendLayerActionForGroup( QAction *action );
bool removeLegendLayerActionsForGroup( const QString &menu );
QList<LegendLayerAction> legendLayerActions( Qgis::LayerType type ) const;
QList<LegendLayerAction> groupLegendLayerActions() const;
QList<QAction *> groupMenuActions( const QString &menu ) const;

protected:
void addCustomLayerActions( QMenu *menu, QgsMapLayer *layer );
void addCustomGroupActions( QMenu *menu );
void addCustomActionsToMenu( QMenu *menu, const QList<LegendLayerAction> &customActions );

QgsLayerTreeView *mView = nullptr;
QgsMapCanvas *mCanvas = nullptr;

QMap<Qgis::LayerType, QList<LegendLayerAction>> mLegendLayerActionMap;

QList<LegendLayerAction> mGroupLegendLayerActionList;
private slots:

void editVectorSymbol( const QString &layerId );
Expand Down
12 changes: 12 additions & 0 deletions src/gui/qgisinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,24 @@ class GUI_EXPORT QgisInterface : public QObject
*/
virtual void addCustomActionForLayer( QAction *action, QgsMapLayer *layer ) = 0;

/**
* Add action to context menu for all group-nodes in the layer tree.
*/
virtual void addCustomActionForGroups( QAction *action, const QString &menu ) = 0;

/**
* Remove action for layers in the layer tree previously added with addCustomActionForLayerType()
* \see addCustomActionForLayerType()
*/
virtual bool removeCustomActionForLayerType( QAction *action ) = 0;

/**
* Remove action from the context menu for all group-nodes in the layer tree
* previously added with addCustomActionForGroups().
* \see addCustomActionForGroups()
*/
virtual bool removeCustomActionForGroups( QAction *action ) = 0;

/**
* Returns a list of all map canvases open in the app.
*/
Expand Down

0 comments on commit 1b99119

Please sign in to comment.