From 91f7918e21e5251a149515200b0f566f8f02c85b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 7 Dec 2015 07:39:20 +1100 Subject: [PATCH] [FEATURE] Add a show/hide all context menu for layer tree symbol items Allows toggling on/off all the symbol items for categorized/graduated/ rule based layers via the right click menu on an item. Previously you'd have to manually toggle each item one-by-one. Fix #13458 --- .../layertree/qgslayertreemodellegendnode.sip | 15 +++++ src/app/qgsapplayertreeviewmenuprovider.cpp | 15 ++++- .../layertree/qgslayertreemodellegendnode.cpp | 26 +++++++++ .../layertree/qgslayertreemodellegendnode.h | 20 +++++++ tests/src/core/testqgslayertree.cpp | 58 +++++++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/python/core/layertree/qgslayertreemodellegendnode.sip b/python/core/layertree/qgslayertreemodellegendnode.sip index 5ab9938dda5..1730e6e7700 100644 --- a/python/core/layertree/qgslayertreemodellegendnode.sip +++ b/python/core/layertree/qgslayertreemodellegendnode.sip @@ -144,6 +144,21 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode //! Get the minimum icon size to prevent cropping //! @note added in 2.10 QSize minimumIconSize() const; + + public slots: + + /** Checks all items belonging to the same layer as this node. + * @note added in QGIS 2.14 + * @see uncheckAllItems() + */ + void checkAllItems(); + + /** Unchecks all items belonging to the same layer as this node. + * @note added in QGIS 2.14 + * @see checkAllItems() + */ + void uncheckAllItems(); + }; diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 68b76c2ce15..8eb4be75c33 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -6,6 +6,7 @@ #include "qgsclipboard.h" #include "qgslayertree.h" #include "qgslayertreemodel.h" +#include "qgslayertreemodellegendnode.h" #include "qgslayertreeviewdefaultactions.h" #include "qgsmaplayerstyleguiutils.h" #include "qgsproject.h" @@ -188,9 +189,19 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu() } } - else + else if ( QgsLayerTreeModelLegendNode* node = mView->layerTreeModel()->index2legendNode( idx ) ) { - // symbology item? + if ( QgsSymbolV2LegendNode* symbolNode = dynamic_cast< QgsSymbolV2LegendNode* >( node ) ) + { + // symbology item + if ( symbolNode->flags() & Qt::ItemIsUserCheckable ) + { + menu->addAction( QgsApplication::getThemeIcon( "/mActionShowAllLayers.png" ), tr( "&Show All Items" ), + symbolNode, SLOT( checkAllItems() ) ); + menu->addAction( QgsApplication::getThemeIcon( "/mActionHideAllLayers.png" ), tr( "&Hide All Items" ), + symbolNode, SLOT( uncheckAllItems() ) ); + } + } } return menu; diff --git a/src/core/layertree/qgslayertreemodellegendnode.cpp b/src/core/layertree/qgslayertreemodellegendnode.cpp index d42c66ab9b0..12b70082781 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.cpp +++ b/src/core/layertree/qgslayertreemodellegendnode.cpp @@ -185,6 +185,16 @@ QSize QgsSymbolV2LegendNode::minimumIconSize() const return minSz; } +void QgsSymbolV2LegendNode::checkAllItems() +{ + checkAll( true ); +} + +void QgsSymbolV2LegendNode::uncheckAllItems() +{ + checkAll( false ); +} + inline QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const { @@ -203,6 +213,22 @@ QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const return validData ? context.take() : 0; } +void QgsSymbolV2LegendNode::checkAll( bool state ) +{ + QgsVectorLayer* vlayer = qobject_cast( mLayerNode->layer() ); + if ( !vlayer || !vlayer->rendererV2() ) + return; + + QgsLegendSymbolListV2 symbolList = vlayer->rendererV2()->legendSymbolItemsV2(); + Q_FOREACH ( const QgsLegendSymbolItemV2& item, symbolList ) + { + vlayer->rendererV2()->checkLegendSymbolItem( item.ruleKey(), state ); + } + + emit dataChanged(); + vlayer->triggerRepaint(); +} + QVariant QgsSymbolV2LegendNode::data( int role ) const { if ( role == Qt::DisplayRole ) diff --git a/src/core/layertree/qgslayertreemodellegendnode.h b/src/core/layertree/qgslayertreemodellegendnode.h index 0b5d1bc49ef..556fe22b68a 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.h +++ b/src/core/layertree/qgslayertreemodellegendnode.h @@ -145,6 +145,8 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject */ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode { + Q_OBJECT + public: QgsSymbolV2LegendNode( QgsLayerTreeLayer* nodeLayer, const QgsLegendSymbolItemV2& item, QObject* parent = 0 ); ~QgsSymbolV2LegendNode(); @@ -173,6 +175,20 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode //! @note added in 2.10 QSize minimumIconSize() const; + public slots: + + /** Checks all items belonging to the same layer as this node. + * @note added in QGIS 2.14 + * @see uncheckAllItems() + */ + void checkAllItems(); + + /** Unchecks all items belonging to the same layer as this node. + * @note added in QGIS 2.14 + * @see checkAllItems() + */ + void uncheckAllItems(); + private: void updateLabel(); @@ -189,6 +205,10 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode // return a temporary context or null if legendMapViewData are not valid QgsRenderContext * createTemporaryRenderContext() const; + /** Sets all items belonging to the same layer as this node to the same check state. + * @param state check state + */ + void checkAll( bool state ); }; diff --git a/tests/src/core/testqgslayertree.cpp b/tests/src/core/testqgslayertree.cpp index fba569d3aca..d960183fea2 100644 --- a/tests/src/core/testqgslayertree.cpp +++ b/tests/src/core/testqgslayertree.cpp @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include class TestQgsLayerTree : public QObject { @@ -34,6 +37,7 @@ class TestQgsLayerTree : public QObject void testCheckStateChildToParent(); void testCheckStateMutuallyExclusive(); void testCheckStateMutuallyExclusiveEdgeCases(); + void testShowHideAllSymbolNodes(); private: @@ -51,6 +55,9 @@ class TestQgsLayerTree : public QObject void TestQgsLayerTree::initTestCase() { + QgsApplication::init(); + QgsApplication::initQgis(); + mRoot = new QgsLayerTreeGroup(); mRoot->addGroup( "grp1" ); mRoot->addGroup( "grp2" ); @@ -62,6 +69,7 @@ void TestQgsLayerTree::initTestCase() void TestQgsLayerTree::cleanupTestCase() { delete mRoot; + QgsApplication::exitQgis(); } void TestQgsLayerTree::testCheckStateParentToChild() @@ -210,6 +218,56 @@ void TestQgsLayerTree::testCheckStateMutuallyExclusiveEdgeCases() delete root3; } +void TestQgsLayerTree::testShowHideAllSymbolNodes() +{ + //new memory layer + QgsVectorLayer* vl = new QgsVectorLayer( "Point?field=col1:integer", "vl", "memory" ); + QVERIFY( vl->isValid() ); + + QgsMapLayerRegistry::instance()->addMapLayers( QList() << vl ); + + //create a categorized renderer for layer + QgsCategorizedSymbolRendererV2* renderer = new QgsCategorizedSymbolRendererV2(); + renderer->setClassAttribute( "col1" ); + renderer->setSourceSymbol( QgsSymbolV2::defaultSymbol( QGis::Point ) ); + renderer->addCategory( QgsRendererCategoryV2( "a", QgsSymbolV2::defaultSymbol( QGis::Point ), "a" ) ); + renderer->addCategory( QgsRendererCategoryV2( "b", QgsSymbolV2::defaultSymbol( QGis::Point ), "b" ) ); + renderer->addCategory( QgsRendererCategoryV2( "c", QgsSymbolV2::defaultSymbol( QGis::Point ), "c" ) ); + vl->setRendererV2( renderer ); + + //create legend with symbology nodes for categorized renderer + QgsLayerTreeGroup* root = new QgsLayerTreeGroup(); + QgsLayerTreeLayer* n = new QgsLayerTreeLayer( vl ); + root->addChildNode( n ); + QgsLayerTreeModel* m = new QgsLayerTreeModel( root, 0 ); + m->refreshLayerLegend( n ); + + //test that all nodes are initially checked + QList nodes = m->layerLegendNodes( n ); + QCOMPARE( nodes.length(), 3 ); + Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes ) + { + QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Checked ); + } + //uncheck all and test that all nodes are unchecked + static_cast< QgsSymbolV2LegendNode* >( nodes.at( 0 ) )->uncheckAllItems(); + Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes ) + { + QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Unchecked ); + } + //check all and test that all nodes are checked + static_cast< QgsSymbolV2LegendNode* >( nodes.at( 0 ) )->checkAllItems(); + Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes ) + { + QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Checked ); + } + + //cleanup + delete m; + delete root; + QgsMapLayerRegistry::instance()->removeMapLayers( QList() << vl ); +} + QTEST_MAIN( TestQgsLayerTree ) #include "testqgslayertree.moc"