Use the new UsersCannotToggleEditing property to refine situations

when various actions and editing dependant states should be
reflected in qgis app

We don't want annotation layers (which are always editable) to
make things like the "cancel edits for all layers" action
to become enabled.
This commit is contained in:
Nyall Dawson 2021-08-18 09:51:36 +10:00
parent 3b2e640cee
commit 1925277b4e
6 changed files with 60 additions and 15 deletions

View File

@ -45,10 +45,14 @@ Convert Qt.CheckState to QString
Convert QString to Qt.CheckState Convert QString to Qt.CheckState
%End %End
static bool layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes ); static bool layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes, bool ignoreLayersWhichCannotBeToggled = false );
%Docstring %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 %End
static bool layersModified( const QList<QgsLayerTreeLayer *> &layerNodes ); static bool layersModified( const QList<QgsLayerTreeLayer *> &layerNodes );
%Docstring %Docstring
Returns ``True`` if any of the layers is modified Returns ``True`` if any of the layers is modified

View File

@ -11468,7 +11468,7 @@ void QgisApp::saveAllEdits( bool verifyAction )
return; return;
} }
const auto layers = editableLayers( true ); const auto layers = editableLayers( true, true );
for ( QgsMapLayer *layer : layers ) for ( QgsMapLayer *layer : layers )
{ {
saveEdits( layer, true, false ); saveEdits( layer, true, false );
@ -11496,7 +11496,7 @@ void QgisApp::rollbackAllEdits( bool verifyAction )
return; return;
} }
const auto layers = editableLayers( true ); const auto layers = editableLayers( true, true );
for ( QgsMapLayer *layer : layers ) for ( QgsMapLayer *layer : layers )
{ {
cancelEdits( layer, true, false ); cancelEdits( layer, true, false );
@ -11524,7 +11524,7 @@ void QgisApp::cancelAllEdits( bool verifyAction )
return; return;
} }
const auto layers = editableLayers(); const auto layers = editableLayers( false, true );
for ( QgsMapLayer *layer : layers ) for ( QgsMapLayer *layer : layers )
{ {
cancelEdits( layer, false, false ); cancelEdits( layer, false, false );
@ -11595,16 +11595,16 @@ void QgisApp::updateLayerModifiedActions()
mActionRollbackEdits->setEnabled( QgsLayerTreeUtils::layersModified( selectedLayerNodes ) ); mActionRollbackEdits->setEnabled( QgsLayerTreeUtils::layersModified( selectedLayerNodes ) );
mActionCancelEdits->setEnabled( QgsLayerTreeUtils::layersEditable( selectedLayerNodes ) ); mActionCancelEdits->setEnabled( QgsLayerTreeUtils::layersEditable( selectedLayerNodes ) );
bool hasEditLayers = !editableLayers().isEmpty(); bool hasEditLayers = !editableLayers( false, true ).isEmpty();
mActionAllEdits->setEnabled( hasEditLayers ); mActionAllEdits->setEnabled( hasEditLayers );
mActionCancelAllEdits->setEnabled( hasEditLayers ); mActionCancelAllEdits->setEnabled( hasEditLayers );
bool hasModifiedLayers = !editableLayers( true ).isEmpty(); bool hasModifiedLayers = !editableLayers( true, true ).isEmpty();
mActionSaveAllEdits->setEnabled( hasModifiedLayers ); mActionSaveAllEdits->setEnabled( hasModifiedLayers );
mActionRollbackAllEdits->setEnabled( hasModifiedLayers ); mActionRollbackAllEdits->setEnabled( hasModifiedLayers );
} }
QList<QgsMapLayer *> QgisApp::editableLayers( bool modified ) const QList<QgsMapLayer *> QgisApp::editableLayers( bool modified, bool ignoreLayersWhichCannotBeToggled ) const
{ {
QList<QgsMapLayer *> editLayers; QList<QgsMapLayer *> editLayers;
// use legend layers (instead of registry) so QList mirrors its order // use legend layers (instead of registry) so QList mirrors its order
@ -11615,7 +11615,7 @@ QList<QgsMapLayer *> QgisApp::editableLayers( bool modified ) const
if ( !layer ) if ( !layer )
continue; continue;
if ( layer->isEditable() && ( !modified || layer->isModified() ) ) if ( layer->isEditable() && ( !modified || layer->isModified() ) && ( !ignoreLayersWhichCannotBeToggled || !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) ) )
editLayers << layer; editLayers << layer;
} }
return editLayers; return editLayers;
@ -15723,7 +15723,6 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
updateUndoActions(); updateUndoActions();
break; break;
} }
} }
refreshFeatureActions(); refreshFeatureActions();

