2012-05-04 05:24:09 +08:00
|
|
|
/* LIBGIMP - The GIMP Library
|
|
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
|
|
*
|
|
|
|
* gimpvaluearray.c ported from GValueArray
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* Library 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
|
2018-07-12 05:27:07 +08:00
|
|
|
* <https://www.gnu.org/licenses/>.
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2024-02-29 05:13:57 +08:00
|
|
|
#include <gegl.h>
|
2012-05-04 05:24:09 +08:00
|
|
|
#include <glib-object.h>
|
2019-08-08 19:01:50 +08:00
|
|
|
#include <gobject/gvaluecollector.h>
|
2012-05-04 05:24:09 +08:00
|
|
|
|
|
|
|
#include "gimpbasetypes.h"
|
|
|
|
|
2024-02-29 05:13:57 +08:00
|
|
|
#include "gimpparamspecs.h"
|
2012-05-04 05:24:09 +08:00
|
|
|
#include "gimpvaluearray.h"
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-31 20:40:20 +08:00
|
|
|
* SECTION:gimpvaluearray
|
2012-05-04 05:24:09 +08:00
|
|
|
* @short_description: A container structure to maintain an array of
|
|
|
|
* generic values
|
|
|
|
* @see_also: #GValue, #GParamSpecValueArray, gimp_param_spec_value_array()
|
2015-06-01 01:44:39 +08:00
|
|
|
* @title: GimpValueArray
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
|
|
|
* The prime purpose of a #GimpValueArray is for it to be used as an
|
|
|
|
* object property that holds an array of values. A #GimpValueArray wraps
|
|
|
|
* an array of #GValue elements in order for it to be used as a boxed
|
|
|
|
* type through %GIMP_TYPE_VALUE_ARRAY.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define GROUP_N_VALUES (1) /* power of 2 !! */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GimpValueArray:
|
|
|
|
*
|
|
|
|
* A #GimpValueArray contains an array of #GValue elements.
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
struct _GimpValueArray
|
|
|
|
{
|
|
|
|
gint n_values;
|
|
|
|
GValue *values;
|
|
|
|
|
|
|
|
gint n_prealloced;
|
|
|
|
gint ref_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_BOXED_TYPE (GimpValueArray, gimp_value_array,
|
|
|
|
gimp_value_array_ref, gimp_value_array_unref)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_index:
|
|
|
|
* @value_array: #GimpValueArray to get a value from
|
2015-05-31 20:40:20 +08:00
|
|
|
* @index: index of the value of interest
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
2016-06-26 04:54:10 +08:00
|
|
|
* Return a pointer to the value at @index contained in @value_array.
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
2024-02-29 05:13:57 +08:00
|
|
|
* *Note*: in binding languages, some custom types fail to be correctly passed
|
|
|
|
* through. For these types, you should use specific functions.
|
|
|
|
* For instance, in the Python binding, a [type@ColorArray] `GValue`
|
|
|
|
* won't be usable with this function. You should use instead
|
|
|
|
* [method@ValueArray.get_color_array].
|
|
|
|
*
|
2015-05-31 20:40:20 +08:00
|
|
|
* Returns: (transfer none): pointer to a value at @index in @value_array
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GValue *
|
|
|
|
gimp_value_array_index (const GimpValueArray *value_array,
|
|
|
|
gint index)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
g_return_val_if_fail (index < value_array->n_values, NULL);
|
|
|
|
|
|
|
|
return value_array->values + index;
|
|
|
|
}
|
|
|
|
|
2024-02-29 05:13:57 +08:00
|
|
|
/**
|
|
|
|
* gimp_value_array_get_color_array:
|
|
|
|
* @value_array: #GimpValueArray to get a value from
|
|
|
|
* @index: index of the value of interest
|
|
|
|
*
|
|
|
|
* Return a pointer to the value at @index contained in @value_array. This value
|
|
|
|
* is supposed to be a [type@ColorArray].
|
|
|
|
*
|
|
|
|
* Returns: (transfer none) (array zero-terminated=1): the [type@ColorArray] stored at @index in @value_array.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
*/
|
|
|
|
/* XXX See: https://gitlab.gnome.org/GNOME/gimp/-/issues/10885#note_2030308
|
|
|
|
* This explains why we created a specific function for GimpColorArray, instead
|
|
|
|
* of using the generic gimp_value_array_index().
|
|
|
|
*/
|
|
|
|
GeglColor **
|
|
|
|
gimp_value_array_get_color_array (const GimpValueArray *value_array,
|
|
|
|
gint index)
|
|
|
|
{
|
|
|
|
GValue *value;
|
|
|
|
GimpColorArray colors;
|
|
|
|
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
g_return_val_if_fail (index < value_array->n_values, NULL);
|
|
|
|
|
|
|
|
value = value_array->values + index;
|
|
|
|
g_return_val_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value), NULL);
|
|
|
|
|
|
|
|
colors = g_value_get_boxed (value);
|
|
|
|
|
|
|
|
return colors;
|
|
|
|
}
|
|
|
|
|
2012-05-04 05:24:09 +08:00
|
|
|
static inline void
|
|
|
|
value_array_grow (GimpValueArray *value_array,
|
2017-06-17 17:38:18 +08:00
|
|
|
gint n_values,
|
|
|
|
gboolean zero_init)
|
2012-05-04 05:24:09 +08:00
|
|
|
{
|
|
|
|
g_return_if_fail ((guint) n_values >= (guint) value_array->n_values);
|
|
|
|
|
|
|
|
value_array->n_values = n_values;
|
|
|
|
if (value_array->n_values > value_array->n_prealloced)
|
|
|
|
{
|
|
|
|
gint i = value_array->n_prealloced;
|
|
|
|
|
|
|
|
value_array->n_prealloced = (value_array->n_values + GROUP_N_VALUES - 1) & ~(GROUP_N_VALUES - 1);
|
|
|
|
value_array->values = g_renew (GValue, value_array->values, value_array->n_prealloced);
|
|
|
|
|
|
|
|
if (!zero_init)
|
2017-06-17 17:38:18 +08:00
|
|
|
i = value_array->n_values;
|
2012-05-04 05:24:09 +08:00
|
|
|
|
|
|
|
memset (value_array->values + i, 0,
|
2017-06-17 17:38:18 +08:00
|
|
|
(value_array->n_prealloced - i) * sizeof (value_array->values[0]));
|
2012-05-04 05:24:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
value_array_shrink (GimpValueArray *value_array)
|
|
|
|
{
|
|
|
|
if (value_array->n_prealloced >= value_array->n_values + GROUP_N_VALUES)
|
|
|
|
{
|
|
|
|
value_array->n_prealloced = (value_array->n_values + GROUP_N_VALUES - 1) & ~(GROUP_N_VALUES - 1);
|
|
|
|
value_array->values = g_renew (GValue, value_array->values, value_array->n_prealloced);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_new:
|
|
|
|
* @n_prealloced: number of values to preallocate space for
|
|
|
|
*
|
|
|
|
* Allocate and initialize a new #GimpValueArray, optionally preserve space
|
|
|
|
* for @n_prealloced elements. New arrays always contain 0 elements,
|
|
|
|
* regardless of the value of @n_prealloced.
|
|
|
|
*
|
|
|
|
* Returns: a newly allocated #GimpValueArray with 0 values
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_new (gint n_prealloced)
|
|
|
|
{
|
2019-07-30 16:12:28 +08:00
|
|
|
GimpValueArray *value_array = g_slice_new0 (GimpValueArray);
|
2012-05-04 05:24:09 +08:00
|
|
|
|
|
|
|
value_array_grow (value_array, n_prealloced, TRUE);
|
|
|
|
value_array->n_values = 0;
|
|
|
|
value_array->ref_count = 1;
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
2019-07-30 16:12:28 +08:00
|
|
|
/**
|
gimppdb: Allow more easy bindable API
Plug-ins that work from different bindings probably want to use their
own list-type to specify arguments, rather than working with a more
cumbersome `GimpValueArray`.
This new API should make it less verbose. For example:
```
args = Gimp.ValueArray.new(5)
args.insert(0, GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE))
args.insert(1, GObject.Value(Gimp.Image, image))
args.insert(2, GObject.Value(Gimp.Drawable, mask))
args.insert(3, GObject.Value(GObject.TYPE_INT, int(time.time())))
args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, turbulence))
Gimp.get_pdb().run_procedure('plug-in-plasma', args)
```
becomes
```
Gimp.get_pdb().run_procedure('plug-in-plasma', [
GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE),
GObject.Value(Gimp.Image, image),
GObject.Value(Gimp.Drawable, mask),
GObject.Value(GObject.TYPE_INT, int(time.time())),
GObject.Value(GObject.TYPE_DOUBLE, turbulence),
])
```
2020-05-20 00:43:43 +08:00
|
|
|
* gimp_value_array_new_from_types: (skip)
|
2019-08-08 19:01:50 +08:00
|
|
|
* @error_msg: return location for an error message.
|
2019-07-30 16:12:28 +08:00
|
|
|
* @first_type: first type in the array, or #G_TYPE_NONE.
|
|
|
|
* @...: the remaining types in the array, terminated by #G_TYPE_NONE
|
|
|
|
*
|
|
|
|
* Allocate and initialize a new #GimpValueArray, and fill it with
|
2019-08-08 19:01:50 +08:00
|
|
|
* values that are given as a list of (#GType, value) pairs,
|
|
|
|
* terminated by #G_TYPE_NONE.
|
2019-07-30 16:12:28 +08:00
|
|
|
*
|
2019-08-08 19:01:50 +08:00
|
|
|
* Returns: (nullable): a newly allocated #GimpValueArray, or %NULL if
|
|
|
|
* an error happened.
|
2019-07-30 16:12:28 +08:00
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
*/
|
|
|
|
GimpValueArray *
|
2019-08-08 19:01:50 +08:00
|
|
|
gimp_value_array_new_from_types (gchar **error_msg,
|
|
|
|
GType first_type,
|
2019-07-30 16:12:28 +08:00
|
|
|
...)
|
|
|
|
{
|
|
|
|
GimpValueArray *value_array;
|
|
|
|
va_list va_args;
|
|
|
|
|
2019-08-08 19:27:04 +08:00
|
|
|
g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL);
|
|
|
|
|
2019-07-30 16:12:28 +08:00
|
|
|
va_start (va_args, first_type);
|
|
|
|
|
2019-08-08 19:01:50 +08:00
|
|
|
value_array = gimp_value_array_new_from_types_valist (error_msg,
|
|
|
|
first_type,
|
2019-07-30 16:12:28 +08:00
|
|
|
va_args);
|
|
|
|
|
|
|
|
va_end (va_args);
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
gimppdb: Allow more easy bindable API
Plug-ins that work from different bindings probably want to use their
own list-type to specify arguments, rather than working with a more
cumbersome `GimpValueArray`.
This new API should make it less verbose. For example:
```
args = Gimp.ValueArray.new(5)
args.insert(0, GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE))
args.insert(1, GObject.Value(Gimp.Image, image))
args.insert(2, GObject.Value(Gimp.Drawable, mask))
args.insert(3, GObject.Value(GObject.TYPE_INT, int(time.time())))
args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, turbulence))
Gimp.get_pdb().run_procedure('plug-in-plasma', args)
```
becomes
```
Gimp.get_pdb().run_procedure('plug-in-plasma', [
GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE),
GObject.Value(Gimp.Image, image),
GObject.Value(Gimp.Drawable, mask),
GObject.Value(GObject.TYPE_INT, int(time.time())),
GObject.Value(GObject.TYPE_DOUBLE, turbulence),
])
```
2020-05-20 00:43:43 +08:00
|
|
|
* gimp_value_array_new_from_types_valist: (skip)
|
2019-08-08 19:27:04 +08:00
|
|
|
* @error_msg: return location for an error message.
|
2019-07-30 16:12:28 +08:00
|
|
|
* @first_type: first type in the array, or #G_TYPE_NONE.
|
|
|
|
* @va_args: a va_list of GTypes and values, terminated by #G_TYPE_NONE
|
|
|
|
*
|
|
|
|
* Allocate and initialize a new #GimpValueArray, and fill it with
|
2019-08-08 19:01:50 +08:00
|
|
|
* @va_args given in the order as passed to
|
|
|
|
* gimp_value_array_new_from_types().
|
2019-07-30 16:12:28 +08:00
|
|
|
*
|
2019-08-08 19:01:50 +08:00
|
|
|
* Returns: (nullable): a newly allocated #GimpValueArray, or %NULL if
|
|
|
|
* an error happened.
|
2019-07-30 16:12:28 +08:00
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
*/
|
|
|
|
GimpValueArray *
|
2019-08-08 19:01:50 +08:00
|
|
|
gimp_value_array_new_from_types_valist (gchar **error_msg,
|
|
|
|
GType first_type,
|
|
|
|
va_list va_args)
|
2019-07-30 16:12:28 +08:00
|
|
|
{
|
2019-08-08 19:27:04 +08:00
|
|
|
GimpValueArray *value_array;
|
2019-07-30 16:12:28 +08:00
|
|
|
GType type;
|
|
|
|
|
2019-08-08 19:27:04 +08:00
|
|
|
g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL);
|
|
|
|
|
2019-07-30 16:12:28 +08:00
|
|
|
type = first_type;
|
|
|
|
|
2019-08-08 19:27:04 +08:00
|
|
|
value_array = gimp_value_array_new (type == G_TYPE_NONE ? 0 : 1);
|
|
|
|
|
2019-07-30 16:12:28 +08:00
|
|
|
while (type != G_TYPE_NONE)
|
|
|
|
{
|
2019-08-08 19:01:50 +08:00
|
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
gchar *my_error = NULL;
|
2019-07-30 16:12:28 +08:00
|
|
|
|
|
|
|
g_value_init (&value, type);
|
2019-08-08 19:01:50 +08:00
|
|
|
|
|
|
|
G_VALUE_COLLECT (&value, va_args, G_VALUE_NOCOPY_CONTENTS, &my_error);
|
|
|
|
|
|
|
|
if (my_error)
|
|
|
|
{
|
|
|
|
if (error_msg)
|
|
|
|
{
|
|
|
|
*error_msg = my_error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_printerr ("%s: %s", G_STRFUNC, my_error);
|
|
|
|
g_free (my_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_value_array_unref (value_array);
|
|
|
|
|
|
|
|
va_end (va_args);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-07-30 16:12:28 +08:00
|
|
|
gimp_value_array_append (value_array, &value);
|
|
|
|
g_value_unset (&value);
|
|
|
|
|
|
|
|
type = va_arg (va_args, GType);
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end (va_args);
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
2023-05-21 07:09:57 +08:00
|
|
|
/**
|
|
|
|
* gimp_value_array_copy:
|
|
|
|
* @value_array: #GimpValueArray to copy
|
|
|
|
*
|
|
|
|
* Return an exact copy of a #GimpValueArray by duplicating all its values.
|
|
|
|
*
|
|
|
|
* Returns: a newly allocated #GimpValueArray.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_copy (const GimpValueArray *value_array)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
|
|
|
|
return gimp_value_array_new_from_values (value_array->values,
|
|
|
|
value_array->n_values);
|
|
|
|
}
|
|
|
|
|
gimppdb: Allow more easy bindable API
Plug-ins that work from different bindings probably want to use their
own list-type to specify arguments, rather than working with a more
cumbersome `GimpValueArray`.
This new API should make it less verbose. For example:
```
args = Gimp.ValueArray.new(5)
args.insert(0, GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE))
args.insert(1, GObject.Value(Gimp.Image, image))
args.insert(2, GObject.Value(Gimp.Drawable, mask))
args.insert(3, GObject.Value(GObject.TYPE_INT, int(time.time())))
args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, turbulence))
Gimp.get_pdb().run_procedure('plug-in-plasma', args)
```
becomes
```
Gimp.get_pdb().run_procedure('plug-in-plasma', [
GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE),
GObject.Value(Gimp.Image, image),
GObject.Value(Gimp.Drawable, mask),
GObject.Value(GObject.TYPE_INT, int(time.time())),
GObject.Value(GObject.TYPE_DOUBLE, turbulence),
])
```
2020-05-20 00:43:43 +08:00
|
|
|
/**
|
|
|
|
* gimp_value_array_new_from_values:
|
|
|
|
* @values: (array length=n_values): The #GValue elements
|
|
|
|
* @n_values: the number of value elements
|
|
|
|
*
|
|
|
|
* Allocate and initialize a new #GimpValueArray, and fill it with
|
2021-04-09 05:14:02 +08:00
|
|
|
* the given #GValues. When no #GValues are given, returns empty #GimpValueArray.
|
gimppdb: Allow more easy bindable API
Plug-ins that work from different bindings probably want to use their
own list-type to specify arguments, rather than working with a more
cumbersome `GimpValueArray`.
This new API should make it less verbose. For example:
```
args = Gimp.ValueArray.new(5)
args.insert(0, GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE))
args.insert(1, GObject.Value(Gimp.Image, image))
args.insert(2, GObject.Value(Gimp.Drawable, mask))
args.insert(3, GObject.Value(GObject.TYPE_INT, int(time.time())))
args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, turbulence))
Gimp.get_pdb().run_procedure('plug-in-plasma', args)
```
becomes
```
Gimp.get_pdb().run_procedure('plug-in-plasma', [
GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE),
GObject.Value(Gimp.Image, image),
GObject.Value(Gimp.Drawable, mask),
GObject.Value(GObject.TYPE_INT, int(time.time())),
GObject.Value(GObject.TYPE_DOUBLE, turbulence),
])
```
2020-05-20 00:43:43 +08:00
|
|
|
*
|
|
|
|
* Returns: a newly allocated #GimpValueArray.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_new_from_values (const GValue *values,
|
|
|
|
gint n_values)
|
|
|
|
{
|
|
|
|
GimpValueArray *value_array;
|
|
|
|
gint i;
|
|
|
|
|
2021-04-09 05:14:02 +08:00
|
|
|
/* n_values is zero if and only if values is NULL. */
|
|
|
|
g_return_val_if_fail ((n_values == 0 && values == NULL) ||
|
|
|
|
(n_values > 0 && values != NULL),
|
|
|
|
NULL);
|
gimppdb: Allow more easy bindable API
Plug-ins that work from different bindings probably want to use their
own list-type to specify arguments, rather than working with a more
cumbersome `GimpValueArray`.
This new API should make it less verbose. For example:
```
args = Gimp.ValueArray.new(5)
args.insert(0, GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE))
args.insert(1, GObject.Value(Gimp.Image, image))
args.insert(2, GObject.Value(Gimp.Drawable, mask))
args.insert(3, GObject.Value(GObject.TYPE_INT, int(time.time())))
args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, turbulence))
Gimp.get_pdb().run_procedure('plug-in-plasma', args)
```
becomes
```
Gimp.get_pdb().run_procedure('plug-in-plasma', [
GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE),
GObject.Value(Gimp.Image, image),
GObject.Value(Gimp.Drawable, mask),
GObject.Value(GObject.TYPE_INT, int(time.time())),
GObject.Value(GObject.TYPE_DOUBLE, turbulence),
])
```
2020-05-20 00:43:43 +08:00
|
|
|
|
|
|
|
value_array = gimp_value_array_new (n_values);
|
|
|
|
|
|
|
|
for (i = 0; i < n_values; i++)
|
|
|
|
{
|
|
|
|
gimp_value_array_insert (value_array, i, &values[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
2012-05-04 05:24:09 +08:00
|
|
|
/**
|
|
|
|
* gimp_value_array_ref:
|
|
|
|
* @value_array: #GimpValueArray to ref
|
|
|
|
*
|
|
|
|
* Adds a reference to a #GimpValueArray.
|
|
|
|
*
|
2019-08-03 06:10:14 +08:00
|
|
|
* Returns: the same @value_array
|
2015-05-31 20:40:20 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_ref (GimpValueArray *value_array)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
|
|
|
|
value_array->ref_count++;
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_unref:
|
|
|
|
* @value_array: #GimpValueArray to unref
|
|
|
|
*
|
|
|
|
* Unref a #GimpValueArray. If the reference count drops to zero, the
|
|
|
|
* array including its contents are freed.
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
gimp_value_array_unref (GimpValueArray *value_array)
|
|
|
|
{
|
|
|
|
g_return_if_fail (value_array != NULL);
|
|
|
|
|
|
|
|
value_array->ref_count--;
|
|
|
|
|
|
|
|
if (value_array->ref_count < 1)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
for (i = 0; i < value_array->n_values; i++)
|
|
|
|
{
|
|
|
|
GValue *value = value_array->values + i;
|
|
|
|
|
|
|
|
if (G_VALUE_TYPE (value) != 0) /* we allow unset values in the array */
|
|
|
|
g_value_unset (value);
|
|
|
|
}
|
2019-07-25 18:37:52 +08:00
|
|
|
|
2012-05-04 05:24:09 +08:00
|
|
|
g_free (value_array->values);
|
|
|
|
g_slice_free (GimpValueArray, value_array);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
gimp_value_array_length (const GimpValueArray *value_array)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, 0);
|
|
|
|
|
|
|
|
return value_array->n_values;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_prepend:
|
|
|
|
* @value_array: #GimpValueArray to add an element to
|
|
|
|
* @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
|
|
|
|
*
|
|
|
|
* Insert a copy of @value as first element of @value_array. If @value is
|
|
|
|
* %NULL, an uninitialized value is prepended.
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): the #GimpValueArray passed in as @value_array
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_prepend (GimpValueArray *value_array,
|
|
|
|
const GValue *value)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
|
|
|
|
return gimp_value_array_insert (value_array, 0, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_append:
|
|
|
|
* @value_array: #GimpValueArray to add an element to
|
|
|
|
* @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
|
|
|
|
*
|
|
|
|
* Insert a copy of @value as last element of @value_array. If @value is
|
|
|
|
* %NULL, an uninitialized value is appended.
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): the #GimpValueArray passed in as @value_array
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_append (GimpValueArray *value_array,
|
|
|
|
const GValue *value)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
|
|
|
|
return gimp_value_array_insert (value_array, value_array->n_values, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_insert:
|
|
|
|
* @value_array: #GimpValueArray to add an element to
|
2015-06-01 01:44:39 +08:00
|
|
|
* @index: insertion position, must be <= gimp_value_array_length()
|
2012-05-04 05:24:09 +08:00
|
|
|
* @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
|
|
|
|
*
|
|
|
|
* Insert a copy of @value at specified position into @value_array. If @value
|
|
|
|
* is %NULL, an uninitialized value is inserted.
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): the #GimpValueArray passed in as @value_array
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_insert (GimpValueArray *value_array,
|
|
|
|
gint index,
|
|
|
|
const GValue *value)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
g_return_val_if_fail (index <= value_array->n_values, value_array);
|
|
|
|
|
|
|
|
i = value_array->n_values;
|
|
|
|
value_array_grow (value_array, value_array->n_values + 1, FALSE);
|
|
|
|
|
|
|
|
if (index + 1 < value_array->n_values)
|
2014-08-12 21:29:34 +08:00
|
|
|
memmove (value_array->values + index + 1, value_array->values + index,
|
|
|
|
(i - index) * sizeof (value_array->values[0]));
|
2012-05-04 05:24:09 +08:00
|
|
|
|
|
|
|
memset (value_array->values + index, 0, sizeof (value_array->values[0]));
|
|
|
|
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
g_value_init (value_array->values + index, G_VALUE_TYPE (value));
|
|
|
|
g_value_copy (value, value_array->values + index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_value_array_remove:
|
|
|
|
* @value_array: #GimpValueArray to remove an element from
|
2015-05-31 20:40:20 +08:00
|
|
|
* @index: position of value to remove, which must be less than
|
2015-06-01 01:44:39 +08:00
|
|
|
* gimp_value_array_length()
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
2015-05-31 20:40:20 +08:00
|
|
|
* Remove the value at position @index from @value_array.
|
2012-05-04 05:24:09 +08:00
|
|
|
*
|
|
|
|
* Returns: (transfer none): the #GimpValueArray passed in as @value_array
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2012-05-04 05:24:09 +08:00
|
|
|
*/
|
|
|
|
GimpValueArray *
|
|
|
|
gimp_value_array_remove (GimpValueArray *value_array,
|
|
|
|
gint index)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
g_return_val_if_fail (index < value_array->n_values, value_array);
|
|
|
|
|
|
|
|
if (G_VALUE_TYPE (value_array->values + index) != 0)
|
|
|
|
g_value_unset (value_array->values + index);
|
|
|
|
|
|
|
|
value_array->n_values--;
|
|
|
|
|
|
|
|
if (index < value_array->n_values)
|
2014-08-12 21:29:34 +08:00
|
|
|
memmove (value_array->values + index, value_array->values + index + 1,
|
|
|
|
(value_array->n_values - index) * sizeof (value_array->values[0]));
|
2012-05-04 05:24:09 +08:00
|
|
|
|
|
|
|
value_array_shrink (value_array);
|
|
|
|
|
|
|
|
if (value_array->n_prealloced > value_array->n_values)
|
|
|
|
memset (value_array->values + value_array->n_values, 0, sizeof (value_array->values[0]));
|
|
|
|
|
|
|
|
return value_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_value_array_truncate (GimpValueArray *value_array,
|
|
|
|
gint n_values)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
g_return_if_fail (value_array != NULL);
|
|
|
|
g_return_if_fail (n_values > 0 && n_values <= value_array->n_values);
|
|
|
|
|
|
|
|
for (i = value_array->n_values; i > n_values; i--)
|
|
|
|
gimp_value_array_remove (value_array, i - 1);
|
|
|
|
}
|
2012-05-04 06:26:26 +08:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GIMP_TYPE_PARAM_VALUE_ARRAY
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void gimp_param_value_array_class_init (GParamSpecClass *klass);
|
|
|
|
static void gimp_param_value_array_init (GParamSpec *pspec);
|
|
|
|
static void gimp_param_value_array_finalize (GParamSpec *pspec);
|
|
|
|
static void gimp_param_value_array_set_default (GParamSpec *pspec,
|
|
|
|
GValue *value);
|
|
|
|
static gboolean gimp_param_value_array_validate (GParamSpec *pspec,
|
|
|
|
GValue *value);
|
|
|
|
static gint gimp_param_value_array_values_cmp (GParamSpec *pspec,
|
|
|
|
const GValue *value1,
|
|
|
|
const GValue *value2);
|
|
|
|
|
|
|
|
GType
|
|
|
|
gimp_param_value_array_get_type (void)
|
|
|
|
{
|
|
|
|
static GType type = 0;
|
|
|
|
|
|
|
|
if (! type)
|
|
|
|
{
|
|
|
|
const GTypeInfo info =
|
|
|
|
{
|
|
|
|
sizeof (GParamSpecClass),
|
|
|
|
NULL, NULL,
|
|
|
|
(GClassInitFunc) gimp_param_value_array_class_init,
|
|
|
|
NULL, NULL,
|
|
|
|
sizeof (GimpParamSpecValueArray),
|
|
|
|
0,
|
|
|
|
(GInstanceInitFunc) gimp_param_value_array_init
|
|
|
|
};
|
|
|
|
|
|
|
|
type = g_type_register_static (G_TYPE_PARAM_BOXED,
|
|
|
|
"GimpParamValueArray", &info, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_param_value_array_class_init (GParamSpecClass *klass)
|
|
|
|
{
|
|
|
|
klass->value_type = GIMP_TYPE_VALUE_ARRAY;
|
|
|
|
klass->finalize = gimp_param_value_array_finalize;
|
|
|
|
klass->value_set_default = gimp_param_value_array_set_default;
|
|
|
|
klass->value_validate = gimp_param_value_array_validate;
|
|
|
|
klass->values_cmp = gimp_param_value_array_values_cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_param_value_array_init (GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
|
|
|
|
aspec->element_spec = NULL;
|
|
|
|
aspec->fixed_n_elements = 0; /* disable */
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline guint
|
|
|
|
gimp_value_array_ensure_size (GimpValueArray *value_array,
|
|
|
|
guint fixed_n_elements)
|
|
|
|
{
|
|
|
|
guint changed = 0;
|
|
|
|
|
|
|
|
if (fixed_n_elements)
|
|
|
|
{
|
|
|
|
while (gimp_value_array_length (value_array) < fixed_n_elements)
|
|
|
|
{
|
|
|
|
gimp_value_array_append (value_array, NULL);
|
|
|
|
changed++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (gimp_value_array_length (value_array) > fixed_n_elements)
|
|
|
|
{
|
|
|
|
gimp_value_array_remove (value_array,
|
|
|
|
gimp_value_array_length (value_array) - 1);
|
|
|
|
changed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_param_value_array_finalize (GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
|
2019-07-25 18:37:52 +08:00
|
|
|
GParamSpecClass *parent_class;
|
2012-05-04 06:26:26 +08:00
|
|
|
|
2019-07-25 18:37:52 +08:00
|
|
|
parent_class = g_type_class_peek (g_type_parent (GIMP_TYPE_PARAM_VALUE_ARRAY));
|
|
|
|
|
|
|
|
g_clear_pointer (&aspec->element_spec, g_param_spec_unref);
|
2012-05-04 06:26:26 +08:00
|
|
|
|
|
|
|
parent_class->finalize (pspec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_param_value_array_set_default (GParamSpec *pspec,
|
|
|
|
GValue *value)
|
|
|
|
{
|
|
|
|
GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
|
2019-07-25 18:37:52 +08:00
|
|
|
if (! value->data[0].v_pointer && aspec->fixed_n_elements)
|
2012-05-04 06:26:26 +08:00
|
|
|
value->data[0].v_pointer = gimp_value_array_new (aspec->fixed_n_elements);
|
|
|
|
|
|
|
|
if (value->data[0].v_pointer)
|
|
|
|
{
|
|
|
|
/* g_value_reset (value); already done */
|
|
|
|
gimp_value_array_ensure_size (value->data[0].v_pointer,
|
|
|
|
aspec->fixed_n_elements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_param_value_array_validate (GParamSpec *pspec,
|
|
|
|
GValue *value)
|
|
|
|
{
|
2019-07-25 18:37:52 +08:00
|
|
|
GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
GimpValueArray *value_array = value->data[0].v_pointer;
|
|
|
|
guint changed = 0;
|
2012-05-04 06:26:26 +08:00
|
|
|
|
2019-07-25 18:37:52 +08:00
|
|
|
if (! value->data[0].v_pointer && aspec->fixed_n_elements)
|
2012-05-04 06:26:26 +08:00
|
|
|
value->data[0].v_pointer = gimp_value_array_new (aspec->fixed_n_elements);
|
|
|
|
|
|
|
|
if (value->data[0].v_pointer)
|
|
|
|
{
|
|
|
|
/* ensure array size validity */
|
|
|
|
changed += gimp_value_array_ensure_size (value_array,
|
|
|
|
aspec->fixed_n_elements);
|
|
|
|
|
|
|
|
/* ensure array values validity against a present element spec */
|
|
|
|
if (aspec->element_spec)
|
|
|
|
{
|
|
|
|
GParamSpec *element_spec = aspec->element_spec;
|
|
|
|
gint length = gimp_value_array_length (value_array);
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
GValue *element = gimp_value_array_index (value_array, i);
|
|
|
|
|
|
|
|
/* need to fixup value type, or ensure that the array
|
|
|
|
* value is initialized at all
|
|
|
|
*/
|
|
|
|
if (! g_value_type_compatible (G_VALUE_TYPE (element),
|
|
|
|
G_PARAM_SPEC_VALUE_TYPE (element_spec)))
|
|
|
|
{
|
|
|
|
if (G_VALUE_TYPE (element) != 0)
|
|
|
|
g_value_unset (element);
|
|
|
|
|
|
|
|
g_value_init (element, G_PARAM_SPEC_VALUE_TYPE (element_spec));
|
|
|
|
g_param_value_set_default (element_spec, element);
|
|
|
|
changed++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate array value against element_spec */
|
|
|
|
changed += g_param_value_validate (element_spec, element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
gimp_param_value_array_values_cmp (GParamSpec *pspec,
|
|
|
|
const GValue *value1,
|
|
|
|
const GValue *value2)
|
|
|
|
{
|
|
|
|
GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
GimpValueArray *value_array1 = value1->data[0].v_pointer;
|
|
|
|
GimpValueArray *value_array2 = value2->data[0].v_pointer;
|
|
|
|
gint length1;
|
|
|
|
gint length2;
|
|
|
|
|
|
|
|
if (!value_array1 || !value_array2)
|
|
|
|
return value_array2 ? -1 : value_array1 != value_array2;
|
|
|
|
|
|
|
|
length1 = gimp_value_array_length (value_array1);
|
|
|
|
length2 = gimp_value_array_length (value_array2);
|
|
|
|
|
|
|
|
if (length1 != length2)
|
|
|
|
{
|
|
|
|
return length1 < length2 ? -1 : 1;
|
|
|
|
}
|
|
|
|
else if (! aspec->element_spec)
|
|
|
|
{
|
|
|
|
/* we need an element specification for comparisons, so there's
|
|
|
|
* not much to compare here, try to at least provide stable
|
|
|
|
* lesser/greater result
|
|
|
|
*/
|
|
|
|
return length1 < length2 ? -1 : length1 > length2;
|
|
|
|
}
|
|
|
|
else /* length1 == length2 */
|
|
|
|
{
|
2021-05-25 04:36:31 +08:00
|
|
|
gint i;
|
2012-05-04 06:26:26 +08:00
|
|
|
|
|
|
|
for (i = 0; i < length1; i++)
|
|
|
|
{
|
|
|
|
GValue *element1 = gimp_value_array_index (value_array1, i);
|
|
|
|
GValue *element2 = gimp_value_array_index (value_array2, i);
|
|
|
|
gint cmp;
|
|
|
|
|
|
|
|
/* need corresponding element types, provide stable result
|
|
|
|
* otherwise
|
|
|
|
*/
|
|
|
|
if (G_VALUE_TYPE (element1) != G_VALUE_TYPE (element2))
|
|
|
|
return G_VALUE_TYPE (element1) < G_VALUE_TYPE (element2) ? -1 : 1;
|
|
|
|
|
|
|
|
cmp = g_param_values_cmp (aspec->element_spec, element1, element2);
|
|
|
|
if (cmp)
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 05:32:15 +08:00
|
|
|
/**
|
|
|
|
* gimp_param_spec_value_array:
|
|
|
|
* @name: Canonical name of the property specified.
|
|
|
|
* @nick: Nick name of the property specified.
|
|
|
|
* @blurb: Description of the property specified.
|
|
|
|
* @element_spec: (nullable): #GParamSpec the contained array's elements
|
|
|
|
* have comply to, or %NULL.
|
|
|
|
* @flags: Flags for the property specified.
|
|
|
|
*
|
|
|
|
* Creates a new #GimpParamSpecValueArray specifying a
|
2023-07-29 18:36:21 +08:00
|
|
|
* [type@GObject.ValueArray] property.
|
2019-08-08 05:32:15 +08:00
|
|
|
*
|
|
|
|
* See g_param_spec_internal() for details on property names.
|
|
|
|
*
|
|
|
|
* Returns: (transfer full): The newly created #GimpParamSpecValueArray.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
**/
|
2012-05-04 06:26:26 +08:00
|
|
|
GParamSpec *
|
|
|
|
gimp_param_spec_value_array (const gchar *name,
|
|
|
|
const gchar *nick,
|
|
|
|
const gchar *blurb,
|
|
|
|
GParamSpec *element_spec,
|
|
|
|
GParamFlags flags)
|
|
|
|
{
|
|
|
|
GimpParamSpecValueArray *aspec;
|
|
|
|
|
|
|
|
if (element_spec)
|
|
|
|
g_return_val_if_fail (G_IS_PARAM_SPEC (element_spec), NULL);
|
|
|
|
|
|
|
|
aspec = g_param_spec_internal (GIMP_TYPE_PARAM_VALUE_ARRAY,
|
|
|
|
name,
|
|
|
|
nick,
|
|
|
|
blurb,
|
|
|
|
flags);
|
|
|
|
if (element_spec)
|
|
|
|
{
|
|
|
|
aspec->element_spec = g_param_spec_ref (element_spec);
|
|
|
|
g_param_spec_sink (element_spec);
|
|
|
|
}
|
|
|
|
|
|
|
|
return G_PARAM_SPEC (aspec);
|
|
|
|
}
|