Skip to content

Commit

Permalink
Warn users on closing projects with populated memory layers present
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nyalldawson committed Aug 3, 2018
1 parent 02bd1cc commit 0dc1a61
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 9 deletions.
77 changes: 68 additions & 9 deletions src/app/qgisapp.cpp
Expand Up @@ -5218,7 +5218,7 @@ void QgisApp::fileExit()
return;
}

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

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

if ( !checkUnsavedLayerEdits() || !saveDirty() )
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
{
return false; //cancel pressed
}
Expand Down Expand Up @@ -5646,7 +5646,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;
Expand Down Expand Up @@ -5680,7 +5680,7 @@ void QgisApp::fileRevert()
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
return;

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

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

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

Expand All @@ -6136,7 +6136,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 );
Expand Down Expand Up @@ -10863,7 +10863,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;
}
Expand Down Expand Up @@ -10917,7 +10919,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()
Expand All @@ -10931,6 +10953,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;
Expand All @@ -10944,6 +10971,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;
Expand Down
12 changes: 12 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -1798,6 +1798,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();

Expand Down

0 comments on commit 0dc1a61

Please sign in to comment.