summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@linux.intel.com>2010-08-18 15:32:27 +0100
committerEmmanuele Bassi <ebassi@linux.intel.com>2010-09-13 12:34:53 +0100
commit9cd43d7a4c5d3a50187c2eaba7ab903cf6456d7d (patch)
tree5641f99d9268939cc7e5844b91831a5c9181e5b1
parent58a40904af47fbc838bea29cc6ab068b26eb74c5 (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.txt1
-rw-r--r--gobject/gobject.c121
-rw-r--r--gobject/gobject.h3
-rw-r--r--gobject/gobject.symbols1
-rw-r--r--gobject/tests/.gitignore1
-rw-r--r--gobject/tests/Makefile.am4
-rw-r--r--gobject/tests/properties.c222
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 ();
+}