summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2013-04-24 11:53:40 -0400
committerEmmanuele Bassi <ebassi@gnome.org>2013-07-05 18:12:55 +0100
commit8c134ac8a4ecfa48891693c41f32caae52ca6d6d (patch)
treeab6df8c443f024429e8ffb279ed80963ab9ca183
parent959940f89620da5953b989fc45a32a87ce0602cb (diff)
downloadglib-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.c446
-rw-r--r--gobject/gproperty.h26
-rw-r--r--gobject/tests/property.c32
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 ();
}