Commit 45e96a0f authored by Michael Natterer's avatar Michael Natterer 😴

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".
parent e4acb969
Pipeline #118425 passed with stages
in 12 minutes and 10 seconds
......@@ -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 = \
......
......@@ -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);
......
/* 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__ */
......@@ -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;
......
......@@ -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);
}
}
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