From b49da36c7fee9b25606ad60da42a4ce05f0e0578 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Tue, 13 Jan 2015 14:51:16 +0100 Subject: [PATCH] * Save and restore visiblity state of embedded group layers (fixes #4097) * Adds support for lists in custom properties --- python/core/layertree/qgslayertreeutils.sip | 13 +++++--- python/core/qgsproject.sip | 6 ++-- src/app/qgisapp.cpp | 4 +-- src/core/layertree/qgslayertreeutils.cpp | 31 +++++++++++++++++-- src/core/layertree/qgslayertreeutils.h | 7 +++-- src/core/qgsobjectcustomproperties.cpp | 34 +++++++++++++++++++-- src/core/qgsproject.cpp | 12 ++++++-- src/core/qgsproject.h | 2 +- 8 files changed, 87 insertions(+), 22 deletions(-) diff --git a/python/core/layertree/qgslayertreeutils.sip b/python/core/layertree/qgslayertreeutils.sip index 1eeaa2c06bb..31304501e3e 100644 --- a/python/core/layertree/qgslayertreeutils.sip +++ b/python/core/layertree/qgslayertreeutils.sip @@ -11,11 +11,11 @@ class QgsLayerTreeUtils public: - //! Try to load layer tree from tag from project files from QGIS 2.2 and below + //! Try to load layer tree from \verbatim \endverbatim tag from project files from QGIS 2.2 and below static bool readOldLegend( QgsLayerTreeGroup* root, const QDomElement& legendElem ); - //! Try to load custom layer order from tag from project files from QGIS 2.2 and below + //! Try to load custom layer order from \verbatim \endverbatim tag from project files from QGIS 2.2 and below static bool readOldLegendLayerOrder( const QDomElement& legendElem, bool& hasCustomOrder, QStringList& order ); - //! Return tag used in QGIS 2.2 and below + //! Return \verbatim \endverbatim tag used in QGIS 2.2 and below static QDomElement writeOldLegend( QDomDocument& doc, QgsLayerTreeGroup* root, bool hasCustomOrder, const QStringList& order ); //! Convert Qt::CheckState to QString @@ -31,7 +31,10 @@ class QgsLayerTreeUtils //! Remove layer nodes that refer to invalid layers static void removeInvalidLayers( QgsLayerTreeGroup* group ); - //! Remove subtree of embedded groups. Useful when saving layer tree - static void removeChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ); + //! Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers + static void replaceChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ); + + //! get invisible layers + static QStringList invisibleLayerList( QgsLayerTreeNode *node ); }; diff --git a/python/core/qgsproject.sip b/python/core/qgsproject.sip index 156b5e2e7c5..5f17bb7757b 100644 --- a/python/core/qgsproject.sip +++ b/python/core/qgsproject.sip @@ -252,10 +252,11 @@ class QgsProject : QObject bool createEmbeddedLayer( const QString& layerId, const QString& projectFilePath, QList& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList, bool saveFlag = true ); */ + /** Create layer group instance defined in an arbitrary project file. * @note: added in version 2.4 */ - QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath ); + QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath, const QStringList &invisibleLayers ); /** Convenience function to set snap settings per layer */ void setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, @@ -305,9 +306,6 @@ class QgsProject : QObject //! @note not available in python bindings // void loadEmbeddedNodes( QgsLayerTreeGroup* group ); - //! @note not available in python bindings - // void updateEmbeddedGroupsProjectPath( QgsLayerTreeGroup* group ); - signals: //! emitted when project is being read void readProject( const QDomDocument & ); diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 5792cc64efe..46f759dacf4 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -7958,7 +7958,7 @@ void QgisApp::embedLayers() QStringList::const_iterator groupIt = groups.constBegin(); for ( ; groupIt != groups.constEnd(); ++groupIt ) { - QgsLayerTreeGroup* newGroup = QgsProject::instance()->createEmbeddedGroup( *groupIt, projectFile ); + QgsLayerTreeGroup* newGroup = QgsProject::instance()->createEmbeddedGroup( *groupIt, projectFile, QStringList() ); if ( newGroup ) QgsProject::instance()->layerTreeRoot()->addChildNode( newGroup ); @@ -9935,7 +9935,7 @@ void QgisApp::writeProject( QDomDocument &doc ) // can be opened in older versions of QGIS without loosing information about layer groups. QgsLayerTreeNode* clonedRoot = QgsProject::instance()->layerTreeRoot()->clone(); - QgsLayerTreeUtils::removeChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( clonedRoot ) ); + QgsLayerTreeUtils::replaceChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( clonedRoot ) ); QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required QDomElement oldLegendElem = QgsLayerTreeUtils::writeOldLegend( doc, QgsLayerTree::toGroup( clonedRoot ), mLayerTreeCanvasBridge->hasCustomLayerOrder(), mLayerTreeCanvasBridge->customLayerOrder() ); diff --git a/src/core/layertree/qgslayertreeutils.cpp b/src/core/layertree/qgslayertreeutils.cpp index eccae5d527e..cabd70bda12 100644 --- a/src/core/layertree/qgslayertreeutils.cpp +++ b/src/core/layertree/qgslayertreeutils.cpp @@ -304,16 +304,43 @@ void QgsLayerTreeUtils::removeInvalidLayers( QgsLayerTreeGroup* group ) group->removeChildNode( node ); } -void QgsLayerTreeUtils::removeChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ) +QStringList QgsLayerTreeUtils::invisibleLayerList( QgsLayerTreeNode *node ) +{ + QStringList list; + + if ( QgsLayerTree::isGroup( node ) ) + { + foreach ( QgsLayerTreeNode *child, QgsLayerTree::toGroup( node )->children() ) + { + list << invisibleLayerList( child ); + } + } + else if ( QgsLayerTree::isLayer( node ) ) + { + QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node ); + + if ( !layer->isVisible() ) + list << layer->layerId(); + } + + return list; +} + +void QgsLayerTreeUtils::replaceChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ) { foreach ( QgsLayerTreeNode* child, group->children() ) { if ( QgsLayerTree::isGroup( child ) ) { if ( child->customProperty( "embedded" ).toInt() ) + { + child->setCustomProperty( "embedded-invisible-layers", invisibleLayerList( child ) ); QgsLayerTree::toGroup( child )->removeAllChildren(); + } else - removeChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( child ) ); + { + replaceChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( child ) ); + } } } } diff --git a/src/core/layertree/qgslayertreeutils.h b/src/core/layertree/qgslayertreeutils.h index 31f750cfbcf..a8357669ba5 100644 --- a/src/core/layertree/qgslayertreeutils.h +++ b/src/core/layertree/qgslayertreeutils.h @@ -23,6 +23,7 @@ class QDomElement; class QDomDocument; class QStringList; +class QgsLayerTreeNode; class QgsLayerTreeGroup; class QgsLayerTreeLayer; @@ -55,12 +56,14 @@ class CORE_EXPORT QgsLayerTreeUtils //! Remove layer nodes that refer to invalid layers static void removeInvalidLayers( QgsLayerTreeGroup* group ); - //! Remove subtree of embedded groups. Useful when saving layer tree - static void removeChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ); + //! Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers + static void replaceChildrenOfEmbeddedGroups( QgsLayerTreeGroup* group ); //! @note not available in python bindings static void updateEmbeddedGroupsProjectPath( QgsLayerTreeGroup* group ); + //! get invisible layers + static QStringList invisibleLayerList( QgsLayerTreeNode *node ); }; #endif // QGSLAYERTREEUTILS_H diff --git a/src/core/qgsobjectcustomproperties.cpp b/src/core/qgsobjectcustomproperties.cpp index 3ef4a8c936d..6d835199e4f 100644 --- a/src/core/qgsobjectcustomproperties.cpp +++ b/src/core/qgsobjectcustomproperties.cpp @@ -88,8 +88,24 @@ void QgsObjectCustomProperties::readXml( const QDomNode& parentNode, const QStri QString key = propElement.attribute( "key" ); if ( key.isEmpty() || key.startsWith( keyStartsWith ) ) { - QString value = propElement.attribute( "value" ); - mMap[key] = QVariant( value ); + if ( propElement.hasAttribute( "value" ) ) + { + QString value = propElement.attribute( "value" ); + mMap[key] = QVariant( value ); + } + else + { + QStringList list; + + for ( QDomElement itemElement = propElement.firstChildElement( "value" ); + !itemElement.isNull(); + itemElement = itemElement.nextSiblingElement( "value" ) ) + { + list << itemElement.text(); + } + + mMap[key] = QVariant( list ); + } } } @@ -110,7 +126,19 @@ void QgsObjectCustomProperties::writeXml( QDomNode& parentNode, QDomDocument& do { QDomElement propElement = doc.createElement( "property" ); propElement.setAttribute( "key", it.key() ); - propElement.setAttribute( "value", it.value().toString() ); + if ( it.value().canConvert() ) + { + propElement.setAttribute( "value", it.value().toString() ); + } + else if ( it.value().canConvert() ) + { + foreach ( QString value, it.value().toStringList() ) + { + QDomElement itemElement = doc.createElement( "value" ); + itemElement.appendChild( doc.createTextNode( value ) ); + propElement.appendChild( itemElement ); + } + } propsElement.appendChild( propElement ); } diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index d251a665c20..c42d19fb9ff 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -974,7 +974,7 @@ void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup* group ) QString projectPath = readPath( childGroup->customProperty( "embedded_project" ).toString() ); childGroup->setCustomProperty( "embedded_project", projectPath ); - QgsLayerTreeGroup* newGroup = createEmbeddedGroup( childGroup->name(), projectPath ); + QgsLayerTreeGroup* newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( "embedded-invisible-layers" ).toStringList() ); if ( newGroup ) { QList clonedChildren; @@ -1083,7 +1083,7 @@ bool QgsProject::write() // write layer tree - make sure it is without embedded subgroups QgsLayerTreeNode* clonedRoot = mRootGroup->clone(); - QgsLayerTreeUtils::removeChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( clonedRoot ) ); + QgsLayerTreeUtils::replaceChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( clonedRoot ) ); QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required clonedRoot->writeXML( qgisNode ); delete clonedRoot; @@ -1794,7 +1794,7 @@ bool QgsProject::createEmbeddedLayer( const QString& layerId, const QString& pro } -QgsLayerTreeGroup* QgsProject::createEmbeddedGroup( const QString& groupName, const QString& projectFilePath ) +QgsLayerTreeGroup* QgsProject::createEmbeddedGroup( const QString& groupName, const QString& projectFilePath, const QStringList &invisibleLayers ) { //open project file, get layer ids in group, add the layers QFile projectFile( projectFilePath ); @@ -1863,6 +1863,12 @@ QgsLayerTreeGroup* QgsProject::createEmbeddedGroup( const QString& groupName, co thisProjectIdentifyDisabledLayers.append( layerId ); QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", thisProjectIdentifyDisabledLayers ); } + + QgsLayerTreeLayer *layer = newGroup->findLayer( layerId ); + if ( layer ) + { + layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked ); + } } return newGroup; diff --git a/src/core/qgsproject.h b/src/core/qgsproject.h index 086194f5b7e..44b77ba8456 100644 --- a/src/core/qgsproject.h +++ b/src/core/qgsproject.h @@ -299,7 +299,7 @@ class CORE_EXPORT QgsProject : public QObject /** Create layer group instance defined in an arbitrary project file. * @note: added in version 2.4 */ - QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath ); + QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath, const QStringList &invisibleLayers ); /** Convenience function to set snap settings per layer */ void setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance,