Commit 25af765f authored by Jehan's avatar Jehan

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

Since commit d916fedf, 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.
parent 289ecebd
......@@ -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;
......
......@@ -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)
{
......
......@@ -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 gimp_fatal_error (const gchar *message) G_GNUC_NORETURN;
void gimp_terminate (const gchar *message) G_GNUC_NORETURN;
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);
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__ */
......@@ -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)
......
......@@ -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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment