From 25af765fe656c2757ac86fc9dab7b09166ec1a1b Mon Sep 17 00:00:00 2001 From: Jehan Date: Thu, 22 Mar 2018 19:29:35 +0100 Subject: [PATCH] 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. --- app/app.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-- app/errors.c | 41 +++++++++++++++++++++++++++++++++++ app/errors.h | 18 +++++++++------- app/gui/gui.c | 45 ++++++++++++++++++++++++++++++++++++++ app/gui/gui.h | 1 + 5 files changed, 155 insertions(+), 10 deletions(-) diff --git a/app/app.c b/app/app.c index 2bc6f68565..d7a7f762fa 100644 --- a/app/app.c +++ b/app/app.c @@ -30,9 +30,12 @@ #include #endif +#include #include #include +#include + #ifdef G_OS_WIN32 #include #include @@ -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; diff --git a/app/errors.c b/app/errors.c index 930b627141..6d63b2dd2b 100644 --- a/app/errors.c +++ b/app/errors.c @@ -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) { diff --git a/app/errors.h b/app/errors.h index 18c8e2cc40..76804cf538 100644 --- a/app/errors.h +++ b/app/errors.h @@ -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__ */ diff --git a/app/gui/gui.c b/app/gui/gui.c index 26afcbd559..9d0a2075f6 100644 --- a/app/gui/gui.c +++ b/app/gui/gui.c @@ -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) diff --git a/app/gui/gui.h b/app/gui/gui.h index bed7c47324..82a0295679 100644 --- a/app/gui/gui.h +++ b/app/gui/gui.h @@ -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__ */