Skip to content
Permalink
Browse files
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.
  • Loading branch information
nyalldawson committed Aug 18, 2021
1 parent 3b2e640 commit 1925277b4e68f843106415c24f6385b9d974f344
@@ -45,10 +45,14 @@ Convert Qt.CheckState to QString
Convert QString to Qt.CheckState
%End

static bool layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes );
static bool layersEditable( const QList<QgsLayerTreeLayer *> &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<QgsLayerTreeLayer *> &layerNodes );
%Docstring
Returns ``True`` if any of the layers is modified
@@ -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<QgsMapLayer *> QgisApp::editableLayers( bool modified ) const
QList<QgsMapLayer *> QgisApp::editableLayers( bool modified, bool ignoreLayersWhichCannotBeToggled ) const
{
QList<QgsMapLayer *> editLayers;
// use legend layers (instead of registry) so QList mirrors its order
@@ -11615,7 +11615,7 @@ QList<QgsMapLayer *> 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();
@@ -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<QgsMapLayer *> editableLayers( bool modified = false ) const;
QList<QgsMapLayer *> editableLayers( bool modified = false, bool ignoreLayersWhichCannotBeToggled = false ) const;

//! emit initializationCompleted signal
void completeInitialization();
@@ -261,7 +261,7 @@ static void _readOldLegendLayer( const QDomElement &layerElem, QgsLayerTreeGroup
parent->addChildNode( layerNode );
}

bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes )
bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes, bool ignoreLayersWhichCannotBeToggled )
{
const auto constLayerNodes = layerNodes;
for ( QgsLayerTreeLayer *layerNode : constLayerNodes )
@@ -270,7 +270,7 @@ bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerN
if ( !layer )
continue;

if ( layer->isEditable() )
if ( layer->isEditable() && ( !ignoreLayersWhichCannotBeToggled || !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) ) )
return true;
}
return false;
@@ -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<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
static bool layersModified( const QList<QgsLayerTreeLayer *> &layerNodes );

@@ -30,6 +30,7 @@
#include <qgssettings.h>
#include "qgslegendsettings.h"
#include "qgsmarkersymbol.h"
#include "qgsannotationlayer.h"
#include <QSignalSpy>

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"

0 comments on commit 1925277

Please sign in to comment.