763 lines
22 KiB
C
763 lines
22 KiB
C
/* LIBPIKA - The PIKA Library
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
*
|
|
* pikavaluearray.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
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <glib-object.h>
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
#include "pikabasetypes.h"
|
|
|
|
#include "pikavaluearray.h"
|
|
|
|
|
|
/**
|
|
* SECTION:pikavaluearray
|
|
* @short_description: A container structure to maintain an array of
|
|
* generic values
|
|
* @see_also: #GValue, #GParamSpecValueArray, pika_param_spec_value_array()
|
|
* @title: PikaValueArray
|
|
*
|
|
* The prime purpose of a #PikaValueArray is for it to be used as an
|
|
* object property that holds an array of values. A #PikaValueArray wraps
|
|
* an array of #GValue elements in order for it to be used as a boxed
|
|
* type through %PIKA_TYPE_VALUE_ARRAY.
|
|
*/
|
|
|
|
|
|
#define GROUP_N_VALUES (1) /* power of 2 !! */
|
|
|
|
|
|
/**
|
|
* PikaValueArray:
|
|
*
|
|
* A #PikaValueArray contains an array of #GValue elements.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
struct _PikaValueArray
|
|
{
|
|
gint n_values;
|
|
GValue *values;
|
|
|
|
gint n_prealloced;
|
|
gint ref_count;
|
|
};
|
|
|
|
|
|
G_DEFINE_BOXED_TYPE (PikaValueArray, pika_value_array,
|
|
pika_value_array_ref, pika_value_array_unref)
|
|
|
|
|
|
/**
|
|
* pika_value_array_index:
|
|
* @value_array: #PikaValueArray to get a value from
|
|
* @index: index of the value of interest
|
|
*
|
|
* Return a pointer to the value at @index contained in @value_array.
|
|
*
|
|
* Returns: (transfer none): pointer to a value at @index in @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GValue *
|
|
pika_value_array_index (const PikaValueArray *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;
|
|
}
|
|
|
|
static inline void
|
|
value_array_grow (PikaValueArray *value_array,
|
|
gint n_values,
|
|
gboolean zero_init)
|
|
{
|
|
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)
|
|
i = value_array->n_values;
|
|
|
|
memset (value_array->values + i, 0,
|
|
(value_array->n_prealloced - i) * sizeof (value_array->values[0]));
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
value_array_shrink (PikaValueArray *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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_new:
|
|
* @n_prealloced: number of values to preallocate space for
|
|
*
|
|
* Allocate and initialize a new #PikaValueArray, optionally preserve space
|
|
* for @n_prealloced elements. New arrays always contain 0 elements,
|
|
* regardless of the value of @n_prealloced.
|
|
*
|
|
* Returns: a newly allocated #PikaValueArray with 0 values
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_new (gint n_prealloced)
|
|
{
|
|
PikaValueArray *value_array = g_slice_new0 (PikaValueArray);
|
|
|
|
value_array_grow (value_array, n_prealloced, TRUE);
|
|
value_array->n_values = 0;
|
|
value_array->ref_count = 1;
|
|
|
|
return value_array;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_new_from_types: (skip)
|
|
* @error_msg: return location for an error message.
|
|
* @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 #PikaValueArray, and fill it with
|
|
* values that are given as a list of (#GType, value) pairs,
|
|
* terminated by #G_TYPE_NONE.
|
|
*
|
|
* Returns: (nullable): a newly allocated #PikaValueArray, or %NULL if
|
|
* an error happened.
|
|
*
|
|
* Since: 3.0
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_new_from_types (gchar **error_msg,
|
|
GType first_type,
|
|
...)
|
|
{
|
|
PikaValueArray *value_array;
|
|
va_list va_args;
|
|
|
|
g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL);
|
|
|
|
va_start (va_args, first_type);
|
|
|
|
value_array = pika_value_array_new_from_types_valist (error_msg,
|
|
first_type,
|
|
va_args);
|
|
|
|
va_end (va_args);
|
|
|
|
return value_array;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_new_from_types_valist: (skip)
|
|
* @error_msg: return location for an error message.
|
|
* @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 #PikaValueArray, and fill it with
|
|
* @va_args given in the order as passed to
|
|
* pika_value_array_new_from_types().
|
|
*
|
|
* Returns: (nullable): a newly allocated #PikaValueArray, or %NULL if
|
|
* an error happened.
|
|
*
|
|
* Since: 3.0
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_new_from_types_valist (gchar **error_msg,
|
|
GType first_type,
|
|
va_list va_args)
|
|
{
|
|
PikaValueArray *value_array;
|
|
GType type;
|
|
|
|
g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL);
|
|
|
|
type = first_type;
|
|
|
|
value_array = pika_value_array_new (type == G_TYPE_NONE ? 0 : 1);
|
|
|
|
while (type != G_TYPE_NONE)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
gchar *my_error = NULL;
|
|
|
|
g_value_init (&value, type);
|
|
|
|
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);
|
|
}
|
|
|
|
pika_value_array_unref (value_array);
|
|
|
|
va_end (va_args);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pika_value_array_append (value_array, &value);
|
|
g_value_unset (&value);
|
|
|
|
type = va_arg (va_args, GType);
|
|
}
|
|
|
|
va_end (va_args);
|
|
|
|
return value_array;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_copy:
|
|
* @value_array: #PikaValueArray to copy
|
|
*
|
|
* Return an exact copy of a #PikaValueArray by duplicating all its values.
|
|
*
|
|
* Returns: a newly allocated #PikaValueArray.
|
|
*
|
|
* Since: 3.0
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_copy (const PikaValueArray *value_array)
|
|
{
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
return pika_value_array_new_from_values (value_array->values,
|
|
value_array->n_values);
|
|
}
|
|
|
|
/**
|
|
* pika_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 #PikaValueArray, and fill it with
|
|
* the given #GValues. When no #GValues are given, returns empty #PikaValueArray.
|
|
*
|
|
* Returns: a newly allocated #PikaValueArray.
|
|
*
|
|
* Since: 3.0
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_new_from_values (const GValue *values,
|
|
gint n_values)
|
|
{
|
|
PikaValueArray *value_array;
|
|
gint i;
|
|
|
|
/* 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);
|
|
|
|
value_array = pika_value_array_new (n_values);
|
|
|
|
for (i = 0; i < n_values; i++)
|
|
{
|
|
pika_value_array_insert (value_array, i, &values[i]);
|
|
}
|
|
|
|
return value_array;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_ref:
|
|
* @value_array: #PikaValueArray to ref
|
|
*
|
|
* Adds a reference to a #PikaValueArray.
|
|
*
|
|
* Returns: the same @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_ref (PikaValueArray *value_array)
|
|
{
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
value_array->ref_count++;
|
|
|
|
return value_array;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_unref:
|
|
* @value_array: #PikaValueArray to unref
|
|
*
|
|
* Unref a #PikaValueArray. If the reference count drops to zero, the
|
|
* array including its contents are freed.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
pika_value_array_unref (PikaValueArray *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);
|
|
}
|
|
|
|
g_free (value_array->values);
|
|
g_slice_free (PikaValueArray, value_array);
|
|
}
|
|
}
|
|
|
|
gint
|
|
pika_value_array_length (const PikaValueArray *value_array)
|
|
{
|
|
g_return_val_if_fail (value_array != NULL, 0);
|
|
|
|
return value_array->n_values;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_prepend:
|
|
* @value_array: #PikaValueArray to add an element to
|
|
* @value: (allow-none): #GValue to copy into #PikaValueArray, 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 #PikaValueArray passed in as @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_prepend (PikaValueArray *value_array,
|
|
const GValue *value)
|
|
{
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
return pika_value_array_insert (value_array, 0, value);
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_append:
|
|
* @value_array: #PikaValueArray to add an element to
|
|
* @value: (allow-none): #GValue to copy into #PikaValueArray, 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 #PikaValueArray passed in as @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_append (PikaValueArray *value_array,
|
|
const GValue *value)
|
|
{
|
|
g_return_val_if_fail (value_array != NULL, NULL);
|
|
|
|
return pika_value_array_insert (value_array, value_array->n_values, value);
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_insert:
|
|
* @value_array: #PikaValueArray to add an element to
|
|
* @index: insertion position, must be <= pika_value_array_length()
|
|
* @value: (allow-none): #GValue to copy into #PikaValueArray, 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 #PikaValueArray passed in as @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_insert (PikaValueArray *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)
|
|
memmove (value_array->values + index + 1, value_array->values + index,
|
|
(i - index) * sizeof (value_array->values[0]));
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* pika_value_array_remove:
|
|
* @value_array: #PikaValueArray to remove an element from
|
|
* @index: position of value to remove, which must be less than
|
|
* pika_value_array_length()
|
|
*
|
|
* Remove the value at position @index from @value_array.
|
|
*
|
|
* Returns: (transfer none): the #PikaValueArray passed in as @value_array
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
PikaValueArray *
|
|
pika_value_array_remove (PikaValueArray *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)
|
|
memmove (value_array->values + index, value_array->values + index + 1,
|
|
(value_array->n_values - index) * sizeof (value_array->values[0]));
|
|
|
|
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
|
|
pika_value_array_truncate (PikaValueArray *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--)
|
|
pika_value_array_remove (value_array, i - 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* PIKA_TYPE_PARAM_VALUE_ARRAY
|
|
*/
|
|
|
|
static void pika_param_value_array_class_init (GParamSpecClass *klass);
|
|
static void pika_param_value_array_init (GParamSpec *pspec);
|
|
static void pika_param_value_array_finalize (GParamSpec *pspec);
|
|
static void pika_param_value_array_set_default (GParamSpec *pspec,
|
|
GValue *value);
|
|
static gboolean pika_param_value_array_validate (GParamSpec *pspec,
|
|
GValue *value);
|
|
static gint pika_param_value_array_values_cmp (GParamSpec *pspec,
|
|
const GValue *value1,
|
|
const GValue *value2);
|
|
|
|
GType
|
|
pika_param_value_array_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (! type)
|
|
{
|
|
const GTypeInfo info =
|
|
{
|
|
sizeof (GParamSpecClass),
|
|
NULL, NULL,
|
|
(GClassInitFunc) pika_param_value_array_class_init,
|
|
NULL, NULL,
|
|
sizeof (PikaParamSpecValueArray),
|
|
0,
|
|
(GInstanceInitFunc) pika_param_value_array_init
|
|
};
|
|
|
|
type = g_type_register_static (G_TYPE_PARAM_BOXED,
|
|
"PikaParamValueArray", &info, 0);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
|
|
static void
|
|
pika_param_value_array_class_init (GParamSpecClass *klass)
|
|
{
|
|
klass->value_type = PIKA_TYPE_VALUE_ARRAY;
|
|
klass->finalize = pika_param_value_array_finalize;
|
|
klass->value_set_default = pika_param_value_array_set_default;
|
|
klass->value_validate = pika_param_value_array_validate;
|
|
klass->values_cmp = pika_param_value_array_values_cmp;
|
|
}
|
|
|
|
static void
|
|
pika_param_value_array_init (GParamSpec *pspec)
|
|
{
|
|
PikaParamSpecValueArray *aspec = PIKA_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
aspec->element_spec = NULL;
|
|
aspec->fixed_n_elements = 0; /* disable */
|
|
}
|
|
|
|
static inline guint
|
|
pika_value_array_ensure_size (PikaValueArray *value_array,
|
|
guint fixed_n_elements)
|
|
{
|
|
guint changed = 0;
|
|
|
|
if (fixed_n_elements)
|
|
{
|
|
while (pika_value_array_length (value_array) < fixed_n_elements)
|
|
{
|
|
pika_value_array_append (value_array, NULL);
|
|
changed++;
|
|
}
|
|
|
|
while (pika_value_array_length (value_array) > fixed_n_elements)
|
|
{
|
|
pika_value_array_remove (value_array,
|
|
pika_value_array_length (value_array) - 1);
|
|
changed++;
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static void
|
|
pika_param_value_array_finalize (GParamSpec *pspec)
|
|
{
|
|
PikaParamSpecValueArray *aspec = PIKA_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
GParamSpecClass *parent_class;
|
|
|
|
parent_class = g_type_class_peek (g_type_parent (PIKA_TYPE_PARAM_VALUE_ARRAY));
|
|
|
|
g_clear_pointer (&aspec->element_spec, g_param_spec_unref);
|
|
|
|
parent_class->finalize (pspec);
|
|
}
|
|
|
|
static void
|
|
pika_param_value_array_set_default (GParamSpec *pspec,
|
|
GValue *value)
|
|
{
|
|
PikaParamSpecValueArray *aspec = PIKA_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
|
|
if (! value->data[0].v_pointer && aspec->fixed_n_elements)
|
|
value->data[0].v_pointer = pika_value_array_new (aspec->fixed_n_elements);
|
|
|
|
if (value->data[0].v_pointer)
|
|
{
|
|
/* g_value_reset (value); already done */
|
|
pika_value_array_ensure_size (value->data[0].v_pointer,
|
|
aspec->fixed_n_elements);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
pika_param_value_array_validate (GParamSpec *pspec,
|
|
GValue *value)
|
|
{
|
|
PikaParamSpecValueArray *aspec = PIKA_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
PikaValueArray *value_array = value->data[0].v_pointer;
|
|
guint changed = 0;
|
|
|
|
if (! value->data[0].v_pointer && aspec->fixed_n_elements)
|
|
value->data[0].v_pointer = pika_value_array_new (aspec->fixed_n_elements);
|
|
|
|
if (value->data[0].v_pointer)
|
|
{
|
|
/* ensure array size validity */
|
|
changed += pika_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 = pika_value_array_length (value_array);
|
|
gint i;
|
|
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
GValue *element = pika_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
|
|
pika_param_value_array_values_cmp (GParamSpec *pspec,
|
|
const GValue *value1,
|
|
const GValue *value2)
|
|
{
|
|
PikaParamSpecValueArray *aspec = PIKA_PARAM_SPEC_VALUE_ARRAY (pspec);
|
|
PikaValueArray *value_array1 = value1->data[0].v_pointer;
|
|
PikaValueArray *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 = pika_value_array_length (value_array1);
|
|
length2 = pika_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 */
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; i < length1; i++)
|
|
{
|
|
GValue *element1 = pika_value_array_index (value_array1, i);
|
|
GValue *element2 = pika_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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pika_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 #PikaParamSpecValueArray specifying a
|
|
* [type@GObject.ValueArray] property.
|
|
*
|
|
* See g_param_spec_internal() for details on property names.
|
|
*
|
|
* Returns: (transfer full): The newly created #PikaParamSpecValueArray.
|
|
*
|
|
* Since: 3.0
|
|
**/
|
|
GParamSpec *
|
|
pika_param_spec_value_array (const gchar *name,
|
|
const gchar *nick,
|
|
const gchar *blurb,
|
|
GParamSpec *element_spec,
|
|
GParamFlags flags)
|
|
{
|
|
PikaParamSpecValueArray *aspec;
|
|
|
|
if (element_spec)
|
|
g_return_val_if_fail (G_IS_PARAM_SPEC (element_spec), NULL);
|
|
|
|
aspec = g_param_spec_internal (PIKA_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);
|
|
}
|