diff --git a/python/PyQt6/gui/auto_generated/qgisinterface.sip.in b/python/PyQt6/gui/auto_generated/qgisinterface.sip.in index 3feaca5784135..751b44266d213 100644 --- a/python/PyQt6/gui/auto_generated/qgisinterface.sip.in +++ b/python/PyQt6/gui/auto_generated/qgisinterface.sip.in @@ -63,6 +63,11 @@ 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; @@ -70,6 +75,14 @@ in order for this method to have any effect. 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 mapCanvases() = 0; diff --git a/python/gui/auto_generated/qgisinterface.sip.in b/python/gui/auto_generated/qgisinterface.sip.in index 3feaca5784135..751b44266d213 100644 --- a/python/gui/auto_generated/qgisinterface.sip.in +++ b/python/gui/auto_generated/qgisinterface.sip.in @@ -63,6 +63,11 @@ 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; @@ -70,6 +75,14 @@ in order for this method to have any effect. 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 mapCanvases() = 0; diff --git a/src/app/qgisappinterface.cpp b/src/app/qgisappinterface.cpp index 6b8617c50a71e..5f3f66d5ae585 100644 --- a/src/app/qgisappinterface.cpp +++ b/src/app/qgisappinterface.cpp @@ -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( qgis->layerTreeView()->menuProvider() ); + if ( !menuProvider ) + return; + + menuProvider->addLegendLayerActionForGroup( action, menu ); +} + bool QgisAppInterface::removeCustomActionForLayerType( QAction *action ) { QgsAppLayerTreeViewMenuProvider *menuProvider = dynamic_cast( qgis->layerTreeView()->menuProvider() ); @@ -103,6 +112,15 @@ bool QgisAppInterface::removeCustomActionForLayerType( QAction *action ) return menuProvider->removeLegendLayerAction( action ); } +bool QgisAppInterface::removeCustomActionForGroups( QAction *action ) +{ + QgsAppLayerTreeViewMenuProvider *menuProvider = dynamic_cast( qgis->layerTreeView()->menuProvider() ); + if ( !menuProvider ) + return false; + + return menuProvider->removeLegendLayerActionForGroup( action ); +} + void QgisAppInterface::zoomFull() { qgis->zoomFull(); diff --git a/src/app/qgisappinterface.h b/src/app/qgisappinterface.h index ca0adceab7a58..798f0b8b7fe65 100644 --- a/src/app/qgisappinterface.h +++ b/src/app/qgisappinterface.h @@ -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 */ diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 5c9cee6548624..37257b33175e4 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -105,6 +105,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 ); @@ -1015,6 +1017,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 QgsAppLayerTreeViewMenuProvider::legendLayerActions( Qgis::LayerType type ) const { #ifdef QGISDEBUG @@ -1034,6 +1063,24 @@ QList QgsAppLayerTreeViewMenuProvider::legendLayerActions( Qg return mLegendLayerActionMap.contains( type ) ? mLegendLayerActionMap.value( type ) : QList(); } +QList QgsAppLayerTreeViewMenuProvider::groupLegendLayerActions() const +{ + return mGroupLegendLayerActionList; +} + +QList QgsAppLayerTreeViewMenuProvider::groupMenuActions( const QString &menu ) const +{ + QList 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 ) @@ -1044,63 +1091,76 @@ void QgsAppLayerTreeViewMenuProvider::addCustomLayerActions( QMenu *menu, QgsMap if ( !lyrActions.isEmpty() ) { - menu->addSeparator(); - QList 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 groupActions = groupLegendLayerActions(); + + if ( !groupActions.isEmpty() ) + { + addCustomActionsToMenu( menu, groupActions ); + } +} + +void QgsAppLayerTreeViewMenuProvider::addCustomActionsToMenu( QMenu *menu, const QList &customActions ) +{ + menu->addSeparator(); + QList 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 ) diff --git a/src/app/qgsapplayertreeviewmenuprovider.h b/src/app/qgsapplayertreeviewmenuprovider.h index 1a0f3a7c0144c..8a2f6e0bde82e 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.h +++ b/src/app/qgsapplayertreeviewmenuprovider.h @@ -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 legendLayerActions( Qgis::LayerType type ) const; + QList groupLegendLayerActions() const; + QList groupMenuActions( const QString &menu ) const; protected: void addCustomLayerActions( QMenu *menu, QgsMapLayer *layer ); + void addCustomGroupActions( QMenu *menu ); + void addCustomActionsToMenu( QMenu *menu, const QList &customActions ); QgsLayerTreeView *mView = nullptr; QgsMapCanvas *mCanvas = nullptr; QMap> mLegendLayerActionMap; - + QList mGroupLegendLayerActionList; private slots: void editVectorSymbol( const QString &layerId ); diff --git a/src/gui/qgisinterface.h b/src/gui/qgisinterface.h index 8f115fa03d786..f8d15d1cf464a 100644 --- a/src/gui/qgisinterface.h +++ b/src/gui/qgisinterface.h @@ -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. */