From 7715a875e07a73cfef4711ba3e50af4fada9a032 Mon Sep 17 00:00:00 2001 From: Jehan Date: Fri, 29 Nov 2024 17:37:23 +0900 Subject: [PATCH] app, libgimp, pdb: new gimp_drawable_filter_update() public API. It is now possible to sync the GimpDrawableFilterConfig with core. Another (simpler on usage) possibility could have been to sync automatically when a property is updated. But considering that some filters can be quite slow to render (especially in real-life usage when working on possibly very big files), and especially that on bindings with no variable args, scripts will likely have to edit properties one by one, it could make editing multiple properties very slow. Therefore the chosen solution is that editing properties stay local on libgimp and all changed properties are synced with core at once (with a frozen render until the end for single computation) when calling gimp_drawable_filter_update(). --- app/core/gimpdrawablefilter.c | 142 +++++++++++++++++++++++++++++++ app/core/gimpdrawablefilter.h | 5 ++ app/pdb/drawable-filter-cmds.c | 62 ++++++++++++++ app/pdb/internal-procs.c | 2 +- libgimp/gimp.def | 1 + libgimp/gimpdrawablefilter.c | 48 +++++++++++ libgimp/gimpdrawablefilter.h | 3 + libgimp/gimpdrawablefilter_pdb.c | 44 ++++++++++ libgimp/gimpdrawablefilter_pdb.h | 23 ++--- libgimp/gimpgpparams-body.c | 27 ++++++ libgimpbase/gimpprotocol.c | 26 ++++++ libgimpbase/gimpprotocol.h | 11 ++- libgimpbase/gimpvaluearray.h | 3 +- pdb/app.pl | 9 ++ pdb/groups/drawable_filter.pdb | 32 +++++++ pdb/pdb.pl | 12 ++- 16 files changed, 436 insertions(+), 14 deletions(-) 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