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".
This commit is contained in:
Michael Natterer 2019-09-26 19:06:17 +02:00
parent e4acb969ba
commit 45e96a0ff4
5 changed files with 303 additions and 77 deletions

View file

@ -105,25 +105,26 @@ libgimp_sources = \
libgimp-intl.h libgimp-intl.h
libgimp_private_sources = \ libgimp_private_sources = \
gimp-debug.c \ gimp-debug.c \
gimp-debug.h \ gimp-debug.h \
gimp-private.h \ gimp-private.h \
gimp-shm.c \ gimp-shm.c \
gimp-shm.h \ gimp-shm.h \
gimpgpparams.c \ gimpgpparams.c \
gimpgpparams.h \ gimpgpparams.h \
gimppdb-private.h \ gimppdb-private.h \
gimppdbprocedure.c \ gimppdbprocedure.c \
gimppdbprocedure.h \ gimppdbprocedure.h \
gimppixbuf.c \ gimppixbuf.c \
gimppixbuf.h \ gimppixbuf.h \
gimpplugin-private.h \ gimpplugin-private.h \
gimpprocedureconfig-private.h \
\ \
gimpunit_pdb.c \ gimpunit_pdb.c \
gimpunit_pdb.h \ gimpunit_pdb.h \
gimppdb_pdb.c \ gimppdb_pdb.c \
gimppdb_pdb.h \ gimppdb_pdb.h \
gimpplugin_pdb.c \ gimpplugin_pdb.c \
gimpplugin_pdb.h gimpplugin_pdb.h
libgimp_extra_sources = \ libgimp_extra_sources = \

View file

