[glib/wip/multibinding] Wip: provide multi-binding functionality
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/multibinding] Wip: provide multi-binding functionality
- Date: Wed, 10 Feb 2016 11:38:37 +0000 (UTC)
commit d70501d86589323e85ba81532521d852d36f29fe
Author: Matthias Clasen <mclasen redhat com>
Date: Wed Feb 18 22:09:19 2015 -0500
Wip: provide multi-binding functionality
This lets one bind multiple source properties to multiple target
properties, e.g. combining string properties a1 and a2 into
"<a1> and <a2>" and setting the result on property c.
glib/glib-object.h | 1 +
gobject/Makefile.am | 2 +
gobject/gmultibinding.c | 447 ++++++++++++++++++++++++++++++++++++++++++
gobject/gmultibinding.h | 88 ++++++++
gobject/tests/Makefile.am | 1 +
gobject/tests/multibinding.c | 341 ++++++++++++++++++++++++++++++++
6 files changed, 880 insertions(+), 0 deletions(-)
---
diff --git a/glib/glib-object.h b/glib/glib-object.h
index 6ad523e..3e2aa8d 100644
--- a/glib/glib-object.h
+++ b/glib/glib-object.h
@@ -23,6 +23,7 @@
#include <gobject/gbinding.h>
#include <gobject/gboxed.h>
#include <gobject/genums.h>
+#include <gobject/gmultibinding.h>
#include <gobject/gobject.h>
#include <gobject/gparam.h>
#include <gobject/gparamspecs.h>
diff --git a/gobject/Makefile.am b/gobject/Makefile.am
index 72ad2cd..7aca492 100644
--- a/gobject/Makefile.am
+++ b/gobject/Makefile.am
@@ -64,6 +64,7 @@ gobject_public_h_sources = \
gobject-autocleanups.h \
glib-types.h \
gbinding.h \
+ gmultibinding.h \
gboxed.h \
gclosure.h \
genums.h \
@@ -92,6 +93,7 @@ gobject_c_sources = \
gobject_probes.d \
gatomicarray.c \
gbinding.c \
+ gmultibinding.c \
gboxed.c \
gclosure.c \
genums.c \
diff --git a/gobject/gmultibinding.c b/gobject/gmultibinding.c
new file mode 100644
index 0000000..9399f1b
--- /dev/null
+++ b/gobject/gmultibinding.c
@@ -0,0 +1,447 @@
+#include "config.h"
+
+#include <string.h>
+
+#include "gmultibinding.h"
+#include "genums.h"
+#include "gmarshal.h"
+#include "gobject.h"
+#include "gsignal.h"
+#include "gparamspecs.h"
+#include "gvaluetypes.h"
+
+#include "glibintl.h"
+
+
+#define G_MULTI_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_MULTI_BINDING,
GMultiBindingClass))
+#define G_IS_MULTI_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_MULTI_BINDING))
+#define G_MULTI_BINDING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_MULTI_BINDING,
GMultiBindingClass))
+
+typedef struct _GMultiBindingClass GMultiBindingClass;
+
+struct _GMultiBinding
+{
+ GObject parent_instance;
+
+ gint n_sources;
+ gint n_targets;
+
+ /* no reference is held on the objects, to avoid cycles */
+ GObject **source;
+ GObject **target;
+
+ /* the property names are interned, so they should not be freed */
+ GParamSpec **source_pspec;
+ GParamSpec **target_pspec;
+
+ GMultiBindingTransformFunc transform;
+ gpointer transform_data;
+ GDestroyNotify notify;
+
+ guint *source_notify;
+
+ /* a guard, to avoid loops */
+ guint is_frozen : 1;
+};
+
+struct _GMultiBindingClass
+{
+ GObjectClass parent_class;
+};
+
+static GQuark quark_gbinding = 0;
+
+G_DEFINE_TYPE (GMultiBinding, g_multi_binding, G_TYPE_OBJECT);
+
+static inline void
+add_binding_qdata (GObject *gobject,
+ GMultiBinding *binding)
+{
+ GHashTable *bindings;
+
+ bindings = g_object_get_qdata (gobject, quark_gbinding);
+ if (bindings == NULL)
+ {
+ bindings = g_hash_table_new (NULL, NULL);
+
+ g_object_set_qdata_full (gobject, quark_gbinding,
+ bindings,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+
+ g_hash_table_add (bindings, binding);
+}
+
+static inline gboolean
+has_binding_qdata (GObject *object,
+ GMultiBinding *binding)
+{
+ GHashTable *bindings;
+
+ bindings = g_object_get_qdata (object, quark_gbinding);
+ if (bindings)
+ return g_hash_table_contains (bindings, binding);
+
+ return FALSE;
+}
+
+static inline void
+remove_binding_qdata (GObject *gobject,
+ GMultiBinding *binding)
+{
+ GHashTable *bindings;
+
+ bindings = g_object_get_qdata (gobject, quark_gbinding);
+ if (binding != NULL)
+ g_hash_table_remove (bindings, binding);
+}
+
+/* the basic assumption is that if either the source or the target
+ * goes away then the binding does not exist any more and it should
+ * be reaped as well
+ */
+static void
+weak_unbind (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ GMultiBinding *binding = user_data;
+ gint i;
+
+ /* if what went away was a source, unset it so that GBinding::finalize
+ * does not try to access it; otherwise, disconnect everything and remove
+ * the GBinding instance from the object's qdata
+ */
+ for (i = 0; i < binding->n_sources; i++)
+ {
+ if (binding->source[i] == where_the_object_was)
+ binding->source[i] = NULL;
+ else
+ {
+ if (binding->source_notify[i] != 0)
+ g_signal_handler_disconnect (binding->source[i], binding->source_notify[i]);
+
+ g_object_weak_unref (binding->source[i], weak_unbind, user_data);
+ remove_binding_qdata (binding->source[i], binding);
+
+ binding->source_notify[i] = 0;
+ binding->source[i] = NULL;
+ }
+ }
+
+ /* as above, but with the targets */
+ for (i = 0; i < binding->n_targets; i++)
+ {
+ if (binding->target[i] == where_the_object_was)
+ binding->target[i] = NULL;
+ else
+ {
+ g_object_weak_unref (binding->target[i], weak_unbind, user_data);
+ remove_binding_qdata (binding->target[i], binding);
+ binding->target[i] = NULL;
+ }
+ }
+
+ /* this will take care of the binding itself */
+ g_object_unref (binding);
+}
+
+static void
+on_source_notify (GObject *gobject,
+ GParamSpec *pspec,
+ GMultiBinding *binding)
+{
+ GValue *from_values;
+ GValue *to_values;
+ gboolean res;
+ gint i;
+
+ if (binding->is_frozen)
+ return;
+
+ from_values = g_new0 (GValue, binding->n_sources);
+ for (i = 0; i < binding->n_sources; i++)
+ {
+ g_value_init (&from_values[i], G_PARAM_SPEC_VALUE_TYPE (binding->source_pspec[i]));
+ g_object_get_property (binding->source[i], binding->source_pspec[i]->name, &from_values[i]);
+ }
+
+ to_values = g_new0 (GValue, binding->n_targets);
+ for (i = 0; i < binding->n_targets; i++)
+ {
+ g_value_init (&to_values[i], G_PARAM_SPEC_VALUE_TYPE (binding->target_pspec[i]));
+ g_object_get_property (binding->target[i], binding->target_pspec[i]->name, &to_values[i]);
+ }
+
+ res = binding->transform (binding, (const GValue *)from_values, to_values, binding->transform_data);
+
+ if (res)
+ {
+ binding->is_frozen = TRUE;
+ for (i = 0; i < binding->n_targets; i++)
+ {
+ g_param_value_validate (binding->target_pspec[i], &to_values[i]);
+ g_object_set_property (binding->target[i], binding->target_pspec[i]->name, &to_values[i]);
+ }
+ binding->is_frozen = FALSE;
+ }
+
+ for (i = 0; i < binding->n_sources; i++)
+ g_value_unset (&from_values[i]);
+ g_free (from_values);
+
+ for (i = 0; i < binding->n_targets; i++)
+ g_value_unset (&to_values[i]);
+ g_free (to_values);
+}
+
+static inline void
+g_multi_binding_unbind_internal (GMultiBinding *binding,
+ gboolean unref_binding)
+{
+ gint i;
+
+ /* dispose of the transformation data */
+ if (binding->notify != NULL)
+ {
+ binding->notify (binding->transform_data);
+
+ binding->transform_data = NULL;
+ binding->notify = NULL;
+ }
+
+ for (i = 0; i < binding->n_sources; i++)
+ {
+ if (binding->source[i] != NULL)
+ {
+ if (binding->source_notify[i] != 0)
+ g_signal_handler_disconnect (binding->source[i], binding->source_notify[i]);
+
+ g_object_weak_unref (binding->source[i], weak_unbind, binding);
+ remove_binding_qdata (binding->source[i], binding);
+
+ binding->source_notify[i] = 0;
+ binding->source[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < binding->n_targets; i++)
+ {
+ if (binding->target[i] != NULL)
+ {
+ g_object_weak_unref (binding->target[i], weak_unbind, binding);
+ remove_binding_qdata (binding->target[i], binding);
+ binding->target[i] = NULL;
+ }
+ }
+
+ if (unref_binding)
+ g_object_unref (binding);
+}
+
+static void
+g_multi_binding_finalize (GObject *gobject)
+{
+ GMultiBinding *binding = G_MULTI_BINDING (gobject);
+
+ g_multi_binding_unbind_internal (binding, FALSE);
+ g_free (binding->source);
+ g_free (binding->source_pspec);
+ g_free (binding->source_notify);
+ g_free (binding->target);
+ g_free (binding->target_pspec);
+
+ G_OBJECT_CLASS (g_multi_binding_parent_class)->finalize (gobject);
+}
+
+static void
+g_multi_binding_class_init (GMultiBindingClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ quark_gbinding = g_quark_from_static_string ("g-multi-binding");
+
+ gobject_class->finalize = g_multi_binding_finalize;
+}
+
+static void
+g_multi_binding_init (GMultiBinding *binding)
+{
+}
+
+gint
+g_multi_binding_get_n_sources (GMultiBinding *binding)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), 0);
+
+ return binding->n_sources;
+}
+
+GObject *
+g_multi_binding_get_source (GMultiBinding *binding,
+ gint idx)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), NULL);
+ g_return_val_if_fail (0 <= idx && idx < binding->n_sources, NULL);
+
+ return binding->source[idx];
+}
+
+const gchar *
+g_multi_binding_get_source_property (GMultiBinding *binding,
+ gint idx)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), NULL);
+ g_return_val_if_fail (0 <= idx && idx < binding->n_sources, NULL);
+
+ return binding->source_pspec[idx]->name;
+}
+
+gint
+g_multi_binding_get_n_targets (GMultiBinding *binding)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), 0);
+
+ return binding->n_targets;
+}
+
+GObject *
+g_multi_binding_get_target (GMultiBinding *binding,
+ gint idx)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), NULL);
+ g_return_val_if_fail (0 <= idx && idx < binding->n_targets, NULL);
+
+ return binding->target[idx];
+}
+
+const gchar *
+g_multi_binding_get_target_property (GMultiBinding *binding,
+ gint idx)
+{
+ g_return_val_if_fail (G_IS_MULTI_BINDING (binding), NULL);
+ g_return_val_if_fail (0 <= idx && idx < binding->n_targets, NULL);
+
+ return binding->target_pspec[idx]->name;
+}
+
+void
+g_multi_binding_unbind (GMultiBinding *binding)
+{
+ g_return_if_fail (G_IS_MULTI_BINDING (binding));
+
+ g_multi_binding_unbind_internal (binding, TRUE);
+}
+
+GMultiBinding *
+g_object_multi_bind_property_v (gint n_sources,
+ GObject *sources[],
+ const gchar *source_properties[],
+ gint n_targets,
+ GObject *targets[],
+ const gchar *target_properties[],
+ GMultiBindingTransformFunc transform,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GMultiBinding *binding;
+ GParamSpec *pspec;
+ gint i;
+ gchar *signal;
+
+ /* FIXME: don't look up pspecs twice */
+ for (i = 0; i < n_sources; i++)
+ {
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (sources[i]), source_properties[i]);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: The source object %d of type %s has no property called '%s'",
+ G_STRLOC,
+ i,
+ G_OBJECT_TYPE_NAME (sources[i]),
+ source_properties[i]);
+ return NULL;
+ }
+
+ if (!(pspec->flags & G_PARAM_READABLE))
+ {
+ g_warning ("%s: The source object %d of type %s has no readable property called '%s'",
+ G_STRLOC,
+ i,
+ G_OBJECT_TYPE_NAME (sources[i]),
+ source_properties[i]);
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < n_targets; i++)
+ {
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (targets[i]), target_properties[i]);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: The target object %d of type %s has no property called '%s'",
+ G_STRLOC,
+ i,
+ G_OBJECT_TYPE_NAME (targets[i]),
+ target_properties[i]);
+ return NULL;
+ }
+
+ if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) || !(pspec->flags & G_PARAM_WRITABLE))
+ {
+ g_warning ("%s: The target object %d of type %s has no writable property called '%s'",
+ G_STRLOC,
+ i,
+ G_OBJECT_TYPE_NAME (targets[i]),
+ target_properties[i]);
+ return NULL;
+ }
+ }
+
+ binding = g_object_new (G_TYPE_MULTI_BINDING, NULL);
+
+ binding->transform = transform;
+ binding->transform_data = user_data;
+ binding->notify = notify;
+
+ binding->n_sources = n_sources;
+
+ binding->source = g_new (GObject *, n_sources);
+ binding->source_pspec = g_new (GParamSpec *, n_sources);
+ binding->source_notify = g_new (guint, n_sources);
+
+ for (i = 0; i < n_sources; i++)
+ {
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (sources[i]), source_properties[i]);
+ binding->source[i] = sources[i];
+ binding->source_pspec[i] = pspec;
+
+ signal = g_strconcat ("notify::", source_properties[i], NULL);
+ binding->source_notify[i] = g_signal_connect (binding->source[i], signal,
+ G_CALLBACK (on_source_notify), binding);
+ g_free (signal);
+ if (!has_binding_qdata (binding->source[i], binding))
+ {
+ g_object_weak_ref (binding->source[i], weak_unbind, binding);
+ add_binding_qdata (binding->source[i], binding);
+ }
+ }
+
+ binding->n_targets = n_targets;
+ binding->target = g_new (GObject *, n_targets);
+ binding->target_pspec = g_new (GParamSpec *, n_targets);
+
+ for (i = 0; i < n_targets; i++)
+ {
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (targets[i]), target_properties[i]);
+ binding->target[i] = targets[i];
+ binding->target_pspec[i] = pspec;
+
+ if (!has_binding_qdata (binding->target[i], binding))
+ {
+ g_object_weak_ref (binding->target[i], weak_unbind, binding);
+ add_binding_qdata (binding->target[i], binding);
+ }
+ }
+
+ return binding;
+}
diff --git a/gobject/gmultibinding.h b/gobject/gmultibinding.h
new file mode 100644
index 0000000..36c1430
--- /dev/null
+++ b/gobject/gmultibinding.h
@@ -0,0 +1,88 @@
+/* gmultibinding.h: Binding for object properties
+ *
+ * Copyright (C) 2015 Red Hat, Inc
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#ifndef __G_MULTI_BINDING_H__
+#define __G_MULTI_BINDING_H__
+
+#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
+#error "Only <glib-object.h> can be included directly."
+#endif
+
+#include <glib.h>
+#include <gobject/gobject.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MULTI_BINDING (g_multi_binding_get_type ())
+#define G_MULTI_BINDING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_MULTI_BINDING,
GMultiBinding))
+#define G_IS_MULTI_BINDING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_MULTI_BINDING))
+
+/**
+ * GMultiBinding:
+ *
+ * GMultiBinding is an opaque structure whose members
+ * cannot be accessed directly.
+ *
+ * Since: 2.44
+ */
+typedef struct _GMultiBinding GMultiBinding;
+
+typedef gboolean (* GMultiBindingTransformFunc) (GMultiBinding *binding,
+ const GValue from_values[],
+ GValue to_values[],
+ gpointer user_data);
+
+GLIB_AVAILABLE_IN_ALL
+GType g_multi_binding_get_type (void) G_GNUC_CONST;
+
+GLIB_AVAILABLE_IN_ALL
+gint g_multi_binding_get_n_sources (GMultiBinding *binding);
+GLIB_AVAILABLE_IN_ALL
+GObject * g_multi_binding_get_source (GMultiBinding *binding,
+ gint idx);
+GLIB_AVAILABLE_IN_ALL
+const gchar * g_multi_binding_get_source_property (GMultiBinding *binding,
+ gint idx);
+
+GLIB_AVAILABLE_IN_ALL
+gint g_multi_binding_get_n_targets (GMultiBinding *binding);
+GLIB_AVAILABLE_IN_ALL
+GObject * g_multi_binding_get_target (GMultiBinding *binding,
+ gint idx);
+GLIB_AVAILABLE_IN_ALL
+const gchar * g_multi_binding_get_target_property (GMultiBinding *binding,
+ gint idx);
+
+GLIB_AVAILABLE_IN_ALL
+void g_multi_binding_unbind (GMultiBinding *binding);
+
+GLIB_AVAILABLE_IN_ALL
+GMultiBinding *g_object_multi_bind_property_v (gint n_sources,
+ GObject *sources[],
+ const gchar *source_properties[],
+ gint n_targets,
+ GObject *targets[],
+ const gchar *target_properties[],
+ GMultiBindingTransformFunc transform,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+
+#endif /* __G_MULTI_BINDING_H__ */
diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am
index 2c5cc1a..43d9f97 100644
--- a/gobject/tests/Makefile.am
+++ b/gobject/tests/Makefile.am
@@ -15,6 +15,7 @@ test_programs = \
threadtests \
dynamictests \
binding \
+ multibinding \
properties \
reference \
valuearray \
diff --git a/gobject/tests/multibinding.c b/gobject/tests/multibinding.c
new file mode 100644
index 0000000..afff184
--- /dev/null
+++ b/gobject/tests/multibinding.c
@@ -0,0 +1,341 @@
+#include <stdlib.h>
+#include <gstdio.h>
+#include <glib-object.h>
+
+typedef struct _BindingSource
+{
+ GObject parent_instance;
+
+ gint foo;
+ gint bar;
+ gdouble value;
+ gboolean toggle;
+} BindingSource;
+
+typedef struct _BindingSourceClass
+{
+ GObjectClass parent_class;
+} BindingSourceClass;
+
+enum
+{
+ PROP_SOURCE_0,
+
+ PROP_SOURCE_FOO,
+ PROP_SOURCE_BAR,
+ PROP_SOURCE_VALUE,
+ PROP_SOURCE_TOGGLE
+};
+
+static GType binding_source_get_type (void);
+G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
+
+static void
+binding_source_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BindingSource *source = (BindingSource *) gobject;
+
+ switch (prop_id)
+ {
+ case PROP_SOURCE_FOO:
+ source->foo = g_value_get_int (value);
+ break;
+
+ case PROP_SOURCE_BAR:
+ source->bar = g_value_get_int (value);
+ break;
+
+ case PROP_SOURCE_VALUE:
+ source->value = g_value_get_double (value);
+ break;
+
+ case PROP_SOURCE_TOGGLE:
+ source->toggle = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+binding_source_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BindingSource *source = (BindingSource *) gobject;
+
+ switch (prop_id)
+ {
+ case PROP_SOURCE_FOO:
+ g_value_set_int (value, source->foo);
+ break;
+
+ case PROP_SOURCE_BAR:
+ g_value_set_int (value, source->bar);
+ break;
+
+ case PROP_SOURCE_VALUE:
+ g_value_set_double (value, source->value);
+ break;
+
+ case PROP_SOURCE_TOGGLE:
+ g_value_set_boolean (value, source->toggle);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+binding_source_class_init (BindingSourceClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = binding_source_set_property;
+ gobject_class->get_property = binding_source_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
+ g_param_spec_int ("foo", "Foo", "Foo",
+ -1, 100,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
+ g_param_spec_int ("bar", "Bar", "Bar",
+ -1, 100,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
+ g_param_spec_double ("value", "Value", "Value",
+ -100.0, 200.0,
+ 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
+ g_param_spec_boolean ("toggle", "Toggle", "Toggle",
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+binding_source_init (BindingSource *self)
+{
+}
+
+typedef struct _BindingTarget
+{
+ GObject parent_instance;
+
+ gint bar;
+ gdouble value;
+ gboolean toggle;
+} BindingTarget;
+
+typedef struct _BindingTargetClass
+{
+ GObjectClass parent_class;
+} BindingTargetClass;
+
+enum
+{
+ PROP_TARGET_0,
+
+ PROP_TARGET_BAR,
+ PROP_TARGET_VALUE,
+ PROP_TARGET_TOGGLE
+};
+
+static GType binding_target_get_type (void);
+G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
+
+static void
+binding_target_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BindingTarget *target = (BindingTarget *) gobject;
+
+ switch (prop_id)
+ {
+ case PROP_TARGET_BAR:
+ target->bar = g_value_get_int (value);
+ break;
+
+ case PROP_TARGET_VALUE:
+ target->value = g_value_get_double (value);
+ break;
+
+ case PROP_TARGET_TOGGLE:
+ target->toggle = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+binding_target_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BindingTarget *target = (BindingTarget *) gobject;
+
+ switch (prop_id)
+ {
+ case PROP_TARGET_BAR:
+ g_value_set_int (value, target->bar);
+ break;
+
+ case PROP_TARGET_VALUE:
+ g_value_set_double (value, target->value);
+ break;
+
+ case PROP_TARGET_TOGGLE:
+ g_value_set_boolean (value, target->toggle);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+binding_target_class_init (BindingTargetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = binding_target_set_property;
+ gobject_class->get_property = binding_target_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
+ g_param_spec_int ("bar", "Bar", "Bar",
+ -1, 100,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
+ g_param_spec_double ("value", "Value", "Value",
+ -100.0, 200.0,
+ 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
+ g_param_spec_boolean ("toggle", "Toggle", "Toggle",
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+binding_target_init (BindingTarget *self)
+{
+}
+
+static gboolean
+munge_two_ints (GMultiBinding *binding,
+ const GValue from_values[],
+ GValue to_values[],
+ gpointer user_data)
+{
+ int v0, v1;
+
+ v0 = g_value_get_int (&from_values[0]);
+ v1 = g_value_get_int (&from_values[1]);
+
+ g_value_set_int (&to_values[0], v0 + v1);
+ g_value_set_int (&to_values[1], v0 - v1);
+}
+
+static void
+multibinding_basic (void)
+{
+ BindingSource *source0 = g_object_new (binding_source_get_type (), NULL);
+ BindingSource *source1 = g_object_new (binding_source_get_type (), NULL);
+ GObject *sources[2];
+ const char *source_props[2];
+ BindingTarget *target0 = g_object_new (binding_target_get_type (), NULL);
+ BindingTarget *target1 = g_object_new (binding_target_get_type (), NULL);
+ GObject *targets[2];
+ const char *target_props[2];
+ GMultiBinding *binding;
+
+ sources[0] = G_OBJECT (source0);
+ sources[1] = G_OBJECT (source1);
+ source_props[0] = "foo";
+ source_props[1] = "bar";
+ targets[0] = G_OBJECT (target0);
+ targets[1] = G_OBJECT (target1);
+ target_props[0] = "bar";
+ target_props[1] = "bar";
+ binding = g_object_multi_bind_property_v (2, sources, source_props,
+ 2, targets, target_props,
+ munge_two_ints,
+ NULL, NULL);
+ g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
+
+ g_assert_cmpint (g_multi_binding_get_n_sources (binding), ==, 2);
+ g_assert (g_multi_binding_get_source (binding, 0) == sources[0]);
+ g_assert (g_multi_binding_get_source (binding, 1) == sources[1]);
+ g_assert_cmpstr (g_multi_binding_get_source_property (binding, 0), ==, source_props[0]);
+ g_assert_cmpstr (g_multi_binding_get_source_property (binding, 1), ==, source_props[1]);
+ g_assert_cmpint (g_multi_binding_get_n_targets (binding), ==, 2);
+ g_assert (g_multi_binding_get_target (binding, 0) == targets[0]);
+ g_assert (g_multi_binding_get_target (binding, 1) == targets[1]);
+ g_assert_cmpstr (g_multi_binding_get_target_property (binding, 0), ==, target_props[0]);
+ g_assert_cmpstr (g_multi_binding_get_target_property (binding, 1), ==, target_props[1]);
+
+ g_assert_cmpint (source0->foo, ==, 0);
+ g_assert_cmpint (source1->bar, ==, 0);
+ g_assert_cmpint (target0->bar, ==, 0);
+ g_assert_cmpint (target1->bar, ==, 0);
+
+ g_object_set (source0, "foo", 1, NULL);
+
+ g_assert_cmpint (source0->foo, ==, 1);
+ g_assert_cmpint (source1->bar, ==, 0);
+ g_assert_cmpint (target0->bar, ==, source0->foo + source1->bar);
+ g_assert_cmpint (target1->bar, ==, source0->foo - source1->bar);
+
+ g_object_set (source1, "foo", 1, NULL);
+
+ g_assert_cmpint (source0->foo, ==, 1);
+ g_assert_cmpint (source1->bar, ==, 0);
+ g_assert_cmpint (target0->bar, ==, source0->foo + source1->bar);
+ g_assert_cmpint (target1->bar, ==, source0->foo - source1->bar);
+
+ g_object_set (source0, "bar", 1, NULL);
+
+ g_assert_cmpint (source0->foo, ==, 1);
+ g_assert_cmpint (source1->bar, ==, 0);
+ g_assert_cmpint (target0->bar, ==, source0->foo + source1->bar);
+ g_assert_cmpint (target1->bar, ==, source0->foo - source1->bar);
+
+ g_object_set (source1, "bar", 1, NULL);
+
+ g_assert_cmpint (source0->foo, ==, 1);
+ g_assert_cmpint (source1->bar, ==, 1);
+ g_assert_cmpint (target0->bar, ==, source0->foo + source1->bar);
+ g_assert_cmpint (target1->bar, ==, source0->foo - source1->bar);
+
+ g_object_unref (source0);
+ g_object_unref (source1);
+ g_object_unref (target0);
+ g_object_unref (target1);
+ g_assert (binding == NULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_bug_base ("http://bugzilla.gnome.org/");
+
+ g_test_add_func ("/multibinding/basic", multibinding_basic);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]