[gimp] libgimp: improve handling of procedure default values a lot



commit 45e96a0ff461f20202655e74749dee43c9d08011
Author: Michael Natterer <mitch gimp org>
Date:   Thu Sep 26 19:06:17 2019 +0200

    libgimp: improve handling of procedure default values a lot
    
    Add internal GimpProcedureConfig API to load/save "default values"
    which are to be treated as if they were the hardcoded GParamSpec
    defaults, but user-configurable. Also make all other load/save
    functions available to other libgimp files.
    
    In gimp_procedure_run(), if incomplete arguments are passed, don't
    just complete them with the GParamSpec defaults, but look up the
    user-saved defaults and use them if they exist. This happens before
    everything else and brings back the PNG export feature of using
    user-saved defaults also in non-interactive mode (but for all
    procedures not just PNG export).
    
    In GimpProcedureDialog, add "Load Defaults" and "Save Defaults"
    buttons, they are the only way of managing the user-configurable
    procedure defaults.
    
    When clicking "Reset", show a popover with the reset options "Initial
    Values" and "Factory Defaults".

 libgimp/Makefile.am                   |  37 ++++----
 libgimp/gimpprocedure.c               |  22 ++++-
 libgimp/gimpprocedureconfig-private.h |  43 +++++++++
 libgimp/gimpprocedureconfig.c         | 118 +++++++++++++++----------
 libgimp/gimpproceduredialog.c         | 160 +++++++++++++++++++++++++++++++---
 5 files changed, 303 insertions(+), 77 deletions(-)
---
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
index a14ad1fc74..b5745dc35e 100644
--- a/libgimp/Makefile.am
+++ b/libgimp/Makefile.am
@@ -105,25 +105,26 @@ libgimp_sources = \
        libgimp-intl.h
 
 libgimp_private_sources = \
-       gimp-debug.c            \
-       gimp-debug.h            \
-       gimp-private.h          \
-       gimp-shm.c              \
-       gimp-shm.h              \
-       gimpgpparams.c          \
-       gimpgpparams.h          \
-       gimppdb-private.h       \
-       gimppdbprocedure.c      \
-       gimppdbprocedure.h      \
-       gimppixbuf.c            \
-       gimppixbuf.h            \
-       gimpplugin-private.h    \
+       gimp-debug.c                    \
+       gimp-debug.h                    \
+       gimp-private.h                  \
+       gimp-shm.c                      \
+       gimp-shm.h                      \
+       gimpgpparams.c                  \
+       gimpgpparams.h                  \
+       gimppdb-private.h               \
+       gimppdbprocedure.c              \
+       gimppdbprocedure.h              \
+       gimppixbuf.c                    \
+       gimppixbuf.h                    \
+       gimpplugin-private.h            \
+       gimpprocedureconfig-private.h   \
        \
-       gimpunit_pdb.c          \
-       gimpunit_pdb.h          \
-       gimppdb_pdb.c           \
-       gimppdb_pdb.h           \
-       gimpplugin_pdb.c        \
+       gimpunit_pdb.c                  \
+       gimpunit_pdb.h                  \
+       gimppdb_pdb.c                   \
+       gimppdb_pdb.h                   \
+       gimpplugin_pdb.c                \
        gimpplugin_pdb.h
 
 libgimp_extra_sources = \
diff --git a/libgimp/gimpprocedure.c b/libgimp/gimpprocedure.c
index c3ae18c838..86829bbd99 100644
--- a/libgimp/gimpprocedure.c
+++ b/libgimp/gimpprocedure.c
@@ -34,6 +34,7 @@
 #include "gimppdb_pdb.h"
 #include "gimpplugin_pdb.h"
 #include "gimpprocedure-private.h"
+#include "gimpprocedureconfig-private.h"
 
 #include "libgimp-intl.h"
 
