Skip to content
Permalink
Browse files

Warn users on closing projects with populated memory layers present

Adds a warning (with a cancel option!) when users start to close
a project containing any memory layers with features. These
layers are temporary only and their contents will be permanently
lost if the project is closed. The warning allows users to
cancel the close operation (that's the default action!) so that
they can then save these layers out to a permanent location.

(cherry-picked from 0dc1a61)
  • Loading branch information
nyalldawson committed Aug 3, 2018
1 parent 7d67897 commit 8ce6d28f5f0fb00b4e75a6a93b546b7c0111efff
Showing with 80 additions and 9 deletions.
  1. +68 −9 src/app/qgisapp.cpp
  2. +12 −0 src/app/qgisapp.h
@@ -5142,7 +5142,7 @@ void QgisApp::fileExit()
return;
}

if ( checkUnsavedLayerEdits() && saveDirty() )
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
{
closeProject();
userProfileManager()->setDefaultFromActive();
@@ -5177,7 +5177,7 @@ bool QgisApp::fileNew( bool promptToSaveFlag, bool forceBlank )

if ( promptToSaveFlag )
{
if ( !checkUnsavedLayerEdits() || !saveDirty() )
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
{
return false; //cancel pressed
}
@@ -5271,7 +5271,7 @@ bool QgisApp::fileNewFromTemplate( const QString &fileName )
if ( checkTasksDependOnProject() )
return false;

if ( !checkUnsavedLayerEdits() || !saveDirty() )
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
{
return false; //cancel pressed
}
@@ -5570,7 +5570,7 @@ void QgisApp::fileOpen()
return;

// possibly save any pending work before opening a new project
if ( checkUnsavedLayerEdits() && saveDirty() )
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
{
// Retrieve last used project dir from persistent settings
QgsSettings settings;
@@ -5604,7 +5604,7 @@ void QgisApp::fileRevert()
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
return;

if ( !checkUnsavedLayerEdits() )
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() )
return;

// re-open the current project
@@ -6034,7 +6034,7 @@ void QgisApp::openProject( QAction *action )
return;

QString debugme = action->data().toString();
if ( checkUnsavedLayerEdits() && saveDirty() )
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
addProject( debugme );
}

@@ -6060,7 +6060,7 @@ void QgisApp::openProject( const QString &fileName )
return;

// possibly save any pending work before opening a different project
if ( checkUnsavedLayerEdits() && saveDirty() )
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
{
// error handling and reporting is in addProject() function
addProject( fileName );
@@ -10777,7 +10777,9 @@ bool QgisApp::saveDirty()
for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
if ( !vl )
// note that we skip the unsaved edits check for memory layers -- it's misleading, because their contents aren't actually
// saved if this is part of a project close operation. Instead we let these get picked up by checkMemoryLayers().
if ( !vl || vl->dataProvider()->name() == QLatin1String( "memory" ) )
{
continue;
}
@@ -10831,7 +10833,27 @@ bool QgisApp::saveDirty()

freezeCanvases( false );

return answer != QMessageBox::Cancel;
if ( answer == QMessageBox::Cancel )
return false;

// for memory layers, we discard all unsaved changes manually. Users have already been warned about
// these by an earlier call to checkMemoryLayers(), and we don't want duplicate "unsaved changes" prompts
// and anyway, saving the changes to a memory layer here won't actually save ANYTHING!
// we do this at the very end here, because if the user opted to cancel above then ALL unsaved
// changes in memory layers should still exist for them.
const QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
for ( auto it = layers.begin(); it != layers.end(); ++it )
{
if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() ) )
{
if ( vl->dataProvider()->name() == QLatin1String( "memory" ) && vl->isEditable() && vl->isModified() )
{
vl->rollBack();
}
}
}

return true;
}

bool QgisApp::checkUnsavedLayerEdits()
@@ -10845,6 +10867,11 @@ bool QgisApp::checkUnsavedLayerEdits()
{
if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() ) )
{
// note that we skip the unsaved edits check for memory layers -- it's misleading, because their contents aren't actually
// saved if this is part of a project close operation. Instead we let these get picked up by checkMemoryLayers()
if ( vl->dataProvider()->name() == QLatin1String( "memory" ) )
continue;

const bool hasUnsavedEdits = ( vl->isEditable() && vl->isModified() );
if ( !hasUnsavedEdits )
continue;
@@ -10858,6 +10885,38 @@ bool QgisApp::checkUnsavedLayerEdits()
return true;
}

bool QgisApp::checkMemoryLayers()
{
// check to see if there are any memory layers present (with features)
bool hasMemoryLayers = false;
const QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
for ( auto it = layers.begin(); it != layers.end(); ++it )
{
if ( it.value() && it.value()->dataProvider()->name() == QLatin1String( "memory" ) )
{
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() );
if ( vl && vl->featureCount() != 0 )
{
hasMemoryLayers = true;
break;
}
}
}

if ( hasMemoryLayers )
{
if ( QMessageBox::warning( this,
tr( "Close Project" ),
tr( "This project includes one or more temporary scratch layers. These layers are not saved to disk and their contents will be permanently lost. Are you sure you want to proceed?" ),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) == QMessageBox::Yes )
return true;
else
return false;
}
else
return true;
}

bool QgisApp::checkTasksDependOnProject()
{
QSet< QString > activeTaskDescriptions;
@@ -1790,6 +1790,18 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
*/
bool checkUnsavedLayerEdits();

/**
* Checks whether memory layers (with features) exist in the project, and if so
* shows a warning to users that their contents will be lost on
* project unload.
*
* Returns true if there are no memory layers present, or if the
* user opted to discard their contents. Returns false if there
* are memory layers present and the user clicked 'Cancel' on
* the warning dialog.
*/
bool checkMemoryLayers();

//! Checks for running tasks dependent on the open project
bool checkTasksDependOnProject();

0 comments on commit 8ce6d28

Please sign in to comment.
You can’t perform that action at this time.