diff --git a/python/core/qgsproject.sip b/python/core/qgsproject.sip index 8e1f6a19e75..058868db967 100644 --- a/python/core/qgsproject.sip +++ b/python/core/qgsproject.sip @@ -630,6 +630,23 @@ Returns the number of registered layers. :rtype: bool %End + bool zip(); +%Docstring + :rtype: bool +%End + + QString zipFileName() const; +%Docstring + :rtype: str +%End + + void setZipFileName( const QString &filename ); + + bool unzipped() const; +%Docstring + :rtype: bool +%End + QList addMapLayers( const QList &mapLayers /Transfer/, bool addToLegend = true); diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 3cf30ae7911..5670cb60e5b 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -5235,10 +5235,12 @@ void QgisApp::fileOpen() // Retrieve last used project dir from persistent settings QgsSettings settings; QString lastUsedDir = settings.value( QStringLiteral( "UI/lastProjectDir" ), QDir::homePath() ).toString(); + const QString qgs_ext = tr( "QGIS files" ) + " (*.qgs *.QGS)"; + const QString zip_ext = tr( "QGZ files" ) + " (*.qgz)"; QString fullPath = QFileDialog::getOpenFileName( this, tr( "Choose a QGIS project file to open" ), lastUsedDir, - tr( "QGIS files" ) + " (*.qgs *.QGS)" ); + qgs_ext + ";;" + zip_ext ); if ( fullPath.isNull() ) { return; @@ -5271,6 +5273,8 @@ void QgisApp::enableProjectMacros() */ bool QgisApp::addProject( const QString &projectFile ) { + bool zip = ( QFileInfo( projectFile ).suffix() == "qgz" ) ? true : false; + // close the previous opened project if any closeProject(); @@ -5283,7 +5287,17 @@ bool QgisApp::addProject( const QString &projectFile ) bool autoSetupOnFirstLayer = mLayerTreeCanvasBridge->autoSetupOnFirstLayer(); mLayerTreeCanvasBridge->setAutoSetupOnFirstLayer( false ); - if ( !QgsProject::instance()->read( projectFile ) ) + bool readOk = false; + if ( zip ) + { + readOk = QgsProject::instance()->unzip( projectFile ); + } + else + { + readOk = QgsProject::instance()->read( projectFile ); + } + + if ( !readOk && !zip ) { QString backupFile = projectFile + "~"; QString loadBackupPrompt; @@ -5417,34 +5431,47 @@ bool QgisApp::fileSave() // that the project file name is reset to null in fileNew() QFileInfo fullPath; - if ( QgsProject::instance()->fileName().isNull() ) + if ( QgsProject::instance()->fileName().isNull() && QgsProject::instance()->zipFileName().isNull() ) { // Retrieve last used project dir from persistent settings QgsSettings settings; QString lastUsedDir = settings.value( QStringLiteral( "UI/lastProjectDir" ), QDir::homePath() ).toString(); + const QString qgs_ext = tr( "QGIS files" ) + " (*.qgs)"; + const QString zip_ext = tr( "QGZ files" ) + " (*.qgz)"; + QString filter; QString path = QFileDialog::getSaveFileName( this, tr( "Choose a QGIS project file" ), lastUsedDir + '/' + QgsProject::instance()->title(), - tr( "QGIS files" ) + " (*.qgs *.QGS)" ); + qgs_ext + ";;" + zip_ext, &filter ); if ( path.isEmpty() ) return false; fullPath.setFile( path ); // make sure we have the .qgs extension in the file name - if ( "qgs" != fullPath.suffix().toLower() ) + if ( filter == zip_ext ) { - fullPath.setFile( fullPath.filePath() + ".qgs" ); + if ( "qgz" != fullPath.suffix().toLower() ) + fullPath.setFile( fullPath.filePath() + ".qgz" ); + + QgsProject::instance()->setZipFileName( fullPath.filePath() ); } + else + { + if ( "qgs" != fullPath.suffix().toLower() ) + fullPath.setFile( fullPath.filePath() + ".qgs" ); - - QgsProject::instance()->setFileName( fullPath.filePath() ); + QgsProject::instance()->setFileName( fullPath.filePath() ); + } } else { QFileInfo fi( QgsProject::instance()->fileName() ); + if ( QgsProject::instance()->unzipped() ) + fi.setFile( QgsProject::instance()->zipFileName() ); + fullPath = fi.absoluteFilePath(); if ( fi.exists() && !mProjectLastModified.isNull() && mProjectLastModified != fi.lastModified() ) { @@ -5468,20 +5495,34 @@ bool QgisApp::fileSave() } } - if ( QgsProject::instance()->write() ) + bool writeOk = false; + QString writtenFileName; + + if ( QgsProject::instance()->unzipped() ) + { + writeOk = QgsProject::instance()->zip(); + writtenFileName = QgsProject::instance()->zipFileName(); + } + else + { + writeOk = QgsProject::instance()->write(); + writtenFileName = QgsProject::instance()->fileName(); + } + + if ( writeOk ) { setTitleBarText_( *this ); // update title bar - mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); + mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( writtenFileName ) ), 5000 ); saveRecentProjectPath( fullPath.filePath() ); - QFileInfo fi( QgsProject::instance()->fileName() ); + QFileInfo fi( writtenFileName ); mProjectLastModified = fi.lastModified(); } else { QMessageBox::critical( this, - tr( "Unable to save project %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), + tr( "Unable to save project %1" ).arg( QDir::toNativeSeparators( writtenFileName ) ), QgsProject::instance()->error() ); return false; } @@ -5501,10 +5542,13 @@ void QgisApp::fileSaveAs() QgsSettings settings; QString lastUsedDir = settings.value( QStringLiteral( "UI/lastProjectDir" ), QDir::homePath() ).toString(); + const QString qgs_ext = tr( "QGIS files" ) + " (*.qgs *.QGS)"; + const QString zip_ext = tr( "QGZ files" ) + " (*.qgz)"; + QString filter; QString path = QFileDialog::getSaveFileName( this, tr( "Choose a file name to save the QGIS project file as" ), lastUsedDir + '/' + QgsProject::instance()->title(), - tr( "QGIS files" ) + " (*.qgs *.QGS)" ); + qgs_ext + ";;" + zip_ext, &filter ); if ( path.isEmpty() ) return; @@ -5512,18 +5556,27 @@ void QgisApp::fileSaveAs() settings.setValue( QStringLiteral( "UI/lastProjectDir" ), fullPath.path() ); - // make sure the .qgs extension is included in the path name. if not, add it... - if ( "qgs" != fullPath.suffix().toLower() ) + bool writeOk = false; + if ( filter == zip_ext ) { - fullPath.setFile( fullPath.filePath() + ".qgs" ); + if ( "qgz" != fullPath.suffix().toLower() ) + fullPath.setFile( fullPath.filePath() + ".qgz" ); + + writeOk = QgsProject::instance()->zip( fullPath.filePath() ); + } + else // .qgs + { + if ( "qgs" != fullPath.suffix().toLower() ) + fullPath.setFile( fullPath.filePath() + ".qgs" ); + + QgsProject::instance()->setFileName( fullPath.filePath() ); + writeOk = QgsProject::instance()->write(); } - QgsProject::instance()->setFileName( fullPath.filePath() ); - - if ( QgsProject::instance()->write() ) + if ( writeOk ) { setTitleBarText_( *this ); // update title bar - mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); + mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( fullPath.filePath() ) ), 5000 ); // add this to the list of recently used project files saveRecentProjectPath( fullPath.filePath() ); mProjectLastModified = fullPath.lastModified(); diff --git a/src/core/qgsarchive.cpp b/src/core/qgsarchive.cpp index 1e9563157b0..6d6adcb73f5 100644 --- a/src/core/qgsarchive.cpp +++ b/src/core/qgsarchive.cpp @@ -95,6 +95,11 @@ QString QgsArchive::filename() const return mFilename; } +void QgsArchive::setFileName( const QString &filename ) +{ + mFilename = filename; +} + QString QgsArchive::projectFile() const { Q_FOREACH ( const QString &file, mFiles ) diff --git a/src/core/qgsarchive.h b/src/core/qgsarchive.h index 7dd55ca5640..8cd58e786ca 100644 --- a/src/core/qgsarchive.h +++ b/src/core/qgsarchive.h @@ -49,6 +49,8 @@ class CORE_EXPORT QgsArchive void addFile( const QString &filename ); + void setFileName( const QString &filename ); + QString filename() const; QString projectFile() const; diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 0a7e6a3e915..ca2d30fe0e9 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -425,6 +425,8 @@ void QgsProject::setFileName( const QString &name ) if ( newHomePath != oldHomePath ) emit homePathChanged(); + mArchive->clear(); + setDirty( true ); } @@ -763,6 +765,9 @@ bool QgsProject::read() { clearError(); + if ( ! mUnzipping ) + mArchive->clear(); + std::unique_ptr doc( new QDomDocument( QStringLiteral( "qgis" ) ) ); if ( !mFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) @@ -2085,27 +2090,38 @@ bool QgsProject::unzip( const QString &filename ) } // read the project file + mUnzipping = true; if ( ! read( archive->projectFile() ) ) { + mUnzipping = false; setError( tr( "Cannot read unzipped qgs project file" ) ); return false; } // keep the archive + mUnzipping = false; mArchive.reset( archive.release() ); return true; } +bool QgsProject::zip() +{ + if ( unzipped() ) + return zip( mArchive->filename() ); + + return false; +} + bool QgsProject::zip( const QString &filename ) { clearError(); // save the current project in a temporary .qgs file - QgsArchive archive; + std::unique_ptr archive( new QgsArchive() ); const QString baseName = QFileInfo( filename ).baseName(); const QString qgsFileName = QString( "%1.qgs" ).arg( baseName ); - QFile qgsFile( QDir( archive.dir() ).filePath( qgsFileName ) ); + QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) ); bool writeOk; if ( qgsFile.open( QIODevice::WriteOnly ) ) @@ -2127,19 +2143,38 @@ bool QgsProject::zip( const QString &filename ) } // create the archive - archive.addFile( qgsFile.fileName() ); + archive->addFile( qgsFile.fileName() ); // zip QString errMsg; - if ( !archive.zip( filename ) ) + if ( !archive->zip( filename ) ) { setError( tr( "Unable to perform zip" ) ); return false; } + // keep the archive + mArchive.reset( archive.release() ); + return true; } +bool QgsProject::unzipped() const +{ + return !mArchive->filename().isEmpty(); +} + +QString QgsProject::zipFileName() const +{ + return mArchive->filename(); +} + +void QgsProject::setZipFileName( const QString &filename ) +{ + mArchive.reset( new QgsArchive() ); + mArchive->setFileName( filename ); +} + QList QgsProject::addMapLayers( const QList &layers, bool addToLegend, diff --git a/src/core/qgsproject.h b/src/core/qgsproject.h index d32b52064d8..2046f4c7df4 100644 --- a/src/core/qgsproject.h +++ b/src/core/qgsproject.h @@ -592,6 +592,14 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera */ bool zip( const QString &filename ); + bool zip(); + + QString zipFileName() const; + + void setZipFileName( const QString &filename ); + + bool unzipped() const; + #ifndef SIP_RUN /** Returns a list of registered map layers with a specified layer type. @@ -1066,6 +1074,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera QVariantMap mCustomVariables; std::unique_ptr mArchive; + bool mUnzipping; QFile mFile; // current physical project file mutable QgsProjectPropertyKey mProperties; // property hierarchy, TODO: this shouldn't be mutable