@ -34,6 +34,7 @@
#include "gimppdb_pdb.h" #include "gimppdb_pdb.h"
#include "gimpplugin_pdb.h" #include "gimpplugin_pdb.h"
#include "gimpprocedure-private.h" #include "gimpprocedure-private.h"
#include "gimpprocedureconfig-private.h"
#include "libgimp-intl.h" #include "libgimp-intl.h"
@ -1580,7 +1581,18 @@ gimp_procedure_run (GimpProcedure *procedure,
/* add missing args with default values */ /* add missing args with default values */
if (gimp_value_array_length (args) < procedure->priv->n_args) 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++) for (i = 0; i < procedure->priv->n_args; i++)
{ {
@ -1595,6 +1607,12 @@ gimp_procedure_run (GimpProcedure *procedure,
g_value_copy (orig, &value); 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 else
{ {
g_param_value_set_default (pspec, &value); g_param_value_set_default (pspec, &value);
@ -1604,6 +1622,8 @@ gimp_procedure_run (GimpProcedure *procedure,
g_value_unset (&value); g_value_unset (&value);
} }
g_clear_object (&config);
/* call the procedure */ /* call the procedure */
return_vals = GIMP_PROCEDURE_GET_CLASS (procedure)->run (procedure, return_vals = GIMP_PROCEDURE_GET_CLASS (procedure)->run (procedure,
complete); complete);

View file

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

View file

@ -23,6 +23,8 @@
#include "gimp.h" #include "gimp.h"
#include "gimpprocedureconfig-private.h"
/** /**
* SECTION: gimpprocedureconfig * SECTION: gimpprocedureconfig
@ -62,34 +64,16 @@ struct _GimpProcedureConfigPrivate
}; };
static void gimp_procedure_config_constructed (GObject *object); static void gimp_procedure_config_constructed (GObject *object);
static void gimp_procedure_config_dispose (GObject *object); static void gimp_procedure_config_dispose (GObject *object);
static void gimp_procedure_config_set_property (GObject *object, static void gimp_procedure_config_set_property (GObject *object,
guint property_id, guint property_id,
const GValue *value, const GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
static void gimp_procedure_config_get_property (GObject *object, static void gimp_procedure_config_get_property (GObject *object,
guint property_id, guint property_id,
GValue *value, GValue *value,
GParamSpec *pspec); 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);
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpProcedureConfig, gimp_procedure_config, 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: case GIMP_RUN_WITH_LAST_VALS:
if (image) if (image)
{ {
loaded = gimp_procedure_config_load_parasite (config, image, loaded = _gimp_procedure_config_load_parasite (config, image,
&error); &error);
if (! loaded && error) if (! loaded && error)
{ {
g_printerr ("Loading last used values from parasite failed: %s\n", g_printerr ("Loading last used values from parasite failed: %s\n",
@ -369,7 +353,7 @@ gimp_procedure_config_begin_run (GimpProcedureConfig *config,
} }
if (! loaded && 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", g_printerr ("Loading last used values from disk failed: %s\n",
error->message); error->message);
@ -422,9 +406,9 @@ gimp_procedure_config_end_run (GimpProcedureConfig *config,
GError *error = NULL; GError *error = NULL;
if (config->priv->image) 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", g_printerr ("Saving last used values to disk failed: %s\n",
error->message); error->message);
@ -453,9 +437,48 @@ gimp_procedure_config_get_file (GimpProcedureConfig *config,
return file; return file;
} }
static gboolean gboolean
gimp_procedure_config_load_last (GimpProcedureConfig *config, _gimp_procedure_config_load_default (GimpProcedureConfig *config,
GError **error) 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"); GFile *file = gimp_procedure_config_get_file (config, ".last");
gboolean success; gboolean success;
@ -467,17 +490,16 @@ gimp_procedure_config_load_last (GimpProcedureConfig *config,
if (! success && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) if (! success && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT)
{ {
g_clear_error (error); g_clear_error (error);
success = TRUE;
} }
g_object_unref (file); g_object_unref (file);
return TRUE; return success;
} }
static gboolean gboolean
gimp_procedure_config_save_last (GimpProcedureConfig *config, _gimp_procedure_config_save_last (GimpProcedureConfig *config,
GError **error) GError **error)
{ {
GFile *file = gimp_procedure_config_get_file (config, ".last"); GFile *file = gimp_procedure_config_get_file (config, ".last");
gboolean success; gboolean success;
@ -500,10 +522,10 @@ gimp_procedure_config_parasite_name (GimpProcedureConfig *config,
return g_strconcat (G_OBJECT_TYPE_NAME (config), suffix, NULL); return g_strconcat (G_OBJECT_TYPE_NAME (config), suffix, NULL);
} }
static gboolean gboolean
gimp_procedure_config_load_parasite (GimpProcedureConfig *config, _gimp_procedure_config_load_parasite (GimpProcedureConfig *config,
GimpImage *image, GimpImage *image,
GError **error) GError **error)
{ {
gchar *name; gchar *name;
GimpParasite *parasite; GimpParasite *parasite;
@ -524,9 +546,9 @@ gimp_procedure_config_load_parasite (GimpProcedureConfig *config,
return success; return success;
} }
static gboolean gboolean
gimp_procedure_config_save_parasite (GimpProcedureConfig *config, _gimp_procedure_config_save_parasite (GimpProcedureConfig *config,
GimpImage *image) GimpImage *image)
{ {
gchar *name; gchar *name;
GimpParasite *parasite; GimpParasite *parasite;

View file

@ -28,6 +28,8 @@
#include "gimp.h" #include "gimp.h"
#include "gimpui.h" #include "gimpui.h"
#include "gimpprocedureconfig-private.h"
#include "libgimp-intl.h" #include "libgimp-intl.h"
@ -46,18 +48,30 @@ struct _GimpProcedureDialogPrivate
{ {
GimpProcedure *procedure; GimpProcedure *procedure;
GimpProcedureConfig *config; GimpProcedureConfig *config;
GimpProcedureConfig *initial_config;
GtkWidget *reset_popover;
}; };
static void gimp_procedure_dialog_dispose (GObject *object); static void gimp_procedure_dialog_dispose (GObject *object);
static void gimp_procedure_dialog_set_property (GObject *object, static void gimp_procedure_dialog_set_property (GObject *object,
guint property_id, guint property_id,
const GValue *value, const GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
static void gimp_procedure_dialog_get_property (GObject *object, static void gimp_procedure_dialog_get_property (GObject *object,
guint property_id, guint property_id,
GValue *value, GValue *value,
GParamSpec *pspec); 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, 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->procedure);
g_clear_object (&dialog->priv->config); 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); G_OBJECT_CLASS (parent_class)->dispose (object);
} }
@ -124,6 +141,10 @@ gimp_procedure_dialog_set_property (GObject *object,
case PROP_CONFIG: case PROP_CONFIG:
dialog->priv->config = g_value_dup_object (value); 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; break;
default: default:
@ -166,6 +187,8 @@ gimp_procedure_dialog_new (GimpProcedure *procedure,
const gchar *help_id; const gchar *help_id;
const gchar *ok_label; const gchar *ok_label;
gboolean use_header_bar; 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 (procedure), NULL);
g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), 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)); 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); return GTK_WIDGET (dialog);
} }
@ -228,7 +275,42 @@ gimp_procedure_dialog_run (GimpProcedureDialog *dialog)
if (response == RESPONSE_RESET) 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 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);
}
}