From a2fce27b6130cd0632a0344cd820e87fe6ee3b9b Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Mon, 14 Nov 2016 19:53:18 +0800 Subject: [PATCH] Propagate layer/group name changes in the layer tree (fixes #15844) (cherry picked from commit 968e02d6fe38b024855ef75852eb033b4ad9ecbd) --- python/core/layertree/qgslayertreelayer.sip | 10 ++++ python/core/layertree/qgslayertreenode.sip | 10 ++++ src/core/layertree/qgslayertreegroup.cpp | 14 +++++ src/core/layertree/qgslayertreegroup.h | 4 +- src/core/layertree/qgslayertreelayer.cpp | 26 +++++++++ src/core/layertree/qgslayertreelayer.h | 10 ++++ src/core/layertree/qgslayertreemodel.cpp | 11 +++- src/core/layertree/qgslayertreemodel.h | 1 + src/core/layertree/qgslayertreenode.cpp | 1 + src/core/layertree/qgslayertreenode.h | 10 ++++ tests/src/core/testqgslayertree.cpp | 62 +++++++++++++++++++++ 11 files changed, 156 insertions(+), 3 deletions(-) diff --git a/python/core/layertree/qgslayertreelayer.sip b/python/core/layertree/qgslayertreelayer.sip index 79b64f245e43..21b694bad9b9 100644 --- a/python/core/layertree/qgslayertreelayer.sip +++ b/python/core/layertree/qgslayertreelayer.sip @@ -31,6 +31,13 @@ class QgsLayerTreeLayer : QgsLayerTreeNode QgsMapLayer* layer() const; + //! Get layer's name + //! @note added in 2.18.1 + QString name() const; + //! Set layer's name + //! @note added in 2.18.1 + void setName( const QString& n ); + QString layerName() const; void setLayerName( const QString& n ); @@ -47,6 +54,9 @@ class QgsLayerTreeLayer : QgsLayerTreeNode protected slots: void registryLayersAdded( const QList& layers ); void registryLayersWillBeRemoved( const QStringList& layerIds ); + //! Emits a nameChanged() signal if layer's name has changed + //! @note added in 2.18.1 + void layerNameChanged(); signals: //! emitted when a previously unavailable layer got loaded diff --git a/python/core/layertree/qgslayertreenode.sip b/python/core/layertree/qgslayertreenode.sip index 23db81c503f7..abf9cb3e1236 100644 --- a/python/core/layertree/qgslayertreenode.sip +++ b/python/core/layertree/qgslayertreenode.sip @@ -76,6 +76,13 @@ class QgsLayerTreeNode : QObject //! Get list of children of the node. Children are owned by the parent QList children(); + //! Return name of the node + //! @note added in 2.18.1 + virtual QString name() const = 0; + //! Set name of the node. Emits nameChanged signal. + //! @note added in 2.18.1 + virtual void setName( const QString& name ) = 0; + //! Read layer tree from XML. Returns new instance static QgsLayerTreeNode *readXML( QDomElement &element ); //! Write layer tree to XML @@ -119,6 +126,9 @@ class QgsLayerTreeNode : QObject void customPropertyChanged( QgsLayerTreeNode *node, const QString& key ); //! Emitted when the collapsed/expanded state of a node within the tree has been changed void expandedChanged( QgsLayerTreeNode *node, bool expanded ); + //! Emitted when the name of the node is changed + //! @note added in 2.18.1 + void nameChanged( QgsLayerTreeNode* node, QString name ); protected: diff --git a/src/core/layertree/qgslayertreegroup.cpp b/src/core/layertree/qgslayertreegroup.cpp index 91dfb914e8ee..6a0aa4678cca 100644 --- a/src/core/layertree/qgslayertreegroup.cpp +++ b/src/core/layertree/qgslayertreegroup.cpp @@ -46,6 +46,20 @@ QgsLayerTreeGroup::QgsLayerTreeGroup( const QgsLayerTreeGroup& other ) connect( this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) ); } +QString QgsLayerTreeGroup::name() const +{ + return mName; +} + +void QgsLayerTreeGroup::setName( const QString& n ) +{ + if ( mName == n ) + return; + + mName = n; + emit nameChanged( this, n ); +} + QgsLayerTreeGroup* QgsLayerTreeGroup::insertGroup( int index, const QString& name ) { diff --git a/src/core/layertree/qgslayertreegroup.h b/src/core/layertree/qgslayertreegroup.h index 4fad9cc48320..5f8fd62f6091 100644 --- a/src/core/layertree/qgslayertreegroup.h +++ b/src/core/layertree/qgslayertreegroup.h @@ -36,9 +36,9 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode QgsLayerTreeGroup( const QgsLayerTreeGroup& other ); //! Get group's name - QString name() const { return mName; } + QString name() const override; //! Set group's name - void setName( const QString& n ) { mName = n; } + void setName( const QString& n ) override; //! Insert a new group node with given name at specified position. Newly created node is owned by this group. QgsLayerTreeGroup* insertGroup( int index, const QString& name ); diff --git a/src/core/layertree/qgslayertreelayer.cpp b/src/core/layertree/qgslayertreelayer.cpp index 2f577df69162..e1a9466c96cc 100644 --- a/src/core/layertree/qgslayertreelayer.cpp +++ b/src/core/layertree/qgslayertreelayer.cpp @@ -58,6 +58,7 @@ void QgsLayerTreeLayer::attachToLayer() { mLayer = l; mLayerName = l->name(); + connect( l, SIGNAL( nameChanged() ), this, SLOT( layerNameChanged() ) ); // make sure we are notified if the layer is removed connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( registryLayersWillBeRemoved( QStringList ) ) ); } @@ -70,6 +71,15 @@ void QgsLayerTreeLayer::attachToLayer() } } +QString QgsLayerTreeLayer::name() const +{ + return layerName(); +} + +void QgsLayerTreeLayer::setName( const QString& n ) +{ + setLayerName( n ); +} QString QgsLayerTreeLayer::layerName() const { @@ -79,9 +89,19 @@ QString QgsLayerTreeLayer::layerName() const void QgsLayerTreeLayer::setLayerName( const QString& n ) { if ( mLayer ) + { + if ( mLayer->name() == n ) + return; mLayer->setName( n ); + // no need to emit signal: we will be notified from layer's nameChanged() signal + } else + { + if ( mLayerName == n ) + return; mLayerName = n; + emit nameChanged( this, n ); + } } void QgsLayerTreeLayer::setVisible( Qt::CheckState state ) @@ -170,3 +190,9 @@ void QgsLayerTreeLayer::registryLayersWillBeRemoved( const QStringList& layerIds mLayer = nullptr; } } + +void QgsLayerTreeLayer::layerNameChanged() +{ + Q_ASSERT( mLayer ); + emit nameChanged( this, mLayer->name() ); +} diff --git a/src/core/layertree/qgslayertreelayer.h b/src/core/layertree/qgslayertreelayer.h index 3030f6c7a99b..2fb3a05329e3 100644 --- a/src/core/layertree/qgslayertreelayer.h +++ b/src/core/layertree/qgslayertreelayer.h @@ -51,6 +51,13 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode QgsMapLayer* layer() const { return mLayer; } + //! Get layer's name + //! @note added in 2.18.1 + QString name() const override; + //! Set layer's name + //! @note added in 2.18.1 + void setName( const QString& n ) override; + QString layerName() const; void setLayerName( const QString& n ); @@ -67,6 +74,9 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode protected slots: void registryLayersAdded( const QList& layers ); void registryLayersWillBeRemoved( const QStringList& layerIds ); + //! Emits a nameChanged() signal if layer's name has changed + //! @note added in 2.18.1 + void layerNameChanged(); signals: //! emitted when a previously unavailable layer got loaded diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index d283752cff2b..366aa1907ef3 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -753,6 +753,15 @@ void QgsLayerTreeModel::nodeVisibilityChanged( QgsLayerTreeNode* node ) emit dataChanged( index, index ); } +void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode* node, const QString& name ) +{ + Q_UNUSED( name ); + Q_ASSERT( node ); + + QModelIndex index = node2index( node ); + emit dataChanged( index, index ); +} + void QgsLayerTreeModel::nodeCustomPropertyChanged( QgsLayerTreeNode* node, const QString& key ) { @@ -865,7 +874,6 @@ void QgsLayerTreeModel::connectToLayer( QgsLayerTreeLayer* nodeLayer ) connect( layer, SIGNAL( editingStarted() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection ); connect( layer, SIGNAL( editingStopped() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection ); connect( layer, SIGNAL( layerModified() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection ); - connect( layer, SIGNAL( layerNameChanged() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection ); } } @@ -938,6 +946,7 @@ void QgsLayerTreeModel::connectToRootNode() connect( mRootNode, SIGNAL( willRemoveChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeWillRemoveChildren( QgsLayerTreeNode*, int, int ) ) ); connect( mRootNode, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) ); connect( mRootNode, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) ); + connect( mRootNode, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeNameChanged( QgsLayerTreeNode*, QString ) ) ); connect( mRootNode, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) ); diff --git a/src/core/layertree/qgslayertreemodel.h b/src/core/layertree/qgslayertreemodel.h index ee1cdeee924f..26a2946f457d 100644 --- a/src/core/layertree/qgslayertreemodel.h +++ b/src/core/layertree/qgslayertreemodel.h @@ -234,6 +234,7 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel void nodeRemovedChildren(); void nodeVisibilityChanged( QgsLayerTreeNode* node ); + void nodeNameChanged( QgsLayerTreeNode* node, const QString& name ); void nodeCustomPropertyChanged( QgsLayerTreeNode* node, const QString& key ); diff --git a/src/core/layertree/qgslayertreenode.cpp b/src/core/layertree/qgslayertreenode.cpp index a77e00c3f27f..a3513fe4d6c0 100644 --- a/src/core/layertree/qgslayertreenode.cpp +++ b/src/core/layertree/qgslayertreenode.cpp @@ -136,6 +136,7 @@ void QgsLayerTreeNode::insertChildrenPrivate( int index, QList children() { return mChildren; } + //! Return name of the node + //! @note added in 2.18.1 + virtual QString name() const = 0; + //! Set name of the node. Emits nameChanged signal. + //! @note added in 2.18.1 + virtual void setName( const QString& name ) = 0; + //! Read layer tree from XML. Returns new instance static QgsLayerTreeNode *readXML( QDomElement &element ); //! Write layer tree to XML @@ -126,6 +133,9 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject void customPropertyChanged( QgsLayerTreeNode *node, const QString& key ); //! Emitted when the collapsed/expanded state of a node within the tree has been changed void expandedChanged( QgsLayerTreeNode *node, bool expanded ); + //! Emitted when the name of the node is changed + //! @note added in 2.18.1 + void nameChanged( QgsLayerTreeNode* node, QString name ); protected: diff --git a/tests/src/core/testqgslayertree.cpp b/tests/src/core/testqgslayertree.cpp index baa98fc1b06b..c354fb819138 100644 --- a/tests/src/core/testqgslayertree.cpp +++ b/tests/src/core/testqgslayertree.cpp @@ -35,6 +35,8 @@ class TestQgsLayerTree : public QObject private slots: void initTestCase(); void cleanupTestCase(); + void testGroupNameChanged(); + void testLayerNameChanged(); void testCheckStateParentToChild(); void testCheckStateChildToParent(); void testCheckStateMutuallyExclusive(); @@ -80,6 +82,66 @@ void TestQgsLayerTree::cleanupTestCase() QgsApplication::exitQgis(); } +void TestQgsLayerTree::testGroupNameChanged() +{ + QgsLayerTreeNode* secondGroup = mRoot->children()[1]; + + QSignalSpy spy( mRoot, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ) ); + secondGroup->setName( "grp2+" ); + + QCOMPARE( secondGroup->name(), QString( "grp2+" ) ); + + QCOMPARE( spy.count(), 1 ); + QList arguments = spy.takeFirst(); + QCOMPARE( arguments.at( 0 ).value(), secondGroup ); + QCOMPARE( arguments.at( 1 ).toString(), QString( "grp2+" ) ); + + secondGroup->setName( "grp2" ); + QCOMPARE( secondGroup->name(), QString( "grp2" ) ); +} + +void TestQgsLayerTree::testLayerNameChanged() +{ + QgsVectorLayer* vl = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ); + QVERIFY( vl->isValid() ); + + QgsLayerTreeLayer* n = new QgsLayerTreeLayer( vl->id(), vl->name() ); + mRoot->addChildNode( n ); + + QSignalSpy spy( mRoot, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ) ); + + QCOMPARE( n->name(), QString( "vl" ) ); + n->setName( "changed 1" ); + + QCOMPARE( n->name(), QString( "changed 1" ) ); + QCOMPARE( spy.count(), 1 ); + QList arguments = spy.takeFirst(); + QCOMPARE( arguments.at( 0 ).value(), n ); + QCOMPARE( arguments.at( 1 ).toString(), QString( "changed 1" ) ); + + QgsMapLayerRegistry::instance()->addMapLayers( QList() << vl ); + + // set name via map layer + vl->setName( "changed 2" ); + QCOMPARE( n->name(), QString( "changed 2" ) ); + QCOMPARE( spy.count(), 1 ); + arguments = spy.takeFirst(); + QCOMPARE( arguments.at( 0 ).value(), n ); + QCOMPARE( arguments.at( 1 ).toString(), QString( "changed 2" ) ); + + // set name via layer tree + n->setName( "changed 3" ); + QCOMPARE( n->name(), QString( "changed 3" ) ); + QCOMPARE( spy.count(), 1 ); + arguments = spy.takeFirst(); + QCOMPARE( arguments.at( 0 ).value(), n ); + QCOMPARE( arguments.at( 1 ).toString(), QString( "changed 3" ) ); + + QgsMapLayerRegistry::instance()->removeMapLayers( QList() << vl ); + + mRoot->removeChildNode( n ); +} + void TestQgsLayerTree::testCheckStateParentToChild() { mRoot->setVisible( Qt::Unchecked );