@@ -1580,7 +1581,18 @@ gimp_procedure_run (GimpProcedure  *procedure,
   /*  add missing args with default values  */
   if (gimp_value_array_length (args) < procedure->priv->n_args)
     {
-      GimpValueArray *complete = gimp_value_array_new (0);
+      GimpProcedureConfig *config;
+      GObjectClass        *config_class = NULL;
+      GimpValueArray      *complete;
+
+      /*  if saved defaults exist, they override GParamSpec  */
+      config = gimp_procedure_create_config (procedure);
+      if (_gimp_procedure_config_load_default (config, NULL))
+        config_class = G_OBJECT_GET_CLASS (config);
+      else
+        g_clear_object (&config);
+
+      complete = gimp_value_array_new (procedure->priv->n_args);
 
       for (i = 0; i < procedure->priv->n_args; i++)
         {
@@ -1595,6 +1607,12 @@ gimp_procedure_run (GimpProcedure  *procedure,
 
               g_value_copy (orig, &value);
             }
+          else if (config &&
+                   g_object_class_find_property (config_class, pspec->name))
+            {
+              g_object_get_property (G_OBJECT (config), pspec->name,
+                                     &value);
+            }
           else
             {
               g_param_value_set_default (pspec, &value);
@@ -1604,6 +1622,8 @@ gimp_procedure_run (GimpProcedure  *procedure,
           g_value_unset (&value);
         }
 
+      g_clear_object (&config);
+
       /*  call the procedure  */
       return_vals = GIMP_PROCEDURE_GET_CLASS (procedure)->run (procedure,
                                                                complete);
diff --git a/libgimp/gimpprocedureconfig-private.h b/libgimp/gimpprocedureconfig-private.h
new file mode 100644
index 0000000000..8db3d11425
--- /dev/null
+++ b/libgimp/gimpprocedureconfig-private.h
@@ -0,0 +1,43 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpprocedureconfig-private.h
+ * Copyright (C) 2019 Michael Natterer <mitch gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PROCEDURE_CONFIG_PRIVATE_H__
+#define __GIMP_PROCEDURE_CONFIG_PRIVATE_H__
+
+
+gboolean   _gimp_procedure_config_load_default  (GimpProcedureConfig  *config,
+                                                 GError              **error);
+gboolean   _gimp_procedure_config_save_default  (GimpProcedureConfig  *config,
+                                                 GError              **error);
+
+gboolean   _gimp_procedure_config_load_last     (GimpProcedureConfig  *config,
+                                                 GError              **error);
+gboolean   _gimp_procedure_config_save_last     (GimpProcedureConfig  *config,
+                                                 GError              **error);
+
+gboolean   _gimp_procedure_config_load_parasite (GimpProcedureConfig  *config,
+                                                 GimpImage            *image,
+                                                 GError              **error);
+gboolean   _gimp_procedure_config_save_parasite (GimpProcedureConfig  *config,
+                                                 GimpImage            *image);
+
+
+#endif /* __GIMP_PROCEDURE_CONFIG_PRIVATE_H__ */
diff --git a/libgimp/gimpprocedureconfig.c b/libgimp/gimpprocedureconfig.c
index 9ad0c0dcd4..7c8c902004 100644
--- a/libgimp/gimpprocedureconfig.c
+++ b/libgimp/gimpprocedureconfig.c
@@ -23,6 +23,8 @@
 
 #include "gimp.h"
 
+#include "gimpprocedureconfig-private.h"
+
 
 /**
  * SECTION: gimpprocedureconfig
@@ -62,34 +64,16 @@ struct _GimpProcedureConfigPrivate
 };
 
 
-static void   gimp_procedure_config_constructed   (GObject              *object);
-static void   gimp_procedure_config_dispose       (GObject              *object);
-static void   gimp_procedure_config_set_property  (GObject              *object,
-                                                   guint                 property_id,
-                                                   const GValue         *value,
-                                                   GParamSpec           *pspec);
-static void   gimp_procedure_config_get_property  (GObject              *object,
-                                                   guint                 property_id,
-                                                   GValue               *value,
-                                                   GParamSpec           *pspec);
-
-static GFile    * gimp_procedure_config_get_file  (GimpProcedureConfig *config,
-                                                   const gchar         *extension);
-static gboolean   gimp_procedure_config_load_last (GimpProcedureConfig  *config,
-                                                   GError              **error);
-static gboolean   gimp_procedure_config_save_last (GimpProcedureConfig  *config,
-                                                   GError              **error);
-
-static gchar    * gimp_procedure_config_parasite_name
-                                                  (GimpProcedureConfig *config,
-                                                   const gchar         *suffix);
-static gboolean   gimp_procedure_config_load_parasite
-                                                  (GimpProcedureConfig  *config,
-                                                   GimpImage            *image,
-                                                   GError              **error);
-static gboolean   gimp_procedure_config_save_parasite
-                                                  (GimpProcedureConfig  *config,
-                                                   GimpImage            *image);
+static void   gimp_procedure_config_constructed   (GObject      *object);
+static void   gimp_procedure_config_dispose       (GObject      *object);
+static void   gimp_procedure_config_set_property  (GObject      *object,
+                                                   guint         property_id,
+                                                   const GValue *value,
+                                                   GParamSpec   *pspec);
+static void   gimp_procedure_config_get_property  (GObject      *object,
+                                                   guint         property_id,
+                                                   GValue       *value,
+                                                   GParamSpec   *pspec);
 
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpProcedureConfig, gimp_procedure_config,
@@ -358,8 +342,8 @@ gimp_procedure_config_begin_run (GimpProcedureConfig  *config,
     case GIMP_RUN_WITH_LAST_VALS:
       if (image)
         {
-          loaded = gimp_procedure_config_load_parasite (config, image,
-                                                        &error);
+          loaded = _gimp_procedure_config_load_parasite (config, image,
+                                                         &error);
           if (! loaded && error)
             {
               g_printerr ("Loading last used values from parasite failed: %s\n",
@@ -369,7 +353,7 @@ gimp_procedure_config_begin_run (GimpProcedureConfig  *config,
         }
 
       if (! loaded &&
-          ! gimp_procedure_config_load_last (config, &error))
+          ! _gimp_procedure_config_load_last (config, &error) && error)
         {
           g_printerr ("Loading last used values from disk failed: %s\n",
                       error->message);
@@ -422,9 +406,9 @@ gimp_procedure_config_end_run (GimpProcedureConfig *config,
       GError *error = NULL;
 
       if (config->priv->image)
-        gimp_procedure_config_save_parasite (config, config->priv->image);
+        _gimp_procedure_config_save_parasite (config, config->priv->image);
 
-      if (! gimp_procedure_config_save_last (config, &error))
+      if (! _gimp_procedure_config_save_last (config, &error))
         {
           g_printerr ("Saving last used values to disk failed: %s\n",
                       error->message);
@@ -453,9 +437,48 @@ gimp_procedure_config_get_file (GimpProcedureConfig *config,
   return file;
 }
 
-static gboolean
-gimp_procedure_config_load_last (GimpProcedureConfig  *config,
-                                 GError              **error)
+gboolean
+_gimp_procedure_config_load_default (GimpProcedureConfig  *config,
+                                     GError              **error)
+{
+  GFile    *file = gimp_procedure_config_get_file (config, ".default");
+  gboolean  success;
+
+  success = gimp_config_deserialize_file (GIMP_CONFIG (config),
+                                          file,
+                                          NULL, error);
+
+  if (! success && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT)
+    {
+      g_clear_error (error);
+    }
+
+  g_object_unref (file);
+
+  return success;
+}
+
+gboolean
+_gimp_procedure_config_save_default (GimpProcedureConfig  *config,
+                                     GError              **error)
+{
+  GFile    *file = gimp_procedure_config_get_file (config, ".default");
+  gboolean  success;
+
+  success = gimp_config_serialize_to_file (GIMP_CONFIG (config),
+                                           file,
+                                           "settings",
+                                           "end of settings",
+                                           NULL, error);
+
+  g_object_unref (file);
+
+  return success;
+}
+
+gboolean
+_gimp_procedure_config_load_last (GimpProcedureConfig  *config,
+                                  GError              **error)
 {
   GFile    *file = gimp_procedure_config_get_file (config, ".last");
   gboolean  success;
@@ -467,17 +490,16 @@ gimp_procedure_config_load_last (GimpProcedureConfig  *config,
   if (! success && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT)
     {
       g_clear_error (error);
-      success = TRUE;
     }
 
   g_object_unref (file);
 
-  return TRUE;
+  return success;
 }
 
-static gboolean
-gimp_procedure_config_save_last (GimpProcedureConfig  *config,
-                                 GError              **error)
+gboolean
+_gimp_procedure_config_save_last (GimpProcedureConfig  *config,
+                                  GError              **error)
 {
   GFile    *file = gimp_procedure_config_get_file (config, ".last");
   gboolean  success;
@@ -500,10 +522,10 @@ gimp_procedure_config_parasite_name (GimpProcedureConfig *config,
   return g_strconcat (G_OBJECT_TYPE_NAME (config), suffix, NULL);
 }
 
-static gboolean
-gimp_procedure_config_load_parasite (GimpProcedureConfig  *config,
-                                     GimpImage            *image,
-                                     GError              **error)
+gboolean
+_gimp_procedure_config_load_parasite (GimpProcedureConfig  *config,
+                                      GimpImage            *image,
+                                      GError              **error)
 {
   gchar        *name;
   GimpParasite *parasite;
@@ -524,9 +546,9 @@ gimp_procedure_config_load_parasite (GimpProcedureConfig  *config,
   return success;
 }
 
-static gboolean
-gimp_procedure_config_save_parasite (GimpProcedureConfig *config,
-                                     GimpImage           *image)
+gboolean
+_gimp_procedure_config_save_parasite (GimpProcedureConfig *config,
+                                      GimpImage           *image)
 {
   gchar        *name;
   GimpParasite *parasite;
diff --git a/libgimp/gimpproceduredialog.c b/libgimp/gimpproceduredialog.c
index 696f38bc12..594969323e 100644
--- a/libgimp/gimpproceduredialog.c
+++ b/libgimp/gimpproceduredialog.c
@@ -28,6 +28,8 @@
 #include "gimp.h"
 #include "gimpui.h"
 
+#include "gimpprocedureconfig-private.h"
+
 #include "libgimp-intl.h"
 
 
@@ -46,18 +48,30 @@ struct _GimpProcedureDialogPrivate
 {
   GimpProcedure       *procedure;
   GimpProcedureConfig *config;
+  GimpProcedureConfig *initial_config;
+
+  GtkWidget           *reset_popover;
 };
 
 
-static void   gimp_procedure_dialog_dispose      (GObject      *object);
-static void   gimp_procedure_dialog_set_property (GObject      *object,
-                                                  guint         property_id,
-                                                  const GValue *value,
-                                                  GParamSpec   *pspec);
-static void   gimp_procedure_dialog_get_property (GObject      *object,
-                                                  guint         property_id,
-                                                  GValue       *value,
-                                                  GParamSpec   *pspec);
+static void   gimp_procedure_dialog_dispose       (GObject      *object);
+static void   gimp_procedure_dialog_set_property  (GObject      *object,
+                                                   guint         property_id,
+                                                   const GValue *value,
+                                                   GParamSpec   *pspec);
+static void   gimp_procedure_dialog_get_property  (GObject      *object,
+                                                   guint         property_id,
+                                                   GValue       *value,
+                                                   GParamSpec   *pspec);
+
+static void   gimp_procedure_dialog_reset_initial (GtkWidget           *button,
+                                                   GimpProcedureDialog *dialog);
+static void   gimp_procedure_dialog_reset_factory (GtkWidget           *button,
+                                                   GimpProcedureDialog *dialog);
+static void   gimp_procedure_dialog_load_defaults (GtkWidget           *button,
+                                                   GimpProcedureDialog *dialog);
+static void   gimp_procedure_dialog_save_defaults (GtkWidget           *button,
+                                                   GimpProcedureDialog *dialog);
 
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpProcedureDialog, gimp_procedure_dialog,
@@ -104,6 +118,9 @@ gimp_procedure_dialog_dispose (GObject *object)
 
   g_clear_object (&dialog->priv->procedure);
   g_clear_object (&dialog->priv->config);
+  g_clear_object (&dialog->priv->initial_config);
+
+  g_clear_pointer (&dialog->priv->reset_popover, gtk_widget_destroy);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -124,6 +141,10 @@ gimp_procedure_dialog_set_property (GObject      *object,
 
     case PROP_CONFIG:
       dialog->priv->config = g_value_dup_object (value);
+
+      if (dialog->priv->config)
+        dialog->priv->initial_config =
+          gimp_config_duplicate (GIMP_CONFIG (dialog->priv->config));
       break;
 
     default:
@@ -166,6 +187,8 @@ gimp_procedure_dialog_new (GimpProcedure       *procedure,
   const gchar *help_id;
   const gchar *ok_label;
   gboolean     use_header_bar;
+  GtkWidget   *hbox;
+  GtkWidget   *button;
 
   g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), NULL);
   g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), NULL);
@@ -214,6 +237,30 @@ gimp_procedure_dialog_new (GimpProcedure       *procedure,
 
   gimp_window_set_transient (GTK_WINDOW (dialog));
 
+  hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+  gtk_box_set_spacing (GTK_BOX (hbox), 6);
+  gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_START);
+  gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+                    hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  button = gtk_button_new_with_mnemonic (_("_Load Defaults"));
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  g_signal_connect (button, "clicked",
+                    G_CALLBACK (gimp_procedure_dialog_load_defaults),
+                    dialog);
+
+  button = gtk_button_new_with_mnemonic (_("_Save Defaults"));
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  g_signal_connect (button, "clicked",
+                    G_CALLBACK (gimp_procedure_dialog_save_defaults),
+                    dialog);
+
   return GTK_WIDGET (dialog);
 }
 
@@ -228,7 +275,42 @@ gimp_procedure_dialog_run (GimpProcedureDialog *dialog)
 
       if (response == RESPONSE_RESET)
         {
-          gimp_config_reset (GIMP_CONFIG (dialog->priv->config));
+          if (! dialog->priv->reset_popover)
+            {
+              GtkWidget *button;
+              GtkWidget *vbox;
+
+              button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog),
+                                                           response);
+
+              dialog->priv->reset_popover = gtk_popover_new (button);
+
+              vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+              gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+              gtk_container_add (GTK_CONTAINER (dialog->priv->reset_popover),
+                                 vbox);
+              gtk_widget_show (vbox);
+
+              button = gtk_button_new_with_mnemonic (_("Reset to _Initial "
+                                                       "Values"));
+              gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+              gtk_widget_show (button);
+
+              g_signal_connect (button, "clicked",
+                                G_CALLBACK (gimp_procedure_dialog_reset_initial),
+                                dialog);
+
+              button = gtk_button_new_with_mnemonic (_("Reset to _Factory "
+                                                       "Defaults"));
+              gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+              gtk_widget_show (button);
+
+              g_signal_connect (button, "clicked",
+                                G_CALLBACK (gimp_procedure_dialog_reset_factory),
+                                dialog);
+            }
+
+          gtk_popover_popup (GTK_POPOVER (dialog->priv->reset_popover));
         }
       else
         {
@@ -236,3 +318,61 @@ gimp_procedure_dialog_run (GimpProcedureDialog *dialog)
         }
     }
 }
+
+
+/*  private functions  */
+
+static void
+gimp_procedure_dialog_reset_initial (GtkWidget           *button,
+                                     GimpProcedureDialog *dialog)
+{
+  gimp_config_copy (GIMP_CONFIG (dialog->priv->initial_config),
+                    GIMP_CONFIG (dialog->priv->config),
+                    0);
+
+  gtk_popover_popdown (GTK_POPOVER (dialog->priv->reset_popover));
+}
+
+static void
+gimp_procedure_dialog_reset_factory (GtkWidget           *button,
+                                     GimpProcedureDialog *dialog)
+{
+  gimp_config_reset (GIMP_CONFIG (dialog->priv->config));
+
+  gtk_popover_popdown (GTK_POPOVER (dialog->priv->reset_popover));
+}
+
+static void
+gimp_procedure_dialog_load_defaults (GtkWidget           *button,
+                                     GimpProcedureDialog *dialog)
+{
+  GError *error = NULL;
+
+  if (! _gimp_procedure_config_load_default (dialog->priv->config, &error))
+    {
+      if (error)
+        {
+          g_printerr ("Loading default values from disk failed: %s\n",
+                      error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          g_printerr ("No default values found on disk\n");
+        }
+    }
+}
+
+static void
+gimp_procedure_dialog_save_defaults (GtkWidget           *button,
+                                     GimpProcedureDialog *dialog)
+{
+  GError *error = NULL;
+
+  if (! _gimp_procedure_config_save_default (dialog->priv->config, &error))
+    {
+      g_printerr ("Saving default values to disk failed: %s\n",
+                  error->message);
+      g_clear_error (&error);
+    }
+}


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