diff --git a/app/core/gimpdrawablefilter.c b/app/core/gimpdrawablefilter.c index 5b4d3f5851..95ec952289 100644 --- a/app/core/gimpdrawablefilter.c +++ b/app/core/gimpdrawablefilter.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" @@ -44,6 +45,7 @@ #include "gimpchannel.h" #include "gimpdrawable-filters.h" #include "gimpdrawablefilter.h" +#include "gimperror.h" #include "gimpidtable.h" #include "gimpimage.h" #include "gimplayer.h" @@ -708,6 +710,146 @@ gimp_drawable_filter_set_preview_split (GimpDrawableFilter *filter, } } +gboolean +gimp_drawable_filter_update (GimpDrawableFilter *filter, + const gchar **propnames, + const GimpValueArray *values, + GError **error) +{ + GParamSpec **pspecs; + gchar *opname; + guint n_pspecs; + gint n_values; + gboolean changed = FALSE; + + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), FALSE); + g_return_val_if_fail (error != NULL && *error == NULL, FALSE); + + n_values = gimp_value_array_length (values); + + if (n_values != g_strv_length ((gchar **) propnames)) + { + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + "%s: the number of property names and values differ.", + G_STRFUNC); + return FALSE; + } + + g_object_freeze_notify (G_OBJECT (filter)); + + gegl_node_get (filter->operation, "operation", &opname, NULL); + + pspecs = gegl_operation_list_properties (opname, &n_pspecs); + for (gint i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + GValue old_value = G_VALUE_INIT; + gint j; + + gegl_node_get_property (filter->operation, pspec->name, &old_value); + + for (j = 0; j < n_values; j++) + if (g_strcmp0 (pspec->name, propnames[j]) == 0) + break; + + if (j < n_values) + { + GValue *new_value; + GValue replaced_value = G_VALUE_INIT; + gboolean new_value_initialized = FALSE; + + new_value = gimp_value_array_index (values, j); + + if (GEGL_IS_PARAM_SPEC_ENUM (pspec) && G_VALUE_HOLDS_STRING (new_value)) + { + /* GEGL enum types are special-cased to be passed as + * GimpChoice (string) param specs on libgimp. + */ + GeglParamSpecEnum *gespec = GEGL_PARAM_SPEC_ENUM (pspec); + GEnumClass *enum_class; + GEnumValue *enum_value; + const gchar *new_str; + + new_str = g_value_get_string (new_value); + enum_class = g_type_class_ref (G_PARAM_SPEC_VALUE_TYPE (pspec)); + for (enum_value = enum_class->values; enum_value->value_name; enum_value++) + { + GSList *iter; + + if (enum_value->value < enum_class->minimum || enum_value->value > enum_class->maximum) + continue; + + for (iter = gespec->excluded_values; iter; iter = iter->next) + if (GPOINTER_TO_INT (iter->data) == enum_value->value) + break; + + if (iter != NULL) + /* Excluded value. */ + continue; + + if (g_strcmp0 (enum_value->value_nick, new_str) == 0) + { + g_value_init (&replaced_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + g_value_set_enum (&replaced_value, enum_value->value); + new_value = &replaced_value; + new_value_initialized = TRUE; + } + } + + g_type_class_unref (enum_class); + } + else if (! G_TYPE_CHECK_VALUE_TYPE (new_value, G_PARAM_SPEC_VALUE_TYPE (pspec))) + { + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + /* TODO: localize after string freeze. */ + "GEGL operation '%s' has been called with a " + "wrong value type for argument '%s' (#%d). " + "Expected %s, got %s.", + opname, pspec->name, i, + g_type_name (pspec->value_type), + g_type_name (G_VALUE_TYPE (new_value))); + + break; + } + + if (g_param_values_cmp (pspec, new_value, &old_value) != 0) + { + gegl_node_set_property (filter->operation, pspec->name, new_value); + changed = TRUE; + } + + if (new_value_initialized) + g_value_unset (new_value); + + continue; + } + + if (! g_param_value_defaults (pspec, &old_value)) + { + GValue default_value = G_VALUE_INIT; + + g_value_init (&default_value, pspec->value_type); + g_param_value_set_default (pspec, &default_value); + gegl_node_set_property (filter->operation, pspec->name, &default_value); + changed = TRUE; + + g_value_unset (&default_value); + } + + g_value_unset (&old_value); + } + + g_object_thaw_notify (G_OBJECT (filter)); + + g_free (pspecs); + g_free (opname); + + if (changed && gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + + return (*error != NULL); +} + void gimp_drawable_filter_set_opacity (GimpDrawableFilter *filter, gdouble opacity) diff --git a/app/core/gimpdrawablefilter.h b/app/core/gimpdrawablefilter.h index 6321d63b79..bb429283e6 100644 --- a/app/core/gimpdrawablefilter.h +++ b/app/core/gimpdrawablefilter.h @@ -98,6 +98,11 @@ void gimp_drawable_filter_set_preview_split gboolean enabled, GimpAlignmentType alignment, gint split_position); + +gboolean gimp_drawable_filter_update (GimpDrawableFilter *filter, + const gchar **propnames, + const GimpValueArray *values, + GError **error); void gimp_drawable_filter_set_opacity (GimpDrawableFilter *filter, gdouble opacity); void gimp_drawable_filter_set_mode (GimpDrawableFilter *filter, diff --git a/app/pdb/drawable-filter-cmds.c b/app/pdb/drawable-filter-cmds.c index b0a991d84e..94e0f7fe68 100644 --- a/app/pdb/drawable-filter-cmds.c +++ b/app/pdb/drawable-filter-cmds.c @@ -186,6 +186,32 @@ drawable_filter_set_visible_invoker (GimpProcedure *procedure, error ? *error : NULL); } +static GimpValueArray * +drawable_filter_update_settings_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + gboolean success = TRUE; + GimpDrawableFilter *filter; + const gchar **propnames; + const GimpValueArray *propvalues; + + filter = g_value_get_object (gimp_value_array_index (args, 0)); + propnames = g_value_get_boxed (gimp_value_array_index (args, 1)); + propvalues = g_value_get_boxed (gimp_value_array_index (args, 2)); + + if (success) + { + success = gimp_drawable_filter_update (filter, propnames, propvalues, error); + } + + return gimp_procedure_get_return_values (procedure, success, + error ? *error : NULL); +} + static GimpValueArray * drawable_filter_get_number_arguments_invoker (GimpProcedure *procedure, Gimp *gimp, @@ -465,6 +491,42 @@ register_drawable_filter_procs (GimpPDB *pdb) gimp_pdb_register_procedure (pdb, procedure); g_object_unref (procedure); + /* + * gimp-drawable-filter-update-settings + */ + procedure = gimp_procedure_new (drawable_filter_update_settings_invoker); + gimp_object_set_static_name (GIMP_OBJECT (procedure), + "gimp-drawable-filter-update-settings"); + gimp_procedure_set_static_help (procedure, + "Update the settings of the specified filter.", + "This procedure updates the settings of the specified filter all at once.\n" + "In particular, update will be frozen and will happen only once for all changed settings.", + NULL); + gimp_procedure_set_static_attribution (procedure, + "Jehan", + "Jehan", + "2024"); + gimp_procedure_add_argument (procedure, + gimp_param_spec_drawable_filter ("filter", + "filter", + "The filter", + FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + g_param_spec_boxed ("propnames", + "propnames", + "Array of property names", + G_TYPE_STRV, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_value_array ("propvalues", + "propvalues", + "Array of values, one per property in propnames", + NULL, + GIMP_PARAM_READWRITE)); + gimp_pdb_register_procedure (pdb, procedure); + g_object_unref (procedure); + /* * gimp-drawable-filter-get-number-arguments */ diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c index 250080995b..8b9dd6ca45 100644 --- a/app/pdb/internal-procs.c +++ b/app/pdb/internal-procs.c @@ -30,7 +30,7 @@ #include "internal-procs.h" -/* 727 procedures registered total */ +/* 728 procedures registered total */ void internal_procs_init (GimpPDB *pdb) diff --git a/libgimp/gimp.def b/libgimp/gimp.def index 71dd892ac6..f805641c3d 100644 --- a/libgimp/gimp.def +++ b/libgimp/gimp.def @@ -223,6 +223,7 @@ EXPORTS gimp_drawable_filter_id_is_valid gimp_drawable_filter_is_valid gimp_drawable_filter_set_visible + gimp_drawable_filter_update gimp_drawable_foreground_extract gimp_drawable_free_shadow gimp_drawable_get_bpp diff --git a/libgimp/gimpdrawablefilter.c b/libgimp/gimpdrawablefilter.c index 1ad868d3b7..4103b60541 100644 --- a/libgimp/gimpdrawablefilter.c +++ b/libgimp/gimpdrawablefilter.c @@ -255,3 +255,51 @@ gimp_drawable_filter_get_config (GimpDrawableFilter *filter) return filter->config; } + +void +gimp_drawable_filter_update (GimpDrawableFilter *filter) +{ + GStrvBuilder *builder = g_strv_builder_new (); + GimpValueArray *values = NULL; + GStrv propnames; + + if (filter->config) + { + GObjectClass *klass; + GParamSpec **pspecs; + guint n_pspecs; + + klass = G_OBJECT_GET_CLASS (filter->config); + pspecs = g_object_class_list_properties (klass, &n_pspecs); + values = gimp_value_array_new (n_pspecs); + + for (guint i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + GValue value = G_VALUE_INIT; + + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (filter->config), pspec->name, &value); + + if (g_param_value_defaults (pspec, &value)) + { + g_value_unset (&value); + continue; + } + + g_strv_builder_add (builder, pspec->name); + gimp_value_array_append (values, &value); + + g_value_unset (&value); + } + + g_free (pspecs); + } + + propnames = g_strv_builder_end (builder); + _gimp_drawable_filter_update_settings (filter, (const gchar **) propnames, values); + + g_strfreev (propnames); + g_strv_builder_unref (builder); + gimp_value_array_unref (values); +} diff --git a/libgimp/gimpdrawablefilter.h b/libgimp/gimpdrawablefilter.h index 1b5727a437..602130d028 100644 --- a/libgimp/gimpdrawablefilter.h +++ b/libgimp/gimpdrawablefilter.h @@ -43,6 +43,9 @@ gboolean gimp_drawable_filter_is_valid (GimpDrawableFilter * GimpDrawableFilterConfig * gimp_drawable_filter_get_config (GimpDrawableFilter *filter); +void gimp_drawable_filter_update (GimpDrawableFilter *filter); + + G_END_DECLS #endif /* __GIMP_DRAWABLE_FILTER_H__ */ diff --git a/libgimp/gimpdrawablefilter_pdb.c b/libgimp/gimpdrawablefilter_pdb.c index fa3b548f05..173ff7801c 100644 --- a/libgimp/gimpdrawablefilter_pdb.c +++ b/libgimp/gimpdrawablefilter_pdb.c @@ -227,6 +227,50 @@ gimp_drawable_filter_set_visible (GimpDrawableFilter *filter, return success; } +/** + * _gimp_drawable_filter_update_settings: + * @filter: The filter. + * @propnames: (array zero-terminated=1): Array of property names. + * @propvalues: Array of values, one per property in propnames. + * + * Update the settings of the specified filter. + * + * This procedure updates the settings of the specified filter all at + * once. + * In particular, update will be frozen and will happen only once for + * all changed settings. + * + * Returns: TRUE on success. + * + * Since: 3.0 + **/ +gboolean +_gimp_drawable_filter_update_settings (GimpDrawableFilter *filter, + const gchar **propnames, + const GimpValueArray *propvalues) +{ + GimpValueArray *args; + GimpValueArray *return_vals; + gboolean success = TRUE; + + args = gimp_value_array_new_from_types (NULL, + GIMP_TYPE_DRAWABLE_FILTER, filter, + G_TYPE_STRV, propnames, + GIMP_TYPE_VALUE_ARRAY, propvalues, + G_TYPE_NONE); + + return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (), + "gimp-drawable-filter-update-settings", + args); + gimp_value_array_unref (args); + + success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS; + + gimp_value_array_unref (return_vals); + + return success; +} + /** * _gimp_drawable_filter_get_number_arguments: * @operation_name: The procedure name. diff --git a/libgimp/gimpdrawablefilter_pdb.h b/libgimp/gimpdrawablefilter_pdb.h index bef1439ca4..abc91ffe7d 100644 --- a/libgimp/gimpdrawablefilter_pdb.h +++ b/libgimp/gimpdrawablefilter_pdb.h @@ -32,16 +32,19 @@ G_BEGIN_DECLS /* For information look into the C source or the html documentation */ -gboolean gimp_drawable_filter_id_is_valid (gint filter_id); -gchar* gimp_drawable_filter_get_name (GimpDrawableFilter *filter); -gchar* gimp_drawable_filter_get_operation_name (GimpDrawableFilter *filter); -gboolean gimp_drawable_filter_get_visible (GimpDrawableFilter *filter); -gboolean gimp_drawable_filter_set_visible (GimpDrawableFilter *filter, - gboolean visible); -G_GNUC_INTERNAL gint _gimp_drawable_filter_get_number_arguments (const gchar *operation_name); -G_GNUC_INTERNAL GParamSpec* _gimp_drawable_filter_get_argument (const gchar *operation_name, - gint arg_num); -gboolean gimp_drawable_filter_delete (GimpDrawableFilter *filter); +gboolean gimp_drawable_filter_id_is_valid (gint filter_id); +gchar* gimp_drawable_filter_get_name (GimpDrawableFilter *filter); +gchar* gimp_drawable_filter_get_operation_name (GimpDrawableFilter *filter); +gboolean gimp_drawable_filter_get_visible (GimpDrawableFilter *filter); +gboolean gimp_drawable_filter_set_visible (GimpDrawableFilter *filter, + gboolean visible); +G_GNUC_INTERNAL gboolean _gimp_drawable_filter_update_settings (GimpDrawableFilter *filter, + const gchar **propnames, + const GimpValueArray *propvalues); +G_GNUC_INTERNAL gint _gimp_drawable_filter_get_number_arguments (const gchar *operation_name); +G_GNUC_INTERNAL GParamSpec* _gimp_drawable_filter_get_argument (const gchar *operation_name, + gint arg_num); +gboolean gimp_drawable_filter_delete (GimpDrawableFilter *filter); G_END_DECLS diff --git a/libgimp/gimpgpparams-body.c b/libgimp/gimpgpparams-body.c index 39fc8d6e28..27d072fe29 100644 --- a/libgimp/gimpgpparams-body.c +++ b/libgimp/gimpgpparams-body.c @@ -1109,6 +1109,16 @@ gimp_gp_param_to_value (gpointer gimp, g_value_take_param (value, pspec); } + else if (GIMP_VALUE_HOLDS_VALUE_ARRAY (value)) + { + GimpValueArray *values; + + values = _gimp_gp_params_to_value_array (gimp, NULL, 0, + param->data.d_value_array.values, + param->data.d_value_array.n_values, + FALSE); + g_value_take_boxed (value, values); + } else { g_warning ("%s: unsupported deserialization to GValue of type '%s'\n", @@ -1639,6 +1649,17 @@ gimp_value_to_gp_param (const GValue *value, _gimp_param_spec_to_gp_param_def (g_value_get_param (value), ¶m->data.d_param_def); } + else if (GIMP_VALUE_HOLDS_VALUE_ARRAY (value)) + { + GimpValueArray *array = g_value_get_boxed (value); + + param->param_type = GP_PARAM_TYPE_VALUE_ARRAY; + + param->data.d_value_array.n_values = gimp_value_array_length (array); + param->data.d_value_array.values = NULL; + if (param->data.d_value_array.n_values > 0) + param->data.d_value_array.values = _gimp_value_array_to_gp_params (array, full_copy); + } if (param->param_type == -1) g_warning ("%s: GValue holds unsupported type '%s'", G_STRFUNC, param->type_name); @@ -1742,6 +1763,12 @@ _gimp_gp_params_free (GPParam *params, g_strcmp0 (params[i].data.d_param_def.type_name, "GeglParamEnum") == 0) g_clear_object (¶ms[i].data.d_param_def.meta.m_choice.choice); break; + + case GP_PARAM_TYPE_VALUE_ARRAY: + _gimp_gp_params_free (params[i].data.d_value_array.values, + params[i].data.d_value_array.n_values, + full_copy); + break; } } diff --git a/libgimpbase/gimpprotocol.c b/libgimpbase/gimpprotocol.c index 884647f1c8..5d1bdae71c 100644 --- a/libgimpbase/gimpprotocol.c +++ b/libgimpbase/gimpprotocol.c @@ -2134,6 +2134,19 @@ _gp_params_read (GIOChannel *channel, user_data)) goto cleanup; break; + + case GP_PARAM_TYPE_VALUE_ARRAY: + { + guint n_values = 0; + + (*params)[i].data.d_value_array.values = NULL; + _gp_params_read (channel, + &(*params)[i].data.d_value_array.values, + &n_values, + user_data); + (*params)[i].data.d_value_array.n_values = (guint32) n_values; + break; + } } } @@ -2357,6 +2370,14 @@ _gp_params_write (GIOChannel *channel, user_data)) return; break; + + case GP_PARAM_TYPE_VALUE_ARRAY: + _gp_params_write (channel, + params[i].data.d_value_array.values, + params[i].data.d_value_array.n_values, + user_data); + break; + } } } @@ -2430,6 +2451,11 @@ _gp_params_destroy (GPParam *params, case GP_PARAM_TYPE_PARAM_DEF: _gp_param_def_destroy (¶ms[i].data.d_param_def); break; + + case GP_PARAM_TYPE_VALUE_ARRAY: + _gp_params_destroy (params[i].data.d_value_array.values, + params[i].data.d_value_array.n_values); + break; } } diff --git a/libgimpbase/gimpprotocol.h b/libgimpbase/gimpprotocol.h index 213acc90bf..9e7eea22ab 100644 --- a/libgimpbase/gimpprotocol.h +++ b/libgimpbase/gimpprotocol.h @@ -78,7 +78,8 @@ typedef enum GP_PARAM_TYPE_ARRAY, GP_PARAM_TYPE_ID_ARRAY, GP_PARAM_TYPE_EXPORT_OPTIONS, - GP_PARAM_TYPE_PARAM_DEF + GP_PARAM_TYPE_PARAM_DEF, + GP_PARAM_TYPE_VALUE_ARRAY, } GPParamType; @@ -106,6 +107,7 @@ typedef struct _GPParamFormat GPParamFormat; typedef struct _GPParamColor GPParamColor; typedef struct _GPParamColorArray GPParamColorArray; typedef struct _GPParamExportOptions GPParamExportOptions; +typedef struct _GPParamValueArray GPParamValueArray; typedef struct _GPProcRun GPProcRun; typedef struct _GPProcReturn GPProcReturn; typedef struct _GPProcInstall GPProcInstall; @@ -306,6 +308,12 @@ struct _GPParamExportOptions */ }; +struct _GPParamValueArray +{ + guint32 n_values; + GPParam *values; +}; + struct _GPParam { GPParamType param_type; @@ -326,6 +334,7 @@ struct _GPParam GPParamIDArray d_id_array; GPParamExportOptions d_export_options; GPParamDef d_param_def; + GPParamValueArray d_value_array; } data; }; diff --git a/libgimpbase/gimpvaluearray.h b/libgimpbase/gimpvaluearray.h index 8cd0895265..0535735526 100644 --- a/libgimpbase/gimpvaluearray.h +++ b/libgimpbase/gimpvaluearray.h @@ -35,7 +35,8 @@ G_BEGIN_DECLS * * Since: 2.10 */ -#define GIMP_TYPE_VALUE_ARRAY (gimp_value_array_get_type ()) +#define GIMP_TYPE_VALUE_ARRAY (gimp_value_array_get_type ()) +#define GIMP_VALUE_HOLDS_VALUE_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_VALUE_ARRAY)) GType gimp_value_array_get_type (void) G_GNUC_CONST; diff --git a/pdb/app.pl b/pdb/app.pl index f7def3cf40..2a0172a410 100644 --- a/pdb/app.pl +++ b/pdb/app.pl @@ -846,6 +846,15 @@ gimp_param_spec_core_object_array ("$name", "$blurb", GIMP_TYPE_PATTERN, $flags) +CODE + } + elsif ($pdbtype eq 'valuearray') { + $pspec = < 'filter', type => 'filter', + desc => 'The filter' }, + { name => 'propnames', type => 'strv', + desc => 'Array of property names' }, + { name => 'propvalues', type => 'valuearray', + desc => 'Array of values, one per property in propnames' }, + ); + + %invoke = ( + code => <<'CODE' +{ + success = gimp_drawable_filter_update (filter, propnames, propvalues, error); +} +CODE + ); +} + sub drawable_filter_get_number_arguments { $blurb = <<'BLURB'; Queries for the number of arguments on the specified filter. @@ -310,6 +341,7 @@ CODE drawable_filter_get_operation_name drawable_filter_get_visible drawable_filter_set_visible + drawable_filter_update_settings drawable_filter_get_number_arguments drawable_filter_get_argument drawable_filter_delete); diff --git a/pdb/pdb.pl b/pdb/pdb.pl index 130c4291cd..62b56c106f 100644 --- a/pdb/pdb.pl +++ b/pdb/pdb.pl @@ -624,7 +624,17 @@ package Gimp::CodeGen::pdb; dup_value_func => '$var = GIMP_VALUES_GET_FONT ($value)', set_value_func => 'g_value_set_object ($value, $var)', take_value_func => 'g_value_set_object ($value, $var)', - headers => [ qw("text/gimpfont.h") ] } + headers => [ qw("text/gimpfont.h") ] }, + + valuearray => { name => 'VALUEARRAY', + gtype => 'GIMP_TYPE_VALUE_ARRAY', + type => 'GimpValueArray *', + const_type => 'const GimpValueArray *', + init_value => 'NULL', + get_value_func => '$var = g_value_get_boxed ($value)', + dup_value_func => '$var = g_value_dup_boxed (gimp_value_array_index ($value))', + set_value_func => 'g_value_set_boxed ($value, $var)', + take_value_func => 'g_value_take_boxed ($value, $var)' }, ); # Split out the parts of an arg constraint