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__ */