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



commit 25af765fe656c2757ac86fc9dab7b09166ec1a1b
Author: Jehan <jehan girinstud io>
Date:   Thu Mar 22 19:29:35 2018 +0100

    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  |   20 ++++++++++--------
 app/gui/gui.c |   45 ++++++++++++++++++++++++++++++++++++++++++
 app/gui/gui.h |    1 +
 5 files changed, 156 insertions(+), 11 deletions(-)
---
diff --git a/app/app.c b/app/app.c
index 2bc6f68..d7a7f76 100644
--- a/app/app.c
+++ b/app/app.c
@@ -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;
diff --git a/app/errors.c b/app/errors.c
index 930b627..6d63b2d 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 18c8e2c..76804cf 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   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__ */
diff --git a/app/gui/gui.c b/app/gui/gui.c
index 26afcbd..9d0a207 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 bed7c47..82a0295 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__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]