View File

@ -706,7 +706,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
* \param modified whether to return only layers that have been modified * \param modified whether to return only layers that have been modified
* \returns list of layers in legend order, or empty list * \returns list of layers in legend order, or empty list
*/ */
QList<QgsMapLayer *> editableLayers( bool modified = false ) const; QList<QgsMapLayer *> editableLayers( bool modified = false, bool ignoreLayersWhichCannotBeToggled = false ) const;
//! emit initializationCompleted signal //! emit initializationCompleted signal
void completeInitialization(); void completeInitialization();

View File

@ -261,7 +261,7 @@ static void _readOldLegendLayer( const QDomElement &layerElem, QgsLayerTreeGroup
parent->addChildNode( layerNode ); parent->addChildNode( layerNode );
} }
bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes ) bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes, bool ignoreLayersWhichCannotBeToggled )
{ {
const auto constLayerNodes = layerNodes; const auto constLayerNodes = layerNodes;
for ( QgsLayerTreeLayer *layerNode : constLayerNodes ) for ( QgsLayerTreeLayer *layerNode : constLayerNodes )
@ -270,7 +270,7 @@ bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerN
if ( !layer ) if ( !layer )
continue; continue;
if ( layer->isEditable() ) if ( layer->isEditable() && ( !ignoreLayersWhichCannotBeToggled || !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) ) )
return true; return true;
} }
return false; return false;

View File

@ -53,8 +53,14 @@ class CORE_EXPORT QgsLayerTreeUtils
//! Convert QString to Qt::CheckState //! Convert QString to Qt::CheckState
static Qt::CheckState checkStateFromXml( const QString &txt ); static Qt::CheckState checkStateFromXml( const QString &txt );
//! Returns TRUE if any of the layers is editable /**
static bool layersEditable( const QList<QgsLayerTreeLayer *> &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<QgsLayerTreeLayer *> &layerNodes, bool ignoreLayersWhichCannotBeToggled = false );
//! Returns TRUE if any of the layers is modified //! Returns TRUE if any of the layers is modified
static bool layersModified( const QList<QgsLayerTreeLayer *> &layerNodes ); static bool layersModified( const QList<QgsLayerTreeLayer *> &layerNodes );

View File

@ -30,6 +30,7 @@
#include <qgssettings.h> #include <qgssettings.h>
#include "qgslegendsettings.h" #include "qgslegendsettings.h"
#include "qgsmarkersymbol.h" #include "qgsmarkersymbol.h"
#include "qgsannotationlayer.h"
#include <QSignalSpy> #include <QSignalSpy>
class TestQgsLayerTree : public QObject class TestQgsLayerTree : public QObject
@ -62,6 +63,7 @@ class TestQgsLayerTree : public QObject
void testSymbolText(); void testSymbolText();
void testNodeDepth(); void testNodeDepth();
void testRasterSymbolNode(); void testRasterSymbolNode();
void testLayersEditable();
private: private:
@ -900,5 +902,39 @@ void TestQgsLayerTree::testRasterSymbolNode()
QCOMPARE( static_cast< int >( rasterNode2.flags() ), static_cast< int >( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ) ); 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 ) QGSTEST_MAIN( TestQgsLayerTree )
#include "testqgslayertree.moc" #include "testqgslayertree.moc"