diff options
author | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-08-18 15:32:27 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-09-13 12:34:53 +0100 |
commit | 9cd43d7a4c5d3a50187c2eaba7ab903cf6456d7d (patch) | |
tree | 5641f99d9268939cc7e5844b91831a5c9181e5b1 | |
parent | 58a40904af47fbc838bea29cc6ab068b26eb74c5 (diff) |
gobject: Add install_properties()
Since we added g_object_notify_by_pspec(), an efficient way to install
and notify properties relies on storing the GParamSpec pointers inside
a static arrays, like we do for signal identifiers.
Instead of multiple calls to g_object_class_install_property(), we
should have a single function to take the static array of GParamSpecs
and iterate it.
https://bugzilla.gnome.org/show_bug.cgi?id=626919
Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
-rw-r--r-- | docs/reference/gobject/gobject-sections.txt | 1 | ||||
-rw-r--r-- | gobject/gobject.c | 121 | ||||
-rw-r--r-- | gobject/gobject.h | 3 | ||||
-rw-r--r-- | gobject/gobject.symbols | 1 | ||||
-rw-r--r-- | gobject/tests/.gitignore | 1 | ||||
-rw-r--r-- | gobject/tests/Makefile.am | 4 | ||||
-rw-r--r-- | gobject/tests/properties.c | 222 |
7 files changed, 350 insertions, 3 deletions
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index 22230bbb1..17ba0df0a 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -244,6 +244,7 @@ G_OBJECT_TYPE_NAME G_OBJECT_CLASS_TYPE G_OBJECT_CLASS_NAME g_object_class_install_property +g_object_class_install_properties g_object_class_find_property g_object_class_list_properties g_object_class_override_property diff --git a/gobject/gobject.c b/gobject/gobject.c index 819333fb3..8fe12d0fb 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -383,7 +383,7 @@ g_object_do_class_init (GObjectClass *class) g_type_add_interface_check (NULL, object_interface_check_properties); } -static void +static inline void install_property_internal (GType g_type, guint property_id, GParamSpec *pspec) @@ -444,7 +444,7 @@ g_object_class_install_property (GObjectClass *class, if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) class->construct_properties = g_slist_prepend (class->construct_properties, pspec); - /* for property overrides of construct poperties, we have to get rid + /* for property overrides of construct properties, we have to get rid * of the overidden inherited construct property */ pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE); @@ -453,6 +453,123 @@ g_object_class_install_property (GObjectClass *class, } /** + * g_object_class_install_properties: + * @oclass: a #GObjectClass + * @n_pspecs: the length of the #GParamSpec<!-- -->s array + * @pspecs: (array length=n_pspecs): the #GParamSpec<!-- -->s array + * defining the new properties + * + * Installs new properties from an array of #GParamSpec<!-- -->s. This is + * usually done in the class initializer. + * + * The property id of each property is the index of each #GParamSpec in + * the @pspecs array. + * + * The property id of 0 is treated specially by #GObject and it should not + * be used to store a #GParamSpec. + * + * This function should be used if you plan to use a static array of + * #GParamSpec<!-- -->s and g_object_notify_pspec(). For instance, this + * class initialization: + * + * |[ + * enum { + * PROP_0, PROP_FOO, PROP_BAR, N_PROPERTIES + * }; + * + * static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + * + * static void + * my_object_class_init (MyObjectClass *klass) + * { + * GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + * + * obj_properties[PROP_FOO] = + * g_param_spec_int ("foo", "Foo", "Foo", + * -1, G_MAXINT, + * 0, + * G_PARAM_READWRITE); + * + * obj_properties[PROP_BAR] = + * g_param_spec_string ("bar", "Bar", "Bar", + * NULL, + * G_PARAM_READWRITE); + * + * gobject_class->set_property = my_object_set_property; + * gobject_class->get_property = my_object_get_property; + * g_object_class_install_properties (gobject_class, + * N_PROPERTIES, + * obj_properties); + * } + * ]| + * + * allows calling g_object_notify_by_pspec() to notify of property changes: + * + * |[ + * void + * my_object_set_foo (MyObject *self, gint foo) + * { + * if (self->foo != foo) + * { + * self->foo = foo; + * g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_FOO]); + * } + * } + * ]| + * + * Since: 2.26 + */ +void +g_object_class_install_properties (GObjectClass *oclass, + guint n_pspecs, + GParamSpec **pspecs) +{ + GType oclass_type; + gint i; + + g_return_if_fail (G_IS_OBJECT_CLASS (oclass)); + g_return_if_fail (n_pspecs > 1); + g_return_if_fail (pspecs[0] == NULL); + + if (CLASS_HAS_DERIVED_CLASS (oclass)) + g_error ("Attempt to add properties to %s after it was derived", + G_OBJECT_CLASS_NAME (oclass)); + + oclass_type = G_OBJECT_CLASS_TYPE (oclass); + + /* we skip the first element of the array as it would have a 0 prop_id */ + for (i = 1; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + + g_return_if_fail (pspec != NULL); + + if (pspec->flags & G_PARAM_WRITABLE) + g_return_if_fail (oclass->set_property != NULL); + if (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) + g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) + g_return_if_fail (pspec->flags & G_PARAM_WRITABLE); + + oclass->flags |= CLASS_HAS_PROPS_FLAG; + install_property_internal (oclass_type, i, pspec); + + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) + oclass->construct_properties = g_slist_prepend (oclass->construct_properties, pspec); + + /* for property overrides of construct properties, we have to get rid + * of the overidden inherited construct property + */ + pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (oclass)), TRUE); + if (pspec && pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) + oclass->construct_properties = g_slist_remove (oclass->construct_properties, pspec); + } +} + +/** * g_object_interface_install_property: * @g_iface: any interface vtable for the interface, or the default * vtable for the interface. diff --git a/gobject/gobject.h b/gobject/gobject.h index 67072245f..c969b8f50 100644 --- a/gobject/gobject.h +++ b/gobject/gobject.h @@ -390,6 +390,9 @@ GParamSpec**g_object_class_list_properties (GObjectClass *oclass, void g_object_class_override_property (GObjectClass *oclass, guint property_id, const gchar *name); +void g_object_class_install_properties (GObjectClass *oclass, + guint n_pspecs, + GParamSpec **pspecs); void g_object_interface_install_property (gpointer g_iface, GParamSpec *pspec); diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols index d1103ea06..670542702 100644 --- a/gobject/gobject.symbols +++ b/gobject/gobject.symbols @@ -140,6 +140,7 @@ g_initially_unowned_get_type g_object_add_weak_pointer g_object_class_find_property g_object_class_install_property +g_object_class_install_properties g_object_class_list_properties g_object_class_override_property g_object_connect G_GNUC_NULL_TERMINATED diff --git a/gobject/tests/.gitignore b/gobject/tests/.gitignore index ca0d62868..ba9d6c837 100644 --- a/gobject/tests/.gitignore +++ b/gobject/tests/.gitignore @@ -1,3 +1,4 @@ binding dynamictests +properties threadtests diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am index 6efbcd9e9..8b1394dd9 100644 --- a/gobject/tests/Makefile.am +++ b/gobject/tests/Makefile.am @@ -5,10 +5,12 @@ INCLUDES = -g $(gobject_INCLUDES) $(GLIB_DEBUG_FLAGS) noinst_PROGRAMS = $(TEST_PROGS) libgobject_LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la -TEST_PROGS += threadtests dynamictests binding +TEST_PROGS += threadtests dynamictests binding properties threadtests_SOURCES = threadtests.c threadtests_LDADD = $(libgobject_LDADD) dynamictests_SOURCES = dynamictests.c dynamictests_LDADD = $(libgobject_LDADD) binding_SOURCES = binding.c binding_LDADD = $(libgobject_LDADD) +properties_SOURCES = properties.c +properties_LDADD = $(libgobject_LDADD) diff --git a/gobject/tests/properties.c b/gobject/tests/properties.c new file mode 100644 index 000000000..e2fef4c65 --- /dev/null +++ b/gobject/tests/properties.c @@ -0,0 +1,222 @@ +#include <stdlib.h> +#include <gstdio.h> +#include <glib-object.h> + +typedef struct _TestObject { + GObject parent_instance; + gint foo; + gboolean bar; + gchar *baz; +} TestObject; + +typedef struct _TestObjectClass { + GObjectClass parent_class; +} TestObjectClass; + +enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, N_PROPERTIES }; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); + +static void +test_object_set_foo (TestObject *obj, + gint foo) +{ + if (obj->foo != foo) + { + obj->foo = foo; + + g_assert (properties[PROP_FOO] != NULL); + g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]); + } +} + +static void +test_object_set_bar (TestObject *obj, + gboolean bar) +{ + bar = !!bar; + + if (obj->bar != bar) + { + obj->bar = bar; + + g_assert (properties[PROP_BAR] != NULL); + g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]); + } +} + +static void +test_object_set_baz (TestObject *obj, + const gchar *baz) +{ + if (g_strcmp0 (obj->baz, baz) != 0) + { + g_free (obj->baz); + obj->baz = g_strdup (baz); + + g_assert (properties[PROP_BAZ] != NULL); + g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]); + } +} + +static void +test_object_finalize (GObject *gobject) +{ + g_free (((TestObject *) gobject)->baz); + + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TestObject *tobj = (TestObject *) gobject; + + g_assert_cmpint (prop_id, !=, 0); + g_assert_cmpint (prop_id, !=, N_PROPERTIES); + g_assert (pspec == properties[prop_id]); + + switch (prop_id) + { + case PROP_FOO: + test_object_set_foo (tobj, g_value_get_int (value)); + break; + + case PROP_BAR: + test_object_set_bar (tobj, g_value_get_boolean (value)); + break; + + case PROP_BAZ: + test_object_set_baz (tobj, g_value_get_string (value)); + break; + + default: + g_assert_not_reached (); + } +} + +static void +test_object_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TestObject *tobj = (TestObject *) gobject; + + g_assert_cmpint (prop_id, !=, 0); + g_assert_cmpint (prop_id, !=, N_PROPERTIES); + g_assert (pspec == properties[prop_id]); + + switch (prop_id) + { + case PROP_FOO: + g_value_set_int (value, tobj->foo); + break; + + case PROP_BAR: + g_value_set_boolean (value, tobj->bar); + break; + + case PROP_BAZ: + g_value_set_string (value, tobj->baz); + break; + + default: + g_assert_not_reached (); + } +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo", + -1, G_MAXINT, + 0, + G_PARAM_READWRITE); + properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar", + FALSE, + G_PARAM_READWRITE); + properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz", + NULL, + G_PARAM_READWRITE); + + gobject_class->set_property = test_object_set_property; + gobject_class->get_property = test_object_get_property; + gobject_class->finalize = test_object_finalize; + + g_object_class_install_properties (gobject_class, N_PROPERTIES, properties); +} + +static void +test_object_init (TestObject *self) +{ + self->foo = 42; + self->bar = TRUE; + self->baz = g_strdup ("Hello"); +} + +static void +properties_install (void) +{ + TestObject *obj = g_object_new (test_object_get_type (), NULL); + GParamSpec *pspec; + + g_assert (properties[PROP_FOO] != NULL); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo"); + g_assert (properties[PROP_FOO] == pspec); + + g_object_unref (obj); +} + +typedef struct { + const gchar *name; + GParamSpec *pspec; +} TestNotifyClosure; + +static void +on_notify (GObject *gobject, + GParamSpec *pspec, + TestNotifyClosure *clos) +{ + g_assert (clos->pspec == pspec); + g_assert_cmpstr (clos->name, ==, pspec->name); +} + +static void +properties_notify (void) +{ + TestObject *obj = g_object_new (test_object_get_type (), NULL); + TestNotifyClosure clos; + + g_assert (properties[PROP_FOO] != NULL); + + clos.name = "foo"; + clos.pspec = properties[PROP_FOO]; + + g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &clos); + g_object_set (obj, "foo", 47, NULL); + + g_object_unref (obj); +} + +int +main (int argc, char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_test_add_func ("/properties/install", properties_install); + g_test_add_func ("/properties/notify", properties_notify); + + return g_test_run (); +} |