From 11b281e95ba463b1df9279c0b302926893b21f43 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 6 Nov 2020 19:12:02 +0100 Subject: [PATCH 01/10] try to fix tree model --- src/core/layertree/qgslayertreemodel.cpp | 42 ++++++++++++++++++++++++ src/core/layertree/qgslayertreemodel.h | 5 +++ 2 files changed, 47 insertions(+) diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index d096db87331..bfac0d87073 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -549,9 +549,20 @@ void QgsLayerTreeModel::refreshLayerLegend( QgsLayerTreeLayer *nodeLayer ) int oldNodeCount = rowCount( idx ); if ( oldNodeCount > 0 ) { + if ( mRemoveRowsNestedLevel > 0 ) + { + endRemoveRows(); + mRemoveRowsNestedLevel--; + Q_ASSERT( mRemoveRowsNestedLevel == 0 ); + } + beginRemoveRows( idx, 0, oldNodeCount - 1 ); + mRemoveRowsNestedLevel++; + removeLegendFromLayer( nodeLayer ); endRemoveRows(); + mRemoveRowsNestedLevel--; + Q_ASSERT( mRemoveRowsNestedLevel == 0 ); } addLegendToLayer( nodeLayer ); @@ -724,7 +735,14 @@ int QgsLayerTreeModel::scaleIconSize( int standardSize ) void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo ) { Q_ASSERT( node ); + if ( mInsertRowsNestedLevel > 0 ) + { + endInsertRows(); + mInsertRowsNestedLevel--; + Q_ASSERT( mInsertRowsNestedLevel == 0 ); + } beginInsertRows( node2index( node ), indexFrom, indexTo ); + mInsertRowsNestedLevel++; } static QList _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo ) @@ -747,6 +765,8 @@ void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom Q_ASSERT( node ); endInsertRows(); + mInsertRowsNestedLevel--; + Q_ASSERT( mInsertRowsNestedLevel == 0 ); const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo ); for ( QgsLayerTreeLayer *newLayerNode : subNodes ) @@ -757,7 +777,14 @@ void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int inde { Q_ASSERT( node ); + if ( mRemoveRowsNestedLevel > 0 ) + { + endRemoveRows(); + mRemoveRowsNestedLevel--; + Q_ASSERT( mRemoveRowsNestedLevel == 0 ); + } beginRemoveRows( node2index( node ), indexFrom, indexTo ); + mRemoveRowsNestedLevel++; // disconnect from layers and remove their legend const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo ); @@ -768,6 +795,8 @@ void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int inde void QgsLayerTreeModel::nodeRemovedChildren() { endRemoveRows(); + mRemoveRowsNestedLevel--; + Q_ASSERT( mRemoveRowsNestedLevel == 0 ); } void QgsLayerTreeModel::nodeVisibilityChanged( QgsLayerTreeNode *node ) @@ -1349,7 +1378,16 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL ) int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count(); if ( !filteredLstNew.isEmpty() ) + { + if ( mInsertRowsNestedLevel > 0 ) + { + endInsertRows(); + mInsertRowsNestedLevel--; + Q_ASSERT( mInsertRowsNestedLevel == 0 ); + } beginInsertRows( node2index( nodeL ), 0, count - 1 ); + mInsertRowsNestedLevel++; + } LayerLegendData data; data.originalNodes = lstNew; @@ -1360,7 +1398,11 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL ) mLegend[nodeL] = data; if ( !filteredLstNew.isEmpty() ) + { endInsertRows(); + mInsertRowsNestedLevel--; + Q_ASSERT( mInsertRowsNestedLevel == 0 ); + } // invalidate map based data even if the data is not map-based to make sure // the symbol sizes are computed at least once diff --git a/src/core/layertree/qgslayertreemodel.h b/src/core/layertree/qgslayertreemodel.h index e0f0fe27fa3..1afdfae264d 100644 --- a/src/core/layertree/qgslayertreemodel.h +++ b/src/core/layertree/qgslayertreemodel.h @@ -471,6 +471,11 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel //! Returns a temporary render context QgsRenderContext *createTemporaryRenderContext() const; + + //! Keep track of the row insertion nested status + int mInsertRowsNestedLevel = 0; + //! Keep track of the row remove nested status + int mRemoveRowsNestedLevel = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayerTreeModel::Flags ) From bc18ec99453786370a06ef0a37ebf08c8b84e517 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sat, 7 Nov 2020 16:24:39 +0100 Subject: [PATCH 02/10] No more warning in the model, but still doesn't work --- src/core/layertree/qgslayertreemodel.cpp | 39 --------------- src/core/layertree/qgslayertreemodel.h | 4 -- src/core/layertree/qgslayertreenode.cpp | 50 ++++++++++++++++--- src/core/layertree/qgslayertreenode.h | 4 ++ .../layertree/qgslayertreeregistrybridge.cpp | 4 +- src/gui/layertree/qgslayertreeview.cpp | 10 ++++ tests/qt_modeltest/modeltest.cpp | 18 ++++--- tests/src/python/test_qgslayertreeview.py | 3 ++ 8 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index bfac0d87073..5d858195d18 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -549,20 +549,9 @@ void QgsLayerTreeModel::refreshLayerLegend( QgsLayerTreeLayer *nodeLayer ) int oldNodeCount = rowCount( idx ); if ( oldNodeCount > 0 ) { - if ( mRemoveRowsNestedLevel > 0 ) - { - endRemoveRows(); - mRemoveRowsNestedLevel--; - Q_ASSERT( mRemoveRowsNestedLevel == 0 ); - } - beginRemoveRows( idx, 0, oldNodeCount - 1 ); - mRemoveRowsNestedLevel++; - removeLegendFromLayer( nodeLayer ); endRemoveRows(); - mRemoveRowsNestedLevel--; - Q_ASSERT( mRemoveRowsNestedLevel == 0 ); } addLegendToLayer( nodeLayer ); @@ -734,15 +723,7 @@ int QgsLayerTreeModel::scaleIconSize( int standardSize ) void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo ) { - Q_ASSERT( node ); - if ( mInsertRowsNestedLevel > 0 ) - { - endInsertRows(); - mInsertRowsNestedLevel--; - Q_ASSERT( mInsertRowsNestedLevel == 0 ); - } beginInsertRows( node2index( node ), indexFrom, indexTo ); - mInsertRowsNestedLevel++; } static QList _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo ) @@ -765,8 +746,6 @@ void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom Q_ASSERT( node ); endInsertRows(); - mInsertRowsNestedLevel--; - Q_ASSERT( mInsertRowsNestedLevel == 0 ); const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo ); for ( QgsLayerTreeLayer *newLayerNode : subNodes ) @@ -777,14 +756,7 @@ void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int inde { Q_ASSERT( node ); - if ( mRemoveRowsNestedLevel > 0 ) - { - endRemoveRows(); - mRemoveRowsNestedLevel--; - Q_ASSERT( mRemoveRowsNestedLevel == 0 ); - } beginRemoveRows( node2index( node ), indexFrom, indexTo ); - mRemoveRowsNestedLevel++; // disconnect from layers and remove their legend const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo ); @@ -795,8 +767,6 @@ void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int inde void QgsLayerTreeModel::nodeRemovedChildren() { endRemoveRows(); - mRemoveRowsNestedLevel--; - Q_ASSERT( mRemoveRowsNestedLevel == 0 ); } void QgsLayerTreeModel::nodeVisibilityChanged( QgsLayerTreeNode *node ) @@ -1379,14 +1349,7 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL ) if ( !filteredLstNew.isEmpty() ) { - if ( mInsertRowsNestedLevel > 0 ) - { - endInsertRows(); - mInsertRowsNestedLevel--; - Q_ASSERT( mInsertRowsNestedLevel == 0 ); - } beginInsertRows( node2index( nodeL ), 0, count - 1 ); - mInsertRowsNestedLevel++; } LayerLegendData data; @@ -1400,8 +1363,6 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL ) if ( !filteredLstNew.isEmpty() ) { endInsertRows(); - mInsertRowsNestedLevel--; - Q_ASSERT( mInsertRowsNestedLevel == 0 ); } // invalidate map based data even if the data is not map-based to make sure diff --git a/src/core/layertree/qgslayertreemodel.h b/src/core/layertree/qgslayertreemodel.h index 1afdfae264d..2c25ec2a2c8 100644 --- a/src/core/layertree/qgslayertreemodel.h +++ b/src/core/layertree/qgslayertreemodel.h @@ -472,10 +472,6 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel //! Returns a temporary render context QgsRenderContext *createTemporaryRenderContext() const; - //! Keep track of the row insertion nested status - int mInsertRowsNestedLevel = 0; - //! Keep track of the row remove nested status - int mRemoveRowsNestedLevel = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayerTreeModel::Flags ) diff --git a/src/core/layertree/qgslayertreenode.cpp b/src/core/layertree/qgslayertreenode.cpp index c5277fb8284..e2e153691ab 100644 --- a/src/core/layertree/qgslayertreenode.cpp +++ b/src/core/layertree/qgslayertreenode.cpp @@ -48,6 +48,22 @@ QgsLayerTreeNode::~QgsLayerTreeNode() qDeleteAll( mChildren ); } +QList QgsLayerTreeNode::abandonChildren() +{ + auto orphans { mChildren }; + mChildren.clear(); + for ( auto orphan : orphans ) + { + orphan->makeOrphan( ); + } + return orphans; +} + +void QgsLayerTreeNode::makeOrphan() +{ + mParent = nullptr; +} + QgsLayerTreeNode *QgsLayerTreeNode::readXml( QDomElement &element, const QgsReadWriteContext &context ) { QgsLayerTreeNode *node = nullptr; @@ -231,12 +247,15 @@ void QgsLayerTreeNode::insertChildrenPrivate( int index, QList= mChildren.count() ) index = mChildren.count(); - int indexTo = index + nodes.count() - 1; - emit willAddChildren( this, index, indexTo ); for ( int i = 0; i < nodes.count(); ++i ) { QgsLayerTreeNode *node = nodes.at( i ); + + const auto orphans { node->abandonChildren() }; + + emit willAddChildren( this, index + i, index + i ); mChildren.insert( index + i, node ); + emit addedChildren( this, index + i, index + i ); // forward the signal towards the root connect( node, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeNode::willAddChildren ); @@ -247,8 +266,14 @@ void QgsLayerTreeNode::insertChildrenPrivate( int index, QListinsertChildrenPrivate( -1, orphans ); + } + } - emit addedChildren( this, index, indexTo ); } void QgsLayerTreeNode::removeChildrenPrivate( int from, int count, bool destroy ) @@ -256,18 +281,27 @@ void QgsLayerTreeNode::removeChildrenPrivate( int from, int count, bool destroy if ( from < 0 || count <= 0 ) return; - int to = from + count - 1; + const int to = from + count - 1; if ( to >= mChildren.count() ) return; - emit willRemoveChildren( this, from, to ); + while ( --count >= 0 ) { - QgsLayerTreeNode *node = mChildren.takeAt( from ); - node->mParent = nullptr; + QgsLayerTreeNode *node = mChildren.at( from ); + + // Remove children first + if ( ! node->children().isEmpty() ) + { + node->removeChildrenPrivate( 0, node->children().count(), destroy ); + } + + emit willRemoveChildren( this, from, from ); + node = mChildren.takeAt( from ); if ( destroy ) delete node; + node->makeOrphan(); + emit removedChildren( this, from, from ); } - emit removedChildren( this, from, to ); } bool QgsLayerTreeNode::takeChild( QgsLayerTreeNode *node ) diff --git a/src/core/layertree/qgslayertreenode.h b/src/core/layertree/qgslayertreenode.h index b69455a5a61..5840b7cab9e 100644 --- a/src/core/layertree/qgslayertreenode.h +++ b/src/core/layertree/qgslayertreenode.h @@ -112,6 +112,10 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject QList children() { return mChildren; } //! Gets list of children of the node. Children are owned by the parent QList children() const { return mChildren; } SIP_SKIP + //! Remove the childrens and sets their parent to null + QList abandonChildren() SIP_SKIP; + //! Set parent to null + void makeOrphan() SIP_SKIP; /** * Returns name of the node diff --git a/src/core/layertree/qgslayertreeregistrybridge.cpp b/src/core/layertree/qgslayertreeregistrybridge.cpp index d8b8074c43e..d5e97c556b5 100644 --- a/src/core/layertree/qgslayertreeregistrybridge.cpp +++ b/src/core/layertree/qgslayertreeregistrybridge.cpp @@ -30,7 +30,7 @@ QgsLayerTreeRegistryBridge::QgsLayerTreeRegistryBridge( QgsLayerTreeGroup *root, , mInsertionPoint( root, 0 ) { connect( mProject, &QgsProject::legendLayersAdded, this, &QgsLayerTreeRegistryBridge::layersAdded ); - connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsLayerTreeRegistryBridge::layersWillBeRemoved ); + connect( mProject, qgis::overload::of( &QgsProject::layersWillBeRemoved ), this, &QgsLayerTreeRegistryBridge::layersWillBeRemoved ); connect( mRoot, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeRegistryBridge::groupWillRemoveChildren ); connect( mRoot, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeRegistryBridge::groupRemovedChildren ); @@ -118,7 +118,7 @@ static void _collectLayerIdsInGroup( QgsLayerTreeGroup *group, int indexFrom, in void QgsLayerTreeRegistryBridge::groupWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo ) { - if ( mRegistryRemovingLayers ) + if ( mRegistryRemovingLayers || QgsLayerTree::isGroup( node ) ) return; // do not try to remove those layers again Q_ASSERT( mLayerIdsForRemoval.isEmpty() ); diff --git a/src/gui/layertree/qgslayertreeview.cpp b/src/gui/layertree/qgslayertreeview.cpp index 72d62a32475..bfabb4d6702 100644 --- a/src/gui/layertree/qgslayertreeview.cpp +++ b/src/gui/layertree/qgslayertreeview.cpp @@ -32,6 +32,10 @@ #include #include +#ifdef ENABLE_MODELTEST +#include "modeltest.h" +#endif + #include "qgslayertreeviewindicator.h" #include "qgslayertreeviewitemdelegate.h" @@ -95,6 +99,12 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model ) ); mProxyModel = new QgsLayerTreeProxyModel( treeModel, this ); + +#ifdef ENABLE_MODELTEST + new ModelTest( treeModel, this ); + new ModelTest( mProxyModel, this ); +#endif + mProxyModel->setShowPrivateLayers( mShowPrivateLayers ); QTreeView::setModel( mProxyModel ); diff --git a/tests/qt_modeltest/modeltest.cpp b/tests/qt_modeltest/modeltest.cpp index 131599fbc53..79cb22b3e99 100644 --- a/tests/qt_modeltest/modeltest.cpp +++ b/tests/qt_modeltest/modeltest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include /*! Connect to all of the models signals. Whenever anything happens @@ -493,17 +494,18 @@ void ModelTest::rowsAboutToBeInserted( const QModelIndex &parent, int start, int void ModelTest::rowsInserted( const QModelIndex &parent, int start, int end ) { Changing c = insert.pop(); + //* + if ( c.next != model->data( model->index( end + 1, 0, c.parent ) ) ) + { + qDebug() << start << end; + for ( int i = 0; i < model->rowCount(); ++i ) + qDebug() << model->index( i, 0 ).data().toString(); + qDebug() << c.next << model->data( model->index( end + 1, 0, c.parent ) ); + } + //*/ Q_ASSERT( c.parent == parent ); Q_ASSERT( c.oldSize + ( end - start + 1 ) == model->rowCount( parent ) ); Q_ASSERT( c.last == model->data( model->index( start - 1, 0, c.parent ) ) ); - /* - if (c.next != model->data(model->index(end + 1, 0, c.parent))) { - qDebug() << start << end; - for (int i=0; i < model->rowCount(); ++i) - qDebug() << model->index(i, 0).data().toString(); - qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); - } - */ Q_ASSERT( c.next == model->data( model->index( end + 1, 0, c.parent ) ) ); } diff --git a/tests/src/python/test_qgslayertreeview.py b/tests/src/python/test_qgslayertreeview.py index e6b4f5f272b..74d189f40a5 100644 --- a/tests/src/python/test_qgslayertreeview.py +++ b/tests/src/python/test_qgslayertreeview.py @@ -509,6 +509,7 @@ class TestQgsLayerTreeView(unittest.TestCase): view.setModel(self.model) if USE_MODEL_TESTER: proxy_tester = QAbstractItemModelTester(view.model()) + tree_tester = QAbstractItemModelTester(view.layerTreeModel()) view.setCurrentLayer(self.layer3) self.layer3.setFlags(self.layer.Private) @@ -520,6 +521,8 @@ class TestQgsLayerTreeView(unittest.TestCase): view.setModel(self.model) if USE_MODEL_TESTER: proxy_tester = QAbstractItemModelTester(view.model()) + tree_tester = QAbstractItemModelTester(view.layerTreeModel()) + tree_model = view.layerTreeModel() proxy_model = view.proxyModel() From f48cd393290b3f00ed6624b81d4fb48234fdb4d4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sat, 7 Nov 2020 17:05:43 +0100 Subject: [PATCH 03/10] Fix snapping model --- src/app/qgssnappinglayertreemodel.cpp | 5 +++++ src/app/qgssnappingwidget.cpp | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/src/app/qgssnappinglayertreemodel.cpp b/src/app/qgssnappinglayertreemodel.cpp index 1e28cc589f7..2c054c3de94 100644 --- a/src/app/qgssnappinglayertreemodel.cpp +++ b/src/app/qgssnappinglayertreemodel.cpp @@ -328,6 +328,11 @@ Qt::ItemFlags QgsSnappingLayerTreeModel::flags( const QModelIndex &idx ) const QModelIndex QgsSnappingLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const { + if ( row < 0 || column < 0 || row >= rowCount( parent ) || column >= columnCount( parent ) ) + { + return QModelIndex(); + } + QModelIndex newIndex = QSortFilterProxyModel::index( row, LayerColumn, parent ); if ( column == LayerColumn ) return newIndex; diff --git a/src/app/qgssnappingwidget.cpp b/src/app/qgssnappingwidget.cpp index d2fa1ede411..c618181e596 100644 --- a/src/app/qgssnappingwidget.cpp +++ b/src/app/qgssnappingwidget.cpp @@ -43,6 +43,9 @@ #include "qgssettings.h" #include "qgsscalewidget.h" +#ifdef ENABLE_MODELTEST +#include "modeltest.h" +#endif class SnapTypeMenu: public QMenu { @@ -88,6 +91,12 @@ QgsSnappingWidget::QgsSnappingWidget( QgsProject *project, QgsMapCanvas *canvas, mLayerTreeView = new QTreeView(); QgsSnappingLayerTreeModel *model = new QgsSnappingLayerTreeModel( mProject, mCanvas, this ); model->setLayerTreeModel( new QgsLayerTreeModel( mProject->layerTreeRoot(), model ) ); + +#ifdef ENABLE_MODELTEST + new ModelTest( model, this ); + new ModelTest( model->layerTreeModel(), this ); +#endif + // connections connect( model, &QgsSnappingLayerTreeModel::rowsInserted, this, &QgsSnappingWidget::onSnappingTreeLayersChanged ); connect( model, &QgsSnappingLayerTreeModel::modelReset, this, &QgsSnappingWidget::onSnappingTreeLayersChanged ); From 093b0a1d11c464a1fd605a728fce2c6ffde6ee61 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sat, 7 Nov 2020 18:18:12 +0100 Subject: [PATCH 04/10] Fix legend nodes --- src/core/layertree/qgslayertreemodel.cpp | 8 ++++++++ tests/qt_modeltest/modeltest.cpp | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index 5d858195d18..46e6ad607ef 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -1349,6 +1349,14 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL ) if ( !filteredLstNew.isEmpty() ) { + // Make sure it's clear + const QModelIndex nodeIndex { node2index( nodeL ) }; + if ( rowCount( nodeIndex ) > 0 ) + { + beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 ); + mLegend[nodeL] = LayerLegendData(); + endRemoveRows(); + } beginInsertRows( node2index( nodeL ), 0, count - 1 ); } diff --git a/tests/qt_modeltest/modeltest.cpp b/tests/qt_modeltest/modeltest.cpp index 79cb22b3e99..de63369b4ac 100644 --- a/tests/qt_modeltest/modeltest.cpp +++ b/tests/qt_modeltest/modeltest.cpp @@ -504,7 +504,7 @@ void ModelTest::rowsInserted( const QModelIndex &parent, int start, int end ) } //*/ Q_ASSERT( c.parent == parent ); - Q_ASSERT( c.oldSize + ( end - start + 1 ) == model->rowCount( parent ) ); + Q_ASSERT_X( c.oldSize + ( end - start + 1 ) == model->rowCount( parent ), "Rows inserted", QStringLiteral( "%1 != %2" ).arg( c.oldSize + ( end - start + 1 ) ).arg( model->rowCount( parent ) ).toStdString().c_str() ); Q_ASSERT( c.last == model->data( model->index( start - 1, 0, c.parent ) ) ); Q_ASSERT( c.next == model->data( model->index( end + 1, 0, c.parent ) ) ); } @@ -549,7 +549,7 @@ void ModelTest::rowsRemoved( const QModelIndex &parent, int start, int end ) { Changing c = remove.pop(); Q_ASSERT( c.parent == parent ); - Q_ASSERT( c.oldSize - ( end - start + 1 ) == model->rowCount( parent ) ); + Q_ASSERT_X( c.oldSize - ( end - start + 1 ) == model->rowCount( parent ), "Rows removed", QStringLiteral( "%1 != %2" ).arg( c.oldSize + ( end - start + 1 ) ).arg( model->rowCount( parent ) ).toStdString().c_str() ); Q_ASSERT( c.last == model->data( model->index( start - 1, 0, c.parent ) ) ); Q_ASSERT( c.next == model->data( model->index( start, 0, c.parent ) ) ); } From 003ca548737c863a6bcd05d081c9efb1ec47d6df Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sat, 7 Nov 2020 18:23:50 +0100 Subject: [PATCH 05/10] Less autos --- src/core/layertree/qgslayertreenode.cpp | 6 +- src/gui/layertree/qgslayertreeview.h | 429 ------------------------ 2 files changed, 3 insertions(+), 432 deletions(-) delete mode 100644 src/gui/layertree/qgslayertreeview.h diff --git a/src/core/layertree/qgslayertreenode.cpp b/src/core/layertree/qgslayertreenode.cpp index e2e153691ab..9bd91d165f5 100644 --- a/src/core/layertree/qgslayertreenode.cpp +++ b/src/core/layertree/qgslayertreenode.cpp @@ -50,9 +50,9 @@ QgsLayerTreeNode::~QgsLayerTreeNode() QList QgsLayerTreeNode::abandonChildren() { - auto orphans { mChildren }; + const QList orphans { mChildren }; mChildren.clear(); - for ( auto orphan : orphans ) + for ( auto orphan : qgis::as_const( orphans ) ) { orphan->makeOrphan( ); } @@ -251,7 +251,7 @@ void QgsLayerTreeNode::insertChildrenPrivate( int index, QListabandonChildren() }; + const QList orphans { node->abandonChildren() }; emit willAddChildren( this, index + i, index + i ); mChildren.insert( index + i, node ); diff --git a/src/gui/layertree/qgslayertreeview.h b/src/gui/layertree/qgslayertreeview.h deleted file mode 100644 index 68efe8a4ef0..00000000000 --- a/src/gui/layertree/qgslayertreeview.h +++ /dev/null @@ -1,429 +0,0 @@ -/*************************************************************************** - qgslayertreeview.h - -------------------------------------- - Date : May 2014 - Copyright : (C) 2014 by Martin Dobias - Email : wonder dot sk at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef QGSLAYERTREEVIEW_H -#define QGSLAYERTREEVIEW_H - -#include -#include "qgis.h" -#include "qgis_gui.h" - -class QgsLayerTreeGroup; -class QgsLayerTreeLayer; -class QgsLayerTreeModel; -class QgsLayerTreeNode; -class QgsLayerTreeModelLegendNode; -class QgsLayerTreeViewDefaultActions; -class QgsLayerTreeViewIndicator; -class QgsLayerTreeViewMenuProvider; -class QgsMapLayer; -class QgsMessageBar; -class QgsLayerTreeFilterProxyModel; - - -#include - -/** - * \ingroup gui - * - * The QgsLayerTreeProxyModel class is a proxy model for QgsLayerTreeModel, supports - * private layers and text filtering. - * - * \since QGIS 3.18 - */ -class GUI_EXPORT QgsLayerTreeProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT - - public: - - /** - * Constructs QgsLayerTreeProxyModel with source model \a treeModel and a \a parent - */ - QgsLayerTreeProxyModel( QgsLayerTreeModel *treeModel, QObject *parent ); - - /** - * Sets filter to \a filterText. - */ - void setFilterText( const QString &filterText = QString() ); - - /** - * Returns if private layers are shown. - */ - bool showPrivateLayers() const; - - /** - * Determines if private layers are shown. - */ - void setShowPrivateLayers( bool showPrivate ); - - protected: - - bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; - - private: - - bool nodeShown( QgsLayerTreeNode *node ) const; - - QgsLayerTreeModel *mLayerTreeModel = nullptr; - QString mFilterText; - bool mShowPrivateLayers = false; -}; - - -/** - * \ingroup gui - * The QgsLayerTreeView class extends QTreeView and provides some additional functionality - * when working with a layer tree. - * - * The view updates expanded state of layer tree nodes and also listens to changes - * to expanded states in the layer tree. - * - * The view keeps track of the current layer and emits a signal when the current layer has changed. - * - * Allows the client to specify a context menu provider with custom actions. Also it comes - * with a set of default actions that can be used when building context menu. - * - * \see QgsLayerTreeModel - * \since QGIS 2.4 - */ -class GUI_EXPORT QgsLayerTreeView : public QTreeView -{ - -#ifdef SIP_RUN - SIP_CONVERT_TO_SUBCLASS_CODE - if ( sipCpp->inherits( "QgsLayerTreeView" ) ) - sipType = sipType_QgsLayerTreeView; - else - sipType = 0; - SIP_END -#endif - - - Q_OBJECT - public: - - //! Constructor for QgsLayerTreeView - explicit QgsLayerTreeView( QWidget *parent SIP_TRANSFERTHIS = nullptr ); - ~QgsLayerTreeView() override; - - //! Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model. - void setModel( QAbstractItemModel *model ) override; - - //! Gets access to the model casted to QgsLayerTreeModel - QgsLayerTreeModel *layerTreeModel() const; - - /** - * Returns the proxy model used by the view. - * - * This can be used to set filters controlling which layers are shown in the view. - * - * \since QGIS 3.18 - */ - QgsLayerTreeProxyModel *proxyModel() const; - - /** - * Returns layer tree node for given proxy model tree \a index. Returns root node for invalid index. - * Returns NULLPTR if index does not refer to a layer tree node (e.g. it is a legend node) - * - * Unlike QgsLayerTreeModel::index2Node(), calling this method correctly accounts - * for mapping the view indexes through the view's proxy model to the source model. - * - * \since QGIS 3.18 - */ - QgsLayerTreeNode *index2node( const QModelIndex &index ) const; - - /** - * Returns proxy model index for a given node. If the node does not belong to the layer tree, the result is undefined - * - * Unlike QgsLayerTreeModel::node2index(), calling this method correctly accounts - * for mapping the view indexes through the view's proxy model to the source model. - * - * \since QGIS 3.18 - */ - QModelIndex node2index( QgsLayerTreeNode *node ) const; - - - /** - * Returns source model index for a given node. If the node does not belong to the layer tree, the result is undefined - * - * \since QGIS 3.18 - */ - QModelIndex node2sourceIndex( QgsLayerTreeNode *node ) const; - - - /** - * Returns legend node for given proxy model tree \a index. Returns NULLPTR for invalid index - * - * Unlike QgsLayerTreeModel::index2legendNode(), calling this method correctly accounts - * for mapping the view indexes through the view's proxy model to the source model. - * - * \since QGIS 3.18 - */ - QgsLayerTreeModelLegendNode *index2legendNode( const QModelIndex &index ) const; - - /** - * Returns proxy model index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined. - * If the legend node is belongs to the tree but it is filtered out, invalid model index is returned. - * - * Unlike QgsLayerTreeModel::legendNode2index(), calling this method correctly accounts - * for mapping the view indexes through the view's proxy model to the source model. - * - * \since QGIS 3.18 - */ - QModelIndex legendNode2index( QgsLayerTreeModelLegendNode *legendNode ); - - /** - * Returns index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined. - * If the legend node is belongs to the tree but it is filtered out, invalid model index is returned. - * - * \since QGIS 3.18 - */ - QModelIndex legendNode2sourceIndex( QgsLayerTreeModelLegendNode *legendNode ); - - //! Gets access to the default actions that may be used with the tree view - QgsLayerTreeViewDefaultActions *defaultActions(); - - //! Sets provider for context menu. Takes ownership of the instance - void setMenuProvider( QgsLayerTreeViewMenuProvider *menuProvider SIP_TRANSFER ); - //! Returns pointer to the context menu provider. May be NULLPTR - QgsLayerTreeViewMenuProvider *menuProvider() const { return mMenuProvider; } - - /** - * Returns the currently selected layer, or NULLPTR if no layers is selected. - * - * \see setCurrentLayer() - */ - QgsMapLayer *currentLayer() const; - - /** - * Convenience methods which sets the visible state of the specified map \a layer. - * - * \see QgsLayerTreeNode::setItemVisibilityChecked() - * \since QGIS 3.10 - */ - void setLayerVisible( QgsMapLayer *layer, bool visible ); - - /** - * Sets the currently selected \a layer. - * - * If \a layer is NULLPTR then all layers will be deselected. - * - * \see currentLayer() - */ - void setCurrentLayer( QgsMapLayer *layer ); - - //! Gets current node. May be NULLPTR - QgsLayerTreeNode *currentNode() const; - //! Gets current group node. If a layer is current node, the function will return parent group. May be NULLPTR. - QgsLayerTreeGroup *currentGroupNode() const; - - /** - * Gets current legend node. May be NULLPTR if current node is not a legend node. - * \since QGIS 2.14 - */ - QgsLayerTreeModelLegendNode *currentLegendNode() const; - - /** - * Returns list of selected nodes - * \param skipInternal If TRUE, will ignore nodes which have an ancestor in the selection - */ - QList selectedNodes( bool skipInternal = false ) const; - //! Returns list of selected nodes filtered to just layer nodes - QList selectedLayerNodes() const; - - //! Gets list of selected layers - QList selectedLayers() const; - - /** - * Gets list of selected layers, including those that are not directly selected, but their - * ancestor groups is selected. If we have a group with two layers L1, L2 and just the group - * node is selected, this method returns L1 and L2, while selectedLayers() returns an empty list. - * \since QGIS 3.4 - */ - QList selectedLayersRecursive() const; - - /** - * Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names - * in the layer tree view. They can be used to show extra information with tree nodes and they allow - * user interaction. - * - * Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes. - * \see removeIndicator - * \see indicators - * \since QGIS 3.2 - */ - void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator ); - - /** - * Removes a previously added indicator to a layer tree node. Does not delete the indicator. - * \see addIndicator - * \see indicators - * \since QGIS 3.2 - */ - void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator ); - - /** - * Returns list of indicators associated with a particular layer tree node. - * \see addIndicator - * \see removeIndicator - * \since QGIS 3.2 - */ - QList indicators( QgsLayerTreeNode *node ) const; - - /** - * Returns width of contextual menu mark, at right of layer node items. - * \see setLayerMarkWidth - * \since QGIS 3.8 - */ - int layerMarkWidth() const { return mLayerMarkWidth; } - -///@cond PRIVATE - - /** - * Returns a list of custom property keys which are considered as related to view operations - * only. E.g. node expanded state. - * - * Changes to these keys will not mark a project as "dirty" and trigger unsaved changes - * warnings. - * - * \since QGIS 3.2 - */ - static QStringList viewOnlyCustomProperties() SIP_SKIP; -///@endcond - - public slots: - //! Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitored by the model - void refreshLayerSymbology( const QString &layerId ); - - /** - * Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes - * \since QGIS 2.18 - */ - void expandAllNodes(); - - /** - * Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes - * \since QGIS 2.18 - */ - void collapseAllNodes(); - - /** - * Set width of contextual menu mark, at right of layer node items. - * \see layerMarkWidth - * \since QGIS 3.8 - */ - void setLayerMarkWidth( int width ) { mLayerMarkWidth = width; } - - /** - * Set the message bar to display messages from the layer tree - * \since QGIS 3.14 - */ - void setMessageBar( QgsMessageBar *messageBar ); - - /** - * Set the show private layers to \a showPrivate - * \since QGIS 3.18 - */ - void setShowPrivateLayers( bool showPrivate ); - - /** - * Returns the show private layers status - * \since QGIS 3.18 - */ - bool showPrivateLayers( ); - - signals: - //! Emitted when a current layer is changed - void currentLayerChanged( QgsMapLayer *layer ); - - protected: - void contextMenuEvent( QContextMenuEvent *event ) override; - - void updateExpandedStateFromNode( QgsLayerTreeNode *node ); - - QgsMapLayer *layerForIndex( const QModelIndex &index ) const; - - void mouseReleaseEvent( QMouseEvent *event ) override; - void keyPressEvent( QKeyEvent *event ) override; - - void dropEvent( QDropEvent *event ) override; - - void resizeEvent( QResizeEvent *event ) override; - - protected slots: - - void modelRowsInserted( const QModelIndex &index, int start, int end ); - void modelRowsRemoved(); - - void updateExpandedStateToNode( const QModelIndex &index ); - - void onCurrentChanged(); - void onExpandedChanged( QgsLayerTreeNode *node, bool expanded ); - void onModelReset(); - - private slots: - void onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key ); - //! Handles updating the viewport to avoid flicker - void onHorizontalScroll( int value ); - - void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ); - - protected: - //! helper class with default actions. Lazily initialized. - QgsLayerTreeViewDefaultActions *mDefaultActions = nullptr; - //! Context menu provider. Owned by the view. - QgsLayerTreeViewMenuProvider *mMenuProvider = nullptr; - //! Keeps track of current layer ID (to check when to emit signal about change of current layer) - QString mCurrentLayerID; - //! Storage of indicators used with the tree view - QHash< QgsLayerTreeNode *, QList > mIndicators; - //! Used by the item delegate for identification of which indicator has been clicked - QPoint mLastReleaseMousePos; - - //! Width of contextual menu mark for layer nodes - int mLayerMarkWidth; - - private: - QgsLayerTreeProxyModel *mProxyModel = nullptr; - - QgsMessageBar *mMessageBar = nullptr; - - bool mShowPrivateLayers = false; - - // friend so it can access viewOptions() method and mLastReleaseMousePos without making them public - friend class QgsLayerTreeViewItemDelegate; -}; - - -/** - * \ingroup gui - * Implementation of this interface can be implemented to allow QgsLayerTreeView - * instance to provide custom context menus (opened upon right-click). - * - * \see QgsLayerTreeView - * \since QGIS 2.4 - */ -class GUI_EXPORT QgsLayerTreeViewMenuProvider -{ - public: - virtual ~QgsLayerTreeViewMenuProvider() = default; - - //! Returns a newly created menu instance (or NULLPTR on error) - virtual QMenu *createContextMenu() = 0 SIP_FACTORY; -}; - - -#endif // QGSLAYERTREEVIEW_H From d49075588f9a256af9746437b642de62b14a7619 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 8 Nov 2020 16:59:46 +0100 Subject: [PATCH 06/10] Re-add header file removed by mistake --- src/gui/layertree/qgslayertreeview.h | 429 +++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 src/gui/layertree/qgslayertreeview.h diff --git a/src/gui/layertree/qgslayertreeview.h b/src/gui/layertree/qgslayertreeview.h new file mode 100644 index 00000000000..68efe8a4ef0 --- /dev/null +++ b/src/gui/layertree/qgslayertreeview.h @@ -0,0 +1,429 @@ +/*************************************************************************** + qgslayertreeview.h + -------------------------------------- + Date : May 2014 + Copyright : (C) 2014 by Martin Dobias + Email : wonder dot sk at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSLAYERTREEVIEW_H +#define QGSLAYERTREEVIEW_H + +#include +#include "qgis.h" +#include "qgis_gui.h" + +class QgsLayerTreeGroup; +class QgsLayerTreeLayer; +class QgsLayerTreeModel; +class QgsLayerTreeNode; +class QgsLayerTreeModelLegendNode; +class QgsLayerTreeViewDefaultActions; +class QgsLayerTreeViewIndicator; +class QgsLayerTreeViewMenuProvider; +class QgsMapLayer; +class QgsMessageBar; +class QgsLayerTreeFilterProxyModel; + + +#include + +/** + * \ingroup gui + * + * The QgsLayerTreeProxyModel class is a proxy model for QgsLayerTreeModel, supports + * private layers and text filtering. + * + * \since QGIS 3.18 + */ +class GUI_EXPORT QgsLayerTreeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + + /** + * Constructs QgsLayerTreeProxyModel with source model \a treeModel and a \a parent + */ + QgsLayerTreeProxyModel( QgsLayerTreeModel *treeModel, QObject *parent ); + + /** + * Sets filter to \a filterText. + */ + void setFilterText( const QString &filterText = QString() ); + + /** + * Returns if private layers are shown. + */ + bool showPrivateLayers() const; + + /** + * Determines if private layers are shown. + */ + void setShowPrivateLayers( bool showPrivate ); + + protected: + + bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; + + private: + + bool nodeShown( QgsLayerTreeNode *node ) const; + + QgsLayerTreeModel *mLayerTreeModel = nullptr; + QString mFilterText; + bool mShowPrivateLayers = false; +}; + + +/** + * \ingroup gui + * The QgsLayerTreeView class extends QTreeView and provides some additional functionality + * when working with a layer tree. + * + * The view updates expanded state of layer tree nodes and also listens to changes + * to expanded states in the layer tree. + * + * The view keeps track of the current layer and emits a signal when the current layer has changed. + * + * Allows the client to specify a context menu provider with custom actions. Also it comes + * with a set of default actions that can be used when building context menu. + * + * \see QgsLayerTreeModel + * \since QGIS 2.4 + */ +class GUI_EXPORT QgsLayerTreeView : public QTreeView +{ + +#ifdef SIP_RUN + SIP_CONVERT_TO_SUBCLASS_CODE + if ( sipCpp->inherits( "QgsLayerTreeView" ) ) + sipType = sipType_QgsLayerTreeView; + else + sipType = 0; + SIP_END +#endif + + + Q_OBJECT + public: + + //! Constructor for QgsLayerTreeView + explicit QgsLayerTreeView( QWidget *parent SIP_TRANSFERTHIS = nullptr ); + ~QgsLayerTreeView() override; + + //! Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model. + void setModel( QAbstractItemModel *model ) override; + + //! Gets access to the model casted to QgsLayerTreeModel + QgsLayerTreeModel *layerTreeModel() const; + + /** + * Returns the proxy model used by the view. + * + * This can be used to set filters controlling which layers are shown in the view. + * + * \since QGIS 3.18 + */ + QgsLayerTreeProxyModel *proxyModel() const; + + /** + * Returns layer tree node for given proxy model tree \a index. Returns root node for invalid index. + * Returns NULLPTR if index does not refer to a layer tree node (e.g. it is a legend node) + * + * Unlike QgsLayerTreeModel::index2Node(), calling this method correctly accounts + * for mapping the view indexes through the view's proxy model to the source model. + * + * \since QGIS 3.18 + */ + QgsLayerTreeNode *index2node( const QModelIndex &index ) const; + + /** + * Returns proxy model index for a given node. If the node does not belong to the layer tree, the result is undefined + * + * Unlike QgsLayerTreeModel::node2index(), calling this method correctly accounts + * for mapping the view indexes through the view's proxy model to the source model. + * + * \since QGIS 3.18 + */ + QModelIndex node2index( QgsLayerTreeNode *node ) const; + + + /** + * Returns source model index for a given node. If the node does not belong to the layer tree, the result is undefined + * + * \since QGIS 3.18 + */ + QModelIndex node2sourceIndex( QgsLayerTreeNode *node ) const; + + + /** + * Returns legend node for given proxy model tree \a index. Returns NULLPTR for invalid index + * + * Unlike QgsLayerTreeModel::index2legendNode(), calling this method correctly accounts + * for mapping the view indexes through the view's proxy model to the source model. + * + * \since QGIS 3.18 + */ + QgsLayerTreeModelLegendNode *index2legendNode( const QModelIndex &index ) const; + + /** + * Returns proxy model index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined. + * If the legend node is belongs to the tree but it is filtered out, invalid model index is returned. + * + * Unlike QgsLayerTreeModel::legendNode2index(), calling this method correctly accounts + * for mapping the view indexes through the view's proxy model to the source model. + * + * \since QGIS 3.18 + */ + QModelIndex legendNode2index( QgsLayerTreeModelLegendNode *legendNode ); + + /** + * Returns index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined. + * If the legend node is belongs to the tree but it is filtered out, invalid model index is returned. + * + * \since QGIS 3.18 + */ + QModelIndex legendNode2sourceIndex( QgsLayerTreeModelLegendNode *legendNode ); + + //! Gets access to the default actions that may be used with the tree view + QgsLayerTreeViewDefaultActions *defaultActions(); + + //! Sets provider for context menu. Takes ownership of the instance + void setMenuProvider( QgsLayerTreeViewMenuProvider *menuProvider SIP_TRANSFER ); + //! Returns pointer to the context menu provider. May be NULLPTR + QgsLayerTreeViewMenuProvider *menuProvider() const { return mMenuProvider; } + + /** + * Returns the currently selected layer, or NULLPTR if no layers is selected. + * + * \see setCurrentLayer() + */ + QgsMapLayer *currentLayer() const; + + /** + * Convenience methods which sets the visible state of the specified map \a layer. + * + * \see QgsLayerTreeNode::setItemVisibilityChecked() + * \since QGIS 3.10 + */ + void setLayerVisible( QgsMapLayer *layer, bool visible ); + + /** + * Sets the currently selected \a layer. + * + * If \a layer is NULLPTR then all layers will be deselected. + * + * \see currentLayer() + */ + void setCurrentLayer( QgsMapLayer *layer ); + + //! Gets current node. May be NULLPTR + QgsLayerTreeNode *currentNode() const; + //! Gets current group node. If a layer is current node, the function will return parent group. May be NULLPTR. + QgsLayerTreeGroup *currentGroupNode() const; + + /** + * Gets current legend node. May be NULLPTR if current node is not a legend node. + * \since QGIS 2.14 + */ + QgsLayerTreeModelLegendNode *currentLegendNode() const; + + /** + * Returns list of selected nodes + * \param skipInternal If TRUE, will ignore nodes which have an ancestor in the selection + */ + QList selectedNodes( bool skipInternal = false ) const; + //! Returns list of selected nodes filtered to just layer nodes + QList selectedLayerNodes() const; + + //! Gets list of selected layers + QList selectedLayers() const; + + /** + * Gets list of selected layers, including those that are not directly selected, but their + * ancestor groups is selected. If we have a group with two layers L1, L2 and just the group + * node is selected, this method returns L1 and L2, while selectedLayers() returns an empty list. + * \since QGIS 3.4 + */ + QList selectedLayersRecursive() const; + + /** + * Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names + * in the layer tree view. They can be used to show extra information with tree nodes and they allow + * user interaction. + * + * Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes. + * \see removeIndicator + * \see indicators + * \since QGIS 3.2 + */ + void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator ); + + /** + * Removes a previously added indicator to a layer tree node. Does not delete the indicator. + * \see addIndicator + * \see indicators + * \since QGIS 3.2 + */ + void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator ); + + /** + * Returns list of indicators associated with a particular layer tree node. + * \see addIndicator + * \see removeIndicator + * \since QGIS 3.2 + */ + QList indicators( QgsLayerTreeNode *node ) const; + + /** + * Returns width of contextual menu mark, at right of layer node items. + * \see setLayerMarkWidth + * \since QGIS 3.8 + */ + int layerMarkWidth() const { return mLayerMarkWidth; } + +///@cond PRIVATE + + /** + * Returns a list of custom property keys which are considered as related to view operations + * only. E.g. node expanded state. + * + * Changes to these keys will not mark a project as "dirty" and trigger unsaved changes + * warnings. + * + * \since QGIS 3.2 + */ + static QStringList viewOnlyCustomProperties() SIP_SKIP; +///@endcond + + public slots: + //! Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitored by the model + void refreshLayerSymbology( const QString &layerId ); + + /** + * Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes + * \since QGIS 2.18 + */ + void expandAllNodes(); + + /** + * Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes + * \since QGIS 2.18 + */ + void collapseAllNodes(); + + /** + * Set width of contextual menu mark, at right of layer node items. + * \see layerMarkWidth + * \since QGIS 3.8 + */ + void setLayerMarkWidth( int width ) { mLayerMarkWidth = width; } + + /** + * Set the message bar to display messages from the layer tree + * \since QGIS 3.14 + */ + void setMessageBar( QgsMessageBar *messageBar ); + + /** + * Set the show private layers to \a showPrivate + * \since QGIS 3.18 + */ + void setShowPrivateLayers( bool showPrivate ); + + /** + * Returns the show private layers status + * \since QGIS 3.18 + */ + bool showPrivateLayers( ); + + signals: + //! Emitted when a current layer is changed + void currentLayerChanged( QgsMapLayer *layer ); + + protected: + void contextMenuEvent( QContextMenuEvent *event ) override; + + void updateExpandedStateFromNode( QgsLayerTreeNode *node ); + + QgsMapLayer *layerForIndex( const QModelIndex &index ) const; + + void mouseReleaseEvent( QMouseEvent *event ) override; + void keyPressEvent( QKeyEvent *event ) override; + + void dropEvent( QDropEvent *event ) override; + + void resizeEvent( QResizeEvent *event ) override; + + protected slots: + + void modelRowsInserted( const QModelIndex &index, int start, int end ); + void modelRowsRemoved(); + + void updateExpandedStateToNode( const QModelIndex &index ); + + void onCurrentChanged(); + void onExpandedChanged( QgsLayerTreeNode *node, bool expanded ); + void onModelReset(); + + private slots: + void onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key ); + //! Handles updating the viewport to avoid flicker + void onHorizontalScroll( int value ); + + void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ); + + protected: + //! helper class with default actions. Lazily initialized. + QgsLayerTreeViewDefaultActions *mDefaultActions = nullptr; + //! Context menu provider. Owned by the view. + QgsLayerTreeViewMenuProvider *mMenuProvider = nullptr; + //! Keeps track of current layer ID (to check when to emit signal about change of current layer) + QString mCurrentLayerID; + //! Storage of indicators used with the tree view + QHash< QgsLayerTreeNode *, QList > mIndicators; + //! Used by the item delegate for identification of which indicator has been clicked + QPoint mLastReleaseMousePos; + + //! Width of contextual menu mark for layer nodes + int mLayerMarkWidth; + + private: + QgsLayerTreeProxyModel *mProxyModel = nullptr; + + QgsMessageBar *mMessageBar = nullptr; + + bool mShowPrivateLayers = false; + + // friend so it can access viewOptions() method and mLastReleaseMousePos without making them public + friend class QgsLayerTreeViewItemDelegate; +}; + + +/** + * \ingroup gui + * Implementation of this interface can be implemented to allow QgsLayerTreeView + * instance to provide custom context menus (opened upon right-click). + * + * \see QgsLayerTreeView + * \since QGIS 2.4 + */ +class GUI_EXPORT QgsLayerTreeViewMenuProvider +{ + public: + virtual ~QgsLayerTreeViewMenuProvider() = default; + + //! Returns a newly created menu instance (or NULLPTR on error) + virtual QMenu *createContextMenu() = 0 SIP_FACTORY; +}; + + +#endif // QGSLAYERTREEVIEW_H From c9133083298b1a9223f2abfb223a00ca8510b079 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 9 Nov 2020 11:34:09 +0100 Subject: [PATCH 07/10] Don't dereference deleted node --- src/core/layertree/qgslayertreenode.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/core/layertree/qgslayertreenode.cpp b/src/core/layertree/qgslayertreenode.cpp index 9bd91d165f5..85087c0572e 100644 --- a/src/core/layertree/qgslayertreenode.cpp +++ b/src/core/layertree/qgslayertreenode.cpp @@ -285,22 +285,30 @@ void QgsLayerTreeNode::removeChildrenPrivate( int from, int count, bool destroy if ( to >= mChildren.count() ) return; + // Remove in reverse order while ( --count >= 0 ) { - QgsLayerTreeNode *node = mChildren.at( from ); + const int last { from + count }; + Q_ASSERT( last >= 0 && last < mChildren.count( ) ); + QgsLayerTreeNode *node = mChildren.at( last ); // Remove children first if ( ! node->children().isEmpty() ) { - node->removeChildrenPrivate( 0, node->children().count(), destroy ); + node->removeChildrenPrivate( 0, node->children().count( ), destroy ); } - emit willRemoveChildren( this, from, from ); - node = mChildren.takeAt( from ); + emit willRemoveChildren( this, last, last ); + node = mChildren.takeAt( last ); if ( destroy ) + { delete node; - node->makeOrphan(); - emit removedChildren( this, from, from ); + } + else + { + node->makeOrphan(); + } + emit removedChildren( this, last, last ); } } From 525adede355ef54ca23742097201e8e83f1cb556 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 9 Nov 2020 17:31:36 +0100 Subject: [PATCH 08/10] Fix QgsLayerDefinition::loadLayerDefinition --- src/core/qgslayerdefinition.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/qgslayerdefinition.cpp b/src/core/qgslayerdefinition.cpp index 7c71ae4ccc6..57348855594 100644 --- a/src/core/qgslayerdefinition.cpp +++ b/src/core/qgslayerdefinition.cpp @@ -186,9 +186,7 @@ bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *proj root->resolveReferences( project ); QList nodes = root->children(); - const auto constNodes = nodes; - for ( QgsLayerTreeNode *node : constNodes ) - root->takeChild( node ); + root->abandonChildren(); delete root; rootGroup->insertChildNodes( -1, nodes ); From 8d7f540427fbc8e85c4121db97e240047887af0c Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 10 Nov 2020 14:44:34 +0100 Subject: [PATCH 09/10] Fix tree model loading from xml --- .../layertree/qgslayertreenode.sip.in | 2 + src/app/qgsprojectlayergroupdialog.cpp | 5 ++- src/core/layertree/qgslayertreemodel.cpp | 19 ++++---- src/core/layertree/qgslayertreenode.cpp | 1 + src/core/layertree/qgslayertreenode.h | 13 +++++- src/core/qgsproject.cpp | 5 ++- src/gui/layertree/qgslayertreeview.cpp | 43 +++++++++++++++++-- src/gui/layertree/qgslayertreeview.h | 4 ++ 8 files changed, 74 insertions(+), 18 deletions(-) diff --git a/python/core/auto_generated/layertree/qgslayertreenode.sip.in b/python/core/auto_generated/layertree/qgslayertreenode.sip.in index 5c88025b03b..e85b8751627 100644 --- a/python/core/auto_generated/layertree/qgslayertreenode.sip.in +++ b/python/core/auto_generated/layertree/qgslayertreenode.sip.in @@ -98,6 +98,8 @@ Gets pointer to the parent. If parent is ``None``, the node is a root node Gets list of children of the node. Children are owned by the parent %End + + virtual QString name() const = 0; %Docstring Returns name of the node diff --git a/src/app/qgsprojectlayergroupdialog.cpp b/src/app/qgsprojectlayergroupdialog.cpp index f6174d285d3..1fabcf811fd 100644 --- a/src/app/qgsprojectlayergroupdialog.cpp +++ b/src/app/qgsprojectlayergroupdialog.cpp @@ -204,7 +204,10 @@ void QgsProjectLayerGroupDialog::changeProjectFile() QDomElement layerTreeElem = projectDom.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) ); if ( !layerTreeElem.isNull() ) { - mRootGroup->readChildrenFromXml( layerTreeElem, QgsReadWriteContext() ); + // Use a temporary tree to read the nodes to prevent signals being delivered to the models + QgsLayerTree tempTree; + tempTree.readChildrenFromXml( layerTreeElem, QgsReadWriteContext() ); + mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() ); } else { diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index 46e6ad607ef..7aea78f0a5e 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -899,12 +899,12 @@ void QgsLayerTreeModel::connectToLayer( QgsLayerTreeLayer *nodeLayer ) { // in order to connect to layer, we need to have it loaded. // keep an eye on the layer ID: once loaded, we will use it - connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded ); + connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection ); return; } // watch if the layer is getting removed - connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded ); + connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection ); if ( testFlag( ShowLegend ) ) { @@ -1003,14 +1003,13 @@ void QgsLayerTreeModel::connectToRootNode() { Q_ASSERT( mRootNode ); - connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren ); - connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren ); - connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren ); - connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren ); - connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged ); - connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged ); - - connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged ); + connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection ); + connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection ); connectToLayers( mRootNode ); } diff --git a/src/core/layertree/qgslayertreenode.cpp b/src/core/layertree/qgslayertreenode.cpp index 85087c0572e..eb3c23238e1 100644 --- a/src/core/layertree/qgslayertreenode.cpp +++ b/src/core/layertree/qgslayertreenode.cpp @@ -61,6 +61,7 @@ QList QgsLayerTreeNode::abandonChildren() void QgsLayerTreeNode::makeOrphan() { + disconnect(); mParent = nullptr; } diff --git a/src/core/layertree/qgslayertreenode.h b/src/core/layertree/qgslayertreenode.h index 5840b7cab9e..cfaf8c68c72 100644 --- a/src/core/layertree/qgslayertreenode.h +++ b/src/core/layertree/qgslayertreenode.h @@ -112,9 +112,18 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject QList children() { return mChildren; } //! Gets list of children of the node. Children are owned by the parent QList children() const { return mChildren; } SIP_SKIP - //! Remove the childrens and sets their parent to null + + /** + * Removes the childrens, disconnect all their signals and sets their parent to NULLPTR + * \return the removed children + * \since QGIS 3.16 + */ QList abandonChildren() SIP_SKIP; - //! Set parent to null + + /** + * Sets parent to NULLPTR and disconnects all signals + * \since QGIS 3.16 + */ void makeOrphan() SIP_SKIP; /** diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 69a51180ffc..a15de57ec66 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -1536,7 +1536,10 @@ bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) ); if ( !layerTreeElem.isNull() ) { - mRootGroup->readChildrenFromXml( layerTreeElem, context ); + // Use a temporary tree to read the nodes to prevent signals being delivered to the models + QgsLayerTree tempTree; + tempTree.readChildrenFromXml( layerTreeElem, context ); + mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() ); } else { diff --git a/src/gui/layertree/qgslayertreeview.cpp b/src/gui/layertree/qgslayertreeview.cpp index bfabb4d6702..d578dae1474 100644 --- a/src/gui/layertree/qgslayertreeview.cpp +++ b/src/gui/layertree/qgslayertreeview.cpp @@ -89,9 +89,6 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model ) if ( !treeModel ) return; - connect( treeModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted ); - connect( treeModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved ); - if ( mMessageBar ) connect( treeModel, &QgsLayerTreeModel::messageEmitted, [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 ) @@ -100,6 +97,9 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model ) mProxyModel = new QgsLayerTreeProxyModel( treeModel, this ); + connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted ); + connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved ); + #ifdef ENABLE_MODELTEST new ModelTest( treeModel, this ); new ModelTest( mProxyModel, this ); @@ -118,6 +118,8 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model ) connect( treeModel, &QAbstractItemModel::dataChanged, this, &QgsLayerTreeView::onDataChanged ); updateExpandedStateFromNode( treeModel->rootGroup() ); + + //checkModel(); } QgsLayerTreeModel *QgsLayerTreeView::layerTreeModel() const @@ -186,7 +188,7 @@ void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event ) void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end ) { - QgsLayerTreeNode *parentNode = layerTreeModel()->index2node( index ); + QgsLayerTreeNode *parentNode = index2node( index ); if ( !parentNode ) return; @@ -313,6 +315,8 @@ void QgsLayerTreeView::onCurrentChanged() layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) ); } + //checkModel(); + emit currentLayerChanged( layerCurrent ); } @@ -342,6 +346,7 @@ void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QS void QgsLayerTreeView::onModelReset() { updateExpandedStateFromNode( layerTreeModel()->rootGroup() ); + //checkModel(); } void QgsLayerTreeView::updateExpandedStateFromNode( QgsLayerTreeNode *node ) @@ -650,8 +655,38 @@ void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIn if ( roles.contains( Qt::SizeHintRole ) ) viewport()->update(); + + //checkModel(); } +#if 0 +// for model debugging +void QgsLayerTreeView::checkModel() +{ + std::function debug; + debug = [ & ]( QgsLayerTreeNode * node, int depth ) + { + if ( depth == 1 ) + qDebug() << "----------------------------------------------"; + + qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) ); + Q_ASSERT( node == index2node( node2index( node ) ) ); + Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) ); + Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) ); + + for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ ) + { + QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) }; + if ( childNode ) + debug( childNode, depth + 1 ); + else + qDebug() << "Warning no child node!"; + } + }; + debug( layerTreeModel()->rootGroup(), 1 ); +} +#endif + QgsLayerTreeProxyModel *QgsLayerTreeView::proxyModel() const { return mProxyModel; diff --git a/src/gui/layertree/qgslayertreeview.h b/src/gui/layertree/qgslayertreeview.h index 68efe8a4ef0..bd0d63347bc 100644 --- a/src/gui/layertree/qgslayertreeview.h +++ b/src/gui/layertree/qgslayertreeview.h @@ -80,6 +80,7 @@ class GUI_EXPORT QgsLayerTreeProxyModel : public QSortFilterProxyModel QgsLayerTreeModel *mLayerTreeModel = nullptr; QString mFilterText; bool mShowPrivateLayers = false; + }; @@ -403,6 +404,9 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView bool mShowPrivateLayers = false; + // For model debugging + // void checkModel( ); + // friend so it can access viewOptions() method and mLastReleaseMousePos without making them public friend class QgsLayerTreeViewItemDelegate; }; From cb43bd8bd2dc26effb7880928d40b06f4985e4d4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 11 Nov 2020 10:54:14 +0100 Subject: [PATCH 10/10] Fix mutex visibility --- .../layertree/qgslayertreegroup.sip.in | 2 ++ .../auto_generated/layertree/qgslayertreenode.sip.in | 2 +- src/core/layertree/qgslayertreegroup.cpp | 7 +++++++ src/core/layertree/qgslayertreegroup.h | 5 +++++ src/core/layertree/qgslayertreemodel.cpp | 4 ++-- src/core/layertree/qgslayertreenode.h | 12 +++++------- src/gui/layertree/qgslayertreeview.cpp | 1 - 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/python/core/auto_generated/layertree/qgslayertreegroup.sip.in b/python/core/auto_generated/layertree/qgslayertreegroup.sip.in index 7d1df8c5217..63ada6a45e3 100644 --- a/python/core/auto_generated/layertree/qgslayertreegroup.sip.in +++ b/python/core/auto_generated/layertree/qgslayertreegroup.sip.in @@ -203,6 +203,7 @@ of -1 will determine automatically (either first one currently checked or none) %End protected slots: + void nodeVisibilityChanged( QgsLayerTreeNode *node ); protected: @@ -216,6 +217,7 @@ Set check state of children - if mutually exclusive + private: QgsLayerTreeGroup( const QgsLayerTreeGroup &other ); diff --git a/python/core/auto_generated/layertree/qgslayertreenode.sip.in b/python/core/auto_generated/layertree/qgslayertreenode.sip.in index e85b8751627..ae0aa5a293b 100644 --- a/python/core/auto_generated/layertree/qgslayertreenode.sip.in +++ b/python/core/auto_generated/layertree/qgslayertreenode.sip.in @@ -99,7 +99,6 @@ Gets list of children of the node. Children are owned by the parent %End - virtual QString name() const = 0; %Docstring Returns name of the node @@ -318,6 +317,7 @@ Low-level removal of children from the node. protected: + }; diff --git a/src/core/layertree/qgslayertreegroup.cpp b/src/core/layertree/qgslayertreegroup.cpp index dd6b2e51fde..3da19445996 100644 --- a/src/core/layertree/qgslayertreegroup.cpp +++ b/src/core/layertree/qgslayertreegroup.cpp @@ -442,6 +442,13 @@ void QgsLayerTreeGroup::updateChildVisibilityMutuallyExclusive() mChangingChildVisibility = false; } +void QgsLayerTreeGroup::makeOrphan() +{ + QgsLayerTreeNode::makeOrphan(); + // Reconnect internal signals + connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged ); +} + void QgsLayerTreeGroup::setItemVisibilityCheckedRecursive( bool checked ) { QgsLayerTreeNode::setItemVisibilityChecked( checked ); diff --git a/src/core/layertree/qgslayertreegroup.h b/src/core/layertree/qgslayertreegroup.h index 63b8a5722fc..75b514f1775 100644 --- a/src/core/layertree/qgslayertreegroup.h +++ b/src/core/layertree/qgslayertreegroup.h @@ -206,6 +206,7 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode void setIsMutuallyExclusive( bool enabled, int initialChildIndex = -1 ); protected slots: + void nodeVisibilityChanged( QgsLayerTreeNode *node ); protected: @@ -228,6 +229,9 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode */ int mMutuallyExclusiveChildIndex = -1; + //! Sets parent to NULLPTR and disconnects all external and forwarded signals + virtual void makeOrphan() override SIP_SKIP; + private: #ifdef SIP_RUN @@ -240,6 +244,7 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode QgsLayerTreeGroup &operator= ( const QgsLayerTreeGroup & ) = delete; + }; diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index 7aea78f0a5e..a2c47953dcf 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -773,7 +773,7 @@ void QgsLayerTreeModel::nodeVisibilityChanged( QgsLayerTreeNode *node ) { Q_ASSERT( node ); - QModelIndex index = node2index( node ); + const QModelIndex index = node2index( node ); emit dataChanged( index, index ); } @@ -782,7 +782,7 @@ void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString & Q_UNUSED( name ) Q_ASSERT( node ); - QModelIndex index = node2index( node ); + const QModelIndex index = node2index( node ); emit dataChanged( index, index ); } diff --git a/src/core/layertree/qgslayertreenode.h b/src/core/layertree/qgslayertreenode.h index cfaf8c68c72..567d7350be8 100644 --- a/src/core/layertree/qgslayertreenode.h +++ b/src/core/layertree/qgslayertreenode.h @@ -114,18 +114,12 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject QList children() const { return mChildren; } SIP_SKIP /** - * Removes the childrens, disconnect all their signals and sets their parent to NULLPTR + * Removes the childrens, disconnect all the forwarded and external signals and sets their parent to NULLPTR * \return the removed children * \since QGIS 3.16 */ QList abandonChildren() SIP_SKIP; - /** - * Sets parent to NULLPTR and disconnects all signals - * \since QGIS 3.16 - */ - void makeOrphan() SIP_SKIP; - /** * Returns name of the node * \since QGIS 3.0 @@ -299,8 +293,12 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject //! custom properties attached to the node QgsObjectCustomProperties mProperties; + //! Sets parent to NULLPTR and disconnects all external and forwarded signals + virtual void makeOrphan() SIP_SKIP; + private: QgsLayerTreeNode &operator=( const QgsLayerTreeNode & ) = delete; + }; diff --git a/src/gui/layertree/qgslayertreeview.cpp b/src/gui/layertree/qgslayertreeview.cpp index d578dae1474..1c828c087ab 100644 --- a/src/gui/layertree/qgslayertreeview.cpp +++ b/src/gui/layertree/qgslayertreeview.cpp @@ -101,7 +101,6 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model ) connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved ); #ifdef ENABLE_MODELTEST - new ModelTest( treeModel, this ); new ModelTest( mProxyModel, this ); #endif