app: output a dialog to recover images salvaged after a crash.

Since commit d916fedf92, GIMP has had the hidden feature to salvage
images (if possible) during a crash into a backup folder. This commit
finishes the feature by opening a dialog proposing to try and recover
the salvaged images.
This is not perfect yet since it doesn't "remember" the XCF path (in
case it was a previously saved image). The images open as new unsaved
and dirty images, but directly from the contents at crash time. For now,
it is up to people to figure out what they correspond to, if relevant.
This commit is contained in:
Jehan 2018-03-22 19:29:35 +01:00
parent 289ecebd69
commit 25af765fe6
5 changed files with 155 additions and 10 deletions

View File

@ -30,9 +30,12 @@
#include <unistd.h>
#endif
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#ifdef G_OS_WIN32
#include <windows.h>
#include <winnls.h>
@ -53,6 +56,7 @@
#include "core/gimp.h"
#include "core/gimp-batch.h"
#include "core/gimp-user-install.h"
#include "core/gimpimage.h"
#include "file/file-open.h"
@ -330,8 +334,60 @@ app_run (const gchar *full_prog_name,
G_CALLBACK (app_exit_after_callback),
&run_loop);
/* Load the images given on the command-line.
*/
#ifndef GIMP_CONSOLE_COMPILATION
if (run_loop && ! no_interface)
{
/* Before opening images from command line, check for salvaged images
* and query interactively to know if we should recover or discard
* them.
*/
GList *recovered_files;
GList *iter;
recovered_files = errors_recovered ();
if (recovered_files &&
gui_recover (g_list_length (recovered_files)))
{
for (iter = recovered_files; iter; iter = iter->next)
{
GFile *file;
GimpImage *image;
GError *error = NULL;
GimpPDBStatusType status;
file = g_file_new_for_path (iter->data);
image = file_open_with_display (gimp,
gimp_get_user_context (gimp),
NULL,
file, as_new,
initial_screen,
initial_monitor,
&status, &error);
if (image)
{
/* Break ties with the backup directory. */
gimp_image_set_file (image, NULL);
/* One of the rare exceptions where we should call
* gimp_image_dirty() directly instead of creating
* an undo. We want the image to be dirty from
* scratch, without anything to undo.
*/
gimp_image_dirty (image, GIMP_DIRTY_IMAGE);
}
g_object_unref (file);
}
}
/* Delete backup XCF images. */
for (iter = recovered_files; iter; iter = iter->next)
{
g_unlink (iter->data);
}
g_list_free_full (recovered_files, g_free);
}
#endif
/* Load the images given on the command-line. */
if (filenames)
{
gint i;

View File

@ -175,6 +175,47 @@ errors_exit (void)
g_free (backup_path);
}
GList *
errors_recovered (void)
{
GList *recovered = NULL;
gchar *backup_path = g_build_filename (gimp_directory (), "backups", NULL);
GDir *backup_dir = NULL;
if ((backup_dir = g_dir_open (backup_path, 0, NULL)))
{
const gchar *file;
while ((file = g_dir_read_name (backup_dir)))
{
if (g_str_has_suffix (file, ".xcf"))
{
gchar *path = g_build_filename (backup_path, file, NULL);
if (g_file_test (path, G_FILE_TEST_IS_REGULAR) &&
! g_file_test (path, G_FILE_TEST_IS_SYMLINK))
{
/* A quick basic security check. It is not foolproof,
* but better than nothing to make sure we are not
* trying to read, then delete a folder or a symlink
* to a file outside the backup directory.
*/
recovered = g_list_append (recovered, path);
}
else
{
g_free (path);
}
}
}
g_dir_close (backup_dir);
}
g_free (backup_path);
return recovered;
}
void
gimp_fatal_error (const gchar *message)
{

View File

@ -23,15 +23,17 @@
#endif
void errors_init (Gimp *gimp,
const gchar *full_prog_name,
gboolean use_debug_handler,
GimpStackTraceMode stack_trace_mode,
const gchar *backtrace_file);
void errors_exit (void);
void errors_init (Gimp *gimp,
const gchar *full_prog_name,
gboolean use_debug_handler,
GimpStackTraceMode stack_trace_mode,
const gchar *backtrace_file);
void errors_exit (void);
void gimp_fatal_error (const gchar *message) G_GNUC_NORETURN;
void gimp_terminate (const gchar *message) G_GNUC_NORETURN;
GList * errors_recovered (void);
void gimp_fatal_error (const gchar *message) G_GNUC_NORETURN;
void gimp_terminate (const gchar *message) G_GNUC_NORETURN;
#endif /* __ERRORS_H__ */

View File

@ -307,6 +307,51 @@ gui_init (Gimp *gimp,
return status_callback;
}
/*
* gui_recover:
* @n_recoveries: number of recovered files.
*
* Query the user interactively if files were saved from a previous
* crash, asking whether to try and recover or discard them.
*
* Returns: TRUE if answer is to try and recover, FALSE otherwise.
*/
gboolean
gui_recover (gint n_recoveries)
{
GtkWidget *dialog;
GtkWidget *box;
gboolean recover;
dialog = gimp_dialog_new (_("Images recovery"), "gimp-recovery",
NULL, GTK_DIALOG_MODAL, NULL, NULL,
_("_Discard"), GTK_RESPONSE_CANCEL,
_("_Recover"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
GTK_RESPONSE_OK);
box = gimp_message_box_new (GIMP_ICON_WILBER_EEK);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
box, TRUE, TRUE, 0);
gtk_widget_show (box);
gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (box),
_("Eeek! It looks like GIMP recovered from a crash!"));
gimp_message_box_set_text (GIMP_MESSAGE_BOX (box),
ngettext ("An image was salvaged from the crash. "
"Do you want to try and recover it?",
"%d images were salvaged from the crash. "
"Do you want to try and recover them?",
n_recoveries), n_recoveries);
recover = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
gtk_widget_destroy (dialog);
return recover;
}
gint
gui_get_initial_monitor (Gimp *gimp,
GdkScreen **screen)

View File

@ -25,5 +25,6 @@ void gui_abort (const gchar *abort_message);
GimpInitStatusFunc gui_init (Gimp *gimp,
gboolean no_splash);
gboolean gui_recover (gint n_recoveries);
#endif /* __GUI_H__ */