diff --git a/python/core/auto_generated/layertree/qgslayertreeutils.sip.in b/python/core/auto_generated/layertree/qgslayertreeutils.sip.in index 9fa9a4a1161..458e65e57fd 100644 --- a/python/core/auto_generated/layertree/qgslayertreeutils.sip.in +++ b/python/core/auto_generated/layertree/qgslayertreeutils.sip.in @@ -45,10 +45,14 @@ Convert Qt.CheckState to QString Convert QString to Qt.CheckState %End - static bool layersEditable( const QList &layerNodes ); + static bool layersEditable( const QList &layerNodes, bool ignoreLayersWhichCannotBeToggled = false ); %Docstring -Returns ``True`` if any of the layers is editable +Returns ``True`` if any of the specified layers is editable. + +The ``ignoreLayersWhichCannotBeToggled`` argument can be used to control whether layers which cannot have their +edit states toggled by users should be ignored or not (since QGIS 3.22). %End + static bool layersModified( const QList &layerNodes ); %Docstring Returns ``True`` if any of the layers is modified diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 3964d40f76d..11f45c1cda4 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -11468,7 +11468,7 @@ void QgisApp::saveAllEdits( bool verifyAction ) return; } - const auto layers = editableLayers( true ); + const auto layers = editableLayers( true, true ); for ( QgsMapLayer *layer : layers ) { saveEdits( layer, true, false ); @@ -11496,7 +11496,7 @@ void QgisApp::rollbackAllEdits( bool verifyAction ) return; } - const auto layers = editableLayers( true ); + const auto layers = editableLayers( true, true ); for ( QgsMapLayer *layer : layers ) { cancelEdits( layer, true, false ); @@ -11524,7 +11524,7 @@ void QgisApp::cancelAllEdits( bool verifyAction ) return; } - const auto layers = editableLayers(); + const auto layers = editableLayers( false, true ); for ( QgsMapLayer *layer : layers ) { cancelEdits( layer, false, false ); @@ -11595,16 +11595,16 @@ void QgisApp::updateLayerModifiedActions() mActionRollbackEdits->setEnabled( QgsLayerTreeUtils::layersModified( selectedLayerNodes ) ); mActionCancelEdits->setEnabled( QgsLayerTreeUtils::layersEditable( selectedLayerNodes ) ); - bool hasEditLayers = !editableLayers().isEmpty(); + bool hasEditLayers = !editableLayers( false, true ).isEmpty(); mActionAllEdits->setEnabled( hasEditLayers ); mActionCancelAllEdits->setEnabled( hasEditLayers ); - bool hasModifiedLayers = !editableLayers( true ).isEmpty(); + bool hasModifiedLayers = !editableLayers( true, true ).isEmpty(); mActionSaveAllEdits->setEnabled( hasModifiedLayers ); mActionRollbackAllEdits->setEnabled( hasModifiedLayers ); } -QList QgisApp::editableLayers( bool modified ) const +QList QgisApp::editableLayers( bool modified, bool ignoreLayersWhichCannotBeToggled ) const { QList editLayers; // use legend layers (instead of registry) so QList mirrors its order @@ -11615,7 +11615,7 @@ QList QgisApp::editableLayers( bool modified ) const if ( !layer ) continue; - if ( layer->isEditable() && ( !modified || layer->isModified() ) ) + if ( layer->isEditable() && ( !modified || layer->isModified() ) && ( !ignoreLayersWhichCannotBeToggled || !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) ) ) editLayers << layer; } return editLayers; @@ -15723,7 +15723,6 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer ) updateUndoActions(); break; } - } refreshFeatureActions(); diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 2cc73724a55..1bfb092e602 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -706,7 +706,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow * \param modified whether to return only layers that have been modified * \returns list of layers in legend order, or empty list */ - QList editableLayers( bool modified = false ) const; + QList editableLayers( bool modified = false, bool ignoreLayersWhichCannotBeToggled = false ) const; //! emit initializationCompleted signal void completeInitialization(); diff --git a/src/core/layertree/qgslayertreeutils.cpp b/src/core/layertree/qgslayertreeutils.cpp index 8432ed80683..b461a327448 100644 --- a/src/core/layertree/qgslayertreeutils.cpp +++ b/src/core/layertree/qgslayertreeutils.cpp @@ -261,7 +261,7 @@ static void _readOldLegendLayer( const QDomElement &layerElem, QgsLayerTreeGroup parent->addChildNode( layerNode ); } -bool QgsLayerTreeUtils::layersEditable( const QList &layerNodes ) +bool QgsLayerTreeUtils::layersEditable( const QList &layerNodes, bool ignoreLayersWhichCannotBeToggled ) { const auto constLayerNodes = layerNodes; for ( QgsLayerTreeLayer *layerNode : constLayerNodes ) @@ -270,7 +270,7 @@ bool QgsLayerTreeUtils::layersEditable( const QList &layerN if ( !layer ) continue; - if ( layer->isEditable() ) + if ( layer->isEditable() && ( !ignoreLayersWhichCannotBeToggled || !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) ) ) return true; } return false; diff --git a/src/core/layertree/qgslayertreeutils.h b/src/core/layertree/qgslayertreeutils.h index e7d2a6641f8..b329483bff1 100644 --- a/src/core/layertree/qgslayertreeutils.h +++ b/src/core/layertree/qgslayertreeutils.h @@ -53,8 +53,14 @@ class CORE_EXPORT QgsLayerTreeUtils //! Convert QString to Qt::CheckState static Qt::CheckState checkStateFromXml( const QString &txt ); - //! Returns TRUE if any of the layers is editable - static bool layersEditable( const QList &layerNodes ); + /** + * Returns TRUE if any of the specified layers is editable. + * + * The \a ignoreLayersWhichCannotBeToggled argument can be used to control whether layers which cannot have their + * edit states toggled by users should be ignored or not (since QGIS 3.22). + */ + static bool layersEditable( const QList &layerNodes, bool ignoreLayersWhichCannotBeToggled = false ); + //! Returns TRUE if any of the layers is modified static bool layersModified( const QList &layerNodes ); diff --git a/tests/src/core/testqgslayertree.cpp b/tests/src/core/testqgslayertree.cpp index d62ce5a164e..82341878669 100644 --- a/tests/src/core/testqgslayertree.cpp +++ b/tests/src/core/testqgslayertree.cpp @@ -30,6 +30,7 @@ #include #include "qgslegendsettings.h" #include "qgsmarkersymbol.h" +#include "qgsannotationlayer.h" #include class TestQgsLayerTree : public QObject @@ -62,6 +63,7 @@ class TestQgsLayerTree : public QObject void testSymbolText(); void testNodeDepth(); void testRasterSymbolNode(); + void testLayersEditable(); private: @@ -900,5 +902,39 @@ void TestQgsLayerTree::testRasterSymbolNode() QCOMPARE( static_cast< int >( rasterNode2.flags() ), static_cast< int >( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ) ); } +void TestQgsLayerTree::testLayersEditable() +{ + QgsProject project; + + QgsVectorLayer *vl1 = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) ); + QgsVectorLayer *vl2 = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) ); + QgsAnnotationLayer *al = new QgsAnnotationLayer( QStringLiteral( "al" ), QgsAnnotationLayer::LayerOptions( project.transformContext() ) ); + + project.addMapLayer( vl1 ); + project.addMapLayer( vl2 ); + project.addMapLayer( al ); + + QgsLayerTree root; + QgsLayerTreeLayer *nodeVl1 = root.addLayer( vl1 ); + QgsLayerTreeGroup *nodeGrp = root.addGroup( QStringLiteral( "grp" ) ); + QgsLayerTreeLayer *nodeVl2 = nodeGrp->addLayer( vl2 ); + QgsLayerTreeLayer *nodeAl = nodeGrp->addLayer( al ); + QVERIFY( !QgsLayerTreeUtils::layersEditable( {} ) ); + QVERIFY( !QgsLayerTreeUtils::layersEditable( {nodeVl1, nodeVl2} ) ); + vl1->startEditing(); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeVl1} ) ); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeVl1, nodeVl2} ) ); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeVl2, nodeVl1 } ) ); + + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeAl} ) ); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeAl, nodeVl1} ) ); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeAl, nodeVl2} ) ); + + // ignore layers which can't be toggled (the annotation layer) + QVERIFY( !QgsLayerTreeUtils::layersEditable( {nodeAl}, true ) ); + QVERIFY( QgsLayerTreeUtils::layersEditable( {nodeAl, nodeVl1}, true ) ); + QVERIFY( !QgsLayerTreeUtils::layersEditable( {nodeAl, nodeVl2}, true ) ); +} + QGSTEST_MAIN( TestQgsLayerTree ) #include "testqgslayertree.moc"