diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2013-04-24 11:53:40 -0400 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2013-07-05 18:12:55 +0100 |
commit | 8c134ac8a4ecfa48891693c41f32caae52ca6d6d (patch) | |
tree | ab6df8c443f024429e8ffb279ed80963ab9ca183 | |
parent | 959940f89620da5953b989fc45a32a87ce0602cb (diff) | |
download | glib-8c134ac8a4ecfa48891693c41f32caae52ca6d6d.tar.gz |
gproperty: Add default values
GProperty should store the default value (if any) of a property, as well
as the eventual overrides coming from the sub-classes of the class that
defined the property.
https://bugzilla.gnome.org/show_bug.cgi?id=648526
-rw-r--r-- | gobject/gproperty.c | 446 | ||||
-rw-r--r-- | gobject/gproperty.h | 26 | ||||
-rw-r--r-- | gobject/tests/property.c | 32 |
3 files changed, 495 insertions, 9 deletions
diff --git a/gobject/gproperty.c b/gobject/gproperty.c index 00bc7f85b..da31d8d8f 100644 --- a/gobject/gproperty.c +++ b/gobject/gproperty.c @@ -355,6 +355,8 @@ struct _GProperty #define g_value_get_uint16 g_value_get_uint #define g_value_get_uint32 g_value_get_uint +static GType g_property_default_value_key = G_TYPE_INVALID; + static GParamFlags property_flags_to_param_flags (GPropertyFlags flags) { @@ -399,6 +401,23 @@ g_property_create (GType pspec_type, return prop; } +static inline void +g_property_ensure_prop_id (GProperty *property) +{ + char *prop_id; + + if (G_LIKELY (property->prop_id != 0)) + return; + + prop_id = g_strconcat ("-g-property-", + G_PARAM_SPEC (property)->name, + NULL); + + property->prop_id = g_quark_from_string (prop_id); + + g_free (prop_id); +} + #define DEFINE_PROPERTY_INTEGER(G_t, g_t, c_t, G_T, minVal, maxVal) \ typedef struct { \ GProperty parent; \ @@ -2610,14 +2629,7 @@ _g_property_set_installed (GProperty *property, property->priv_offset = g_type_class_get_instance_private_offset (g_class); } - if (property->prop_id == 0) - { - gchar *prop_id = g_strconcat ("-g-property-id-", - G_PARAM_SPEC (property)->name, - NULL); - property->prop_id = g_quark_from_string (prop_id); - g_free (prop_id); - } + g_property_ensure_prop_id (property); property->is_installed = TRUE; } @@ -3343,6 +3355,397 @@ g_property_get_range (GProperty *property, return retval; } +static void +value_unset_and_free (gpointer data) +{ + if (G_LIKELY (data != NULL)) + { + GValue *value = data; + + g_value_unset (value); + g_free (value); + } +} + +static inline void +g_property_set_default_value_internal (GProperty *property, + GValue *value) +{ + GHashTable *default_values; + + g_property_ensure_prop_id (property); + + default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property), + property->prop_id); + if (default_values == NULL) + { + default_values = g_hash_table_new_full (NULL, NULL, + NULL, + value_unset_and_free); + g_param_spec_set_qdata_full (G_PARAM_SPEC (property), + property->prop_id, + default_values, + (GDestroyNotify) g_hash_table_unref); + } + + g_hash_table_replace (default_values, + GSIZE_TO_POINTER (g_property_default_value_key), + value); +} + +static inline const GValue * +g_property_get_default_value_internal (GProperty *property, + GType gtype) +{ + GHashTable *default_values; + const GValue *default_value = NULL; + GType iter; + + if (property->prop_id == 0) + return NULL; + + default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property), + property->prop_id); + if (default_values == NULL) + return NULL; + + /* we look for overridden default values on ourselves and our parent + * classes first...*/ + iter = gtype; + while (iter != G_TYPE_INVALID && default_value == NULL) + { + default_value = g_hash_table_lookup (default_values, GSIZE_TO_POINTER (iter)); + + iter = g_type_parent (iter); + } + + /* ...as well as on our interfaces... */ + if (default_value == NULL) + { + GType *ifaces = NULL; + guint n_ifaces = 0; + + ifaces = g_type_interfaces (gtype, &n_ifaces); + while (n_ifaces-- && default_value == NULL) + { + iter = ifaces[n_ifaces]; + default_value = g_hash_table_lookup (default_values, GSIZE_TO_POINTER (iter)); + } + + g_free (ifaces); + } + + /* ...and if we fail, we pick the default value set by the class that + * installed the property first + */ + if (default_value == NULL) + { + default_value = g_hash_table_lookup (default_values, GSIZE_TO_POINTER (g_property_default_value_key)); + } + + return default_value; +} + +static inline void +g_property_override_default_value_internal (GProperty *property, + GType class_type, + GValue *value) +{ + GHashTable *default_values; + + g_property_ensure_prop_id (property); + + default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property), + property->prop_id); + if (default_values == NULL) + { + default_values = g_hash_table_new_full (NULL, NULL, + NULL, + value_unset_and_free); + g_param_spec_set_qdata_full (G_PARAM_SPEC (property), + property->prop_id, + default_values, + (GDestroyNotify) g_hash_table_unref); + } + + g_hash_table_replace (default_values, + GSIZE_TO_POINTER (class_type), + value); +} + +/** + * g_property_set_default_value: + * @property: a #GProperty + * @value: the default value of the property + * + * Sets the default value of @property using @value. + * + * This function will copy the passed #GValue, transforming it into the + * type required by the @property using the #GValue transformation rules + * if necessary. + * + * Since: 2.38 + */ +void +g_property_set_default_value (GProperty *property, + const GValue *value) +{ + GValue *default_value; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (value != NULL); + + default_value = g_new0 (GValue, 1); + g_value_init (default_value, G_PARAM_SPEC (property)->value_type); + + if (!g_value_transform (value, default_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 (G_VALUE_TYPE (default_value))); + return; + } + + /* will take ownership of the GValue */ + g_property_set_default_value_internal (property, default_value); +} + +/** + * g_property_get_default_value: + * @property: a #GProperty + * @gobject: a #GObject + * @value: a #GValue + * + * Retrieves the default value of @property using the type of + * the @object instance, and places it into @value. + * + * The default value takes into account eventual overrides installed by the + * class of @gobject. + * + * This function will initialize @value to the type of the property, + * if @value is not initialized; otherwise, it will obey #GValue + * transformation rules between the type of the property and the type + * of the passed #GValue. + * + * Since: 2.38 + */ +void +g_property_get_default_value (GProperty *property, + gpointer gobject, + GValue *value) +{ + const GValue *default_value; + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + + gtype = G_OBJECT_TYPE (gobject); + default_value = g_property_get_default_value_internal (property, gtype); + + /* initialize the GValue */ + if (!G_IS_VALUE (value)) + { + g_value_init (value, G_PARAM_SPEC (property)->value_type); + + if (default_value != NULL) + g_value_copy (default_value, value); + } + else + { + if (default_value == NULL) + return; + + 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))); + } + } +} + +/** + * g_property_override_default_value: + * @property: a #GProperty + * @class_type: the type that is overriding the default value of @property + * @value: a #GValue containing the new default value + * + * Overrides the default value of @property for the class of @class_type. + * + * This function should be called by sub-classes that desire overriding + * the default value of @property. + * + * Since: 2.38 + */ +void +g_property_override_default_value (GProperty *property, + GType class_type, + const GValue *value) +{ + GValue *default_value; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (class_type != G_TYPE_INVALID); + g_return_if_fail (value != NULL); + + default_value = g_new0 (GValue, 1); + g_value_init (default_value, G_PARAM_SPEC (property)->value_type); + + if (!g_value_transform (value, default_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 (G_VALUE_TYPE (default_value))); + return; + } + + /* will take ownership of the GValue */ + g_property_override_default_value_internal (property, class_type, default_value); +} + +/** + * g_property_set_default: + * @property: a #GProperty + * @...: the default value for the property + * + * Sets the default value of @property. + * + * This function is for the convenience of the C API users; language + * bindings should use the equivalent g_property_set_default_value() + * instead. + * + * Since: 2.38 + */ +void +g_property_set_default (GProperty *property, + ...) +{ + va_list var_args; + GValue *value; + char *error; + + g_return_if_fail (G_IS_PROPERTY (property)); + + va_start (var_args, property); + + value = g_new0 (GValue, 1); + G_VALUE_COLLECT_INIT (value, G_PARAM_SPEC (property)->value_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 */ + g_property_set_default_value_internal (property, value); + } + + va_end (var_args); +} + +/** + * g_property_get_default: + * @property: a #GProperty + * @gobject: a #GObject + * @...: return location for the default value + * + * Retrieves the default value of @property for the given @gobject instance. + * + * The default value takes into account eventual overrides installed by the + * class of @gobject. + * + * Since: 2.38 + */ +void +g_property_get_default (GProperty *property, + gpointer gobject, + ...) +{ + const GValue *default_value; + GValue value = G_VALUE_INIT; + char *error = NULL; + va_list var_args; + GType gtype; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + + g_value_init (&value, G_PARAM_SPEC (property)->value_type); + + gtype = G_OBJECT_TYPE (gobject); + default_value = g_property_get_default_value_internal (property, gtype); + if (default_value != NULL) + g_value_copy (default_value, &value); + + 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_type: the type that is overriding the default value of @property + * @...: the default value for the property + * + * Overrides the default value of @property for the class of @class_type. + * + * This function should be called by sub-classes that desire overriding + * the default value of @property. + * + * This function is for the convenience of the C API users; language + * bindings should use the equivalent g_property_override_default_value() + * instead. + * + * Since: 2.38 + */ +void +g_property_override_default (GProperty *property, + GType class_type, + ...) +{ + va_list var_args; + GValue *value; + char *error; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (class_type != G_TYPE_INVALID); + + va_start (var_args, class_type); + + value = g_new0 (GValue, 1); + G_VALUE_COLLECT_INIT (value, G_PARAM_SPEC (property)->value_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 */ + g_property_override_default_value_internal (property, class_type, value); + } + + va_end (var_args); +} + /** * g_property_set_va: * @property: a #GProperty @@ -4117,6 +4520,20 @@ g_property_get_value (GProperty *property, g_value_unset (value_p); } +void +g_property_init_default (GProperty *property, + gpointer gobject) +{ + const GValue *default_value; + + g_return_if_fail (G_IS_PROPERTY (property)); + g_return_if_fail (G_IS_OBJECT (gobject)); + + default_value = g_property_get_default_value_internal (property, G_OBJECT_TYPE (gobject)); + if (default_value != NULL) + g_property_set_value_internal (property, gobject, default_value); +} + /** * g_property_get_value_type: * @property: a #GProperty @@ -4555,12 +4972,25 @@ property_values_cmp (GParamSpec *pspec, } static void +property_value_set_default (GParamSpec *pspec, + GValue *value) +{ + const GValue *v; + + v = g_property_get_default_value_internal ((GProperty *) pspec, G_TYPE_INVALID); + + if (v != NULL) + g_value_copy (v, value); +} + +static void property_class_init (GParamSpecClass *klass) { klass->value_type = G_TYPE_INVALID; klass->value_validate = property_validate; klass->values_cmp = property_values_cmp; + klass->value_set_default = property_value_set_default; klass->finalize = property_finalize; } diff --git a/gobject/gproperty.h b/gobject/gproperty.h index 0335112f2..e632c26ea 100644 --- a/gobject/gproperty.h +++ b/gobject/gproperty.h @@ -118,7 +118,31 @@ void g_property_set_range (GProperty *property, GLIB_AVAILABLE_IN_2_38 gboolean g_property_get_range (GProperty *property, ...); - +GLIB_AVAILABLE_IN_2_38 +void g_property_set_default_value (GProperty *property, + const GValue *value); +GLIB_AVAILABLE_IN_2_38 +void g_property_get_default_value (GProperty *property, + gpointer gobject, + GValue *value); +GLIB_AVAILABLE_IN_2_38 +void g_property_override_default_value (GProperty *property, + GType gtype, + const GValue *value); +GLIB_AVAILABLE_IN_2_38 +void g_property_set_default (GProperty *property, + ...); +GLIB_AVAILABLE_IN_2_38 +void g_property_get_default (GProperty *property, + gpointer gobject, + ...); +GLIB_AVAILABLE_IN_2_38 +void g_property_override_default (GProperty *property, + GType gtype, + ...); +GLIB_AVAILABLE_IN_2_38 +void g_property_init_default (GProperty *property, + gpointer gobject); GLIB_AVAILABLE_IN_2_38 void g_property_set_prerequisite (GProperty *property, ...); diff --git a/gobject/tests/property.c b/gobject/tests/property.c index 49d3cf546..27cbf7116 100644 --- a/gobject/tests/property.c +++ b/gobject/tests/property.c @@ -33,6 +33,8 @@ struct _TestObjectPrivate TestEnum enum_val; guint enum_val_set : 1; + + guint8 with_default; }; GType test_enum_get_type (void); @@ -71,6 +73,7 @@ enum PROP_STRING_VAL, PROP_BOOL_VAL, PROP_ENUM_VAL, + PROP_WITH_DEFAULT, LAST_PROP }; @@ -145,6 +148,14 @@ test_object_class_init (TestObjectClass *klass) g_property_set_prerequisite ((GProperty *) test_object_properties[PROP_ENUM_VAL], test_enum_get_type ()); + test_object_properties[PROP_WITH_DEFAULT] = + g_uint8_property_new ("with-default", + G_PROPERTY_READWRITE, + G_STRUCT_OFFSET (TestObjectPrivate, with_default), + NULL, + NULL); + g_property_set_default ((GProperty *) test_object_properties[PROP_WITH_DEFAULT], 255); + g_object_class_install_properties (gobject_class, LAST_PROP, test_object_properties); } @@ -154,6 +165,8 @@ test_object_init (TestObject *self) TestObjectPrivate *priv = test_object_get_private (self); priv->enum_val = TEST_ENUM_UNSET; + + g_property_init_default ((GProperty *) test_object_properties[PROP_WITH_DEFAULT], self); } static void @@ -258,6 +271,24 @@ gproperty_explicit_set (void) g_object_unref (obj); } +static void +gproperty_default_init (void) +{ + TestObject *obj = g_object_new (test_object_get_type (), NULL); + guint8 with_default = 0; + + g_object_get (obj, "with-default", &with_default, NULL); + g_assert_cmpint (with_default, ==, 255); + + g_object_unref (obj); + + obj = g_object_new (test_object_get_type (), "with-default", 128, NULL); + g_object_get (obj, "with-default", &with_default, NULL); + g_assert_cmpint (with_default, ==, 128); + + g_object_unref (obj); +} + int main (int argc, char *argv[]) { @@ -269,6 +300,7 @@ main (int argc, char *argv[]) g_test_add_func ("/gproperty/object-set", gproperty_object_set); g_test_add_func ("/gproperty/object-get", gproperty_object_get); g_test_add_func ("/gproperty/explicit-set", gproperty_explicit_set); + g_test_add_func ("/gproperty/default/init", gproperty_default_init); return g_test_run (); } |