diff --git a/docs/reference/gobject/gobject-docs.xml b/docs/reference/gobject/gobject-docs.xml index 201d63bca..244e5f93c 100644 --- a/docs/reference/gobject/gobject-docs.xml +++ b/docs/reference/gobject/gobject-docs.xml @@ -83,6 +83,7 @@ + diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index 5d142dd44..36f079653 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -944,3 +944,84 @@ G_IS_BINDING g_binding_flags_get_type g_binding_get_type + +
+gproperty +GProperty +GPropertyFlags +GPropertyCollectFlags + +g_property_is_writable +g_property_is_redable +g_property_is_deprecated +g_property_is_atomic +g_property_is_copy_set +g_property_is_copy_get +g_property_describe +g_property_set_range_values +g_property_get_range_values +g_property_set_range +g_property_get_range +g_property_set_default_value +g_property_set_default +g_property_override_default_value +g_property_override_default +g_property_get_default_value_for_type +g_property_get_default_value +g_property_get_default +g_property_set_prerequisite +g_property_validate_value +g_property_validate +g_property_set_value +g_property_set_va +g_property_set +g_property_get_value +g_property_get_va +g_property_get +GPropertyLockFunc +GPropertyUnlockFunc +g_property_set_lock_functions +g_property_lock +g_property_unlock +g_property_canonicalize_name + +g_boolean_property_new +g_int_property_new +g_int8_property_new +g_int16_property_new +g_int32_property_new +g_int64_property_new +g_long_property_new +g_char_property_new +g_uint_property_new +g_uint8_property_new +g_uint16_property_new +g_uint32_property_new +g_uint64_property_new +g_ulong_property_new +g_uchar_property_new +g_unichar_property_new +g_float_property_new +g_double_property_new +g_enum_property_new +g_flags_property_new +g_string_property_new +g_boxed_property_new +g_object_property_new +g_pointer_property_new + +G_DECLARE_PROPERTY_GET +G_DECLARE_PROPERTY_SET +G_DECLARE_PROPERTY_GET_SET +G_DEFINE_PROPERTY_GET_WITH_CODE +G_DEFINE_PROPERTY_GET +G_DEFINE_PROPERTY_SET_WITH_CODE +G_DEFINE_PROPERTY_SET +G_DEFINE_PROPERTY_GET_SET + +G_PROPERTY +G_IS_PROPERTY +G_TYPE_PROPERTY + +g_property_get_type +
diff --git a/glib/glib-object.h b/glib/glib-object.h index 336ba931d..d760c88ea 100644 --- a/glib/glib-object.h +++ b/glib/glib-object.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/gobject/Makefile.am b/gobject/Makefile.am index 94fd777b2..937e910e1 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -97,6 +97,7 @@ gobject_public_h_sources = \ gobject.h \ gparam.h \ gparamspecs.h \ + gproperty.h \ gsignal.h \ gsourceclosure.h \ gtype.h \ @@ -126,6 +127,7 @@ gobject_c_sources = \ gobject_trace.h \ gparam.c \ gparamspecs.c \ + gproperty.c \ gsignal.c \ gsourceclosure.c \ gtype.c \ diff --git a/gobject/gobject.c b/gobject/gobject.c index 5f60dd6da..3f35b93d5 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -545,9 +545,9 @@ g_object_class_install_property (GObjectClass *class, class->flags |= CLASS_HAS_PROPS_FLAG; g_return_if_fail (pspec->flags & (G_PARAM_READABLE | G_PARAM_WRITABLE)); - if (pspec->flags & G_PARAM_WRITABLE) + if (pspec->flags & G_PARAM_WRITABLE && !G_IS_PROPERTY (pspec)) g_return_if_fail (class->set_property != NULL); - if (pspec->flags & G_PARAM_READABLE) + if (pspec->flags & G_PARAM_READABLE && !G_IS_PROPERTY (pspec)) g_return_if_fail (class->get_property != NULL); g_return_if_fail (property_id > 0); g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ @@ -558,6 +558,9 @@ g_object_class_install_property (GObjectClass *class, install_property_internal (G_OBJECT_CLASS_TYPE (class), property_id, pspec); + if (G_IS_PROPERTY (pspec)) + _g_property_set_installed (G_PROPERTY (pspec), class, G_OBJECT_CLASS_TYPE (class)); + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) class->construct_properties = g_slist_prepend (class->construct_properties, pspec); @@ -662,9 +665,9 @@ g_object_class_install_properties (GObjectClass *oclass, g_return_if_fail (pspec != NULL); - if (pspec->flags & G_PARAM_WRITABLE) + if (!G_IS_PROPERTY (pspec) && pspec->flags & G_PARAM_WRITABLE) g_return_if_fail (oclass->set_property != NULL); - if (pspec->flags & G_PARAM_READABLE) + if (!G_IS_PROPERTY (pspec) && pspec->flags & G_PARAM_READABLE) g_return_if_fail (oclass->get_property != NULL); g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ if (pspec->flags & G_PARAM_CONSTRUCT) @@ -675,6 +678,9 @@ g_object_class_install_properties (GObjectClass *oclass, oclass->flags |= CLASS_HAS_PROPS_FLAG; install_property_internal (oclass_type, i, pspec); + if (G_IS_PROPERTY (pspec)) + _g_property_set_installed (G_PROPERTY (pspec), oclass, oclass_type); + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) oclass->construct_properties = g_slist_prepend (oclass->construct_properties, pspec); @@ -906,6 +912,129 @@ g_object_class_list_properties (GObjectClass *class, return pspecs; } +static inline void +override_property_default_value (GObjectClass *oclass, + GProperty *property, + const GValue *value) +{ + g_property_override_default_value (property, G_OBJECT_CLASS_TYPE (oclass), value); +} + +/** + * g_object_class_override_property_default_value: + * @oclass: a #GObjectClass + * @property_name: the name of the property to override + * @value: a valid #GValue containing the new default value + * + * Overrides the default value of @property_name for @oclass with + * a new value. + * + * See g_object_class_override_property_default() for more information. + * + * This function is meant for language bindings. + * + * Since: 2.36 + */ +void +g_object_class_override_property_default_value (GObjectClass *oclass, + const gchar *property_name, + const GValue *default_value) +{ + GParamSpec *pspec; + + pspec = g_object_class_find_property (oclass, property_name); + if (pspec == NULL) + { + g_critical ("The class '%s' does not have a property named '%s'", + g_type_name (G_OBJECT_CLASS_TYPE (oclass)), + property_name); + return; + } + + if (!G_IS_PROPERTY (pspec)) + { + g_critical ("The property '%s' of class '%s' is of type '%s' and " + "not a GProperty. Overriding the default is allowed " + "only on properties defined using the GProperty API.", + G_PARAM_SPEC (pspec)->name, + g_type_name (G_OBJECT_CLASS_TYPE (oclass)), + g_type_name (G_PARAM_SPEC_TYPE (pspec))); + return; + } + + override_property_default_value (oclass, G_PROPERTY (pspec), default_value); +} + +/** + * g_object_class_override_property_default: + * @oclass: a #GObjectClass + * @property_name: the name of the property to override + * @...: the new default value of the property + * + * Sets the default value of @property_name for the given #GObject + * class. + * + * This function can only be used with properties installed using + * the #GProperty API. + * + * This function is the equivalent of: + * + * |[ + * GParamSpec *pspec = g_object_class_find_property (oclass, property_name); + * + * g_property_set_default (G_PROPERTY (pspec), oclass, new_value); + * ]| + * + * Since: 2.36 + */ +void +g_object_class_override_property_default (GObjectClass *oclass, + const gchar *property_name, + ...) +{ + GParamSpec *pspec; + GValue value = { 0, }; + va_list args; + gchar *error = NULL; + + pspec = g_object_class_find_property (oclass, property_name); + if (pspec == NULL) + { + g_critical ("The class '%s' does not have a property named '%s'", + g_type_name (G_OBJECT_CLASS_TYPE (oclass)), + property_name); + return; + } + + if (!G_IS_PROPERTY (pspec)) + { + g_critical ("The property '%s' of class '%s' is of type '%s' and " + "not a GProperty. Overriding the default is allowed " + "only on properties defined using the GProperty API.", + G_PARAM_SPEC (pspec)->name, + g_type_name (G_OBJECT_CLASS_TYPE (oclass)), + g_type_name (G_PARAM_SPEC_TYPE (pspec))); + return; + } + + va_start (args, property_name); + + G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), args, 0, &error); + if (error != NULL) + { + g_critical (G_STRLOC ": %s", error); + g_free (error); + va_end (args); + return; + } + + override_property_default_value (oclass, G_PROPERTY (pspec), &value); + + g_value_unset (&value); + + va_end (args); +} + /** * g_object_interface_list_properties: * @g_iface: any interface vtable for the interface, or the default @@ -1290,8 +1419,15 @@ object_get_property (GObject *object, redirect = g_param_spec_get_redirect_target (pspec); if (redirect) pspec = redirect; - - class->get_property (object, param_id, value, pspec); + + if (G_IS_PROPERTY (pspec)) + { + GProperty *prop = G_PROPERTY (pspec); + + g_property_get_value (prop, object, value); + } + else + class->get_property (object, param_id, value, pspec); } static inline void @@ -1352,14 +1488,21 @@ object_set_property (GObject *object, } else { - GParamSpec *notify_pspec; + if (G_IS_PROPERTY (pspec)) + { + g_property_set_value ((GProperty *) pspec, object, &tmp_value); + } + else + { + GParamSpec *notify_pspec; - class->set_property (object, param_id, &tmp_value, pspec); + class->set_property (object, param_id, &tmp_value, pspec); - notify_pspec = get_notify_pspec (pspec); + notify_pspec = get_notify_pspec (pspec); - if (notify_pspec != NULL) - g_object_notify_queue_add (object, nqueue, notify_pspec); + if (notify_pspec != NULL) + g_object_notify_queue_add (object, nqueue, notify_pspec); + } } g_value_unset (&tmp_value); } @@ -1883,35 +2026,22 @@ g_object_constructed (GObject *object) /* empty default impl to allow unconditional upchaining */ } -/** - * g_object_set_valist: (skip) - * @object: a #GObject - * @first_property_name: name of the first property to set - * @var_args: value for the first property, followed optionally by more - * name/value pairs, followed by %NULL - * - * Sets properties on an object. - */ -void -g_object_set_valist (GObject *object, - const gchar *first_property_name, - va_list var_args) +static inline void +object_set_valist_internal (GObject *object, + const gchar *first_property_name, + va_list *args) { GObjectNotifyQueue *nqueue; const gchar *name; - - g_return_if_fail (G_IS_OBJECT (object)); - + g_object_ref (object); nqueue = g_object_notify_queue_freeze (object, FALSE); - + name = first_property_name; while (name) { - GValue value = G_VALUE_INIT; GParamSpec *pspec; - gchar *error = NULL; - + pspec = g_param_spec_pool_lookup (pspec_pool, name, G_OBJECT_TYPE (object), @@ -1939,26 +2069,136 @@ g_object_set_valist (GObject *object, break; } - G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args, - 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRFUNC, error); - g_free (error); + if (G_IS_PROPERTY (pspec)) + { + GProperty *property = (GProperty *) pspec; + + g_property_set_va (property, object, 0, args); + } + else + { + GValue value = G_VALUE_INIT; + gchar *error = NULL; + + G_VALUE_COLLECT_INIT (&value, pspec->value_type, *args, 0, &error); + if (error != NULL) + { + g_warning ("%s: %s", G_STRFUNC, error); + g_free (error); + g_value_unset (&value); + break; + } + + object_set_property (object, pspec, &value, nqueue); g_value_unset (&value); - break; - } - - object_set_property (object, pspec, &value, nqueue); - g_value_unset (&value); - - name = va_arg (var_args, gchar*); + } + + name = va_arg (*args, gchar*); } g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); } +/** + * g_object_set_valist: (skip) + * @object: a #GObject + * @first_property_name: name of the first property to set + * @var_args: value for the first property, followed optionally by more + * name/value pairs, followed by %NULL + * + * Sets properties on an object. + */ +void +g_object_set_valist (GObject *object, + const gchar *first_property_name, + va_list var_args) +{ + va_list va_copy; + + g_return_if_fail (G_IS_OBJECT (object)); + + G_VA_COPY (va_copy, var_args); + object_set_valist_internal (object, first_property_name, &va_copy); + va_end (va_copy); +} + +static inline void +object_get_valist_internal (GObject *object, + const gchar *first_property_name, + va_list *args) +{ + const gchar *name; + + g_object_ref (object); + + name = first_property_name; + + while (name) + { + GParamSpec *pspec; + + pspec = g_param_spec_pool_lookup (pspec_pool, + name, + G_OBJECT_TYPE (object), + TRUE); + if (!pspec) + { + g_warning ("%s: object class `%s' has no property named `%s'", + G_STRFUNC, + G_OBJECT_TYPE_NAME (object), + name); + break; + } + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("%s: property `%s' of object class `%s' is not readable", + G_STRFUNC, + pspec->name, + G_OBJECT_TYPE_NAME (object)); + break; + } + + if (G_IS_PROPERTY (pspec)) + { + GProperty *property = (GProperty *) pspec; + GPropertyFlags flags = G_PROPERTY_COLLECT_REF + | G_PROPERTY_COLLECT_COPY; + + /* we use G_PROPERTY_COLLECT_COPY and G_PROPERTY_COLLECT_REF to + * preserve the semantics of GValues holding a typed pointer + * value, like GObject, GBoxed and strings + */ + if (!g_property_get_va (property, object, flags, args)) + break; + } + else + { + GValue value = G_VALUE_INIT; + gchar *error; + + g_value_init (&value, pspec->value_type); + + object_get_property (object, pspec, &value); + + G_VALUE_LCOPY (&value, *args, 0, &error); + if (error != NULL) + { + g_warning ("%s: %s", G_STRFUNC, error); + g_free (error); + g_value_unset (&value); + break; + } + + g_value_unset (&value); + } + + name = va_arg (*args, gchar*); + } + + g_object_unref (object); +} + /** * g_object_get_valist: (skip) * @object: a #GObject @@ -1979,60 +2219,13 @@ g_object_get_valist (GObject *object, const gchar *first_property_name, va_list var_args) { - const gchar *name; - + va_list va_copy; + g_return_if_fail (G_IS_OBJECT (object)); - - g_object_ref (object); - - name = first_property_name; - - while (name) - { - GValue value = G_VALUE_INIT; - GParamSpec *pspec; - gchar *error; - - pspec = g_param_spec_pool_lookup (pspec_pool, - name, - G_OBJECT_TYPE (object), - TRUE); - if (!pspec) - { - g_warning ("%s: object class `%s' has no property named `%s'", - G_STRFUNC, - G_OBJECT_TYPE_NAME (object), - name); - break; - } - if (!(pspec->flags & G_PARAM_READABLE)) - { - g_warning ("%s: property `%s' of object class `%s' is not readable", - G_STRFUNC, - pspec->name, - G_OBJECT_TYPE_NAME (object)); - break; - } - - g_value_init (&value, pspec->value_type); - - object_get_property (object, pspec, &value); - - G_VALUE_LCOPY (&value, var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRFUNC, error); - g_free (error); - g_value_unset (&value); - break; - } - - g_value_unset (&value); - - name = va_arg (var_args, gchar*); - } - - g_object_unref (object); + + G_VA_COPY (va_copy, var_args); + object_get_valist_internal (object, first_property_name, &va_copy); + va_end (va_copy); } /** @@ -2055,7 +2248,7 @@ g_object_set (gpointer _object, g_return_if_fail (G_IS_OBJECT (object)); va_start (var_args, first_property_name); - g_object_set_valist (object, first_property_name, var_args); + object_set_valist_internal (object, first_property_name, &var_args); va_end (var_args); } @@ -2106,7 +2299,7 @@ g_object_get (gpointer _object, g_return_if_fail (G_IS_OBJECT (object)); va_start (var_args, first_property_name); - g_object_get_valist (object, first_property_name, var_args); + object_get_valist_internal (object, first_property_name, &var_args); va_end (var_args); } diff --git a/gobject/gobject.h b/gobject/gobject.h index f63e4d164..4da6cca63 100644 --- a/gobject/gobject.h +++ b/gobject/gobject.h @@ -402,6 +402,15 @@ GParamSpec* g_object_interface_find_property (gpointer g_iface, GParamSpec**g_object_interface_list_properties (gpointer g_iface, guint *n_properties_p); +GLIB_AVAILABLE_IN_2_36 +void g_object_class_override_property_default (GObjectClass *oclass, + const gchar *property_name, + ...); +GLIB_AVAILABLE_IN_2_36 +void g_object_class_override_property_default_value (GObjectClass *oclass, + const gchar *property_name, + const GValue *default_value); + GType g_object_get_type (void) G_GNUC_CONST; gpointer g_object_new (GType object_type, const gchar *first_property_name, diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols index e5c53cc2e..f13351b61 100644 --- a/gobject/gobject.symbols +++ b/gobject/gobject.symbols @@ -222,8 +222,8 @@ g_param_spec_steal_qdata g_param_spec_set_qdata g_param_spec_set_qdata_full g_param_spec_get_qdata -g_param_spec_set_static_nick g_param_spec_set_static_blurb +g_param_spec_set_static_nick g_param_value_convert g_param_value_defaults g_param_values_cmp @@ -240,6 +240,58 @@ g_value_set_param g_value_dup_param g_value_take_param g_value_set_param_take_ownership +g_boolean_property_new +g_boxed_property_new +g_double_property_new +g_flags_property_new +g_float_property_new +g_enum_property_new +g_int16_property_new +g_int32_property_new +g_int64_property_new +g_int8_property_new +g_int_property_new +g_long_property_new +g_object_property_new +g_pointer_property_new +g_property_canonicalize_name +g_property_describe +g_property_get +g_property_get_default +g_property_get_default_value +g_property_get_default_value_for_type +g_property_get_range +g_property_get_range_values +g_property_get_type +g_property_get_va +g_property_get_value +g_property_get_value_type +g_property_is_atomic +g_property_is_copy_get +g_property_is_copy_set +g_property_is_deprecated +g_property_is_readable +g_property_is_writable +g_property_lock +g_property_set +g_property_set_default +g_property_set_default_value +g_property_set_lock_functions +g_property_set_prerequisite +g_property_set_range +g_property_set_range_values +g_property_set_va +g_property_set_value +g_property_unlock +g_property_validate +g_property_validate_value +g_string_property_new +g_uint16_property_new +g_uint32_property_new +g_uint64_property_new +g_uint8_property_new +g_uint_property_new +g_ulong_property_new g_pointer_type_register_static g_strdup_value_contents g_value_set_boolean diff --git a/gobject/gproperty.c b/gobject/gproperty.c new file mode 100644 index 000000000..55c69c0ca --- /dev/null +++ b/gobject/gproperty.c @@ -0,0 +1,5604 @@ +/* gproperty.c: Property definitions for GObject + * + * Copyright © 2012 Emmanuele Bassi + * + * 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 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gproperty + * @Title: GProperty + * @Short_Desc: Property definitions for GObject + * + * #GProperty is a type of #GParamSpec for defining properties for #GObject. + * + * The main difference between #GProperty and #GParamSpec is that #GProperty + * enforces a specific set of best practices for accessing values exposed + * as #GObject properties. + * + * #GProperty uses direct access to the fields in the instance private data + * structure whenever possible, and allows specifying accessor functions in + * cases where it is necessary. For direct access to the private structure + * members, #GProperty will assume that the instance structure contains a + * pointer to the private data structure as its last member, e.g.: + * + * |[ + * typedef struct _TestObject TestObject; + * typedef struct _TestObjectPrivate TestObjectPrivate; + * + * struct _TestObject + * { + * GObject parent_instance; + * + * TestObjectPrivate *priv; + * }; + * ]| + * + * #GProperty is strongly typed, both at compilation time and at run time. + * + * Unlike #GParamSpec, there is a single public #GProperty class with various + * constructors; all public #GProperty methods operate depending on the type + * handled by the #GProperty. + * + * The #GParamSpec methods can be used with #GProperty transparently, to + * allow backward compatibility with existing code that introspects #GObject + * properties through the #GParamSpec API. + * + * + * Using GProperty + * A typical example of #GProperty usage is: + * + * |[ + * /* the private data structure */ + * struct _TestObjectPrivate + * { + * int x; + * int y; + * int width; + * int height; + * }; + * + * /* identifiers for each property in the array */ + * enum { PROP_0, PROP_X, PROP_Y, PROP_WIDTH, PROP_HEIGHT, LAST_PROP }; + * + * /* an array of properties */ + * static GParamSpec *test_object_properties[LAST_PROP] = { 0, }; + * + * static void + * test_object_class_init (TestObjectClass *klass) + * { + * g_type_class_add_private (klass, sizeof (TestObjectPrivate)); + * + * test_object_properties[PROP_X] = + * g_int_property_new ("x", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, x), + * NULL, + * NULL); + * + * test_object_properties[PROP_Y] = + * g_int_property_new ("y", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, y), + * NULL, + * NULL); + * + * test_object_properties[PROP_WIDTH] = + * g_int_property_new ("width", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, width), + * NULL, + * NULL); + * + * test_object_properties[PROP_HEIGHT] = + * g_int_property_new ("height", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, height), + * NULL, + * NULL); + * + * g_object_class_install_properties (G_OBJECT_CLASS (klass), + * G_N_ELEMENTS (test_object_properties), + * test_object_properties); + * } + * ]| + * The main differences with the #GParamSpec creation and installation + * code are: + * + * + * the constructors take the same parameters + * there are not #GObject set_property and get_property + * virtual function assignments + * all properties use direct access of the member in the + * instance private data structure + * + * + * + * Setting and getting values + * Writing accessors for properties defined using #GProperties is + * a simple case of calling g_property_set() or g_property_get(), for + * instance the code below is the simplest form of setter and getter + * pair for the "x" property as defined above: + * |[ + * void + * test_object_set_x (TestObject *self, + * int x) + * { + * g_return_if_fail (TEST_IS_OBJECT (self)); + * + * g_property_set (G_PROPERTY (test_object_properties[PROP_X]), self, x); + * } + * + * int + * test_object_get_x (TestObject *self) + * { + * int retval; + * + * g_return_val_if_fail (TEST_IS_OBJECT (self), 0); + * + * g_property_get (G_PROPERTY (test_object_properties[PROP_X]), + * self, + * &retval); + * + * return retval; + * } + * ]| + * Note that calling g_property_set() for a property holding a + * complex type (e.g. #GObject or #GBoxed) without a specific setter + * function will, by default, result in the pointer to the new value + * being copied in the private data structure's field; if you need to + * copy a boxed type, or take a reference on an object type, you will + * need to set the %G_PROPERTY_COPY_SET flag when creating the + * property. + * + * Calling g_property_get() will return a pointer to the private + * data structure's field, unless %G_PROPERTY_COPY_GET is set when + * creating the property, in which case the returned value will either + * be a copy of the private data structure field if it is a boxed type + * or the instance with its reference count increased if it is an object + * type. + * + * + * + * Ranges and validation + * For different property types it is possible to set a range of + * valid values; the setter function can then use g_property_validate() + * to check whether the new value is valid. + * + * The range is set using g_property_set_range(): + * + * |[ + * test_object_properties[PROP_WIDTH] = + * g_int_property_new ("width", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, width), + * NULL, NULL); + * g_property_set_range (G_PROPERTY (test_object_properties[PROP_WIDTH]), + * 0.0, /* minimum value */ + * G_MAXINT /* maximum value */) + * + * test_object_properties[PROP_HEIGHT] = + * g_int_property_new ("height", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, height), + * NULL, NULL); + * g_property_set_range (G_PROPERTY (test_object_properties[PROP_HEIGHT]), + * 0.0, /* minimum value */ + * G_MAXINT /* maximum value */) + * ]| + * + * The example above keeps the "width" and "height" properties as + * integers, but limits the range of valid values to [ 0, %G_MAXINT ]. + * + * Validation is automatically performed when setting a property + * through g_property_set(); explicit setter methods can use g_property_validate() + * to perform the validation, e.g.: + * + * |[ + * void + * test_object_set_width (TestObject *self, + * int width) + * { + * GProperty *property; + * + * property = G_PROPERTY (test_object_properties[PROP_WIDTH]); + * + * g_return_if_fail (!g_property_validate (property, width)); + * + * /* we deliberately do not use g_property_set() here because + * * g_property_set() will implicitly call g_property_validate() + * * prior to setting the property + * */ + * if (self->priv->width == width) + * return; + * + * self->priv->width = width; + * + * g_object_notify_by_pspec (G_OBJECT (self), G_PARAM_SPEC (property)); + * + * test_object_queue_foo (self); + * } + * ]| + * + * + * + * Default values + * Default values are declared using g_property_set_default(), e.g.: + * |[ + * test_object_properties[PROP_WIDTH] = + * g_int_property_new ("width", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, width), + * NULL, NULL); + * g_property_set_range (G_PROPERTY (test_object_properties[PROP_WIDTH]), + * 0, /* minimum value */ + * G_MAXINT /* maximum value */) + * g_property_set_default (G_PROPERTY (test_object_properties[PROP_WIDTH]), + * G_OBJECT_CLASS (klass), + * 1); + * + * test_object_properties[PROP_HEIGHT] = + * g_int_property_new ("height", G_PROPERTY_READWRITE, + * G_STRUCT_OFFSET (TestObjectPrivate, height), + * NULL, NULL); + * g_property_set_range (G_PROPERTY (test_object_properties[PROP_HEIGHT]), + * 0, /* minimum value */ + * G_MAXINT /* maximum value */) + * g_property_set_default (G_PROPERTY (test_object_properties[PROP_HEIGHT]), + * G_OBJECT_CLASS (klass), + * 1); + * ]| + * + * Sub-classes can use g_property_override_default() on an existing property + * of their parent classes to override the default value: + * + * |[ + * property = g_object_class_find_property (G_OBJECT_CLASS (klass), "width"); + * g_property_override_default (G_PROPERTY (property), + * G_OBJECT_CLASS (klass), + * 200); + * ]| + * + * The default value can be retrieved using g_property_get_default(), + * for instance during instance initialization: + * + * |[ + * static void + * test_object_init (TestObject *self) + * { + * self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TEST_TYPE_OBJECT, + * TestObjectPrivate); + * + * g_property_get_default (G_PROPERTY (test_object_properties[PROP_WIDTH]), + * self, + * &(self->priv->width)); + * g_property_get_default (G_PROPERTY (test_object_properties[PROP_HEIGHT]), + * self, + * &(self->priv->height)); + * } + * ]| + * + * or to return the default value when writing a getter: + * + * |[ + * int + * test_object_get_width (TestObject *self) + * { + * GProperty *property; + * int retval; + * + * property = G_PROPERTY (test_object_properties[PROP_WIDTH]); + * + * /* return the default width if self has not been foobarized */ + * if (!self->priv->is_foobarized) + * g_property_get_default (property, self, &retval); + * else + * g_property_get (property, self, &retval); + * + * return retval; + * } + * ]| + * + * The reason why the object instance is necessary when calling + * g_property_get_default() is to retrieve the correct default for the + * given instance type, in case sub-classes overridden the default value + * using g_property_override_default(). + * + * + * + * Custom accessors + * For cases where the direct access to a structure field does not + * match the semantics of the property it is possible to pass a setter + * and a getter function when creating a #GProperty: + * + * |[ + * test_object_property[PROP_COMPLEX] = + * g_object_property_new ("complex", G_PROPERTY_READWRITE, -1, + * test_object_set_complex, + * test_object_get_complex); + * g_property_set_prerequisite (G_PROPERTY (test_object_property[PROP_COMPLEX]), + * TEST_TYPE_COMPLEX); + * ]| + * + * The accessors can be public or private functions. The implementation + * of an explicit setter will be called under the #GProperty lock if the + * property is declared using the %G_PROPERTY_ATOMIC flag; the setter should + * not notify the property on changes, and should return %TRUE if the value + * was modified. An example of a setter is: + * + * |[ + * static gboolean + * test_object_set_complex (gpointer self_, + * gpointer value_) + * { + * TestObject *self = self_; + * TestComplex *value = value_; + * + * if (self->priv->complex == value) + * return FALSE; + * + * if (self->priv->complex != NULL) + * { + * test_complex_set_back_pointer (self->priv->complex, NULL); + * g_object_unref (self->priv->complex); + * } + * + * self->priv->complex = value; + * + * if (self->priv->complex != NULL) + * { + * g_object_ref (self->priv->complex); + * test_complex_set_back_pointer (self->priv->complex, self); + * } + * + * test_object_queue_foo (self); + * + * return TRUE; + * } + * ]| + * + * It is also possible to still pass the offset of the structure + * field, and provide either the setter or the getter function: + * + * |[ + * test_object_property[PROP_WIDTH] = + * g_int_property_new ("width", G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + * G_STRUCT_OFFSET (TestObjectPrivate, width), + * test_object_set_width, /* explicit setter */ + * NULL /* implicit getter */); + * ]| + * + * Calling g_property_set() using the "width" property in the example + * above will result in calling test_object_set_width(); calling, instead, + * g_property_get() using the "width" property will result in accessing + * the width structure member. + * + * You must not call g_property_set() inside the implementation + * of test_object_set_width(), in order to avoid loops. + * + * + * + * Special flags + * #GProperty has its own set of flags to be passed to its + * constructor functions. Alongside the usual %G_PROPERTY_READABLE + * and %G_PROPERTY_WRITABLE, which control the access policy for + * setter and getter functions, there are the following flags: + * + * + * %G_PROPERTY_DEPRECATED, a flag for marking + * deprecated properties. If the G_ENABLE_DIAGNOSTIC environment + * variable is set, calling g_property_set() and g_property_get() + * on the deprecated properties will result in a run time warning + * printed on the console. + * %G_PROPERTY_ATOMIC, a flag for marking access + * to properties a threadsafe operation. It is possible to change + * the locking mechanism by using g_property_set_lock_functions() + * and replacing both the lock and the unlock function, which can + * then be called by g_property_lock() and g_property_unlock() + * when needed. + * + * + * + * + * + * + * Auto-generating setters and getters + * Similarly to the #G_DEFINE_TYPE macro for defining a new + * #GObject type, GObject also provides various macros for + * auto-generating the accessors pair of functions. + * The most simple way to define a setter and getter pair is + * to use the #G_DEFINE_PROPERTY_GET_SET macro. This macro generates + * a getter and a setter using the type name, the type of the + * property and the name of the property, for instance: + * + * |[ + * G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, int, x) + * G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, int, y) + * G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, int, width) + * G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, int, height) + * ]| + * + * The generated accessor pairs will use the #GProperty API + * to validate and access the property value. It is also possible + * to separate the setter and getter generation by using the + * #G_DEFINE_PROPERTY_GET and #G_DEFINE_PROPERTY_SET macros. + * + * If either the setter or the getter functions require additional + * custom code to be added to the auto-generated one, it is possible to + * use the #G_DEFINE_PROPERTY_GET_WITH_CODE macro and its sibling, the + * #G_DEFINE_PROPERTY_SET_WITH_CODE macro, for instance: + * + * |[ + * G_DEFINE_PROPERTY_GET (TestObject, test_object, int, width) + * G_DEFINE_PROPERTY_SET_WITH_CODE (Test_object, test_object, int, width, + * test_object_queue_action (self)) + * ]| + * + * The WITH_CODE variant of the setter will define the "self" and + * "value" variables for the instance and the new value respectively; the + * custom code will be executed only after the value has been set. + * For the WITH_CODE variant of the getter, the macro will define the + * "self" and "retval" variables for the instance and the returned value + * respectively; the custom code will be executed before retrieving the + * value of the property to be returned. + * + * + * + * Base object class using GProperty + * The example below shows how to use #GProperty and the code + * generation macros to implement a simple base class called + * "TestFile". + * + * + * FIXME: MISSING XINCLUDE CONTENT + * + * + * + * + * + * Derived object class using GProperty + * The example below shows how to use #GProperty and the code + * generation macros to implement a simple base class called + * "TestFile" and a derived class called "TestFileMp3". + * + * + * FIXME: MISSING XINCLUDE CONTENT + * + * + * + */ + +#include "config.h" + +#include + +#include "gproperty.h" + +#include "gvaluecollector.h" +#include "gparam.h" +#include "gtype.h" +#include "gvalue.h" +#include "gvaluetypes.h" + +struct _GProperty +{ + GParamSpec parent_instance; + + guint flags : 15; + guint is_installed : 1; + + guint16 type_size; + guint16 priv_offset; + guint16 field_offset; + + GQuark prop_id; + + GPropertyLockFunc lock_func; + GPropertyUnlockFunc unlock_func; +}; + +/* defines for the integer sub-types we don't have */ +#define g_value_get_int8 g_value_get_int +#define g_value_get_int16 g_value_get_int +#define g_value_get_int32 g_value_get_int + +#define g_value_get_uint8 g_value_get_uint +#define g_value_get_uint16 g_value_get_uint +#define g_value_get_uint32 g_value_get_uint + +#define DEFINE_PROPERTY_INTEGER(G_t, g_t, c_t, G_T, defVal, minVal, maxVal) \ +typedef struct { \ + GProperty parent; \ +\ + c_t min_value; \ + c_t max_value; \ +\ + GProperty##G_t##Set setter; \ + GProperty##G_t##Get getter; \ +} G##G_t##Property; \ +\ +static gint \ +property_##g_t##_values_cmp (GParamSpec *pspec, \ + const GValue *value_a, \ + const GValue *value_b) \ +{ \ + c_t val_a = g_value_get_##g_t (value_a); \ + c_t val_b = g_value_get_##g_t (value_b); \ +\ + if (val_a < val_b) \ + return -1; \ +\ + if (val_a > val_b) \ + return 1; \ +\ + return 0; \ +} \ +\ +static gboolean \ +property_##g_t##_validate (GParamSpec *pspec, \ + GValue *value) \ +{ \ + G##G_t##Property *internal = (G##G_t##Property *) pspec; \ + c_t oval = g_value_get_##g_t (value); \ + c_t nval = oval; \ +\ + nval = CLAMP (nval, internal->min_value, internal->max_value); \ +\ + return nval != oval; \ +} \ +\ +static void \ +property_##g_t##_class_init (GParamSpecClass *klass) \ +{ \ + klass->value_type = G_T; \ +\ + klass->value_validate = property_##g_t##_validate; \ + klass->values_cmp = property_##g_t##_values_cmp; \ +} \ +\ +static void \ +property_##g_t##_init (GParamSpec *pspec) \ +{ \ + G##G_t##Property *property = (G##G_t##Property *) pspec; \ +\ + property->min_value = minVal; \ + property->max_value = maxVal; \ +} \ +\ +GType _g_##g_t##_property_get_type (void); /* forward declaration for -Wmissing-prototypes */ \ +\ +GType \ +_g_##g_t##_property_get_type (void) \ +{ \ + static volatile gsize pspec_type_id__volatile = 0; \ +\ + if (g_once_init_enter (&pspec_type_id__volatile)) \ + { \ + const GTypeInfo info = { \ + sizeof (GParamSpecClass), \ + NULL, NULL, \ + (GClassInitFunc) property_##g_t##_class_init, \ + NULL, NULL, \ + sizeof (G##G_t##Property), \ + 0, \ + (GInstanceInitFunc) property_##g_t##_init, \ + }; \ +\ + GType pspec_type_id = \ + g_type_register_static (G_TYPE_PROPERTY, \ + g_intern_static_string ("G" #G_t "Property"), \ + &info, 0); \ +\ + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); \ + } \ +\ + return pspec_type_id__volatile; \ +} \ +\ +GParamSpec * \ +g_##g_t##_property_new (const gchar *name, \ + GPropertyFlags flags, \ + gssize offset, \ + GProperty##G_t##Set setter, \ + GProperty##G_t##Get getter) \ +{ \ + GProperty *prop; \ + G##G_t##Property *internal; \ +\ + g_return_val_if_fail (name != NULL, NULL); \ +\ + if (setter == NULL && getter == NULL) \ + g_return_val_if_fail (offset >= 0, NULL); \ +\ + prop = g_param_spec_internal (_g_##g_t##_property_get_type (), \ + name, NULL, NULL, \ + property_flags_to_param_flags (flags)); \ +\ + prop->flags = flags; \ +\ + G_PARAM_SPEC (prop)->value_type = G_T; \ +\ + prop->field_offset = offset; \ +\ + prop->is_installed = FALSE; \ +\ + prop->type_size = sizeof (c_t); \ +\ + internal = (G##G_t##Property *) prop; \ + internal->setter = setter; \ + internal->getter = getter; \ +\ + return G_PARAM_SPEC (prop); \ +} \ +\ +static inline void \ +g_##g_t##_property_set_range (GProperty *property, \ + c_t min_value, \ + c_t max_value) \ +{ \ + if (min_value > max_value) \ + { \ + g_critical (G_STRLOC ": Invalid range for " #g_t " property '%s'", \ + G_PARAM_SPEC (property)->name); \ + return; \ + } \ +\ + ((G##G_t##Property *) property)->min_value = min_value; \ + ((G##G_t##Property *) property)->max_value = max_value; \ +} \ +\ +static inline void \ +g_##g_t##_property_get_range (GProperty *property, \ + c_t *min_value, \ + c_t *max_value) \ +{ \ + *min_value = ((G##G_t##Property *) property)->min_value; \ + *max_value = ((G##G_t##Property *) property)->max_value; \ +} \ +\ +static inline gboolean \ +g_##g_t##_property_validate (GProperty *property, \ + c_t value) \ +{ \ + G##G_t##Property *internal = (G##G_t##Property *) property; \ +\ + if (value >= internal->min_value && \ + value <= internal->max_value) \ + return TRUE; \ +\ + return FALSE; \ +} \ +\ +static inline gboolean \ +g_##g_t##_property_set_value (GProperty *property, \ + gpointer gobject, \ + c_t value) \ +{ \ + gboolean retval; \ +\ + if ((property->flags & G_PROPERTY_WRITABLE) == 0) \ + { \ + g_critical ("The property '%s' of object '%s' is not writable", \ + G_PARAM_SPEC (property)->name, \ + G_OBJECT_TYPE_NAME (gobject)); \ + return FALSE; \ + } \ +\ + if (!g_##g_t##_property_validate (property, value)) \ + { \ + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", \ + G_PARAM_SPEC (property)->name, \ + G_OBJECT_TYPE_NAME (gobject)); \ + return FALSE; \ + } \ +\ + if (((G##G_t##Property *) property)->setter != NULL) \ + { \ + property_lock_internal (property, gobject); \ +\ + retval = ((G##G_t##Property *) property)->setter (gobject, value); \ +\ + property_unlock_internal (property, gobject); \ +\ + if (retval) \ + g_object_notify_by_pspec (gobject, (GParamSpec *) property); \ + } \ + else if (property->field_offset >= 0) \ + { \ + gpointer priv_p, field_p; \ +\ + property_lock_internal (property, gobject); \ +\ + priv_p = get_private_pointer (gobject, property->priv_offset); \ + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); \ +\ + if ((* (c_t *) field_p) == value) \ + { \ + property_unlock_internal (property, gobject); \ + return FALSE; \ + } \ +\ + (* (c_t *) field_p) = value; \ +\ + property_unlock_internal (property, gobject); \ +\ + g_object_notify_by_pspec (gobject, (GParamSpec *) property); \ +\ + retval = TRUE; \ + } \ + else \ + { \ + g_critical (G_STRLOC ": No setter function or field offset specified " \ + "for property '%s'", \ + G_PARAM_SPEC (property)->name); \ +\ + retval = FALSE; \ + } \ +\ + return retval; \ +} \ +\ +static inline c_t \ +g_##g_t##_property_get_value (GProperty *property, \ + gpointer gobject) \ +{ \ + if ((property->flags & G_PROPERTY_READABLE) == 0) \ + { \ + g_critical ("The property '%s' of object '%s' is not readable", \ + G_PARAM_SPEC (property)->name, \ + G_OBJECT_TYPE_NAME (gobject)); \ + return FALSE; \ + } \ +\ + if (((G##G_t##Property *) property)->getter != NULL) \ + { \ + return ((G##G_t##Property *) property)->getter (gobject); \ + } \ + else if (property->field_offset >= 0) \ + { \ + gpointer priv_p, field_p; \ +\ + priv_p = get_private_pointer (gobject, property->priv_offset); \ + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); \ +\ + return (* (c_t *) field_p); \ + } \ + else \ + { \ + g_critical (G_STRLOC ": No setter function or field offset specified " \ + "for property '%s'", \ + G_PARAM_SPEC (property)->name); \ + return defVal; \ + } \ +} + +static void +g_property_default_lock (GProperty *property, + gpointer gobject) +{ + gpointer bit_lock_p; + + bit_lock_p = g_object_get_qdata (gobject, property->prop_id); + if (bit_lock_p == NULL) + { + bit_lock_p = g_new0 (gint, 1); + g_object_set_qdata_full (gobject, property->prop_id, bit_lock_p, g_free); + } + + g_bit_lock (bit_lock_p, 0); +} + +static void +g_property_default_unlock (GProperty *property, + gpointer gobject) +{ + gpointer bit_lock_p; + + bit_lock_p = g_object_get_qdata (gobject, property->prop_id); + if (bit_lock_p == NULL) + return; + + g_bit_unlock (bit_lock_p, 0); + g_object_set_qdata (gobject, property->prop_id, NULL); +} + +static inline void +property_lock_internal (GProperty *property, + gpointer gobject) +{ + if (G_LIKELY ((property->flags & G_PROPERTY_ATOMIC) == 0)) + return; + + if (property->lock_func != NULL) + property->lock_func (property, gobject); + else + g_property_default_lock (property, gobject); +} + +static inline void +property_unlock_internal (GProperty *property, + gpointer gobject) +{ + if (G_LIKELY ((property->flags & G_PROPERTY_ATOMIC) == 0)) + return; + + if (property->unlock_func != NULL) + property->unlock_func (property, gobject); + else + g_property_default_unlock (property, gobject); +} + +static inline gpointer +get_private_pointer (gpointer instance, + gssize offset) +{ + gpointer priv_p; + + if (offset < 0) + priv_p = g_type_instance_get_private (instance, G_OBJECT_TYPE (instance)); + else + priv_p = G_STRUCT_MEMBER_P (instance, offset); + + return priv_p; +} + +static GParamFlags +property_flags_to_param_flags (GPropertyFlags flags) +{ + GParamFlags retval = 0; + + if (flags & G_PROPERTY_READABLE) + retval |= G_PARAM_READABLE; + + if (flags & G_PROPERTY_WRITABLE) + retval |= G_PARAM_WRITABLE; + + if (flags & G_PROPERTY_DEPRECATED) + retval |= G_PARAM_DEPRECATED; + + return retval; +} + +/* forward declaration */ +static void property_set_default (GParamSpec *pspec, + GValue *value); + +static const GValue * +property_get_default_for_type (GProperty *property, + GType gtype) +{ + GParamSpec *pspec = (GParamSpec *) property; + + if (gtype == G_TYPE_INVALID) + { + if (G_UNLIKELY (property->prop_id == 0)) + { + gchar *lock_name = g_strconcat ("__g_property_id_", pspec->name, NULL); + + property->prop_id = g_quark_from_string (lock_name); + g_free (lock_name); + } + + return g_param_spec_get_qdata (pspec, property->prop_id); + } + else + return g_param_spec_get_qdata (pspec, g_type_qname (gtype)); +} + +static void +value_unset_and_free (gpointer data) +{ + GValue *value = data; + + g_value_unset (value); + g_free (value); +} + +static inline void +property_set_default_for_type (GProperty *property, + GType gtype, + GValue *value) +{ + GParamSpec *pspec = (GParamSpec *) property; + + if (gtype == G_TYPE_INVALID) + { + if (G_UNLIKELY (property->prop_id == 0)) + { + gchar *lock_name = g_strconcat ("__g_property_id_", pspec->name, NULL); + + property->prop_id = g_quark_from_string (lock_name); + g_free (lock_name); + } + + if (g_param_spec_get_qdata (pspec, property->prop_id) != NULL) + { + g_critical (G_STRLOC ": The property '%s' already has a default " + "value. Use g_property_override_default() instead.", + pspec->name); + return; + } + + g_param_spec_set_qdata_full (pspec, property->prop_id, + value, + value_unset_and_free); + } + else + g_param_spec_set_qdata_full (pspec, g_type_qname (gtype), + value, + value_unset_and_free); +} + +/** + * g_boolean_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a boolean value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Boolean, boolean, gboolean, G_TYPE_BOOLEAN, FALSE, FALSE, TRUE) + +/** + * g_int_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an integer value. + * + * The default range of valid values is [ %G_MININT, %G_MAXINT ]. + * + * If you require a specific integer size, use g_int8_property_new(), + * g_int16_property_new(), g_int32_property_new() or g_int64_property_new(). + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Int, int, int, G_TYPE_INT, 0, G_MININT, G_MAXINT) + +/** + * g_int8_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an 8 bits integer value. + * + * The default range of valid values is [ %G_MININT8, %G_MAXINT8 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Int8, int8, gint8, G_TYPE_INT, 0, G_MININT8, G_MAXINT8) + +/** + * g_int16_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to a 16 bits integer value. + * + * The default range of valid values is [ %G_MININT16, %G_MAXINT16 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Int16, int16, gint16, G_TYPE_INT, 0, G_MININT16, G_MAXINT16) + +/** + * g_int32_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to a 32 bits integer value. + * + * The default range of valid values is [ %G_MININT32, %G_MAXINT32 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Int32, int32, gint32, G_TYPE_INT, 0, G_MININT32, G_MAXINT32) + +/** + * g_int64_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to a 64 bits integer value. + * + * The default range of valid values is [ %G_MININT64, %G_MAXINT64 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Int64, int64, gint64, G_TYPE_INT64, 0, G_MININT64, G_MAXINT64) + +/** + * g_long_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to a long integer value. + * + * The default range of valid values is [ %G_MINLONG, %G_MAXLONG ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (Long, long, long, G_TYPE_LONG, 0, G_MINLONG, G_MAXLONG) + +/** + * g_uint_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned integer value. + * + * The default range of valid values is [ 0, %G_MAXUINT ]. + * + * If you require a specific integer size, use g_uint8_property_new(), + * g_uint16_property_new(), g_uint32_property_new() or g_uint64_property_new(). + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (UInt, uint, guint, G_TYPE_UINT, 0, 0, G_MAXUINT) + +/** + * g_uint8_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned 8 bits integer value. + * + * The default range of valid values is [ 0, %G_MAXUINT8 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (UInt8, uint8, guint8, G_TYPE_UINT, 0, 0, G_MAXUINT8) + +/** + * g_uint16_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned 16 bits integer value. + * + * The default range of valid values is [ 0, %G_MAXUINT16 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (UInt16, uint16, guint16, G_TYPE_UINT, 0, 0, G_MAXUINT16) + +/** + * g_uint32_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned 32 bits integer value. + * + * The default range of valid values is [ 0, %G_MAXUINT32 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (UInt32, uint32, guint32, G_TYPE_UINT, 0, 0, G_MAXUINT32) + +/** + * g_uint64_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned 64 bits integer value. + * + * The default range of valid values is [ 0, %G_MAXUINT64 ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (UInt64, uint64, guint64, G_TYPE_UINT64, 0, 0, G_MAXUINT64) + +/** + * g_ulong_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property, or %NULL + * @getter: (allow-none): the getter function for the property, or %NULL + * + * Creates a new #GProperty mapping to an unsigned long integer value. + * + * The default range of valid values is [ 0, %G_MAXULONG ]. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +DEFINE_PROPERTY_INTEGER (ULong, ulong, gulong, G_TYPE_ULONG, 0, 0, G_MAXULONG) + +/* + * GEnum + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_enum_property_get_type (void); + +typedef struct { + GProperty parent; + + GEnumClass *e_class; + + GPropertyEnumSet setter; + GPropertyEnumGet getter; +} GEnumProperty; + +static gboolean +property_enum_validate (GParamSpec *pspec, + GValue *value) +{ + GEnumProperty *property = (GEnumProperty *) pspec; + glong oval = value->data[0].v_long; + + if (property->e_class == NULL || + g_enum_get_value (property->e_class, value->data[0].v_long) == NULL) + property_set_default (pspec, value); + + return value->data[0].v_long != oval; +} + +static void +property_enum_finalize (GParamSpec *pspec) +{ + GEnumProperty *property = (GEnumProperty *) pspec; + GParamSpecClass *parent_class = + g_type_class_peek (g_type_parent (_g_enum_property_get_type ())); + + if (property->e_class) + { + g_type_class_unref (property->e_class); + property->e_class = NULL; + } + + parent_class->finalize (pspec); +} + +static void +property_enum_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_FLAGS; + + klass->value_validate = property_enum_validate; + + klass->finalize = property_enum_finalize; +} + +static void +property_enum_init (GParamSpec *pspec) +{ +} + +GType +_g_enum_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_enum_class_init, + NULL, NULL, + sizeof (GEnumProperty), + 0, + (GInstanceInitFunc) property_enum_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GEnumProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_enum_property_new: + * @name: canonical name of the property + * @enum: enum for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a enumeration type registered + * as a sub-type of %G_TYPE_ENUM. + * + * You should use g_property_set_prerequisite() to set the type + * of the enumeration for validation; if the pre-requisite is unset, + * setting or getting this property will result in a warning. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_enum_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyEnumSet setter, + GPropertyEnumGet getter) +{ + GProperty *prop; + GEnumProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_enum_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_ENUM; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (glong); + + internal = (GEnumProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_enum_property_validate (GProperty *property, + glong value) +{ + GEnumProperty *e_prop = (GEnumProperty *) property; + + if (e_prop->e_class != NULL) + { + if (g_enum_get_value (e_prop->e_class, value) != NULL) + return TRUE; + } + + return FALSE; +} + +static inline gboolean +g_enum_property_set_value (GProperty *property, + gpointer gobject, + glong value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_enum_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GEnumProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GEnumProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gulong *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + (* (gulong *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gulong +g_enum_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GEnumProperty *) property)->getter != NULL) + { + return ((GEnumProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + return (* (gulong *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return 0.0; + } +} + +/* + * GFlags + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_flags_property_get_type (void); + +typedef struct { + GProperty parent; + + GFlagsClass *f_class; + + GPropertyFlagsSet setter; + GPropertyFlagsGet getter; +} GFlagsProperty; + +static gboolean +property_flags_validate (GParamSpec *pspec, + GValue *value) +{ + GFlagsProperty *property = (GFlagsProperty *) pspec; + gulong oval = value->data[0].v_ulong; + + if (property->f_class != NULL) + value->data[0].v_ulong &= property->f_class->mask; + else + property_set_default (pspec, value); + + return value->data[0].v_ulong != oval; +} + +static void +property_flags_finalize (GParamSpec *pspec) +{ + GFlagsProperty *property = (GFlagsProperty *) pspec; + GParamSpecClass *parent_class = + g_type_class_peek (g_type_parent (_g_flags_property_get_type ())); + + if (property->f_class) + { + g_type_class_unref (property->f_class); + property->f_class = NULL; + } + + parent_class->finalize (pspec); +} + +static void +property_flags_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_FLAGS; + + klass->value_validate = property_flags_validate; + + klass->finalize = property_flags_finalize; +} + +static void +property_flags_init (GParamSpec *pspec) +{ +} + +GType +_g_flags_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_flags_class_init, + NULL, NULL, + sizeof (GFlagsProperty), + 0, + (GInstanceInitFunc) property_flags_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GFlagsProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_flags_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a flag type registered + * as a sub-type of %G_TYPE_FLAGS. + * + * You should use g_property_set_prerequisite() to set the type + * of the flags for validation; if the pre-requisite is unset, + * setting or getting this property will result in a warning. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_flags_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyFlagsSet setter, + GPropertyFlagsGet getter) +{ + GProperty *prop; + GFlagsProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_flags_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_FLAGS; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gulong); + + internal = (GFlagsProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_flags_property_validate (GProperty *property, + gulong value) +{ + GFlagsProperty *f_prop = (GFlagsProperty *) property; + + if (f_prop->f_class != NULL) + { + gulong masked_value = value; + + masked_value &= f_prop->f_class->mask; + + return masked_value == value; + } + + return FALSE; +} + +static inline gboolean +g_flags_property_set_value (GProperty *property, + gpointer gobject, + gulong value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_flags_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GFlagsProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GFlagsProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gulong *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + (* (gulong *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gulong +g_flags_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GFlagsProperty *) property)->getter != NULL) + { + return ((GFlagsProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + return (* (gulong *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return 0.0; + } +} + +/* + * GFloat + */ + +#define G_FLOAT_EPSILON (1e-30) + +/* forward declaration for -Wmissing-prototypes */ +GType _g_float_property_get_type (void); + +typedef struct { + GProperty parent; + + gfloat min_value; + gfloat max_value; + gfloat epsilon; + + GPropertyFloatSet setter; + GPropertyFloatGet getter; +} GFloatProperty; + +static gboolean +property_float_validate (GParamSpec *pspec, + GValue *value) +{ + GFloatProperty *property = (GFloatProperty *) pspec; + gfloat oval = value->data[0].v_float; + + value->data[0].v_float = CLAMP (value->data[0].v_float, + property->min_value, + property->max_value); + + return value->data[0].v_float != oval; +} + +static gint +property_float_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + gfloat epsilon = ((GFloatProperty *) pspec)->epsilon; + + if (value1->data[0].v_float < value2->data[0].v_float) + return - (value2->data[0].v_float - value1->data[0].v_float > epsilon); + else + return value1->data[0].v_float - value2->data[0].v_float > epsilon; +} + +static void +property_float_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_FLOAT; + + klass->value_validate = property_float_validate; + klass->values_cmp = property_float_values_cmp; +} + +static void +property_float_init (GParamSpec *pspec) +{ + GFloatProperty *property = (GFloatProperty *) pspec; + + property->min_value = -G_MAXFLOAT; + property->max_value = G_MAXFLOAT; + property->epsilon = G_FLOAT_EPSILON; +} + +GType +_g_float_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_float_class_init, + NULL, NULL, + sizeof (GFloatProperty), + 0, + (GInstanceInitFunc) property_float_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GFloatProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_float_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a single precision floating + * point value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_float_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyFloatSet setter, + GPropertyFloatGet getter) +{ + GProperty *prop; + GFloatProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_float_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_FLOAT; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gfloat); + + internal = (GFloatProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline void +g_float_property_set_range (GProperty *property, + gfloat min_value, + gfloat max_value) +{ + if (min_value > max_value) + { + g_critical (G_STRLOC ": Invalid range for the property '%s'", + G_PARAM_SPEC (property)->name); + return; + } + + ((GFloatProperty *) property)->min_value = min_value; + ((GFloatProperty *) property)->max_value = max_value; +} + +static inline void +g_float_property_get_range (GProperty *property, + gfloat *min_value, + gfloat *max_value) +{ + *min_value = ((GFloatProperty *) property)->min_value; + *max_value = ((GFloatProperty *) property)->max_value; +} + +static inline gboolean +g_float_property_validate (GProperty *property, + gfloat value) +{ + GFloatProperty *internal = (GFloatProperty *) property; + + if (value >= internal->min_value && + value <= internal->max_value) + return TRUE; + + return FALSE; +} + +static inline gboolean +g_float_property_set_value (GProperty *property, + gpointer gobject, + gfloat value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_float_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GFloatProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GFloatProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gfloat *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + (* (gfloat *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gfloat +g_float_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GFloatProperty *) property)->getter != NULL) + { + return ((GFloatProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + return (* (gfloat *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return 0.0; + } +} + +/* + * GDouble + */ + +#define G_DOUBLE_EPSILON (1e-90) + +/* forward declaration for -Wmissing-prototypes */ +GType _g_double_property_get_type (void); + +typedef struct { + GProperty parent; + + gdouble min_value; + gdouble max_value; + gdouble epsilon; + + GPropertyDoubleSet setter; + GPropertyDoubleGet getter; +} GDoubleProperty; + +static gboolean +property_double_validate (GParamSpec *pspec, + GValue *value) +{ + GDoubleProperty *property = (GDoubleProperty *) pspec; + gdouble oval = value->data[0].v_double; + + value->data[0].v_double = CLAMP (value->data[0].v_double, + property->min_value, + property->max_value); + + return value->data[0].v_double != oval; +} + +static gint +property_double_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + gdouble epsilon = ((GDoubleProperty *) pspec)->epsilon; + + if (value1->data[0].v_double < value2->data[0].v_double) + return - (value2->data[0].v_double - value1->data[0].v_double > epsilon); + else + return value1->data[0].v_double - value2->data[0].v_double > epsilon; +} + +static void +property_double_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_DOUBLE; + + klass->value_validate = property_double_validate; + klass->values_cmp = property_double_values_cmp; +} + +static void +property_double_init (GParamSpec *pspec) +{ + GDoubleProperty *property = (GDoubleProperty *) pspec; + + property->min_value = -G_MAXDOUBLE; + property->max_value = G_MAXDOUBLE; + property->epsilon = G_DOUBLE_EPSILON; +} + +GType +_g_double_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_double_class_init, + NULL, NULL, + sizeof (GDoubleProperty), + 0, + (GInstanceInitFunc) property_double_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GDoubleProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_double_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a double precision floating + * point value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_double_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyDoubleSet setter, + GPropertyDoubleGet getter) +{ + GProperty *prop; + GDoubleProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_double_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_DOUBLE; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gdouble); + + internal = (GDoubleProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline void +g_double_property_set_range (GProperty *property, + gdouble min_value, + gdouble max_value) +{ + if (min_value > max_value) + { + g_critical (G_STRLOC ": Invalid range for property '%s'", + G_PARAM_SPEC (property)->name); + return; + } + + ((GDoubleProperty *) property)->min_value = min_value; + ((GDoubleProperty *) property)->max_value = max_value; +} + +static inline void +g_double_property_get_range (GProperty *property, + gdouble *min_value, + gdouble *max_value) +{ + *min_value = ((GDoubleProperty *) property)->min_value; + *max_value = ((GDoubleProperty *) property)->max_value; +} + +static inline gboolean +g_double_property_validate (GProperty *property, + gdouble value) +{ + GDoubleProperty *internal = (GDoubleProperty *) property; + + if (value >= internal->min_value && + value <= internal->max_value) + return TRUE; + + return FALSE; +} + +static inline gboolean +g_double_property_set_value (GProperty *property, + gpointer gobject, + gdouble value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_double_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GDoubleProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GDoubleProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gdouble *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + (* (gdouble *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gdouble +g_double_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GDoubleProperty *) property)->getter != NULL) + { + return ((GDoubleProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + return (* (gdouble *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return 0.0; + } +} + +/* + * GString + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_string_property_get_type (void); + +typedef struct { + GProperty parent; + + GPropertyStringSet setter; + GPropertyStringGet getter; +} GStringProperty; + +static void +property_string_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_STRING; +} + +static void +property_string_init (GParamSpec *pspec) +{ +} + +GType +_g_string_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_string_class_init, + NULL, NULL, + sizeof (GStringProperty), + 0, + (GInstanceInitFunc) property_string_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GStringProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_string_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a string value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_string_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyStringSet setter, + GPropertyStringGet getter) +{ + GProperty *prop; + GStringProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_string_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_STRING; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gchar*); + + internal = (GStringProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_string_property_validate (GProperty *property, + const gchar *value) +{ + return TRUE; +} + +static inline gboolean +g_string_property_set_value (GProperty *property, + gpointer gobject, + const gchar *value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_string_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GStringProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GStringProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + gchar *str; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + str = (* (gpointer *) field_p); + + if (g_strcmp0 (str, value) == 0) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + if (property->flags & G_PROPERTY_COPY_SET) + { + g_free (str); + (* (gpointer *) field_p) = g_strdup (value); + } + else + (* (gpointer *) field_p) = (gpointer) value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline const gchar * +g_string_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GStringProperty *) property)->getter != NULL) + { + return ((GStringProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + gchar *retval; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if (property->flags & G_PROPERTY_COPY_GET) + retval = g_strdup ((* (gpointer *) field_p)); + else + retval = (* (gpointer *) field_p); + + return retval; + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return NULL; + } +} + +/* + * GBoxed + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_boxed_property_get_type (void); + +typedef struct { + GProperty parent; + + GPropertyBoxedSet setter; + GPropertyBoxedGet getter; +} GBoxedProperty; + +static void +property_boxed_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_BOXED; +} + +static void +property_boxed_init (GParamSpec *pspec) +{ +} + +GType +_g_boxed_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_boxed_class_init, + NULL, NULL, + sizeof (GBoxedProperty), + 0, + (GInstanceInitFunc) property_boxed_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GBoxedProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_boxed_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to a boxed value. + * + * You can use g_property_set_prerequisite() to specify the #GType + * of the boxed value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_boxed_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyBoxedSet setter, + GPropertyBoxedGet getter) +{ + GProperty *prop; + GBoxedProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_boxed_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_BOXED; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gpointer); + + internal = (GBoxedProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_boxed_property_validate (GProperty *property, + gconstpointer value) +{ + return TRUE; +} + +static inline gboolean +g_boxed_property_set_value (GProperty *property, + gpointer gobject, + gpointer value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_boxed_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GBoxedProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GBoxedProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + gpointer old_value; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if (property->flags & G_PROPERTY_COPY_SET) + { + old_value = (* (gpointer *) field_p); + + if (value != NULL) + (* (gpointer *) field_p) = g_boxed_copy (((GParamSpec *) property)->value_type, value); + else + (* (gpointer *) field_p) = NULL; + + if (old_value != NULL) + g_boxed_free (((GParamSpec *) property)->value_type, old_value); + } + else + (* (gpointer *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gpointer +g_boxed_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GBoxedProperty *) property)->getter != NULL) + { + return ((GBoxedProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + gpointer value; + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if (property->flags & G_PROPERTY_COPY_GET) + value = g_boxed_copy (((GParamSpec *) property)->value_type, (* (gpointer *) field_p)); + else + value = (* (gpointer *) field_p); + + return value; + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return NULL; + } +} + +/* + * GObject + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_object_property_get_type (void); + +typedef struct { + GProperty parent; + + GPropertyObjectSet setter; + GPropertyObjectGet getter; +} GObjectProperty; + +static void +property_object_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_OBJECT; +} + +static void +property_object_init (GParamSpec *pspec) +{ +} + +GType +_g_object_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_object_class_init, + NULL, NULL, + sizeof (GObjectProperty), + 0, + (GInstanceInitFunc) property_object_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GObjectProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_object_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to an object value. + * + * You can use g_property_set_prerequisite() to specify the #GType + * of the object value. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_object_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyObjectSet setter, + GPropertyObjectGet getter) +{ + GProperty *prop; + GObjectProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_object_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_OBJECT; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gpointer); + + internal = (GObjectProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_object_property_validate (GProperty *property, + gconstpointer value) +{ + if (value == NULL) + return FALSE; + + return g_type_is_a (G_OBJECT_TYPE (value), G_PARAM_SPEC (property)->value_type); +} + +static inline gboolean +g_object_property_set_value (GProperty *property, + gpointer gobject, + gpointer value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_object_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GObjectProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GObjectProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + gpointer obj; + + g_return_val_if_fail (value == NULL || G_IS_OBJECT (value), FALSE); + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gpointer *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + if (property->flags & G_PROPERTY_COPY_SET) + { + obj = (* (gpointer *) field_p); + if (obj != NULL) + g_object_unref (obj); + + (* (gpointer *) field_p) = obj = value; + + if (obj != NULL) + { + if (G_IS_INITIALLY_UNOWNED (obj)) + g_object_ref_sink (obj); + else + g_object_ref (obj); + } + } + else + (* (gpointer *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gpointer +g_object_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GObjectProperty *) property)->getter != NULL) + { + return ((GObjectProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = g_type_instance_get_private (gobject, G_OBJECT_TYPE (gobject)); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if (property->flags & G_PROPERTY_COPY_GET) + { + gpointer value = (* (gpointer *) field_p); + + if (value != NULL) + return g_object_ref (value); + else + return NULL; + } + else + return (* (gpointer *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return NULL; + } +} + +/* + * gpointer + */ + +/* forward declaration for -Wmissing-prototypes */ +GType _g_pointer_property_get_type (void); + +typedef struct { + GProperty parent; + + GPropertyPointerSet setter; + GPropertyPointerGet getter; +} GPointerProperty; + +static void +property_pointer_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_POINTER; +} + +static void +property_pointer_init (GParamSpec *pspec) +{ +} + +GType +_g_pointer_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_pointer_class_init, + NULL, NULL, + sizeof (GPointerProperty), + 0, + (GInstanceInitFunc) property_pointer_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PROPERTY, + g_intern_static_string ("GPointerProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} + +/** + * g_pointer_property_new: + * @name: canonical name of the property + * @flags: flags for the property + * @offset: the offset in the private structure of the field + * that stores the property, or -1 + * @setter: (allow-none): the setter function for the property + * @getter: (allow-none): the getter function for the property + * + * Creates a new #GProperty mapping to an untyped pointer. + * + * Return value: the newly created #GProperty + * + * Since: 2.36 + */ +GParamSpec * +g_pointer_property_new (const gchar *name, + GPropertyFlags flags, + gssize offset, + GPropertyObjectSet setter, + GPropertyObjectGet getter) +{ + GProperty *prop; + GPointerProperty *internal; + + if (setter == NULL && getter == NULL) + g_return_val_if_fail (offset >= 0, NULL); + + prop = g_param_spec_internal (_g_pointer_property_get_type (), + name, NULL, NULL, + property_flags_to_param_flags (flags)); + + prop->flags = flags; + + G_PARAM_SPEC (prop)->value_type = G_TYPE_POINTER; + + prop->field_offset = offset; + + prop->is_installed = FALSE; + + prop->type_size = sizeof (gpointer); + + internal = (GPointerProperty *) prop; + internal->setter = setter; + internal->getter = getter; + + return G_PARAM_SPEC (prop); +} + +static inline gboolean +g_pointer_property_validate (GProperty *property, + gconstpointer value) +{ + return TRUE; +} + +static inline gboolean +g_pointer_property_set_value (GProperty *property, + gpointer gobject, + gpointer value) +{ + gboolean retval = FALSE; + + if ((property->flags & G_PROPERTY_WRITABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not writable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (!g_pointer_property_validate (property, value)) + { + g_warning ("The value for the property '%s' of object '%s' is out of the valid range", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GPointerProperty *) property)->setter != NULL) + { + property_lock_internal (property, gobject); + + retval = ((GPointerProperty *) property)->setter (gobject, value); + + property_unlock_internal (property, gobject); + + if (retval) + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + property_lock_internal (property, gobject); + + priv_p = get_private_pointer (gobject, property->priv_offset); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + if ((* (gpointer *) field_p) == value) + { + property_unlock_internal (property, gobject); + return FALSE; + } + + (* (gpointer *) field_p) = value; + + property_unlock_internal (property, gobject); + + g_object_notify_by_pspec (gobject, (GParamSpec *) property); + + retval = TRUE; + } + else + g_critical (G_STRLOC ": No setter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + + return retval; +} + +static inline gpointer +g_pointer_property_get_value (GProperty *property, + gpointer gobject) +{ + if ((property->flags & G_PROPERTY_READABLE) == 0) + { + g_critical ("The property '%s' of object '%s' is not readable", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + return FALSE; + } + + if (((GPointerProperty *) property)->getter != NULL) + { + return ((GPointerProperty *) property)->getter (gobject); + } + else if (property->field_offset >= 0) + { + gpointer priv_p, field_p; + + priv_p = g_type_instance_get_private (gobject, G_OBJECT_TYPE (gobject)); + field_p = G_STRUCT_MEMBER_P (priv_p, property->field_offset); + + return (* (gpointer *) field_p); + } + else + { + g_critical (G_STRLOC ": No getter function or field offset specified " + "for property '%s'", + G_PARAM_SPEC (property)->name); + return NULL; + } +} + +/* + * GProperty common API + */ + +/*< private > + * g_property_set_installed: + * @property: a #GProperty + * @class_gtype: the #GType of the class that installed @property + * + * Performs additional work once a type class has been associated to + * the property. + */ +void +_g_property_set_installed (GProperty *property, + gpointer g_class, + GType class_gtype) +{ + if (property->field_offset >= 0) + { + gboolean is_interface = G_TYPE_IS_INTERFACE (class_gtype); + + if (is_interface) + { + g_critical (G_STRLOC ": The property '%s' has a field offset value " + "but it is being installed on an interface of type '%s'. " + "Properties installed on interfaces cannot have direct " + "access to a structure field.", + G_PARAM_SPEC (property)->name, + g_type_name (class_gtype)); + property->priv_offset = -1; + } + + property->priv_offset = g_type_class_get_instance_private_offset (g_class, class_gtype); + } + else + property->priv_offset = -1; + + /* if the property is using the default locking, pre-compute the + * quark for the lock + */ + if ((property->flags & G_PROPERTY_ATOMIC) != 0 && + property->prop_id == 0 && + property->lock_func == NULL) + { + gchar *lock_n = g_strconcat ("-g-property-id-", + G_PARAM_SPEC (property)->name, + NULL); + property->prop_id = g_quark_from_string (lock_n); + g_free (lock_n); + } + + property->is_installed = TRUE; +} + +static gboolean +is_canonical (const gchar *key) +{ + const gchar *p; + + for (p = key; *p != 0; p++) + { + gchar c = *p; + + if (c != '-' && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) + return FALSE; + } + + return TRUE; +} + +static void +canonicalize_name (gchar *key) +{ + gchar *p; + + for (p = key; *p != 0; p++) + { + gchar c = *p; + + if (c != '-' && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) + *p = '-'; + } +} + +/** + * g_property_canonicalize_name: + * @name: a string + * + * Canonicalizes a string into a property name. + * + * Return value: (transfer full): a newly allocated string with + * the canonical version of @name + * + * Since: 2.36 + */ +gchar * +g_property_canonicalize_name (const gchar *name) +{ + gchar *retval; + + g_return_val_if_fail (name != NULL, NULL); + + if (is_canonical (name)) + return g_strdup (g_intern_string (name)); + + retval = g_strdup (name); + canonicalize_name (retval); + g_intern_string (retval); + + return retval; +} + +/** + * g_property_describe: + * @property: a #GProperty + * @nick: a static string with the user-readable name + * of the property + * @blurb: a static string with the user-readable description + * of the property + * + * Sets the user-readable, and optionally translatable, name and + * description of the property. + * + * This function cannot be called more than once. + * + * This function is a convenience wrapper around g_param_spec_set_static_nick() + * and g_param_spec_set_static_blurb(). + * + * Since: 2.36 + */ +void +g_property_describe (GProperty *property, + const char *nick, + const char *blurb) +{ + GParamSpec *pspec; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (nick != NULL); + g_return_if_fail (blurb != NULL); + + pspec = G_PARAM_SPEC (property); + + g_param_spec_set_static_nick (pspec, nick); + g_param_spec_set_static_blurb (pspec, blurb); +} + +/** + * g_property_set_prerequisite: + * @property: a #GProperty + * @gtype: the prerequisite type + * + * Sets the prerequisite type for the @property. + * + * The prerequisite type must have the @property GType as a super-type, + * and will be used to make the type checking stricter. + * + * Since: 2.36 + */ +void +g_property_set_prerequisite (GProperty *property, + GType gtype) +{ + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (gtype != G_TYPE_INVALID); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + g_return_if_fail (g_type_is_a (gtype, G_PARAM_SPEC (property)->value_type)); + + switch (G_PARAM_SPEC (property)->value_type) + { + case G_TYPE_BOXED: + case G_TYPE_OBJECT: + G_PARAM_SPEC (property)->value_type = gtype; + break; + + case G_TYPE_ENUM: + G_PARAM_SPEC (property)->value_type = gtype; + ((GEnumProperty *) property)->e_class = g_type_class_ref (gtype); + break; + + case G_TYPE_FLAGS: + G_PARAM_SPEC (property)->value_type = gtype; + ((GFlagsProperty *) property)->f_class = g_type_class_ref (gtype); + break; + + default: + break; + } +} + +/** + * g_property_set_range_values: + * @property: a #GProperty + * @min_value: a #GValue with the minimum value of the range + * @max_value: a #GValue with the maximum value of the range + * + * Sets the valid range of @property, using #GValues. + * + * This function is intended for language bindings. + * + * Since: 2.36 + */ +void +g_property_set_range_values (GProperty *property, + const GValue *min_value, + const GValue *max_value) +{ + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + g_return_if_fail (!property->is_installed); + g_return_if_fail (min_value != NULL && max_value != NULL); + + gtype = G_PARAM_SPEC (property)->value_type; + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (min_value), gtype)); + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (max_value), gtype)); + + switch (gtype) + { + case G_TYPE_BOOLEAN: + g_boolean_property_set_range (property, + g_value_get_boolean (min_value), + g_value_get_boolean (max_value)); + break; + + case G_TYPE_INT: + { + gint min_v = g_value_get_int (min_value); + gint max_v = g_value_get_int (max_value); + + switch (property->type_size) + { + case 1: + g_int8_property_set_range (property, min_v, max_v); + break; + + case 2: + g_int16_property_set_range (property, min_v, max_v); + break; + + case 4: + g_int32_property_set_range (property, min_v, max_v); + break; + + default: + g_int_property_set_range (property, min_v, max_v); + break; + } + } + break; + + case G_TYPE_INT64: + g_int64_property_set_range (property, + g_value_get_int64 (min_value), + g_value_get_int64 (max_value)); + break; + + case G_TYPE_LONG: + g_long_property_set_range (property, + g_value_get_long (min_value), + g_value_get_long (max_value)); + break; + + case G_TYPE_UINT: + { + guint min_v = g_value_get_uint (min_value); + guint max_v = g_value_get_uint (max_value); + + switch (property->type_size) + { + case 1: + g_uint8_property_set_range (property, min_v, max_v); + break; + + case 2: + g_uint16_property_set_range (property, min_v, max_v); + break; + + case 4: + g_uint32_property_set_range (property, min_v, max_v); + break; + + default: + g_uint_property_set_range (property, min_v, max_v); + break; + } + } + break; + + case G_TYPE_UINT64: + g_uint64_property_set_range (property, + g_value_get_uint64 (min_value), + g_value_get_uint64 (max_value)); + break; + + case G_TYPE_ULONG: + g_ulong_property_set_range (property, + g_value_get_ulong (min_value), + g_value_get_ulong (max_value)); + break; + + case G_TYPE_FLOAT: + g_float_property_set_range (property, + g_value_get_float (min_value), + g_value_get_float (max_value)); + break; + + case G_TYPE_DOUBLE: + g_double_property_set_range (property, + g_value_get_double (min_value), + g_value_get_double (max_value)); + break; + + default: + break; + } +} + +/** + * g_property_get_range_values: + * @property: a #GProperty + * @min_value: a valid #GValue, initialized to the type of @property + * @max_value: a valid #GValue, initialized to the type of @property + * + * Retrieves the bounds of the range of valid values for @property + * and stores them into @min_value and @max_value. + * + * Return value: %TRUE if successful, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_get_range_values (GProperty *property, + GValue *min_value, + GValue *max_value) +{ + gboolean retval; + GType gtype; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (min_value != NULL, FALSE); + g_return_val_if_fail (max_value != NULL, FALSE); + + gtype = G_PARAM_SPEC (property)->value_type; + g_return_val_if_fail (g_value_type_compatible (gtype, G_VALUE_TYPE (min_value)), FALSE); + g_return_val_if_fail (g_value_type_compatible (gtype, G_VALUE_TYPE (max_value)), FALSE); + + switch (gtype) + { + case G_TYPE_BOOLEAN: + { + gboolean min_v, max_v; + + g_boolean_property_get_range (property, &min_v, &max_v); + g_value_set_boolean (min_value, min_v); + g_value_set_boolean (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_INT: + { + gint min_v, max_v; + + switch (property->type_size) + { + case 1: + g_int8_property_get_range (property, (gint8 *) &min_v, (gint8 *) &max_v); + break; + + case 2: + g_int16_property_get_range (property, (gint16 *) &min_v, (gint16 *) &max_v); + break; + + case 4: + g_int32_property_get_range (property, (gint32 *) &min_v, (gint32 *) &max_v); + break; + + default: + g_int_property_get_range (property, &min_v, &max_v); + break; + } + + g_value_set_int (min_value, min_v); + g_value_set_int (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_INT64: + { + gint64 min_v, max_v; + + g_int64_property_get_range (property, &min_v, &max_v); + g_value_set_int64 (min_value, min_v); + g_value_set_int64 (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_LONG: + { + glong min_v, max_v; + + g_long_property_get_range (property, &min_v, &max_v); + g_value_set_long (min_value, min_v); + g_value_set_long (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_UINT: + { + guint min_v, max_v; + + switch (property->type_size) + { + case 1: + g_uint8_property_get_range (property, (guint8 *) &min_v, (guint8 *) &max_v); + break; + + case 2: + g_uint16_property_get_range (property, (guint16 *) &min_v, (guint16 *) &max_v); + break; + + case 4: + g_uint32_property_get_range (property, (guint32 *) &min_v, (guint32 *) &max_v); + break; + + default: + g_uint_property_get_range (property, &min_v, &max_v); + break; + } + + g_value_set_uint (min_value, min_v); + g_value_set_uint (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_UINT64: + { + guint64 min_v, max_v; + + g_uint64_property_get_range (property, &min_v, &max_v); + g_value_set_uint64 (min_value, min_v); + g_value_set_uint64 (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_ULONG: + { + gulong min_v, max_v; + + g_ulong_property_get_range (property, &min_v, &max_v); + g_value_set_ulong (min_value, min_v); + g_value_set_ulong (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_FLOAT: + { + gfloat min_v, max_v; + + g_float_property_get_range (property, &min_v, &max_v); + g_value_set_float (min_value, min_v); + g_value_set_float (max_value, max_v); + } + retval = TRUE; + break; + + case G_TYPE_DOUBLE: + { + gdouble min_v, max_v; + + g_double_property_get_range (property, &min_v, &max_v); + g_value_set_double (min_value, min_v); + g_value_set_double (max_value, max_v); + } + retval = TRUE; + break; + + default: + g_critical (G_STRLOC ": Invalid type '%s'", g_type_name (gtype)); + retval = FALSE; + break; + } + + return retval; +} + +/** + * g_property_set_range: + * @property: a #GProperty + * @...: the minimum and maximum values of the range + * + * Sets the range of valid values for @property. + * + * Since: 2.36 + */ +void +g_property_set_range (GProperty *property, + ...) +{ + GType gtype; + va_list args; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (!property->is_installed); + + gtype = G_PARAM_SPEC (property)->value_type; + g_return_if_fail (gtype != G_TYPE_INVALID); + + va_start (args, property); + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + { + gboolean min_v = va_arg (args, gboolean); + gboolean max_v = va_arg (args, gboolean); + + g_boolean_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_INT: + { + gint min_v = va_arg (args, gint); + gint max_v = va_arg (args, gint); + + switch (property->type_size) + { + case 1: + g_int8_property_set_range (property, min_v, max_v); + break; + + case 2: + g_int16_property_set_range (property, min_v, max_v); + break; + + case 4: + g_int32_property_set_range (property, min_v, max_v); + break; + + default: + g_int_property_set_range (property, min_v, max_v); + break; + } + } + break; + + case G_TYPE_INT64: + { + gint64 min_v = va_arg (args, gint64); + gint64 max_v = va_arg (args, gint64); + + g_int64_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_LONG: + { + glong min_v = va_arg (args, glong); + glong max_v = va_arg (args, glong); + + g_long_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_UINT: + { + guint min_v = va_arg (args, guint); + guint max_v = va_arg (args, guint); + + switch (property->type_size) + { + case 1: + g_uint8_property_set_range (property, min_v, max_v); + break; + + case 2: + g_uint16_property_set_range (property, min_v, max_v); + break; + + case 4: + g_uint32_property_set_range (property, min_v, max_v); + break; + + default: + g_uint_property_set_range (property, min_v, max_v); + break; + } + } + break; + + case G_TYPE_UINT64: + { + guint64 min_v = va_arg (args, guint64); + guint64 max_v = va_arg (args, guint64); + + g_uint64_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_ULONG: + { + gulong min_v = va_arg (args, gulong); + gulong max_v = va_arg (args, gulong); + + g_ulong_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_FLOAT: + { + gfloat min_v = va_arg (args, gdouble); + gfloat max_v = va_arg (args, gdouble); + + g_float_property_set_range (property, min_v, max_v); + } + break; + + case G_TYPE_DOUBLE: + { + gdouble min_v = va_arg (args, gdouble); + gdouble max_v = va_arg (args, gdouble); + + g_double_property_set_range (property, min_v, max_v); + } + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + } + + va_end (args); +} + +/** + * g_property_get_range: + * @property: a #GProperty + * @...: the return locations for the minimum and maximum values + * of the range + * + * Retrieves the bounds of the range of valid values for @property. + * + * Return value: %TRUE on success, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_get_range (GProperty *property, + ...) +{ + va_list var_args; + GType gtype; + gboolean retval; + gpointer min_p, max_p; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID, FALSE); + + gtype = G_PARAM_SPEC (property)->value_type; + + va_start (var_args, property); + + min_p = va_arg (var_args, gpointer); + max_p = va_arg (var_args, gpointer); + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + g_boolean_property_get_range (property, (gboolean *) min_p, (gboolean *) max_p); + retval = TRUE; + break; + + case G_TYPE_INT: + switch (property->type_size) + { + case 1: + g_int8_property_get_range (property, (gint8 *) min_p, (gint8 *) max_p); + retval = TRUE; + break; + + case 2: + g_int16_property_get_range (property, (gint16 *) min_p, (gint16 *) max_p); + retval = TRUE; + break; + + case 4: + g_int32_property_get_range (property, (gint32 *) min_p, (gint32 *) max_p); + retval = TRUE; + break; + + default: + g_int_property_get_range (property, (gint *) min_p, (gint *) max_p); + retval = TRUE; + break; + } + break; + + case G_TYPE_INT64: + g_int64_property_get_range (property, (gint64 *) min_p, (gint64 *) max_p); + retval = TRUE; + break; + + case G_TYPE_LONG: + g_long_property_get_range (property, (glong *) min_p, (glong *) max_p); + retval = TRUE; + break; + + case G_TYPE_UINT: + switch (property->type_size) + { + case 1: + g_uint8_property_get_range (property, (guint8 *) min_p, (guint8 *) max_p); + retval = TRUE; + break; + + case 2: + g_uint16_property_get_range (property, (guint16 *) min_p, (guint16 *) max_p); + retval = TRUE; + break; + + case 4: + g_uint32_property_get_range (property, (guint32 *) min_p, (guint32 *) max_p); + retval = TRUE; + break; + + default: + g_uint_property_get_range (property, (guint *) min_p, (guint *) max_p); + retval = TRUE; + break; + } + break; + + case G_TYPE_UINT64: + g_uint64_property_get_range (property, (guint64 *) min_p, (guint64 *) max_p); + retval = TRUE; + break; + + case G_TYPE_ULONG: + g_ulong_property_get_range (property, (gulong *) min_p, (gulong *) max_p); + retval = TRUE; + break; + + case G_TYPE_FLOAT: + g_float_property_get_range (property, (gfloat *) min_p, (gfloat *) max_p); + retval = TRUE; + break; + + case G_TYPE_DOUBLE: + g_double_property_get_range (property, (gdouble *) min_p, (gdouble *) max_p); + retval = TRUE; + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + retval = FALSE; + } + + va_end (var_args); + + return retval; +} + +/** + * g_property_set_default_value: + * @property: a #GProperty + * @gobject_class: a #GObject class pointer + * @default_value: a #GValue, initialized to the property type + * containing the default value for the given class + * + * Sets the default value of @property for the given class. + * + * This function is a #GValue variant of g_property_set_default(), and + * it is meant to be used by language bindings. + * + * Since: 2.36 + */ +void +g_property_set_default_value (GProperty *property, + const GValue *default_value) +{ + GValue *value; + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + g_return_if_fail (default_value != NULL); + + gtype = G_PARAM_SPEC (property)->value_type; + + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (default_value), gtype)); + + value = g_new0 (GValue, 1); + g_value_init (value, gtype); + if (!g_value_transform (default_value, value)) + { + g_critical (G_STRLOC ": unable to set the default value for " + "property '%s': the type %s of the value is not " + "compatible with the type of the %s property", + G_PARAM_SPEC (property)->name, + g_type_name (G_VALUE_TYPE (default_value)), + g_type_name (gtype)); + + g_value_unset (value); + g_free (value); + return; + } + + property_set_default_for_type (property, G_TYPE_INVALID, value); +} + +/** + * g_property_override_default_value: + * @property: a #GProperty + * @class_gtype: the type of the class overriding the value + * @default_value: a #GValue containing a value with the type of the + * property or a transformable type + * + * Overrides the default value of a @property for the given class + * type. + * + * This function should only be called by language bindings. + * + * Since: 2.36 + */ +void +g_property_override_default_value (GProperty *property, + GType class_gtype, + const GValue *default_value) +{ + GValue *value; + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + g_return_if_fail (g_type_name (class_gtype) != 0); + g_return_if_fail (default_value != NULL); + + gtype = G_PARAM_SPEC (property)->value_type; + + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (default_value), gtype)); + + value = g_new0 (GValue, 1); + g_value_init (value, gtype); + if (!g_value_transform (default_value, value)) + { + g_critical (G_STRLOC ": unable to set the default value for " + "property '%s': the type %s of the value is not " + "compatible with the type of the %s property", + G_PARAM_SPEC (property)->name, + g_type_name (G_VALUE_TYPE (default_value)), + g_type_name (gtype)); + + g_value_unset (value); + g_free (value); + return; + } + + /* takes ownership of value */ + property_set_default_for_type (property, class_gtype, value); +} + +/** + * g_property_get_default_value_for_type: + * @property: a #GProperty + * @gtype: a valid #GType + * @value: a #GValue initialized to the property type + * + * Retrieves the default value of the property for the given type. + * + * This function is meant to be used by language bindings and other + * introspection tools; #GObject implementations should use + * g_property_get_default() instead. + * + * Return value: %TRUE if there is a default got the given type, + * and %FALSE otherwise. + * + * Since: 2.36 + */ +gboolean +g_property_get_default_value_for_type (GProperty *property, + GType gtype, + GValue *value) +{ + const GValue *default_value = NULL; + GType iter; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID, FALSE); + g_return_val_if_fail (g_type_name (gtype) != 0, FALSE); + + /* we need to recurse through the inheritance chain... */ + iter = gtype; + while (iter != G_TYPE_INVALID && default_value == NULL) + { + default_value = property_get_default_for_type (property, iter); + gtype = g_type_parent (iter); + } + + if (default_value != NULL) + goto out; + + /* ... and eventually check the implemented interfaces */ + if (default_value == NULL) + { + GType *ifaces; + guint n_ifaces; + + ifaces = g_type_interfaces (gtype, &n_ifaces); + while (n_ifaces-- && default_value == NULL) + { + iter = ifaces[n_ifaces]; + default_value = property_get_default_for_type (property, iter); + } + + g_free (ifaces); + } + + if (default_value != NULL) + goto out; + + /* if the property hasn't been overridden then we look for the default */ + default_value = property_get_default_for_type (property, G_TYPE_INVALID); + + if (default_value == NULL) + { + g_critical (G_STRLOC ": No default value of property '%s' " + "was found for type '%s'", + G_PARAM_SPEC (property)->name, + g_type_name (gtype)); + + return FALSE; + } + +out: + if (!g_value_transform (default_value, value)) + { + g_critical (G_STRLOC ": Unable to transform a value of type '%s' " + "into a value of type '%s'", + g_type_name (G_VALUE_TYPE (default_value)), + g_type_name (G_VALUE_TYPE (value))); + return FALSE; + } + + return TRUE; +} + +/** + * g_property_get_default_value: + * @property: a #GProperty + * @gobject: a #GObject + * @value: a #GValue initialized to the type of the property + * + * Retrieves the default value of @property for the given @gobject + * type. + * + * This function should only be used by language bindings and other + * introspection tools. + * + * Return value: %TRUE if a default value was found, and %FALSE + * otherwise + * + * Since: 2.36 + */ +gboolean +g_property_get_default_value (GProperty *property, + gpointer gobject, + GValue *value) +{ + g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE); + + return g_property_get_default_value_for_type (property, + G_OBJECT_TYPE (gobject), + value); +} + +/** + * g_property_set_default: + * @property: a #GProperty + * @...: the default value for the property + * + * Sets the default value of @property. + * + * This function can only be called once for each property; derived + * types should call g_property_override_default() instead. + * + * See also g_property_override_default() and + * g_object_class_override_property_default(). + * + * Since: 2.36 + */ +void +g_property_set_default (GProperty *property, + ...) +{ + GValue *value; + GType p_type; + gchar *error; + va_list var_args; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + + p_type = G_PARAM_SPEC (property)->value_type; + + va_start (var_args, property); + + value = g_new0 (GValue, 1); + G_VALUE_COLLECT_INIT (value, p_type, var_args, 0, &error); + if (error != NULL) + { + g_critical (G_STRLOC ": %s", error); + g_free (error); + g_value_unset (value); + g_free (value); + } + else + { + /* takes ownership of the GValue */ + property_set_default_for_type (property, G_TYPE_INVALID, value); + } + + va_end (var_args); +} + +/** + * g_property_get_default: + * @property: a #GProperty + * @gobject: a #GObject instance + * @...: the return location for the default value + * + * Retrieves the default value of @property for the type of the + * instance passed. + * + * Since: 2.36 + */ +void +g_property_get_default (GProperty *property, + gpointer gobject, + ...) +{ + GValue value = { 0, }; + GType gtype, p_type; + gchar *error; + va_list var_args; + const GValue *default_value = NULL; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + + p_type = G_PARAM_SPEC (property)->value_type; + + /* we perform a copy here because if the default value was not found, + * we can use the pre-initialized value safely in G_VALUE_LCOPY and + * return something sensible + */ + g_value_init (&value, p_type); + + gtype = G_OBJECT_TYPE (gobject); + + /* we need to recurse through the inheritance chain... */ + while (gtype != G_TYPE_INVALID && default_value == NULL) + { + default_value = property_get_default_for_type (property, gtype); + gtype = g_type_parent (gtype); + } + + if (default_value != NULL) + goto lcopy; + + /* ... and eventually check the implemented interfaces */ + if (default_value == NULL) + { + GType *ifaces; + guint n_ifaces; + + gtype = G_OBJECT_TYPE (gobject); + + ifaces = g_type_interfaces (gtype, &n_ifaces); + while (n_ifaces-- && default_value == NULL) + { + gtype = ifaces[n_ifaces]; + default_value = property_get_default_for_type (property, gtype); + } + + g_free (ifaces); + } + + if (default_value != NULL) + goto lcopy; + + /* if the property hasn't been overridden then we look for the default */ + default_value = property_get_default_for_type (property, G_TYPE_INVALID); + +lcopy: + if (default_value != NULL) + g_value_copy (default_value, &value); + else + g_critical (G_STRLOC ": No default value of property '%s' " + "was found for type '%s'", + G_PARAM_SPEC (property)->name, + G_OBJECT_TYPE_NAME (gobject)); + + va_start (var_args, gobject); + + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error != NULL) + { + g_warning (G_STRLOC ": %s", error); + g_free (error); + } + + va_end (var_args); + g_value_unset (&value); +} + +/** + * g_property_override_default: + * @property: a #GProperty + * @class_gtype: the type of the class overriding the default + * @...: the new default value for the property + * + * Overrides the default value of @property for the given class type. + * + * Since: 2.36 + */ +void +g_property_override_default (GProperty *property, + GType class_gtype, + ...) +{ + GValue *value; + GType p_type; + gchar *error; + va_list var_args; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_PARAM_SPEC (property)->value_type != G_TYPE_INVALID); + g_return_if_fail (g_type_name (class_gtype) != 0); + + p_type = G_PARAM_SPEC (property)->value_type; + + va_start (var_args, class_gtype); + + value = g_new0 (GValue, 1); + G_VALUE_COLLECT_INIT (value, p_type, var_args, 0, &error); + if (error != NULL) + { + g_critical (G_STRLOC ": %s", error); + g_free (error); + g_value_unset (value); + g_free (value); + } + else + { + /* takes ownership of the GValue */ + property_set_default_for_type (property, class_gtype, value); + } + + va_end (var_args); +} + +/** + * g_property_set_va: + * @property: a #GProperty + * @gobject: a #GObject instance + * @flags: collection flags, as a bitwise or of #GPropertyCollectFlags values + * @args: the value to set, inside a pointer to a #va_list + * + * Sets the value of @property for the given #GObject instance. + * + * This function is the va_list variant of g_property_set(). + * + * Return value: %TRUE if the value was set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_set_va (GProperty *property, + gpointer gobject, + GPropertyCollectFlags flags, + va_list *args) +{ + gboolean retval; + GType gtype; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE); + g_return_val_if_fail (property->is_installed, FALSE); + + g_object_ref (gobject); + + gtype = ((GParamSpec *) property)->value_type; + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + retval = g_boolean_property_set_value (property, gobject, va_arg (*args, gboolean)); + break; + + case G_TYPE_INT: + switch (property->type_size) + { + case 1: + retval = g_int8_property_set_value (property, gobject, va_arg (*args, gint)); + break; + + case 2: + retval = g_int16_property_set_value (property, gobject, va_arg (*args, gint)); + break; + + case 4: + retval = g_int32_property_set_value (property, gobject, va_arg (*args, gint)); + break; + + default: + retval = g_int_property_set_value (property, gobject, va_arg (*args, gint)); + break; + } + break; + + case G_TYPE_INT64: + retval = g_int64_property_set_value (property, gobject, va_arg (*args, gint64)); + break; + + case G_TYPE_LONG: + retval = g_long_property_set_value (property, gobject, va_arg (*args, glong)); + break; + + case G_TYPE_UINT: + switch (property->type_size) + { + case 1: + retval = g_uint8_property_set_value (property, gobject, va_arg (*args, guint)); + break; + + case 2: + retval = g_uint16_property_set_value (property, gobject, va_arg (*args, guint)); + break; + + case 4: + retval = g_uint32_property_set_value (property, gobject, va_arg (*args, guint)); + break; + + default: + retval = g_uint_property_set_value (property, gobject, va_arg (*args, guint)); + break; + } + break; + + case G_TYPE_UINT64: + retval = g_uint64_property_set_value (property, gobject, va_arg (*args, guint64)); + break; + + case G_TYPE_ULONG: + retval = g_ulong_property_set_value (property, gobject, va_arg (*args, gulong)); + break; + + case G_TYPE_ENUM: + retval = g_enum_property_set_value (property, gobject, va_arg (*args, glong)); + break; + + case G_TYPE_FLAGS: + retval = g_flags_property_set_value (property, gobject, va_arg (*args, gulong)); + break; + + case G_TYPE_FLOAT: + retval = g_float_property_set_value (property, gobject, va_arg (*args, gdouble)); + break; + + case G_TYPE_DOUBLE: + retval = g_double_property_set_value (property, gobject, va_arg (*args, gdouble)); + break; + + case G_TYPE_STRING: + retval = g_string_property_set_value (property, gobject, va_arg (*args, gchar *)); + break; + + case G_TYPE_BOXED: + retval = g_boxed_property_set_value (property, gobject, va_arg (*args, gpointer)); + break; + + case G_TYPE_OBJECT: + retval = g_object_property_set_value (property, gobject, va_arg (*args, gpointer)); + break; + + case G_TYPE_POINTER: + retval = g_pointer_property_set_value (property, gobject, va_arg (*args, gpointer)); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + retval = FALSE; + break; + } + + g_object_unref (gobject); + + return retval; +} + +/** + * g_property_get_va: + * @property: a #GProperty + * @gobject: a #GObject instance + * @flags: collection flags + * @args: a pointer to a #va_list with the property + * + * Retrieves the value of @property for the given #GObject instance. + * + * This function is the va_list variant of g_property_get(). + * + * Return value: %TRUE if the value was successfully retrieved, and + * %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_get_va (GProperty *property, + gpointer gobject, + GPropertyCollectFlags flags, + va_list *args) +{ + GType gtype; + gpointer ret_p = NULL; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE); + g_return_val_if_fail (property->is_installed, FALSE); + + gtype = G_PARAM_SPEC (property)->value_type; + + ret_p = va_arg (*args, gpointer); + if (ret_p == NULL) + { + g_critical (G_STRLOC ": value location for a property of type '%s' passed as NULL", + g_type_name (gtype)); + return FALSE; + } + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + (* (gboolean *) ret_p) = g_boolean_property_get_value (property, gobject); + break; + + case G_TYPE_INT: + switch (property->type_size) + { + case 1: + (* (gint8 *) ret_p) = g_int8_property_get_value (property, gobject); + break; + + case 2: + (* (gint16 *) ret_p) = g_int16_property_get_value (property, gobject); + break; + + case 4: + (* (gint32 *) ret_p) = g_int32_property_get_value (property, gobject); + break; + + default: + (* (gint *) ret_p) = g_int_property_get_value (property, gobject); + break; + } + break; + + case G_TYPE_INT64: + (* (gint64 *) ret_p) = g_int64_property_get_value (property, gobject); + break; + + case G_TYPE_LONG: + (* (glong *) ret_p) = g_long_property_get_value (property, gobject); + break; + + case G_TYPE_UINT: + switch (property->type_size) + { + case 1: + (* (guint8 *) ret_p) = g_uint8_property_get_value (property, gobject); + break; + + case 2: + (* (guint16 *) ret_p) = g_uint16_property_get_value (property, gobject); + break; + + case 4: + (* (guint32 *) ret_p) = g_uint32_property_get_value (property, gobject); + break; + + default: + (* (guint *) ret_p) = g_uint_property_get_value (property, gobject); + break; + } + break; + + case G_TYPE_UINT64: + (* (guint64 *) ret_p) = g_uint64_property_get_value (property, gobject); + break; + + case G_TYPE_ULONG: + (* (gulong *) ret_p) = g_ulong_property_get_value (property, gobject); + break; + + case G_TYPE_ENUM: + (* (glong *) ret_p) = g_enum_property_get_value (property, gobject); + break; + + case G_TYPE_FLAGS: + (* (gulong *) ret_p) = g_flags_property_get_value (property, gobject); + break; + + case G_TYPE_FLOAT: + (* (gfloat *) ret_p) = g_float_property_get_value (property, gobject); + break; + + case G_TYPE_DOUBLE: + (* (gdouble *) ret_p) = g_double_property_get_value (property, gobject); + break; + + case G_TYPE_STRING: + { + const gchar *value; + + value = g_string_property_get_value (property, gobject); + + if (((flags & G_PROPERTY_COLLECT_COPY) != 0) && + (property->flags & G_PROPERTY_COPY_GET) == 0) + { + (* (gchar **) ret_p) = g_strdup (value); + } + else + (* (gconstpointer *) ret_p) = value; + } + break; + + case G_TYPE_BOXED: + { + gpointer boxed; + + boxed = g_boxed_property_get_value (property, gobject); + + if (((flags & G_PROPERTY_COLLECT_COPY) != 0) && + (property->flags & G_PROPERTY_COPY_GET) == 0) + { + if (boxed != NULL) + (* (gpointer *) ret_p) = g_boxed_copy (gtype, boxed); + else + (* (gpointer *) ret_p) = NULL; + } + else + (* (gpointer *) ret_p) = (gpointer) boxed; + } + break; + + case G_TYPE_OBJECT: + { + gpointer obj = g_object_property_get_value (property, gobject); + + if ((((flags & G_PROPERTY_COLLECT_REF) != 0) && + (property->flags & G_PROPERTY_COPY_GET) == 0) && + (obj != NULL)) + { + (* (gpointer *) ret_p) = g_object_ref (obj); + } + else + (* (gpointer *) ret_p) = obj; + } + break; + + case G_TYPE_POINTER: + (* (gpointer *) ret_p) = g_pointer_property_get_value (property, gobject); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + return FALSE; + } + + return TRUE; +} + +/** + * g_property_set: + * @property: a #GProperty + * @gobject: a #GObject instance + * @...: the value to be set + * + * Sets the value of the @property for the given #GObject instance. + * + * The value will either be copied or have its reference count increased. + * + * Since: 2.36 + */ +gboolean +g_property_set (GProperty *property, + gpointer gobject, + ...) +{ + va_list args; + gboolean res; + + va_start (args, gobject); + res = g_property_set_va (property, gobject, 0, &args); + va_end (args); + + return res; +} + +/** + * g_property_get: + * @property: a #GProperty + * @gobject: a #GObject instance + * @...: a pointer to the value to be retrieved + * + * Retrieves the value of the @property for the given #GObject instance. + * + * Since: 2.36 + */ +gboolean +g_property_get (GProperty *property, + gpointer gobject, + ...) +{ + va_list args; + gboolean retval; + + va_start (args, gobject); + retval = g_property_get_va (property, gobject, 0, &args); + va_end (args); + + return retval; +} + +/** + * g_property_set_value: + * @property: a #GProperty + * @gobject: a #GObject instance + * @value: a #GValue + * + * Sets the value of the @property for the given #GObject instance + * by unboxing it from the #GValue, honouring eventual transformation + * functions between the #GValue type and the property type. + * + * Since: 2.36 + */ +void +g_property_set_value (GProperty *property, + gpointer gobject, + const GValue *value) +{ + GValue copy = { 0, }; + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + g_return_if_fail (value != NULL); + g_return_if_fail (property->is_installed); + + gtype = G_PARAM_SPEC (property)->value_type; + + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (value), gtype)); + + g_value_init (©, gtype); + if (!g_value_transform (value, ©)) + { + g_critical (G_STRLOC ": Unable to transform a value of type '%s' " + "into a value of type '%s'", + g_type_name (G_VALUE_TYPE (value)), + g_type_name (gtype)); + return; + } + + g_object_ref (gobject); + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + g_boolean_property_set_value (property, gobject, g_value_get_boolean (©)); + break; + + case G_TYPE_INT: + { + gint val = g_value_get_int (©); + + switch (property->type_size) + { + case 1: + g_int8_property_set_value (property, gobject, val); + break; + + case 2: + g_int16_property_set_value (property, gobject, val); + break; + + case 4: + g_int32_property_set_value (property, gobject, val); + break; + + default: + g_int_property_set_value (property, gobject, val); + break; + } + } + break; + + case G_TYPE_INT64: + g_int64_property_set_value (property, gobject, g_value_get_int64 (©)); + break; + + case G_TYPE_LONG: + g_long_property_set_value (property, gobject, g_value_get_long (©)); + break; + + case G_TYPE_UINT: + { + guint val = g_value_get_uint (©); + + switch (property->type_size) + { + case 1: + g_uint8_property_set_value (property, gobject, val); + break; + + case 2: + g_uint16_property_set_value (property, gobject, val); + break; + + case 4: + g_uint32_property_set_value (property, gobject, val); + break; + + default: + g_uint_property_set_value (property, gobject, val); + break; + } + } + break; + + case G_TYPE_UINT64: + g_uint64_property_set_value (property, gobject, g_value_get_uint64 (©)); + break; + + case G_TYPE_ULONG: + g_ulong_property_set_value (property, gobject, g_value_get_ulong (©)); + break; + + case G_TYPE_FLOAT: + g_float_property_set_value (property, gobject, g_value_get_float (©)); + break; + + case G_TYPE_DOUBLE: + g_double_property_set_value (property, gobject, g_value_get_double (©)); + break; + + case G_TYPE_ENUM: + g_enum_property_set_value (property, gobject, g_value_get_enum (©)); + break; + + case G_TYPE_FLAGS: + g_flags_property_set_value (property, gobject, g_value_get_flags (©)); + break; + + case G_TYPE_STRING: + g_string_property_set_value (property, gobject, g_value_get_string (©)); + break; + + case G_TYPE_BOXED: + g_boxed_property_set_value (property, gobject, g_value_get_boxed (©)); + break; + + case G_TYPE_OBJECT: + g_object_property_set_value (property, gobject, g_value_get_object (©)); + break; + + case G_TYPE_POINTER: + g_pointer_property_set_value (property, gobject, g_value_get_pointer (©)); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (G_VALUE_TYPE (©))); + break; + } + + g_object_unref (gobject); + + g_value_unset (©); +} + +/** + * g_property_get_value: + * @property: a #GProperty + * @gobject: a #GObject instance + * @value: a #GValue, initialized to the type of the property or to a + * type that satisfies the transformable relation + * + * Retrieves the value of @property for the object instance, and + * boxes it inside a #GValue, honouring eventual transformation + * functions between the #GValue type and the property type. + * + * Since: 2.36 + */ +void +g_property_get_value (GProperty *property, + gpointer gobject, + GValue *value) +{ + GType gtype; + GValue copy = { 0, }; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + g_return_if_fail (value != NULL); + g_return_if_fail (property->is_installed); + + gtype = G_PARAM_SPEC (property)->value_type; + + g_return_if_fail (g_value_type_transformable (G_VALUE_TYPE (value), gtype)); + + g_value_init (©, gtype); + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (©, g_boolean_property_get_value (property, gobject)); + break; + + case G_TYPE_INT: + { + gint val; + + switch (property->type_size) + { + case 1: + val = g_int8_property_get_value (property, gobject); + break; + + case 2: + val = g_int16_property_get_value (property, gobject); + break; + + case 4: + val = g_int32_property_get_value (property, gobject); + break; + + default: + val = g_int_property_get_value (property, gobject); + break; + } + + g_value_set_int (©, val); + } + break; + + case G_TYPE_INT64: + g_value_set_int64 (©, g_int64_property_get_value (property, gobject)); + break; + + case G_TYPE_LONG: + g_value_set_long (©, g_long_property_get_value (property, gobject)); + break; + + case G_TYPE_UINT: + { + guint val; + + switch (property->type_size) + { + case 1: + val = g_uint8_property_get_value (property, gobject); + break; + + case 2: + val = g_uint16_property_get_value (property, gobject); + break; + + case 4: + val = g_uint32_property_get_value (property, gobject); + break; + + default: + val = g_uint_property_get_value (property, gobject); + break; + } + + g_value_set_uint (©, val); + } + break; + + case G_TYPE_UINT64: + g_value_set_uint64 (©, g_uint64_property_get_value (property, gobject)); + break; + + case G_TYPE_ULONG: + g_value_set_ulong (©, g_ulong_property_get_value (property, gobject)); + break; + + case G_TYPE_STRING: + g_value_set_string (©, g_string_property_get_value (property, gobject)); + break; + + case G_TYPE_CHAR: + g_value_set_schar (©, g_int8_property_get_value (property, gobject)); + break; + + case G_TYPE_UCHAR: + g_value_set_uchar (©, g_uint8_property_get_value (property, gobject)); + break; + + case G_TYPE_ENUM: + g_value_set_enum (©, g_enum_property_get_value (property, gobject)); + break; + + case G_TYPE_FLAGS: + g_value_set_flags (©, g_flags_property_get_value (property, gobject)); + break; + + case G_TYPE_FLOAT: + g_value_set_float (©, g_float_property_get_value (property, gobject)); + break; + + case G_TYPE_DOUBLE: + g_value_set_double (©, g_double_property_get_value (property, gobject)); + break; + + case G_TYPE_BOXED: + g_value_set_boxed (©, g_boxed_property_get_value (property, gobject)); + break; + + case G_TYPE_OBJECT: + g_value_set_object (©, g_object_property_get_value (property, gobject)); + break; + + case G_TYPE_POINTER: + g_value_set_pointer (©, g_pointer_property_get_value (property, gobject)); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + break; + } + + if (!g_value_transform (©, value)) + { + g_critical (G_STRLOC ": Unable to transform a value of type '%s' into " + "a value of type '%s'", + g_type_name (gtype), + g_type_name (G_VALUE_TYPE (value))); + } + + g_value_unset (©); +} + +/** + * g_property_get_value_type: + * @property: a #GProperty + * + * Retrieves the #GType of the value stored by the property. + * + * If a prerequisite type has been set, it will be the returned type. + * + * Return value: a #GType + * + * Since: 2.36 + */ +GType +g_property_get_value_type (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), G_TYPE_INVALID); + + return G_PARAM_SPEC (property)->value_type; +} + +/** + * g_property_validate: + * @property: a #GProperty + * @...: the value to validate + * + * Validates the passed value against the validation rules of + * the @property. + * + * Return value: %TRUE if the value is valid, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_validate (GProperty *property, + ...) +{ + gboolean retval = FALSE; + GType gtype; + va_list args; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + va_start (args, property); + + gtype = G_PARAM_SPEC (property)->value_type; + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + retval = g_boolean_property_validate (property, va_arg (args, gboolean)); + break; + + case G_TYPE_INT: + switch (property->type_size) + { + case 1: + retval = g_int8_property_validate (property, va_arg (args, gint)); + break; + + case 2: + retval = g_int16_property_validate (property, va_arg (args, gint)); + break; + + case 4: + retval = g_int32_property_validate (property, va_arg (args, gint)); + break; + + default: + retval = g_int_property_validate (property, va_arg (args, gint)); + break; + } + break; + + case G_TYPE_INT64: + retval = g_int64_property_validate (property, va_arg (args, gint64)); + break; + + case G_TYPE_LONG: + retval = g_long_property_validate (property, va_arg (args, glong)); + break; + + case G_TYPE_UINT: + switch (property->type_size) + { + case 1: + retval = g_uint8_property_validate (property, va_arg (args, guint)); + break; + + case 2: + retval = g_uint16_property_validate (property, va_arg (args, guint)); + break; + + case 4: + retval = g_uint32_property_validate (property, va_arg (args, guint)); + break; + + default: + retval = g_uint_property_validate (property, va_arg (args, guint)); + break; + } + break; + + case G_TYPE_UINT64: + retval = g_uint64_property_validate (property, va_arg (args, guint64)); + break; + + case G_TYPE_ULONG: + retval = g_ulong_property_validate (property, va_arg (args, gulong)); + break; + + case G_TYPE_FLOAT: + retval = g_float_property_validate (property, va_arg (args, gdouble)); + break; + + case G_TYPE_DOUBLE: + retval = g_double_property_validate (property, va_arg (args, gdouble)); + break; + + case G_TYPE_ENUM: + retval = g_enum_property_validate (property, va_arg (args, glong)); + break; + + case G_TYPE_FLAGS: + retval = g_enum_property_validate (property, va_arg (args, gulong)); + break; + + case G_TYPE_STRING: + retval = g_string_property_validate (property, va_arg (args, gchar *)); + break; + + case G_TYPE_BOXED: + retval = g_boxed_property_validate (property, va_arg (args, gpointer)); + break; + + case G_TYPE_OBJECT: + retval = g_object_property_validate (property, va_arg (args, gpointer)); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + break; + } + + va_end (args); + + return retval; +} + +/** + * g_property_validate_value: + * @property: a #GProperty + * @value: a #GValue initialized to the property type or to a type + * that is transformable into the property type + * + * Validates the value stored inside the passed #GValue against the + * @property rules. + * + * Return value: %TRUE if the value is valid, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_validate_value (GProperty *property, + GValue *value) +{ + GValue copy = { 0, }; + gboolean retval = FALSE; + GType gtype; + + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + gtype = G_PARAM_SPEC (property)->value_type; + + g_return_val_if_fail (g_value_type_transformable (gtype, G_VALUE_TYPE (value)), FALSE); + + g_value_init (©, gtype); + + if (!g_value_transform (value, ©)) + { + g_critical (G_STRLOC ": Unable to transform a value of type '%s' " + "to a value of type '%s'", + g_type_name (G_VALUE_TYPE (value)), + g_type_name (gtype)); + g_value_unset (©); + return FALSE; + } + + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_BOOLEAN: + retval = g_boolean_property_validate (property, g_value_get_boolean (©)); + break; + + case G_TYPE_INT: + { + gint val = g_value_get_int (©); + + switch (property->type_size) + { + case 1: + retval = g_int8_property_validate (property, val); + break; + + case 2: + retval = g_int16_property_validate (property, val); + break; + + case 4: + retval = g_int32_property_validate (property, val); + break; + + default: + retval = g_int_property_validate (property, val); + break; + } + } + break; + + case G_TYPE_INT64: + retval = g_int64_property_validate (property, g_value_get_int64 (©)); + break; + + case G_TYPE_LONG: + retval = g_long_property_validate (property, g_value_get_long (©)); + break; + + case G_TYPE_UINT: + { + guint val = g_value_get_uint (©); + + switch (property->type_size) + { + case 1: + retval = g_uint8_property_validate (property, val); + break; + + case 2: + retval = g_uint16_property_validate (property, val); + break; + + case 4: + retval = g_uint32_property_validate (property, val); + break; + + default: + retval = g_uint_property_validate (property, val); + break; + } + } + break; + + case G_TYPE_UINT64: + retval = g_uint64_property_validate (property, g_value_get_uint64 (©)); + break; + + case G_TYPE_ULONG: + retval = g_ulong_property_validate (property, g_value_get_ulong (©)); + break; + + case G_TYPE_FLOAT: + retval = g_float_property_validate (property, g_value_get_float (©)); + break; + + case G_TYPE_DOUBLE: + retval = g_double_property_validate (property, g_value_get_double (©)); + break; + + case G_TYPE_ENUM: + retval = g_enum_property_validate (property, g_value_get_enum (©)); + break; + + case G_TYPE_FLAGS: + retval = g_flags_property_validate (property, g_value_get_flags (©)); + break; + + case G_TYPE_STRING: + retval = g_string_property_validate (property, g_value_get_string (©)); + break; + + case G_TYPE_BOXED: + retval = g_boxed_property_validate (property, g_value_get_boxed (©)); + break; + + case G_TYPE_OBJECT: + retval = g_object_property_validate (property, g_value_get_object (©)); + break; + + default: + g_critical (G_STRLOC ": Invalid type %s", g_type_name (gtype)); + retval = FALSE; + break; + } + + g_value_unset (©); + + return retval; +} + +/** + * g_property_is_writable: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_WRITABLE flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_writable (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_WRITABLE) != 0; +} + +/** + * g_property_is_readable: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_READABLE flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_readable (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_READABLE) != 0; +} + +/** + * g_property_is_deprecated: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_DEPRECATED flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_deprecated (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_DEPRECATED) != 0; +} + +/** + * g_property_is_atomic: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_ATOMIC flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_atomic (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_ATOMIC) != 0; +} + +/** + * g_property_is_copy_set: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_COPY_SET flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_copy_set (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_COPY_SET) != 0; +} + +/** + * g_property_is_copy_get: + * @property: a #GProperty + * + * Checks whether the @property has the %G_PROPERTY_COPY_GET flag set. + * + * Return value: %TRUE if the flag is set, and %FALSE otherwise + * + * Since: 2.36 + */ +gboolean +g_property_is_copy_get (GProperty *property) +{ + g_return_val_if_fail (G_IS_PROPERTY (property), FALSE); + + return (property->flags & G_PROPERTY_COPY_GET) != 0; +} + +/** + * g_property_lock: + * @property: a #GProperty + * @gobject: a #GObject + * + * Locks a property on the given object. + * + * Use g_property_unlock() to unlock the property when done. + * + * Since: 2.36 + */ +void +g_property_lock (GProperty *property, + gpointer gobject) +{ + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + + property_lock_internal (property, gobject); +} + +/** + * g_property_unlock: + * @property: a #GProperty + * @gobject: a #GObject + * + * Unlocks a property on the given object previously locked + * using g_property_lock(). + * + * Since: 2.36 + */ +void +g_property_unlock (GProperty *property, + gpointer gobject) +{ + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + + property_unlock_internal (property, gobject); +} + +/** + * g_property_set_lock_functions: + * @property: a #GProperty + * @lock_func: (allow-none): the function to be called when locking + * the @property, or %NULL for the default locking function + * @unlock_func: (allow-none): the function to be called when unlocking + * the @property, or %NULL for the default unlocking function + * + * Replaces the locking and unlocking functions for @property with + * custom functions. + * + * Since: 2.36 + */ +void +g_property_set_lock_functions (GProperty *property, + GPropertyLockFunc lock_func, + GPropertyUnlockFunc unlock_func) +{ + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (!property->is_installed); + + if (lock_func == NULL) + g_return_if_fail (unlock_func == NULL); + + property->lock_func = lock_func; + property->unlock_func = unlock_func; +} + +static void +property_finalize (GParamSpec *pspec) +{ + GParamSpecClass *parent_class = g_type_class_peek (g_type_parent (G_TYPE_PROPERTY)); + + parent_class->finalize (pspec); +} + +static void +property_set_default (GParamSpec *pspec, + GValue *value) +{ + GProperty *property = G_PROPERTY (pspec); + const GValue *default_value; + + default_value = property_get_default_for_type (property, G_TYPE_INVALID); + if (default_value != NULL) + g_value_copy (default_value, value); +} + +static gboolean +property_validate (GParamSpec *pspec, + GValue *value) +{ + GProperty *property = G_PROPERTY (pspec); + + if (!g_value_type_transformable (G_VALUE_TYPE (value), pspec->value_type)) + return TRUE; + + return !g_property_validate_value (property, value); +} + +static gint +property_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + return 0; +} + +static void +property_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_INVALID; + + klass->value_set_default = property_set_default; + klass->value_validate = property_validate; + klass->values_cmp = property_values_cmp; + + klass->finalize = property_finalize; +} + +static void +property_init (GParamSpec *pspec) +{ + GProperty *property = G_PROPERTY (pspec); + + pspec->value_type = G_TYPE_INVALID; + + property->field_offset = -1; + property->priv_offset = -1; + + property->lock_func = NULL; + property->unlock_func = NULL; +} + +GType +g_property_get_type (void) +{ + static volatile gsize pspec_type_id__volatile = 0; + + if (g_once_init_enter (&pspec_type_id__volatile)) + { + const GTypeInfo info = { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) property_class_init, + NULL, NULL, + sizeof (GProperty), + 0, + (GInstanceInitFunc) property_init, + }; + + GType pspec_type_id = + g_type_register_static (G_TYPE_PARAM, + g_intern_static_string ("GProperty"), + &info, 0); + + g_once_init_leave (&pspec_type_id__volatile, pspec_type_id); + } + + return pspec_type_id__volatile; +} diff --git a/gobject/gproperty.h b/gobject/gproperty.h new file mode 100644 index 000000000..c78e44d28 --- /dev/null +++ b/gobject/gproperty.h @@ -0,0 +1,745 @@ +/* gproperty.h: Property definitions for GObject + * + * Copyright © 2012 Emmanuele Bassi + * + * 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 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_PROPERTY_H__ +#define __G_PROPERTY_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_PROPERTY (g_property_get_type ()) +#define G_PROPERTY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_PROPERTY, GProperty)) +#define G_IS_PROPERTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_PROPERTY)) + +/** + * GProperty: + * + * The GProperty structure is an opaque structure + * whose members cannot be directly accessed. + * + * Since: 2.36 + */ +typedef struct _GProperty GProperty; + +/** + * GPropertyFlags: + * @G_PROPERTY_READABLE: Whether the property is readable + * @G_PROPERTY_WRITABLE: Whether the property is writable + * @G_PROPERTY_READWRITE: Whether the property is readable and writable + * @G_PROPERTY_DEPRECATED: Whether the property is deprecated and should + * not be accessed in newly written code. + * @G_PROPERTY_ATOMIC: Whether the autogenerated setter function should + * be thread-safe, and acquire a lock when changing the value of the + * property. + * @G_PROPERTY_COPY_SET: Whether the property will make a copy or + * take a reference when being set to a new value + * @G_PROPERTY_COPY_GET: Whether the property will make a copy or + * take a reference when the value is being retrieved + * @G_PROPERTY_COPY: Whether the property will make a copy, or take a + * reference, of the new value being set, and return a copy, or + * increase the reference count, of the value being retrieved + * + * Flags for properties declared using #GProperty and relative macros. + * + * This enumeration might be extended at later date. + * + * Since: 2.36 + */ +typedef enum { + G_PROPERTY_READABLE = 1 << 0, + G_PROPERTY_WRITABLE = 1 << 1, + G_PROPERTY_READWRITE = (G_PROPERTY_READABLE | G_PROPERTY_WRITABLE), + + G_PROPERTY_DEPRECATED = 1 << 2, + G_PROPERTY_ATOMIC = 1 << 3, + G_PROPERTY_COPY_SET = 1 << 4, + G_PROPERTY_COPY_GET = 1 << 5, + G_PROPERTY_COPY = (G_PROPERTY_COPY_SET | G_PROPERTY_COPY_GET) +} GPropertyFlags; + +GLIB_AVAILABLE_IN_2_36 +GType g_property_get_type (void) G_GNUC_CONST; + +/* general purpose API */ +GLIB_AVAILABLE_IN_2_36 +gchar * g_property_canonicalize_name (const char *name); + +GLIB_AVAILABLE_IN_2_36 +GType g_property_get_value_type (GProperty *property); + +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_writable (GProperty *property); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_readable (GProperty *property); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_deprecated (GProperty *property); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_atomic (GProperty *property); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_copy_set (GProperty *property); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_is_copy_get (GProperty *property); + +GLIB_AVAILABLE_IN_2_36 +void g_property_describe (GProperty *property, + const char *nick, + const char *blurb); + +GLIB_AVAILABLE_IN_2_36 +void g_property_set_range_values (GProperty *property, + const GValue *min_value, + const GValue *max_value); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get_range_values (GProperty *property, + GValue *min_value, + GValue *max_value); +GLIB_AVAILABLE_IN_2_36 +void g_property_set_range (GProperty *property, + ...); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get_range (GProperty *property, + ...); + +GLIB_AVAILABLE_IN_2_36 +void g_property_set_default_value (GProperty *property, + const GValue *value); +GLIB_AVAILABLE_IN_2_36 +void g_property_set_default (GProperty *property, + ...); +GLIB_AVAILABLE_IN_2_36 +void g_property_override_default_value (GProperty *property, + GType class_gtype, + const GValue *value); +GLIB_AVAILABLE_IN_2_36 +void g_property_override_default (GProperty *property, + GType class_gtype, + ...); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get_default_value_for_type (GProperty *property, + GType gtype, + GValue *value); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get_default_value (GProperty *property, + gpointer gobject, + GValue *value); +GLIB_AVAILABLE_IN_2_36 +void g_property_get_default (GProperty *property, + gpointer gobject, + ...); + +GLIB_AVAILABLE_IN_2_36 +void g_property_set_prerequisite (GProperty *property, + GType gtype); + +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_validate (GProperty *property, + ...); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_validate_value (GProperty *property, + GValue *value); + +GLIB_AVAILABLE_IN_2_36 +void g_property_set_value (GProperty *property, + gpointer gobject, + const GValue *value); +GLIB_AVAILABLE_IN_2_36 +void g_property_get_value (GProperty *property, + gpointer gobject, + GValue *value); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_set (GProperty *property, + gpointer gobject, + ...); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get (GProperty *property, + gpointer gobject, + ...); + +/** + * GPropertyCollectFlags: + * @G_PROPERTY_COLLECT_NONE: No flags + * @G_PROPERTY_COLLECT_COPY: Make a copy when collecting pointer + * locations for boxed and string values + * @G_PROPERTY_COLLECT_REF: Take a reference when collecting + * pointer locations for object values + * + * Flags to pass to g_property_collect() and g_property_lcopy(). + * + * Since: 2.36 + */ +typedef enum { /*< prefix=G_PROPERTY_COLLECT >*/ + G_PROPERTY_COLLECT_NONE = 0, + + G_PROPERTY_COLLECT_COPY = 1 << 0, + G_PROPERTY_COLLECT_REF = 1 << 1 +} GPropertyCollectFlags; + +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_set_va (GProperty *property, + gpointer gobject, + GPropertyCollectFlags flags, + va_list *app); +GLIB_AVAILABLE_IN_2_36 +gboolean g_property_get_va (GProperty *property, + gpointer gobject, + GPropertyCollectFlags flags, + va_list *app); + +typedef void (* GPropertyLockFunc) (GProperty *property, + gpointer gobject); + +typedef void (* GPropertyUnlockFunc) (GProperty *property, + gpointer gobject); + +GLIB_AVAILABLE_IN_2_36 +void g_property_set_lock_functions (GProperty *property, + GPropertyLockFunc lock_func, + GPropertyUnlockFunc unlock_func); +GLIB_AVAILABLE_IN_2_36 +void g_property_lock (GProperty *property, + gpointer gobject); +GLIB_AVAILABLE_IN_2_36 +void g_property_unlock (GProperty *property, + gpointer gobject); + +/* private API */ +GLIB_AVAILABLE_IN_2_36 +void _g_property_set_installed (GProperty *property, + gpointer g_class, + GType class_gtype); + +/* per-type specific accessors */ +typedef gboolean (* GPropertyBooleanSet) (gpointer gobject, + gboolean value); +typedef gboolean (* GPropertyBooleanGet) (gpointer gobject); + +typedef gboolean (* GPropertyIntSet) (gpointer gobject, + gint value); +typedef gint (* GPropertyIntGet) (gpointer gobject); + +typedef gboolean (* GPropertyInt8Set) (gpointer gobject, + gint8 value); +typedef gint8 (* GPropertyInt8Get) (gpointer gobject); + +typedef gboolean (* GPropertyInt16Set) (gpointer gobject, + gint16 value); +typedef gint16 (* GPropertyInt16Get) (gpointer gobject); + +typedef gboolean (* GPropertyInt32Set) (gpointer gobject, + gint32 value); +typedef gint32 (* GPropertyInt32Get) (gpointer gobject); + +typedef gboolean (* GPropertyInt64Set) (gpointer gobject, + gint64 value); +typedef gint64 (* GPropertyInt64Get) (gpointer gobject); + +typedef gboolean (* GPropertyLongSet) (gpointer gobject, + glong value); +typedef glong (* GPropertyLongGet) (gpointer gobject); + +typedef gboolean (* GPropertyUIntSet) (gpointer gobject, + guint value); +typedef guint (* GPropertyUIntGet) (gpointer gobject); + +typedef gboolean (* GPropertyUInt8Set) (gpointer gobject, + guint8 value); +typedef guint8 (* GPropertyUInt8Get) (gpointer gobject); + +typedef gboolean (* GPropertyUInt16Set) (gpointer gobject, + guint16 value); +typedef guint16 (* GPropertyUInt16Get) (gpointer gobject); + +typedef gboolean (* GPropertyUInt32Set) (gpointer gobject, + guint32 value); +typedef guint32 (* GPropertyUInt32Get) (gpointer gobject); + +typedef gboolean (* GPropertyUInt64Set) (gpointer gobject, + guint64 value); +typedef guint64 (* GPropertyUInt64Get) (gpointer gobject); + +typedef gboolean (* GPropertyULongSet) (gpointer gobject, + gulong value); +typedef gulong (* GPropertyULongGet) (gpointer gobject); + +typedef gboolean (* GPropertyEnumSet) (gpointer gobject, + glong value); +typedef glong (* GPropertyEnumGet) (gpointer gobject); + +typedef gboolean (* GPropertyFlagsSet) (gpointer gobject, + glong value); +typedef glong (* GPropertyFlagsGet) (gpointer gobject); + +typedef gboolean (* GPropertyFloatSet) (gpointer gobject, + gfloat value); +typedef gfloat (* GPropertyFloatGet) (gpointer gobject); + +typedef gboolean (* GPropertyDoubleSet) (gpointer gobject, + gdouble value); +typedef gdouble (* GPropertyDoubleGet) (gpointer gobject); + +typedef gboolean (* GPropertyStringSet) (gpointer gobject, + const char *value); +typedef const char * (* GPropertyStringGet) (gpointer gobject); + +typedef gboolean (* GPropertyBoxedSet) (gpointer gobject, + gpointer value); +typedef gpointer (* GPropertyBoxedGet) (gpointer gobject); + +typedef gboolean (* GPropertyObjectSet) (gpointer gobject, + gpointer value); +typedef gpointer (* GPropertyObjectGet) (gpointer gobject); + +typedef gboolean (* GPropertyPointerSet) (gpointer gobject, + gpointer value); +typedef gpointer (* GPropertyPointerGet) (gpointer gobject); + +/* per-type specific constructors */ +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_boolean_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyBooleanSet setter, + GPropertyBooleanGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_int_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyIntSet setter, + GPropertyIntGet getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_int8_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyInt8Set setter, + GPropertyInt8Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_int16_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyInt16Set setter, + GPropertyInt16Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_int32_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyInt32Set setter, + GPropertyInt32Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_int64_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyInt64Set setter, + GPropertyInt64Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_long_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyLongSet setter, + GPropertyLongGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_uint_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyUIntSet setter, + GPropertyUIntGet getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_uint8_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyUInt8Set setter, + GPropertyUInt8Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_uint16_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyUInt16Set setter, + GPropertyUInt16Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_uint32_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyUInt32Set setter, + GPropertyUInt32Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_uint64_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyUInt64Set setter, + GPropertyUInt64Get getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_ulong_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyULongSet setter, + GPropertyULongGet getter); + +#define g_char_property_new g_int8_property_new +#define g_uchar_property_new g_uint8_property_new +#define g_unichar_property_new g_uint32_property_new + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_enum_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyEnumSet setter, + GPropertyEnumGet getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_flags_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyFlagsSet setter, + GPropertyFlagsGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_float_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyFloatSet setter, + GPropertyFloatGet getter); +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_double_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyDoubleSet setter, + GPropertyDoubleGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_string_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyStringSet setter, + GPropertyStringGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_boxed_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyBoxedSet setter, + GPropertyBoxedGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_object_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyObjectSet setter, + GPropertyObjectGet getter); + +GLIB_AVAILABLE_IN_2_36 +GParamSpec * g_pointer_property_new (const gchar *name, + GPropertyFlags flags, + gssize field_offset, + GPropertyPointerSet setter, + GPropertyPointerGet getter); + +/* accessors generation */ +#define _G_DECLARE_PROPERTY_GETTER(T_n, t_n, f_t, f_n) f_t t_n##_get_##f_n (T_n *self) + +#define _G_DEFINE_PROPERTY_GETTER_BEGIN(T_n, t_n, f_t, f_n) \ +{ \ + GProperty *g_property = NULL; \ + f_t retval; \ +\ + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, t_n##_get_type ()), (f_t) 0); \ +\ + { \ + GObjectClass *_self_class; \ + _self_class = G_OBJECT_GET_CLASS (self); \ + g_property = (GProperty *) g_object_class_find_property (_self_class, #f_n); \ + if (G_UNLIKELY (g_property == NULL)) \ + { \ + g_critical (G_STRLOC ": No property " #f_n " found for class %s", \ + G_OBJECT_TYPE_NAME (self)); \ + return (f_t) 0; \ + } \ + } \ +\ + if (!G_IS_PROPERTY (g_property)) \ + { \ + g_critical (G_STRLOC ": Property " #f_n " is not a GProperty"); \ + return (f_t) 0; \ + } \ +\ + if (!g_property_is_readable (g_property)) \ + { \ + g_critical (G_STRLOC ": The property " #f_n " is not readable"); \ + return (f_t) 0; \ + } \ +\ + if (!g_property_get (g_property, self, &retval)) \ + { \ + g_property_get_default (g_property, self, &retval); \ + return retval; \ + } \ +\ + { /* custom code follows */ +#define _G_DEFINE_PROPERTY_GETTER_END \ + } /* following custom code */ \ +\ + return retval; \ +} + +#define _G_DECLARE_PROPERTY_SETTER(T_n, t_n, f_t, f_n) void t_n##_set_##f_n (T_n *self, f_t value) + +#define _G_DEFINE_PROPERTY_SETTER_BEGIN(T_n, t_n, f_t, f_n) \ +{ \ + GProperty *g_property = NULL; \ +\ + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, t_n##_get_type ())); \ +\ + { \ + GObjectClass *_self_class; \ + _self_class = G_OBJECT_GET_CLASS (self); \ + g_property = (GProperty *) g_object_class_find_property (_self_class, #f_n); \ + if (G_UNLIKELY (g_property == NULL)) \ + { \ + g_critical (G_STRLOC ": No property " #f_n " found for class %s", G_OBJECT_TYPE_NAME (self)); \ + return; \ + } \ + } \ +\ + if (!G_IS_PROPERTY (g_property)) \ + { \ + g_critical (G_STRLOC ": Property " #f_n " is not a GProperty"); \ + return; \ + } \ +\ + if (!g_property_is_writable (g_property)) \ + { \ + g_critical (G_STRLOC ": The property " #f_n " is not writable"); \ + return; \ + } \ +\ + if (!g_property_set (g_property, self, value)) \ + return; \ +\ + { /* custom code follows */ +#define _G_DEFINE_PROPERTY_SETTER_END \ + }/* following custom code */ \ +} + +/** + * G_DECLARE_PROPERTY_GET_SET: + * @TypeName: the name of the type, in Camel case + * @type_name: the name of the type, in lowercase, with words separated by '_' + * @field_type: the type of the property, which must match the type of the + * field in the @TypeNamePrivate structure + * @field_name: the name of the property, which must match the name of the + * field in the @TypeNamePrivate structure + * + * Declares the accessor functions for a @field_name property in the + * class @TypeName. This macro should only be used in header files. + * + * Since: 2.30 + */ +#define G_DECLARE_PROPERTY_GET_SET(T_n, t_n, f_t, f_n) \ +_G_DECLARE_PROPERTY_SETTER (T_n, t_n, f_t, f_n); \ +_G_DECLARE_PROPERTY_GETTER (T_n, t_n, f_t, f_n); + +/** + * G_DECLARE_PROPERTY_GET: + * @T_n: the name of the type, in Camel case + * @t_n: the name of the type, in lowercase, with words separated by '_' + * @f_t: the type of the property, which must match the type of the field + * @f_n: the name of the property, which must match the name of the field + * + * Declares the getter function for a @f_n property in the @T_n class. + * + * This macro should only be used in header files. + * + * Since: 2.30 + */ +#define G_DECLARE_PROPERTY_GET(T_n, t_n, f_t, f_n) _G_DECLARE_PROPERTY_GETTER (T_n, t_n, f_t, f_n); + +/** + * G_DECLARE_PROPERTY_SET: + * @T_n: the name of the type, in Camel case + * @t_n: the name of the type, in lowercase, with words separated by '_' + * @f_t: the type of the property, which must match the type of the field + * @f_n: the name of the property, which must match the name of the field + * + * Declares the setter function for a @f_n property in the @T_n class. + * + * This macro should only be used in header files. + * + * Since: 2.30 + */ +#define G_DECLARE_PROPERTY_SET(T_n, t_n, f_t, f_n) _G_DECLARE_PROPERTY_SETTER (T_n, t_n, f_t, f_n); + +/** + * G_DEFINE_PROPERTY_SET_WITH_CODE: + * @TypeName: the name of the type, in Camel case + * @type_name: the name of the type, in lowercase, with words separated by '_' + * @field_type: the type of the property, which must match the type of the + * field in the @TypeNamePrivate structure + * @field_name: the name of the property, which must match the name of the + * field in the @TypeNamePrivate structure + * @_C_: C code that should be called after the property has been set + * + * Defines the setter function for a @field_name property in the + * class @TypeName, with the possibility of calling custom code. + * + * This macro should only be used in C source files. + * + * |[ + * G_DEFINE_PROPERTY_SET_WITH_CODE (ClutterActor, clutter_actor, + * int, margin_top, + * clutter_actor_queue_redraw (self)) + * ]| + * + * Since: 2.30 + */ + +#define G_DEFINE_PROPERTY_SET_WITH_CODE(T_n, t_n, f_t, f_n, _C_) \ +_G_DECLARE_PROPERTY_SETTER (T_n, t_n, f_t, f_n) \ +_G_DEFINE_PROPERTY_SETTER_BEGIN (T_n, t_n, f_t, f_n) \ +{ _C_; } \ +_G_DEFINE_PROPERTY_SETTER_END + +/** + * G_DEFINE_PROPERTY_GET_WITH_CODE: + * @T_n: the name of the type, in Camel case + * @t_n: the name of the type, in lowercase, with words separated by '_' + * @f_t: the type of the property, which must match the type of the + * field in the @T_nPrivate structure + * @f_n: the name of the property, which must match the name of the + * field in the @T_nPrivate structure + * @_C_: C code to be called after the property has been retrieved + * + * Defines the getter function for a @f_n property in the + * class @T_n, with the possibility of calling custom code. + * + * This macro should only be used in C source files. + * + * Since: 2.30 + */ +#define G_DEFINE_PROPERTY_GET_WITH_CODE(T_n, t_n, f_t, f_n, _C_) \ +_G_DECLARE_PROPERTY_GETTER (T_n, t_n, f_t, f_n) \ +_G_DEFINE_PROPERTY_GETTER_BEGIN (T_n, t_n, f_t, f_n) \ +{ _C_; } \ +_G_DEFINE_PROPERTY_GETTER_END + +/** + * G_DEFINE_PROPERTY_SET: + * @TypeName: the name of the type, in Camel case + * @type_name: the name of the type, in lowercase, with words separated by '_' + * @field_type: the type of the property, which must match the type of the + * field in the @TypeNamePrivate structure + * @field_name: the name of the property, which must match the name of the + * field in the @TypeNamePrivate structure + * + * Defines the setter function for a @field_name property in the + * class @TypeName. This macro should only be used in C source files. + * + * See also: %G_DEFINE_PROPERTY_SET_WITH_CODE + * + * Since: 2.30 + */ +#define G_DEFINE_PROPERTY_SET(T_n, t_n, f_t, f_n) G_DEFINE_PROPERTY_SET_WITH_CODE (T_n, t_n, f_t, f_n, ;) + +/** + * G_DEFINE_PROPERTY_GET: + * @TypeName: the name of the type, in Camel case + * @type_name: the name of the type, in lowercase, with words separated by '_' + * @field_type: the type of the property, which must match the type of the + * field in the @TypeNamePrivate structure + * @field_name: the name of the property, which must match the name of the + * field in the @TypeNamePrivate structure + * + * Defines the getter function for a @field_name property in the + * class @TypeName. This macro should only be used in C source files. + * + * See also %G_DEFINE_PROPERTY_GET_WITH_CODE. + */ +#define G_DEFINE_PROPERTY_GET(T_n, t_n, f_t, f_n) G_DEFINE_PROPERTY_GET_WITH_CODE (T_n, t_n, f_t, f_n, ;) + +/** + * G_DEFINE_PROPERTY_GET_SET: + * @T_n: the name of the type, in Camel case + * @t_n: the name of the type, in lowercase, with words separated by '_' + * @f_t: the type of the property, which must match the type of the + * field in the @TypeNamePrivate structure + * @f_n: the name of the property, which must match the name of the + * field in the @TypeNamePrivate structure + * + * Defines the accessor functions for a @f_n property in the class @T_n. + * + * This macro should only be used in C source files, for instance: + * + * |[ + * G_DEFINE_PROPERTY_GET_SET (ClutterActor, clutter_actor, int, margin_top) + * ]| + * + * will synthesize the equivalent of the following code: + * + * |[ + * void + * clutter_actor_set_margin_top (ClutterActor *self, + * int value) + * { + * ClutterActorPrivate *priv; + * + * g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, clutter_actor_get_type ())); + * + * priv = self->priv; + * + * if (priv->margin_top == value) + * return; + * + * priv->value = value; + * + * g_object_notify (G_OBJECT (self), "margin-top"); + * } + * + * int + * clutter_actor_get_margin_top (ClutterActor *self) + * { + * g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, clutter_actor_get_type ()), 0); + * + * return self->priv->margin_top; + * } + * ]| + * + * This macro will generate both the setter and getter functions; if the + * property is not readable and writable, the generated functions will + * warn at run-time. + * + * For greater control on the setter and getter implementation, see also the + * %G_DEFINE_PROPERTY_GET and %G_DEFINE_PROPERTY_SET macros, along with their + * %G_DEFINE_PROPERTY_GET_WITH_CODE and %G_DEFINE_PROPERTY_SET_WITH_CODE + * variants. + * + * Since: 2.30 + */ +#define G_DEFINE_PROPERTY_GET_SET(T_n, t_n, f_t, f_n) \ +G_DEFINE_PROPERTY_GET (T_n, t_n, f_t, f_n) \ +G_DEFINE_PROPERTY_SET (T_n, t_n, f_t, f_n) + +G_END_DECLS + +#endif /* __G_PROPERTY_H__ */ diff --git a/gobject/gtype.c b/gobject/gtype.c index 96e82b946..a2dcf60cf 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -4501,16 +4501,82 @@ g_type_class_add_private (gpointer g_class, G_WRITE_UNLOCK (&type_rw_lock); } -gpointer -g_type_instance_get_private (GTypeInstance *instance, - GType private_type) +static gssize +lookup_instance_private_offset (GTypeClass *class, + GType private_type) { TypeNode *instance_node; TypeNode *private_node; TypeNode *parent_node; - GTypeClass *class; gsize offset; + g_return_val_if_fail (class != NULL, -1); + + instance_node = lookup_type_node_I (class->g_type); + if (G_UNLIKELY (!instance_node || !instance_node->is_instantiatable)) + { + g_warning ("class of invalid non-instantiatable type `%s'", + type_descriptive_name_I (class->g_type)); + return -1; + } + + private_node = lookup_type_node_I (private_type); + if (G_UNLIKELY (!private_node || !NODE_IS_ANCESTOR (private_node, instance_node))) + { + g_warning ("attempt to retrieve private data for invalid type '%s'", + type_descriptive_name_I (private_type)); + return -1; + } + + offset = ALIGN_STRUCT (instance_node->data->instance.instance_size); + + if (NODE_PARENT_TYPE (private_node)) + { + parent_node = lookup_type_node_I (NODE_PARENT_TYPE (private_node)); + g_assert (parent_node->data && NODE_REFCOUNT (parent_node) > 0); + + if (G_UNLIKELY (private_node->data->instance.private_size == parent_node->data->instance.private_size)) + { + g_warning ("g_type_class_get_instance_private_offset() requires a " + "prior call to g_type_class_add_private()"); + return -1; + } + + offset += ALIGN_STRUCT (parent_node->data->instance.private_size); + } + + return offset; +} + +gssize +g_type_class_get_instance_private_offset (gpointer g_class, + GType private_type) +{ + gssize offset; + + g_return_val_if_fail (g_class != NULL, -1); + + /* unlike with g_type_instance_get_private(), we need to acquire the + * read lock here because we may be called during class initialization + * type, which means that nodes may not be filled yet. + */ + G_READ_LOCK (&type_rw_lock); + + offset = lookup_instance_private_offset (g_class, private_type); + + G_READ_UNLOCK (&type_rw_lock); + + return offset; +} + +gpointer +g_type_instance_get_private (GTypeInstance *instance, + GType private_type) +{ + GTypeClass *class; + TypeNode *instance_node; + gssize offset; + g_return_val_if_fail (instance != NULL && instance->g_class != NULL, NULL); /* while instances are initialized, their class pointers change, @@ -4524,15 +4590,7 @@ g_type_instance_get_private (GTypeInstance *instance, if (G_UNLIKELY (!instance_node || !instance_node->is_instantiatable)) { g_warning ("instance of invalid non-instantiatable type `%s'", - type_descriptive_name_I (instance->g_class->g_type)); - return NULL; - } - - private_node = lookup_type_node_I (private_type); - if (G_UNLIKELY (!private_node || !NODE_IS_ANCESTOR (private_node, instance_node))) - { - g_warning ("attempt to retrieve private data for invalid type '%s'", - type_descriptive_name_I (private_type)); + type_descriptive_name_I (class->g_type)); return NULL; } @@ -4542,22 +4600,9 @@ g_type_instance_get_private (GTypeInstance *instance, * and node->data->instance.private_size are not going to be changed. * for any of the relevant types. */ - - offset = ALIGN_STRUCT (instance_node->data->instance.instance_size); - - if (NODE_PARENT_TYPE (private_node)) - { - parent_node = lookup_type_node_I (NODE_PARENT_TYPE (private_node)); - g_assert (parent_node->data && NODE_REFCOUNT (parent_node) > 0); - - if (G_UNLIKELY (private_node->data->instance.private_size == parent_node->data->instance.private_size)) - { - g_warning ("g_type_instance_get_private() requires a prior call to g_type_class_add_private()"); - return NULL; - } - - offset += ALIGN_STRUCT (parent_node->data->instance.private_size); - } + offset = lookup_instance_private_offset (class, private_type); + if (offset < 0) + return NULL; return G_STRUCT_MEMBER_P (instance, offset); } diff --git a/gobject/gtype.h b/gobject/gtype.h index 80a4ddf24..a18cec2a1 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -1264,6 +1264,10 @@ gpointer g_type_class_get_private (GTypeClass *klass, GLIB_AVAILABLE_IN_2_34 void g_type_ensure (GType type); +GLIB_AVAILABLE_IN_2_36 +gssize g_type_class_get_instance_private_offset (gpointer g_class, + GType private_type); + /* --- GType boilerplate --- */ /** * G_DEFINE_TYPE: diff --git a/gobject/tests/.gitignore b/gobject/tests/.gitignore index 30fcd4116..fd6462840 100644 --- a/gobject/tests/.gitignore +++ b/gobject/tests/.gitignore @@ -1,7 +1,10 @@ +autoproperties binding boxed dynamictests enums +gproperty-example-base +gproperty-example-derived ifaceproperties param properties diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am index 212cbda60..7117220b8 100644 --- a/gobject/tests/Makefile.am +++ b/gobject/tests/Makefile.am @@ -11,8 +11,8 @@ else glib_genmarshal=$(top_builddir)/gobject/glib-genmarshal endif -noinst_PROGRAMS = $(TEST_PROGS) LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la +noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS) TEST_PROGS += \ qdata \ @@ -26,7 +26,14 @@ TEST_PROGS += \ properties \ reference \ ifaceproperties \ - valuearray + valuearray \ + autoproperties \ + $(NULL) + +SAMPLE_PROGS = \ + gproperty-example-base \ + gproperty-example-derived \ + $(NULL) signals_SOURCES = signals.c marshalers.c diff --git a/gobject/tests/autoproperties.c b/gobject/tests/autoproperties.c new file mode 100644 index 000000000..81e090a79 --- /dev/null +++ b/gobject/tests/autoproperties.c @@ -0,0 +1,596 @@ +#include +#include + +/* XXX: yuck, but a lot of the functions are macro-ized */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +typedef struct _TestObject TestObject; +typedef struct _TestObjectPrivate TestObjectPrivate; +typedef struct _TestObjectClass TestObjectClass; + +typedef enum { + TEST_ENUM_VALUE_FOO = -1, + TEST_ENUM_VALUE_BAR = 0, + TEST_ENUM_VALUE_BAZ = 1 +} TestEnumValue; + +typedef enum { + TEST_FLAGS_VALUE_FOO = 0, + TEST_FLAGS_VALUE_BAR = 1 << 0, + TEST_FLAGS_VALUE_BAZ = 1 << 1 +} TestFlagsValue; + +typedef struct { + int x, y, width, height; + + int ref_count; +} TestBoxed; + +struct _TestObject +{ + GObject parent_instance; + + TestObjectPrivate *priv; +}; + +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +struct _TestObjectPrivate +{ + gint dummy; + + gint foo; + + gboolean bar; + + gchar *str; + gboolean str_set; + + gint8 single_byte; + gint16 double_byte; + gint32 four_bytes; + + float width; + double x_align; + + TestEnumValue enum_value; + TestFlagsValue flags_value; + + TestBoxed *boxed; +}; + +enum +{ + PROP_0, + + PROP_FOO, + PROP_BAR, + PROP_STR, + PROP_STR_SET, + PROP_BAZ, + PROP_SINGLE_BYTE, + PROP_DOUBLE_BYTE, + PROP_FOUR_BYTES, + PROP_WIDTH, + PROP_X_ALIGN, + PROP_ENUM_VALUE, + PROP_FLAGS_VALUE, + PROP_BOXED, + + LAST_PROP +}; + +GType test_enum_value_get_type (void); /* for -Wmissing-prototypes */ + +GType +test_enum_value_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GEnumValue values[] = { + { TEST_ENUM_VALUE_FOO, "TEST_ENUM_VALUE_FOO", "foo" }, + { TEST_ENUM_VALUE_BAR, "TEST_ENUM_VALUE_BAR", "bar" }, + { TEST_ENUM_VALUE_BAZ, "TEST_ENUM_VALUE_BAZ", "baz" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_enum_register_static (g_intern_static_string ("TestEnumValue"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +GType test_flags_value_get_type (void); /* for -Wmissing-prototypes */ + +GType +test_flags_value_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GFlagsValue values[] = { + { TEST_FLAGS_VALUE_FOO, "TEST_FLAGS_VALUE_FOO", "foo" }, + { TEST_FLAGS_VALUE_BAR, "TEST_FLAGS_VALUE_BAR", "bar" }, + { TEST_FLAGS_VALUE_BAZ, "TEST_FLAGS_VALUE_BAZ", "baz" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_flags_register_static (g_intern_static_string ("TestFlagsValue"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +static TestBoxed * +test_boxed_new (int x, + int y, + int width, + int height) +{ + TestBoxed *retval = g_new (TestBoxed, 1); + + retval->x = x; + retval->y = y; + retval->width = width; + retval->height = height; + retval->ref_count = 1; + + return retval; +} + +static gpointer +test_boxed_copy (gpointer data) +{ + if (data != NULL) + { + TestBoxed *boxed = data; + + if (g_test_verbose ()) + g_print ("*** copy of boxed %p (ref count: %d) ***\n", boxed, boxed->ref_count); + + if (boxed->ref_count < 0) + return test_boxed_new (boxed->x, boxed->y, boxed->width, boxed->height); + + boxed->ref_count += 1; + } + + return data; +} + +static void +test_boxed_free (gpointer data) +{ + if (data != NULL) + { + TestBoxed *boxed = data; + + if (g_test_verbose ()) + g_print ("*** free of boxed %p (ref count: %d) ***\n", boxed, boxed->ref_count); + + if (boxed->ref_count < 0) + return; + + boxed->ref_count -= 1; + + if (boxed->ref_count == 0) + g_free (boxed); + } +} + +GType test_boxed_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_BOXED_TYPE (TestBoxed, test_boxed, test_boxed_copy, test_boxed_free) + +static GParamSpec *test_object_properties[LAST_PROP] = { NULL, }; + +GType test_object_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT) + +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, int, foo) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, gboolean, bar) +G_DEFINE_PROPERTY_GET (TestObject, test_object, gboolean, str_set) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, gint8, single_byte) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, gint16, double_byte) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, gint32, four_bytes) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, float, width) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, double, x_align) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, TestEnumValue, enum_value) +G_DEFINE_PROPERTY_GET_SET (TestObject, test_object, TestFlagsValue, flags_value) + +G_DEFINE_PROPERTY_SET (TestObject, test_object, const TestBoxed *, boxed) + +void +test_object_get_boxed (TestObject *self, + TestBoxed *value) +{ + TestBoxed *boxed; + + g_property_get (G_PROPERTY (test_object_properties[PROP_BOXED]), self, &boxed); + + /* make sure that g_property_get() didn't copy/ref the pointer */ + g_assert (boxed == self->priv->boxed); + g_assert_cmpint (boxed->ref_count, ==, self->priv->boxed->ref_count); + + *value = *boxed; +} + +gboolean +test_object_set_str (TestObject *self, + const gchar *value) +{ + TestObjectPrivate *priv; + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, test_object_get_type ()), FALSE); + + priv = self->priv; + + if (g_strcmp0 (priv->str, value) == 0) + return FALSE; + + g_free (priv->str); + priv->str = g_strdup (value); + + if (priv->str != NULL) + priv->str_set = TRUE; + else + priv->str_set = FALSE; + + g_object_notify_by_pspec (G_OBJECT (self), test_object_properties[PROP_STR_SET]); + + return TRUE; +} + +G_DEFINE_PROPERTY_GET (TestObject, test_object, const gchar *, str); + +static void +test_object_finalize (GObject *gobject) +{ + TestObjectPrivate *priv = ((TestObject *) gobject)->priv; + + test_boxed_free (priv->boxed); + g_free (priv->str); + + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = test_object_finalize; + + g_type_class_add_private (klass, sizeof (TestObjectPrivate)); + + test_object_properties[PROP_FOO] = + g_int_property_new ("foo", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, foo), + NULL, NULL); + g_property_set_range (G_PROPERTY (test_object_properties[PROP_FOO]), -1, 100); + g_property_set_default (G_PROPERTY (test_object_properties[PROP_FOO]), 50); + + test_object_properties[PROP_BAR] = + g_boolean_property_new ("bar", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, bar), + NULL, NULL); + + test_object_properties[PROP_STR] = + g_string_property_new ("str", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, str), + (GPropertyStringSet) test_object_set_str, + NULL); + + test_object_properties[PROP_STR_SET] = + g_boolean_property_new ("str-set", G_PROPERTY_READABLE, + G_STRUCT_OFFSET (TestObjectPrivate, str_set), + NULL, NULL); + + test_object_properties[PROP_BAZ] = + g_int_property_new ("baz", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, foo), + NULL, NULL); + + test_object_properties[PROP_SINGLE_BYTE] = + g_int8_property_new ("single-byte", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, single_byte), + NULL, NULL); + + test_object_properties[PROP_DOUBLE_BYTE] = + g_int16_property_new ("double-byte", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, double_byte), + NULL, NULL); + + test_object_properties[PROP_FOUR_BYTES] = + g_int32_property_new ("four-bytes", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, four_bytes), + NULL, NULL); + + test_object_properties[PROP_WIDTH] = + g_float_property_new ("width", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, width), + NULL, NULL); + g_property_set_range (G_PROPERTY (test_object_properties[PROP_WIDTH]), 0.0, G_MAXFLOAT); + + test_object_properties[PROP_X_ALIGN] = + g_double_property_new ("x-align", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, x_align), + NULL, NULL); + g_property_set_range (G_PROPERTY (test_object_properties[PROP_X_ALIGN]), 0.0, 1.0); + g_property_set_default (G_PROPERTY (test_object_properties[PROP_X_ALIGN]), 0.5); + + test_object_properties[PROP_ENUM_VALUE] = + g_enum_property_new ("enum-value", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, enum_value), + NULL, NULL); + g_property_set_prerequisite (G_PROPERTY (test_object_properties[PROP_ENUM_VALUE]), + test_enum_value_get_type ()); + g_property_set_default (G_PROPERTY (test_object_properties[PROP_ENUM_VALUE]), + TEST_ENUM_VALUE_BAR); + + test_object_properties[PROP_FLAGS_VALUE] = + g_flags_property_new ("flags-value", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, flags_value), + NULL, NULL); + g_property_set_prerequisite (G_PROPERTY (test_object_properties[PROP_FLAGS_VALUE]), + test_flags_value_get_type ()); + g_property_set_default (G_PROPERTY (test_object_properties[PROP_FLAGS_VALUE]), + TEST_FLAGS_VALUE_FOO); + + test_object_properties[PROP_BOXED] = + g_boxed_property_new ("boxed", G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + G_STRUCT_OFFSET (TestObjectPrivate, boxed), + NULL, NULL); + g_property_set_prerequisite (G_PROPERTY (test_object_properties[PROP_BOXED]), + test_boxed_get_type ()); + + g_object_class_install_properties (G_OBJECT_CLASS (klass), + G_N_ELEMENTS (test_object_properties), + test_object_properties); +} + +static void +test_object_init (TestObject *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, test_object_get_type (), TestObjectPrivate); + + g_property_get_default (G_PROPERTY (test_object_properties[PROP_FOO]), + self, + &(self->priv->foo)); + g_property_get_default (G_PROPERTY (test_object_properties[PROP_X_ALIGN]), + self, + &(self->priv->x_align)); + g_property_get_default (G_PROPERTY (test_object_properties[PROP_ENUM_VALUE]), + self, + &(self->priv->enum_value)); + g_property_get_default (G_PROPERTY (test_object_properties[PROP_FLAGS_VALUE]), + self, + &(self->priv->flags_value)); +} + +static void +autoproperties_base (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (t, test_object_get_type ())); + + g_object_unref (t); +} + +static void +autoproperties_constructor (void) +{ + TestObject *t = g_object_new (test_object_get_type (), + "str", "Hello, World!", + "x-align", 1.0, + NULL); + + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (t, test_object_get_type ())); + g_assert_cmpstr (test_object_get_str (t), ==, "Hello, World!"); + g_assert_cmpfloat (test_object_get_x_align (t), ==, 1.0); + + g_object_unref (t); +} + +typedef TestObject TestDerived; +typedef TestObjectClass TestDerivedClass; + +G_DEFINE_TYPE (TestDerived, test_derived, test_object_get_type ()) + +static void +test_derived_class_init (TestDerivedClass *klass) +{ + g_object_class_override_property_default (G_OBJECT_CLASS (klass), "foo", -1); + g_object_class_override_property_default (G_OBJECT_CLASS (klass), "enum-value", TEST_ENUM_VALUE_BAZ); +} + +static void +test_derived_init (TestDerived *self) +{ + GValue value = { 0, }; + + g_value_init (&value, g_property_get_value_type (G_PROPERTY (test_object_properties[PROP_FOO]))); + g_property_get_default_value_for_type (G_PROPERTY (test_object_properties[PROP_FOO]), + test_derived_get_type (), + &value); + + g_assert_cmpint (g_value_get_int (&value), !=, 50); + g_assert_cmpint (g_value_get_int (&value), ==, -1); + + test_object_set_foo ((TestObject *) self, g_value_get_int (&value)); + + g_value_unset (&value); + + test_object_set_enum_value ((TestObject *) self, TEST_ENUM_VALUE_BAZ); +} + +static void +autoproperties_default (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + + g_assert (g_type_is_a (G_OBJECT_TYPE (t), test_object_get_type ())); + g_assert_cmpint (test_object_get_foo (t), ==, 50); + g_assert_cmpfloat (test_object_get_x_align (t), ==, 0.5f); + g_assert (test_object_get_enum_value (t) == TEST_ENUM_VALUE_BAR); + g_assert (test_object_get_flags_value (t) == TEST_FLAGS_VALUE_FOO); + + g_object_unref (t); + + t = g_object_new (test_derived_get_type (), NULL); + + g_assert (g_type_is_a (G_OBJECT_TYPE (t), test_object_get_type ())); + g_assert (g_type_is_a (G_OBJECT_TYPE (t), test_derived_get_type ())); + g_assert_cmpint (test_object_get_foo (t), ==, -1); + g_assert (test_object_get_enum_value (t) == TEST_ENUM_VALUE_BAZ); + + g_object_unref (t); +} + +static void +autoproperties_range (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + GProperty *p; + gint i_min, i_max; + gdouble d_min, d_max; + + p = (GProperty *) g_object_class_find_property (G_OBJECT_GET_CLASS (t), "foo"); + g_assert (G_IS_PROPERTY (p)); + + g_property_get_range (p, &i_min, &i_max); + g_assert_cmpint (i_min, ==, -1); + g_assert_cmpint (i_max, ==, 100); + + p = (GProperty *) g_object_class_find_property (G_OBJECT_GET_CLASS (t), "x-align"); + g_assert (G_IS_PROPERTY (p)); + + g_property_get_range (p, &d_min, &d_max); + g_assert_cmpfloat (d_min, ==, 0.0); + g_assert_cmpfloat (d_max, ==, 1.0); + + g_object_unref (t); +} + +static void +autoproperties_accessors (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + + test_object_set_foo (t, 42); + g_assert_cmpint (test_object_get_foo (t), ==, 42); + + test_object_set_str (t, "hello"); + g_assert_cmpstr (test_object_get_str (t), ==, "hello"); + g_assert (test_object_get_str_set (t)); + + g_assert (!test_object_get_bar (t)); + + test_object_set_single_byte (t, 64); + g_assert_cmpint (test_object_get_single_byte (t), ==, 64); + + test_object_set_double_byte (t, G_MAXINT16 / 2); + g_assert_cmpint (test_object_get_double_byte (t), ==, G_MAXINT16 / 2); + + test_object_set_four_bytes (t, 47); + g_assert_cmpint (test_object_get_four_bytes (t), ==, 47); + + test_object_set_width (t, 640); + g_assert_cmpfloat (test_object_get_width (t), ==, 640.0f); + + test_object_set_x_align (t, 1.0); + g_assert_cmpfloat (test_object_get_x_align (t), ==, 1.0); + + g_object_unref (t); +} + +static void +autoproperties_validate (void) +{ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT|G_TEST_TRAP_SILENCE_STDERR)) + { + TestObject *t = g_object_new (test_object_get_type (), NULL); + test_object_set_foo (t, 101); + g_object_unref (t); + exit (0); + } + g_test_trap_assert_failed (); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT|G_TEST_TRAP_SILENCE_STDERR)) + { + TestObject *t = g_object_new (test_object_get_type (), NULL); + test_object_set_foo (t, -10); + g_object_unref (t); + exit (0); + } + g_test_trap_assert_failed (); +} + +static void +autoproperties_object_set (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + TestBoxed boxed = { 0, 0, 200, 200, -1 }; + TestBoxed check; + + g_object_set (t, + "foo", 42, + "bar", TRUE, + "flags-value", (TEST_FLAGS_VALUE_BAR | TEST_FLAGS_VALUE_BAZ), + "boxed", &boxed, + NULL); + + g_assert_cmpint (test_object_get_foo (t), ==, 42); + g_assert (test_object_get_bar (t)); + g_assert ((test_object_get_flags_value (t) & TEST_FLAGS_VALUE_BAZ) != 0); + test_object_get_boxed (t, &check); + g_assert_cmpint (boxed.y, ==, check.y); + g_assert_cmpint (boxed.width, ==, check.width); + + g_object_unref (t); +} + +static void +autoproperties_object_get (void) +{ + TestObject *t = g_object_new (test_object_get_type (), NULL); + TestBoxed *boxed; + gdouble x_align; + gfloat width; + + g_object_get (t, "x-align", &x_align, "width", &width, "boxed", &boxed, NULL); + g_assert_cmpfloat (x_align, ==, 0.5); + g_assert_cmpfloat (width, ==, 0); + g_assert (boxed == NULL); + + g_object_unref (t); +} + +#pragma GCC diagnostic pop + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_test_add_func ("/auto-properties/base", autoproperties_base); + g_test_add_func ("/auto-properties/constructor", autoproperties_constructor); + g_test_add_func ("/auto-properties/default", autoproperties_default); + g_test_add_func ("/auto-properties/range", autoproperties_range); + g_test_add_func ("/auto-properties/accessors", autoproperties_accessors); + g_test_add_func ("/auto-properties/validate", autoproperties_validate); + g_test_add_func ("/auto-properties/object-set", autoproperties_object_set); + g_test_add_func ("/auto-properties/object-get", autoproperties_object_get); + + return g_test_run (); +} diff --git a/gobject/tests/gproperty-example-base.c b/gobject/tests/gproperty-example-base.c new file mode 100644 index 000000000..1ffee63da --- /dev/null +++ b/gobject/tests/gproperty-example-base.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include + +#define TEST_TYPE_FILE (test_file_get_type ()) +#define TEST_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_FILE, TestFile)) +#define TEST_IS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_FILE)) + +typedef struct _TestFile TestFile; +typedef struct _TestFilePrivate TestFilePrivate; +typedef struct _TestFileClass TestFileClass; + +struct _TestFile +{ + GObject parent_instance; + + TestFilePrivate *priv; +}; + +struct _TestFileClass +{ + GObjectClass parent_class; +}; + +struct _TestFilePrivate +{ + gchar *path; + + gint64 size; +}; + +GType test_file_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (TestFile, test_file, G_TYPE_OBJECT) + +enum { PROP_0, PROP_PATH, PROP_SIZE, LAST_PROP }; + +static GParamSpec *test_file_properties[LAST_PROP] = { 0, }; + +/* for -Wmissing-prototypes */ +G_DECLARE_PROPERTY_GET_SET (TestFile, test_file, const gchar *, path) + +G_DEFINE_PROPERTY_GET (TestFile, test_file, const gchar *, path) + +void +test_file_set_path (TestFile *self, + const gchar *value) +{ + GStatBuf s_buf; + + g_return_if_fail (TEST_IS_FILE (self)); + g_return_if_fail (value != NULL && *value != '\0'); + + if (g_strcmp0 (value, self->priv->path) == 0) + return; + + if (g_stat (value, &s_buf) == -1) + { + int saved_errno = errno; + + g_warning ("Unable to access the path: %s", g_strerror (saved_errno)); + + return; + } + + self->priv->size = (gint64) s_buf.st_size; + + g_free (self->priv->path); + self->priv->path = g_strdup (value); + + g_object_notify_by_pspec (G_OBJECT (self), test_file_properties[PROP_SIZE]); +} + +/* for -Wmissing-prototypes */ +G_DECLARE_PROPERTY_GET (TestFile, test_file, gint64, size) + +G_DEFINE_PROPERTY_GET (TestFile, test_file, gint64, size) + +static void +test_file_finalize (GObject *gobject) +{ + TestFilePrivate *priv = TEST_FILE (gobject)->priv; + + g_free (priv->path); + + G_OBJECT_CLASS (test_file_parent_class)->finalize (gobject); +} + +static void +test_file_class_init (TestFileClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = test_file_finalize; + + g_type_class_add_private (klass, sizeof (TestFilePrivate)); + + test_file_properties[PROP_PATH] = + g_string_property_new ("path", + G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + G_STRUCT_OFFSET (TestFilePrivate, path), + (GPropertyStringSet) test_file_set_path, + NULL); + + test_file_properties[PROP_SIZE] = + g_string_property_new ("size", G_PROPERTY_READABLE, + G_STRUCT_OFFSET (TestFilePrivate, size), + NULL, NULL); + + g_object_class_install_properties (G_OBJECT_CLASS (klass), + G_N_ELEMENTS (test_file_properties), + test_file_properties); +} + +static void +test_file_init (TestFile *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TEST_TYPE_FILE, + TestFilePrivate); +} + +int +main (int argc, + char *argv[]) +{ + TestFile *f; + int i; + + f = g_object_new (TEST_TYPE_FILE, NULL); + + for (i = 1; i < argc; i++) + { + test_file_set_path (f, argv[i]); + + g_print ("File: %s, size: %" G_GINT64_FORMAT "\n", + test_file_get_path (f), + test_file_get_size (f)); + } + + g_object_unref (f); + + return EXIT_SUCCESS; +} diff --git a/gobject/tests/gproperty-example-derived.c b/gobject/tests/gproperty-example-derived.c new file mode 100644 index 000000000..6334a94f8 --- /dev/null +++ b/gobject/tests/gproperty-example-derived.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include +#include +#include +#include + +/* Test::File {{{ */ + +#define TEST_TYPE_FILE (test_file_get_type ()) +#define TEST_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_FILE, TestFile)) +#define TEST_IS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_FILE)) + +typedef struct _TestFile TestFile; +typedef struct _TestFilePrivate TestFilePrivate; +typedef struct _TestFileClass TestFileClass; + +struct _TestFile +{ + GObject parent_instance; + + TestFilePrivate *priv; +}; + +struct _TestFileClass +{ + GObjectClass parent_class; +}; + +struct _TestFilePrivate +{ + gchar *path; + gchar *extension; + + gint64 size; +}; + +GType test_file_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (TestFile, test_file, G_TYPE_OBJECT) + +enum { PROP_FILE_0, PROP_PATH, PROP_SIZE, PROP_EXTENSION, LAST_FILE_PROP }; + +static GParamSpec *test_file_properties[LAST_FILE_PROP] = { NULL, }; + +G_DECLARE_PROPERTY_GET_SET (TestFile, test_file, const gchar *, path) + +G_DEFINE_PROPERTY_GET (TestFile, test_file, const gchar *, path) + +void +test_file_set_path (TestFile *self, + const gchar *value) +{ + GStatBuf s_buf; + + g_return_if_fail (TEST_IS_FILE (self)); + g_return_if_fail (value != NULL && *value != '\0'); + + if (g_strcmp0 (value, self->priv->path) == 0) + return; + + if (g_stat (value, &s_buf) == -1) + { + int saved_errno = errno; + + g_warning ("Unable to access the path: %s", g_strerror (saved_errno)); + + return; + } + + self->priv->size = (gint64) s_buf.st_size; + + g_free (self->priv->path); + self->priv->path = g_strdup (value); + + self->priv->extension = strrchr (self->priv->path, '.'); + + if (self->priv->extension != NULL && + strlen (self->priv->extension) != 0) + { + self->priv->extension += 1; + } + else + self->priv->extension = NULL; + + g_object_notify_by_pspec (G_OBJECT (self), test_file_properties[PROP_PATH]); + g_object_notify_by_pspec (G_OBJECT (self), test_file_properties[PROP_SIZE]); + g_object_notify_by_pspec (G_OBJECT (self), test_file_properties[PROP_EXTENSION]); +} + +G_DECLARE_PROPERTY_GET (TestFile, test_file, const gchar *, extension) +G_DEFINE_PROPERTY_GET (TestFile, test_file, const gchar *, extension) + +G_DECLARE_PROPERTY_GET (TestFile, test_file, gint64, size) +G_DEFINE_PROPERTY_GET (TestFile, test_file, gint64, size) + +static void +test_file_finalize (GObject *gobject) +{ + TestFilePrivate *priv = TEST_FILE (gobject)->priv; + + g_free (priv->path); + + G_OBJECT_CLASS (test_file_parent_class)->finalize (gobject); +} + +static void +test_file_class_init (TestFileClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = test_file_finalize; + + g_type_class_add_private (klass, sizeof (TestFilePrivate)); + + test_file_properties[PROP_PATH] = + g_string_property_new ("path", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestFilePrivate, path), + (GPropertyStringSet) test_file_set_path, + NULL); + + test_file_properties[PROP_EXTENSION] = + g_string_property_new ("extension", G_PROPERTY_READABLE, + G_STRUCT_OFFSET (TestFilePrivate, extension), + NULL, NULL); + + test_file_properties[PROP_SIZE] = + g_string_property_new ("size", G_PROPERTY_READABLE, + G_STRUCT_OFFSET (TestFilePrivate, size), + NULL, NULL); + + g_object_class_install_properties (G_OBJECT_CLASS (klass), + G_N_ELEMENTS (test_file_properties), + test_file_properties); +} + +static void +test_file_init (TestFile *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TEST_TYPE_FILE, + TestFilePrivate); +} + +/* }}} */ + +/* Test::File::Mp3 {{{ */ + +#define TEST_TYPE_FILE_MP3 (test_file_mp3_get_type ()) +#define TEST_FILE_MP3(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_FILE_MP3, TestFileMp3)) +#define TEST_IS_FILE_MP3(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_FILE_MP3)) + +typedef struct _TestFileMp3 TestFileMp3; +typedef struct _TestFileMp3Private TestFileMp3Private; +typedef struct _TestFileMp3Class TestFileMp3Class; + +struct _TestFileMp3 +{ + TestFile parent_instance; + + TestFileMp3Private *priv; +}; + +struct _TestFileMp3Class +{ + TestFileClass parent_class; +}; + +struct _TestFileMp3Private +{ + gchar *artist; + gchar *title; + gchar *album; + + gint64 duration; +}; + +GType test_file_mp3_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (TestFileMp3, test_file_mp3, TEST_TYPE_FILE) + +enum { PROP_MP3_0, PROP_ARTIST, PROP_TITLE, PROP_ALBUM, PROP_DURATION, LAST_MP3_PROP }; + +static GParamSpec *test_file_mp3_properties[LAST_MP3_PROP] = { NULL, }; + +G_DECLARE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, artist) /* for -Wmissing-prototypes */ +G_DEFINE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, artist) + +G_DECLARE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, title) /* for -Wmissing-prototypes */ +G_DEFINE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, title) + +G_DECLARE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, album) /* for -Wmissing-prototypes */ +G_DEFINE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, const gchar *, album) + +G_DECLARE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, gint64, duration) /* for -Wmissing-prototypes */ +G_DEFINE_PROPERTY_GET_SET (TestFileMp3, test_file_mp3, gint64, duration) + +static void +test_file_mp3_play (TestFileMp3 *file) +{ + g_return_if_fail (TEST_IS_FILE_MP3 (file)); + + g_print ("Playing...\n"); +} + +static void +test_file_mp3_finalize (GObject *gobject) +{ + TestFileMp3Private *priv = TEST_FILE_MP3 (gobject)->priv; + + g_free (priv->artist); + g_free (priv->album); + g_free (priv->title); + + G_OBJECT_CLASS (test_file_mp3_parent_class)->finalize (gobject); +} + +static void +test_file_mp3_class_init (TestFileMp3Class *klass) +{ + G_OBJECT_CLASS (klass)->finalize = test_file_mp3_finalize; + + g_type_class_add_private (klass, sizeof (TestFileMp3Private)); + + test_file_mp3_properties[PROP_ALBUM] = + g_string_property_new ("album", + G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + G_STRUCT_OFFSET (TestFileMp3Private, album), + NULL, NULL); + g_property_set_default (G_PROPERTY (test_file_mp3_properties[PROP_ALBUM]), + klass, + "Unknown Album"); + + test_file_mp3_properties[PROP_ARTIST] = + g_string_property_new ("artist", + G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + G_STRUCT_OFFSET (TestFileMp3Private, artist), + NULL, NULL); + g_property_set_default (G_PROPERTY (test_file_mp3_properties[PROP_ARTIST]), + klass, + "Unknown Author"); + + test_file_mp3_properties[PROP_TITLE] = + g_string_property_new ("title", + G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET, + G_STRUCT_OFFSET (TestFileMp3Private, title), + NULL, NULL); + g_property_set_default (G_PROPERTY (test_file_mp3_properties[PROP_TITLE]), + klass, + "Unknown Track"); + + test_file_mp3_properties[PROP_DURATION] = + g_int64_property_new ("duration", G_PROPERTY_READABLE, + G_STRUCT_OFFSET (TestFileMp3Private, duration), + NULL, NULL); + + g_object_class_install_properties (G_OBJECT_CLASS (klass), + G_N_ELEMENTS (test_file_mp3_properties), + test_file_mp3_properties); +} + +static void +test_file_mp3_init (TestFileMp3 *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TEST_TYPE_FILE_MP3, + TestFileMp3Private); + + g_property_get_default (G_PROPERTY (test_file_mp3_properties[PROP_ARTIST]), + self, + &(self->priv->artist)); + g_property_get_default (G_PROPERTY (test_file_mp3_properties[PROP_ALBUM]), + self, + &(self->priv->album)); + g_property_get_default (G_PROPERTY (test_file_mp3_properties[PROP_TITLE]), + self, + &(self->priv->title)); +} + +/* }}} */ + +/* main {{{ */ +int +main (int argc, + char *argv[]) +{ + TestFile *f; + int i; + + f = g_object_new (TEST_TYPE_FILE_MP3, NULL); + + for (i = 1; i < argc; i++) + { + test_file_set_path (f, argv[i]); + + if (g_strcmp0 (test_file_get_extension (f), "mp3") != 0) + continue; + + g_print ("File: %s, size: %" G_GINT64_FORMAT "\n", + test_file_get_path (f), + test_file_get_size (f)); + g_print (" Track: %s - %s\n", + test_file_mp3_get_artist (TEST_FILE_MP3 (f)), + test_file_mp3_get_title (TEST_FILE_MP3 (f))); + + test_file_mp3_play (TEST_FILE_MP3 (f)); + } + + g_object_unref (f); + + return EXIT_SUCCESS; +} +/* }}} */ diff --git a/tests/gobject/performance.c b/tests/gobject/performance.c index e9310a587..13b0ec72b 100644 --- a/tests/gobject/performance.c +++ b/tests/gobject/performance.c @@ -424,6 +424,333 @@ complex_object_init (ComplexObject *complex_object) complex_object->val2 = 43; } +/**************************************************************** + * ParamspecObject is an object using GParamSpec for properties * + ****************************************************************/ + +#define PARAMSPEC_TYPE_OBJECT (paramspec_object_get_type ()) +typedef struct _ParamspecObject ParamspecObject; +typedef struct _ParamspecObjectClass ParamspecObjectClass; +typedef struct _ParamspecObjectPrivate ParamspecObjectPrivate; + +struct _ParamspecObject +{ + GObject parent_instance; + + ParamspecObjectPrivate *priv; +}; + +struct _ParamspecObjectClass +{ + GObjectClass parent_class; +}; + +GType paramspec_object_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (ParamspecObject, paramspec_object, G_TYPE_OBJECT) + +#define PARAMSPEC_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PARAMSPEC_TYPE_OBJECT, ParamspecObject)) + +struct _ParamspecObjectPrivate +{ + int foo; + gchar *bar; + float baz; +}; + +enum { + PROP_PSPEC_0, + PROP_PSPEC_FOO, + PROP_PSPEC_BAR, + PROP_PSPEC_BAZ, + PROP_PSPEC_LAST +}; + +static GParamSpec *pspec_props[PROP_PSPEC_LAST] = { NULL, }; + +static void +paramspec_object_finalize (GObject *object) +{ + ParamspecObjectPrivate *priv = PARAMSPEC_OBJECT (object)->priv; + + g_free (priv->bar); + + G_OBJECT_CLASS (paramspec_object_parent_class)->finalize (object); +} + +static void +paramspec_object_set_foo (ParamspecObject *object, + int value) +{ + if (object->priv->foo == value) + return; + + object->priv->foo = value; + + g_object_notify_by_pspec (G_OBJECT (object), pspec_props[PROP_PSPEC_FOO]); +} + +static int +paramspec_object_get_foo (ParamspecObject *object) +{ + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, paramspec_object_get_type ()), 0); + + return object->priv->foo; +} + +static void +paramspec_object_set_bar (ParamspecObject *object, + const char *value) +{ + if (g_strcmp0 (object->priv->bar, value) == 0) + return; + + g_free (object->priv->bar); + object->priv->bar = g_strdup (value); + + g_object_notify_by_pspec (G_OBJECT (object), pspec_props[PROP_PSPEC_BAR]); +} + +static void +paramspec_object_set_baz (ParamspecObject *object, + float value) +{ + if (object->priv->baz == value) + return; + + object->priv->baz = value; + + g_object_notify_by_pspec (G_OBJECT (object), pspec_props[PROP_PSPEC_BAZ]); +} + +static float +paramspec_object_get_baz (ParamspecObject *object) +{ + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, paramspec_object_get_type ()), 0.0); + + return object->priv->baz; +} + +static void +paramspec_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_PSPEC_FOO: + paramspec_object_set_foo (PARAMSPEC_OBJECT (object), g_value_get_int (value)); + break; + case PROP_PSPEC_BAR: + paramspec_object_set_bar (PARAMSPEC_OBJECT (object), g_value_get_string (value)); + break; + case PROP_PSPEC_BAZ: + paramspec_object_set_baz (PARAMSPEC_OBJECT (object), g_value_get_float (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +paramspec_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_PSPEC_FOO: + g_value_set_int (value, PARAMSPEC_OBJECT (object)->priv->foo); + break; + case PROP_PSPEC_BAR: + g_value_set_string (value, PARAMSPEC_OBJECT (object)->priv->bar); + break; + case PROP_PSPEC_BAZ: + g_value_set_float (value, PARAMSPEC_OBJECT (object)->priv->baz); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +paramspec_object_class_init (ParamspecObjectClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (ParamspecObjectPrivate)); + + object_class->finalize = paramspec_object_finalize; + object_class->set_property = paramspec_object_set_property; + object_class->get_property = paramspec_object_get_property; + + pspec_props[PROP_PSPEC_FOO] = + g_param_spec_int ("foo", + NULL, NULL, + -100, 100, 50, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + pspec_props[PROP_PSPEC_BAR] = + g_param_spec_string ("bar", + NULL, NULL, + "Hello", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + pspec_props[PROP_PSPEC_BAZ] = + g_param_spec_float ("baz", + NULL, NULL, + 0.0f, G_MAXFLOAT, 0.0f, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_PSPEC_LAST, pspec_props); +} + +static void +paramspec_object_init (ParamspecObject *self) +{ + self->priv = g_type_instance_get_private ((GTypeInstance *) self, PARAMSPEC_TYPE_OBJECT); + + self->priv->foo = 50; + self->priv->bar = g_strdup ("Hello"); + self->priv->baz = 0.0f; +} + +/************************************************************** + * PropertyObject is an object using GProperty for properties * + **************************************************************/ + +#define PROPERTY_TYPE_OBJECT (property_object_get_type ()) +typedef struct _PropertyObject PropertyObject; +typedef struct _PropertyObjectClass PropertyObjectClass; +typedef struct _PropertyObjectPrivate PropertyObjectPrivate; + +struct _PropertyObject +{ + GObject parent_instance; + + PropertyObjectPrivate *priv; +}; + +struct _PropertyObjectClass +{ + GObjectClass parent_class; +}; + +GType property_object_get_type (void); /* for -Wmissing-prototypes */ + +G_DEFINE_TYPE (PropertyObject, property_object, G_TYPE_OBJECT) + +#define PROPERTY_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PROPERTY_TYPE_OBJECT, PropertyObject)) + +struct _PropertyObjectPrivate +{ + int foo; + gchar *bar; + float baz; +}; + +enum { + PROP_PROPERTY_0, + PROP_PROPERTY_FOO, + PROP_PROPERTY_BAR, + PROP_PROPERTY_BAZ, + PROP_PROPERTY_LAST +}; + +static GParamSpec *property_props[PROP_PROPERTY_LAST] = { NULL, }; + +static void +property_object_finalize (GObject *object) +{ + PropertyObjectPrivate *priv = PROPERTY_OBJECT (object)->priv; + + g_free (priv->bar); + + G_OBJECT_CLASS (property_object_parent_class)->finalize (object); +} + +static void +property_object_class_init (PropertyObjectClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (PropertyObjectPrivate)); + + object_class->finalize = property_object_finalize; + + property_props[PROP_PROPERTY_FOO] = + g_int_property_new ("foo", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (PropertyObjectPrivate, foo), + NULL, NULL); + g_property_set_range ((GProperty *) property_props[PROP_PROPERTY_FOO], -100, 100); + g_property_set_default ((GProperty *) property_props[PROP_PROPERTY_FOO], 50); + + property_props[PROP_PROPERTY_BAR] = + g_string_property_new ("bar", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (PropertyObjectPrivate, bar), + NULL, NULL); + g_property_set_default ((GProperty *) property_props[PROP_PROPERTY_BAR], "Hello"); + + property_props[PROP_PROPERTY_BAZ] = + g_float_property_new ("baz", G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (PropertyObjectPrivate, baz), + NULL, NULL); + g_property_set_range ((GProperty *) property_props[PROP_PROPERTY_BAZ], 0.0f, G_MAXFLOAT); + g_property_set_default ((GProperty *) property_props[PROP_PROPERTY_BAZ], 0.0f); + + g_object_class_install_properties (object_class, PROP_PROPERTY_LAST, property_props); +} + +static void +property_object_init (PropertyObject *self) +{ + self->priv = g_type_instance_get_private ((GTypeInstance *) self, PROPERTY_TYPE_OBJECT); + + g_property_get_default ((GProperty *) property_props[PROP_PROPERTY_FOO], + self, + &(self->priv->foo)); + g_property_get_default ((GProperty *) property_props[PROP_PROPERTY_BAR], + self, + &(self->priv->bar)); + g_property_get_default ((GProperty *) property_props[PROP_PROPERTY_BAZ], + self, + &(self->priv->baz)); +} + +static void +property_object_set_foo (PropertyObject *self, int value) +{ + g_property_set (((GProperty *) property_props[PROP_PROPERTY_FOO]), self, value); +} + +static int +property_object_get_foo (PropertyObject *self) +{ + gint value; + + g_property_get (((GProperty *) property_props[PROP_PROPERTY_FOO]), self, &value); + + return value; +} + +static void +property_object_set_baz (PropertyObject *self, float value) +{ + g_property_set (((GProperty *) property_props[PROP_PROPERTY_BAZ]), self, value); +} + +static float +property_object_get_baz (PropertyObject *self) +{ + float value; + + g_property_get (((GProperty *) property_props[PROP_PROPERTY_BAZ]), self, &value); + + return value; +} + /************************************************************* * Test object construction performance *************************************************************/ @@ -508,6 +835,186 @@ test_construction_print_result (PerformanceTest *test, data->n_objects / time); } +/************************************************************* + * Test property accessors using GParamSpec + *************************************************************/ + +#define NUM_ACCESSES_PER_ROUND 10000 + +struct PropertyTest { + gpointer object; + int n_accesses; +}; + +static gpointer +test_property_setup (PerformanceTest *test) +{ + struct PropertyTest *data; + GType gtype; + + gtype = ((GType (*)())test->extra_data)(); + + data = g_new0 (struct PropertyTest, 1); + data->object = g_object_new (gtype, NULL); + + return data; +} + +static void +test_property_init (PerformanceTest *test, + gpointer _data, + double factor) +{ + struct PropertyTest *data = _data; + + data->n_accesses = factor * NUM_ACCESSES_PER_ROUND; +} + +static void +test_paramspec_direct_set_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo = i % 16; + gfloat baz = foo * 3.14f; + + paramspec_object_set_foo (data->object, foo); + paramspec_object_set_baz (data->object, baz); + } +} + +static void +test_paramspec_direct_get_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo; + gfloat baz; + + foo = paramspec_object_get_foo (data->object); + baz = paramspec_object_get_baz (data->object); + + g_assert_cmpfloat (baz, ==, 0); + g_assert_cmpint (foo, ==, 50); + } +} + + +static void +test_paramspec_object_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo = i % 16; + gfloat baz = foo * 3.14f; + + g_object_set (data->object, + "foo", foo, + "baz", baz, + NULL); + } +} + +static void +test_property_direct_set_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo = i % 16; + gfloat baz = foo * 3.14f; + + property_object_set_foo (data->object, foo); + property_object_set_baz (data->object, baz); + } +} + +static void +test_property_direct_get_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo; + gfloat baz; + + foo = property_object_get_foo (data->object); + baz = property_object_get_baz (data->object); + + g_assert_cmpfloat (baz, ==, 0); + g_assert_cmpint (foo, ==, 50); + } +} + +static void +test_property_object_run (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + int n_accesses, i; + + n_accesses = data->n_accesses; + for (i = 0; i < n_accesses; i++) + { + gint foo = i % 16; + gfloat baz = foo * 3.14f; + + g_object_set (data->object, + "foo", foo, + "baz", baz, + NULL); + } +} + +static void +test_property_finish (PerformanceTest *test, + gpointer _data) +{ +} + +static void +test_property_print_result (PerformanceTest *test, + gpointer _data, + gdouble time) +{ + struct PropertyTest *data = _data; + g_print ("Number of accesses per second: %.0f\n", + data->n_accesses / time); +} + +static void +test_property_teardown (PerformanceTest *test, + gpointer _data) +{ + struct PropertyTest *data = _data; + + g_object_unref (data->object); + g_free (data); +} + /************************************************************* * Test runtime type check performance *************************************************************/ @@ -781,6 +1288,66 @@ static PerformanceTest tests[] = { test_construction_teardown, test_construction_print_result }, + { + "gparamspec-direct-get", + paramspec_object_get_type, + test_property_setup, + test_property_init, + test_paramspec_direct_get_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, + { + "gproperty-direct-get", + property_object_get_type, + test_property_setup, + test_property_init, + test_property_direct_get_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, + { + "gparamspec-direct-set", + paramspec_object_get_type, + test_property_setup, + test_property_init, + test_paramspec_direct_set_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, + { + "gproperty-direct-set", + property_object_get_type, + test_property_setup, + test_property_init, + test_property_direct_set_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, + { + "gparamspec-gobject-access", + paramspec_object_get_type, + test_property_setup, + test_property_init, + test_paramspec_object_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, + { + "gproperty-gobject-access", + property_object_get_type, + test_property_setup, + test_property_init, + test_property_object_run, + test_property_finish, + test_property_teardown, + test_property_print_result + }, { "type-check", NULL,