diff --git a/python/core/qgslayerdefinition.sip b/python/core/qgslayerdefinition.sip new file mode 100644 index 00000000000..dc35d0bab12 --- /dev/null +++ b/python/core/qgslayerdefinition.sip @@ -0,0 +1,11 @@ +class CORE_EXPORT QgsLayerDefinition +{ +%TypeHeaderCode +#include +%End +public: + static bool openLayerDefinition( const QString & path, QgsLayerTreeGroup* rootGroup, QString &errorMessage /Out/ ); + static bool openLayerDefinition( QDomDocument doc, QgsLayerTreeGroup* rootGroup, QString &errorMessage /Out/ ); + static bool exportLayerDefinition( QString path, QList selectedTreeNodes, QString &errorMessage /Out/ ); +}; + diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index fc5c2341632..1baaa2b55dd 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -138,6 +138,7 @@ #include "qgsgpsinformationwidget.h" #include "qgsguivectorlayertools.h" #include "qgslabelinggui.h" +#include "qgslayerdefinition.h" #include "qgslayertree.h" #include "qgslayertreemapcanvasbridge.h" #include "qgslayertreemodel.h" @@ -4124,8 +4125,13 @@ void QgisApp::dxfExport() void QgisApp::openLayerDefinition( const QString & path ) { - QList layers = QgsMapLayer::fromLayerDefinitionFile( path ); - QgsMapLayerRegistry::instance()->addMapLayers( layers ); + QString errorMessage; + bool loaded = QgsLayerDefinition::loadLayerDefinition( path, QgsProject::instance()->layerTreeRoot(), errorMessage ); + if ( !loaded ) + { + QgsDebugMsg( errorMessage ); + messageBar()->pushMessage( tr( "Error loading layer definition" ), errorMessage, QgsMessageBar::WARNING ); + } } // Open the project file corresponding to the @@ -5038,26 +5044,17 @@ void QgisApp::saveAsFile() void QgisApp::saveAsLayerDefinition() { - QList layers = mLayerTreeView->selectedLayers(); - - if ( layers.isEmpty() ) - return; QString path = QFileDialog::getSaveFileName( this, "Save as Layer Definition File", QDir::home().path(), "*.qlr" ); QgsDebugMsg( path ); if ( path.isEmpty() ) return; - if ( !path.endsWith( ".qlr" ) ) - path = path.append( ".qlr" ); - - QFile file( path ); - QFileInfo fileinfo( file ); - QDomDocument doc = QgsMapLayer::asLayerDefinition( layers, fileinfo.canonicalFilePath() ); - if ( file.open( QFile::WriteOnly | QFile::Truncate ) ) + QString errorMessage; + bool saved = QgsLayerDefinition::exportLayerDefinition( path, mLayerTreeView->selectedNodes(), errorMessage ); + if ( !saved ) { - QTextStream qlayerstream( &file ); - doc.save( qlayerstream, 2 ); + messageBar()->pushMessage( tr( "Error saving layer definintion file" ), errorMessage, QgsMessageBar::WARNING ); } } diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 69920949a30..ddbecf1962f 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -56,6 +56,8 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu() if ( mView->selectedNodes( true ).count() >= 2 ) menu->addAction( actions->actionGroupSelected( menu ) ); + menu->addAction( tr( "Save As Layer Definition File..." ), QgisApp::instance(), SLOT( saveAsLayerDefinition() ) ); + menu->addAction( actions->actionAddGroup( menu ) ); } else if ( QgsLayerTree::isLayer( node ) ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3798ab75e48..378d40a941f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -95,6 +95,7 @@ SET(QGIS_CORE_SRCS qgsgeometryvalidator.cpp qgsgml.cpp qgsgmlschema.cpp + qgslayerdefinition.cpp qgslabel.cpp qgslabelattributes.cpp qgslabelsearchtree.cpp @@ -480,6 +481,7 @@ SET(QGIS_CORE_HDRS qgsfontutils.h qgsgeometry.h qgsgeometrycache.h + qgslayerdefinition.h qgslabel.h qgslabelattributes.h qgslabelsearchtree.h diff --git a/src/core/qgslayerdefinition.cpp b/src/core/qgslayerdefinition.cpp new file mode 100644 index 00000000000..d30bec34a1d --- /dev/null +++ b/src/core/qgslayerdefinition.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +#include "qgslogger.h" +#include "qgsmaplayer.h" +#include "qgslayertree.h" +#include "qgsmaplayerregistry.h" +#include "qgslayerdefinition.h" + +bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsLayerTreeGroup *rootGroup, QString &errorMessage ) +{ + QFile file( path ); + if ( !file.open( QIODevice::ReadOnly ) ) + { + errorMessage = QString( "Can not open file" ); + return false; + } + + QDomDocument doc; + QString message; + if ( !doc.setContent( &file, &message ) ) + { + errorMessage = message; + return false; + } + + QFileInfo fileinfo( file ); + QDir::setCurrent( fileinfo.absoluteDir().path() ); + + return loadLayerDefinition( doc, rootGroup, errorMessage ); +} + +bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsLayerTreeGroup *rootGroup, QString &errorMessage ) +{ + QgsLayerTreeGroup* root = new QgsLayerTreeGroup; + // We have to replace the IDs before we load them because it's too late once they are loaded + QDomNodeList ids = doc.elementsByTagName( "id" ); + for ( int i = 0; i < ids.size(); ++i ) + { + QDomNode idnode = ids.at( i ); + QDomElement idElem = idnode.toElement(); + QString oldid = idElem.text(); + // Strip the date part because we will replace it. + QString layername = oldid.left( oldid.length() - 17 ); + QDateTime dt = QDateTime::currentDateTime(); + QString newid = layername + dt.toString( "yyyyMMddhhmmsszzz" ); + idElem.firstChild().setNodeValue( newid ); + QDomNodeList treeLayerNodes = doc.elementsByTagName( "layer-tree-layer" ); + + for ( int i = 0; i < treeLayerNodes.count(); ++i ) + { + QDomNode layerNode = treeLayerNodes.at( i ); + QDomElement layerElem = layerNode.toElement(); + if ( layerElem.attribute( "id" ) == oldid ) + { + layerNode.toElement().setAttribute( "id", newid ); + } + } + } + + QDomElement layerTreeElem = doc.documentElement().firstChildElement( "layer-tree-group" ); + bool loadInLegend = true; + if ( !layerTreeElem.isNull() ) + { + root->readChildrenFromXML( layerTreeElem ); + loadInLegend = false; + } + + QList layers = QgsMapLayer::fromLayerDefinition( doc ); + QgsMapLayerRegistry::instance()->addMapLayers( layers, loadInLegend ); + + QList nodes = root->children(); + rootGroup->insertChildNodes( -1, nodes ); + return true; + +} + +bool QgsLayerDefinition::exportLayerDefinition( QString path, QList selectedTreeNodes, QString &errorMessage ) +{ + if ( !path.endsWith( ".qlr" ) ) + path = path.append( ".qlr" ); + + QFile file( path ); + QFileInfo fileinfo( file ); + + QDomDocument doc( "qgis-layer-definition" ); + QDomElement qgiselm = doc.createElement( "qlr" ); + doc.appendChild( qgiselm ); + QList nodes = selectedTreeNodes; + QgsLayerTreeGroup* root = new QgsLayerTreeGroup; + foreach ( QgsLayerTreeNode* node, nodes ) + { + QgsLayerTreeNode* newnode = node->clone(); + root->addChildNode( newnode ); + } + root->writeXML( qgiselm ); + + QDomElement layerselm = doc.createElement( "maplayers" ); + QList layers = root->findLayers(); + foreach ( QgsLayerTreeLayer* layer, layers ) + { + QDomElement layerelm = doc.createElement( "maplayer" ); + layer->layer()->writeLayerXML( layerelm, doc, fileinfo.canonicalFilePath() ); + layerselm.appendChild( layerelm ); + } + qgiselm.appendChild( layerselm ); + + if ( file.open( QFile::WriteOnly | QFile::Truncate ) ) + { + QTextStream qlayerstream( &file ); + doc.save( qlayerstream, 2 ); + return true; + } + else + { + errorMessage = file.errorString(); + return false; + } +} diff --git a/src/core/qgslayerdefinition.h b/src/core/qgslayerdefinition.h new file mode 100644 index 00000000000..ce458ece40c --- /dev/null +++ b/src/core/qgslayerdefinition.h @@ -0,0 +1,20 @@ +#ifndef QGSLAYERDEFINITION_H +#define QGSLAYERDEFINITION_H + +#include "qgslayertreegroup.h" + +/** + * @brief The QgsLayerDefinition class holds generic methods for loading/exporting QLR files. + */ +class CORE_EXPORT QgsLayerDefinition +{ +public: + /* Loads the QLR at path into QGIS. New layers are added to rootGroup and the map layer registry*/ + static bool loadLayerDefinition( const QString & path, QgsLayerTreeGroup* rootGroup, QString &errorMessage); + /* Loads the QLR from the XML document. New layers are added to rootGroup and the map layer registry */ + static bool loadLayerDefinition( QDomDocument doc, QgsLayerTreeGroup* rootGroup, QString &errorMessage); + /* Export the selected layer tree nodes to a QLR file */ + static bool exportLayerDefinition( QString path, QList selectedTreeNodes, QString &errorMessage ); +}; + +#endif // QGSLAYERDEFINITION_H diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index 8243275284b..d1c8506fae1 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -617,15 +617,16 @@ bool QgsMapLayer::writeLayerXML( QDomElement& layerElement, QDomDocument& docume QDomDocument QgsMapLayer::asLayerDefinition( QList layers, QString relativeBasePath ) { QDomDocument doc( "qgis-layer-definition" ); + QDomElement qgiselm = doc.createElement( "qlr" ); + doc.appendChild( qgiselm ); QDomElement layerselm = doc.createElement( "maplayers" ); foreach ( QgsMapLayer* layer, layers ) { QDomElement layerelm = doc.createElement( "maplayer" ); layer->writeLayerXML( layerelm, doc, relativeBasePath ); - layerelm.removeChild( layerelm.firstChildElement( "id" ) ); layerselm.appendChild( layerelm ); } - doc.appendChild( layerselm ); + qgiselm.appendChild( layerselm ); return doc; }