/* gbinding.c: Binding for object properties * * Copyright (C) 2010 Intel Corp. * * SPDX-License-Identifier: LGPL-2.1-or-later * * 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.1 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 . * * Author: Emmanuele Bassi */ /** * GBinding: * * `GObject` instance (or source) and another property on another `GObject` * instance (or target). * * Whenever the source property changes, the same value is applied to the * target property; for instance, the following binding: * * ```c * g_object_bind_property (object1, "property-a", * object2, "property-b", * G_BINDING_DEFAULT); * ``` * * will cause the property named "property-b" of @object2 to be updated * every time [method@GObject.set] or the specific accessor changes the value of * the property "property-a" of @object1. * * It is possible to create a bidirectional binding between two properties * of two `GObject` instances, so that if either property changes, the * other is updated as well, for instance: * * ```c * g_object_bind_property (object1, "property-a", * object2, "property-b", * G_BINDING_BIDIRECTIONAL); * ``` * * will keep the two properties in sync. * * It is also possible to set a custom transformation function (in both * directions, in case of a bidirectional binding) to apply a custom * transformation from the source value to the target value before * applying it; for instance, the following binding: * * ```c * g_object_bind_property_full (adjustment1, "value", * adjustment2, "value", * G_BINDING_BIDIRECTIONAL, * celsius_to_fahrenheit, * fahrenheit_to_celsius, * NULL, NULL); * ``` * * will keep the "value" property of the two adjustments in sync; the * @celsius_to_fahrenheit function will be called whenever the "value" * property of @adjustment1 changes and will transform the current value * of the property before applying it to the "value" property of @adjustment2. * * Vice versa, the @fahrenheit_to_celsius function will be called whenever * the "value" property of @adjustment2 changes, and will transform the * current value of the property before applying it to the "value" property * of @adjustment1. * * Note that #GBinding does not resolve cycles by itself; a cycle like * * ``` * object1:propertyA -> object2:propertyB * object2:propertyB -> object3:propertyC * object3:propertyC -> object1:propertyA * ``` * * might lead to an infinite loop. The loop, in this particular case, * can be avoided if the objects emit the `GObject::notify` signal only * if the value has effectively been changed. A binding is implemented * using the `GObject::notify` signal, so it is susceptible to all the * various ways of blocking a signal emission, like [func@GObject.signal_stop_emission] * or [func@GObject.signal_handler_block]. * * A binding will be severed, and the resources it allocates freed, whenever * either one of the `GObject` instances it refers to are finalized, or when * the #GBinding instance loses its last reference. * * Bindings for languages with garbage collection can use * [method@GObject.Binding.unbind] to explicitly release a binding between the source * and target properties, instead of relying on the last reference on the * binding, source, and target instances to drop. * * Since: 2.26 */ #include "config.h" #include #include "gbinding.h" #include "genums.h" #include "gmarshal.h" #include "gobject.h" #include "gsignal.h" #include "gparamspecs.h" #include "gvaluetypes.h" #include "glibintl.h" GType g_binding_flags_get_type (void) { static GType static_g_define_type_id = 0; if (g_once_init_enter_pointer (&static_g_define_type_id)) { static const GFlagsValue values[] = { { G_BINDING_DEFAULT, "G_BINDING_DEFAULT", "default" }, { G_BINDING_BIDIRECTIONAL, "G_BINDING_BIDIRECTIONAL", "bidirectional" }, { G_BINDING_SYNC_CREATE, "G_BINDING_SYNC_CREATE", "sync-create" }, { G_BINDING_INVERT_BOOLEAN, "G_BINDING_INVERT_BOOLEAN", "invert-boolean" }, { 0, NULL, NULL } }; GType g_define_type_id = g_flags_register_static (g_intern_static_string ("GBindingFlags"), values); g_once_init_leave_pointer (&static_g_define_type_id, g_define_type_id); } return static_g_define_type_id; } /* Reference counted helper struct that is passed to all callbacks to ensure * that they never work with already freed objects without having to store * strong references for them. * * Using strong references anywhere is not possible because of the API * requirements of GBinding, specifically that the initial reference of the * GBinding is owned by the source/target and the caller and can be released * either by the source/target being finalized or calling g_binding_unbind(). * * As such, the only strong reference has to be owned by both weak notifies of * the source and target and the first to be called has to release it. */ typedef struct { GWeakRef binding; GWeakRef source; GWeakRef target; gboolean binding_removed; } BindingContext; static BindingContext * binding_context_ref (BindingContext *context) { return g_atomic_rc_box_acquire (context); } static void binding_context_clear (BindingContext *context) { g_weak_ref_clear (&context->binding); g_weak_ref_clear (&context->source); g_weak_ref_clear (&context->target); } static void binding_context_unref (BindingContext *context) { g_atomic_rc_box_release_full (context, (GDestroyNotify) binding_context_clear); } /* Reference counting for the transform functions to ensure that they're always * valid while making use of them in the property notify callbacks. * * The transform functions are released when unbinding but unbinding can happen * while the transform functions are currently in use inside the notify callbacks. */ typedef struct { GBindingTransformFunc transform_s2t; GBindingTransformFunc transform_t2s; gpointer transform_data; GDestroyNotify destroy_notify; } TransformFunc; static TransformFunc * transform_func_new (GBindingTransformFunc transform_s2t, GBindingTransformFunc transform_t2s, gpointer transform_data, GDestroyNotify destroy_notify) { TransformFunc *func = g_atomic_rc_box_new0 (TransformFunc); func->transform_s2t = transform_s2t; func->transform_t2s = transform_t2s; func->transform_data = transform_data; func->destroy_notify = destroy_notify; return func; } static TransformFunc * transform_func_ref (TransformFunc *func) { return g_atomic_rc_box_acquire (func); } static void transform_func_clear (TransformFunc *func) { if (func->destroy_notify) func->destroy_notify (func->transform_data); } static void transform_func_unref (TransformFunc *func) { g_atomic_rc_box_release_full (func, (GDestroyNotify) transform_func_clear); } #define G_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_BINDING, GBindingClass)) #define G_IS_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_BINDING)) #define G_BINDING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_BINDING, GBindingClass)) typedef struct _GBindingClass GBindingClass; struct _GBinding { GObject parent_instance; /* no reference is held on the objects, to avoid cycles */ BindingContext *context; /* protects transform_func, source, target property notify and * target_weak_notify_installed for unbinding */ GMutex unbind_lock; /* transform functions, only NULL after unbinding */ TransformFunc *transform_func; /* LOCK: unbind_lock */ /* the property names are interned, so they should not be freed */ const gchar *source_property; const gchar *target_property; GParamSpec *source_pspec; GParamSpec *target_pspec; GBindingFlags flags; guint source_notify; /* LOCK: unbind_lock */ guint target_notify; /* LOCK: unbind_lock */ gboolean target_weak_notify_installed; /* LOCK: unbind_lock */ /* a guard, to avoid loops */ guint is_frozen : 1; }; struct _GBindingClass { GObjectClass parent_class; }; enum { PROP_0, PROP_SOURCE, PROP_TARGET, PROP_SOURCE_PROPERTY, PROP_TARGET_PROPERTY, PROP_FLAGS }; static guint gobject_notify_signal_id; G_DEFINE_TYPE (GBinding, g_binding, G_TYPE_OBJECT) static void weak_unbind (gpointer user_data, GObject *where_the_object_was); /* Must be called with the unbind lock held, context/binding != NULL and strong * references to source/target or NULL. * Return TRUE if the binding was actually removed and FALSE if it was already * removed before. */ static gboolean unbind_internal_locked (BindingContext *context, GBinding *binding, GObject *source, GObject *target) { gboolean binding_was_removed = FALSE; g_assert (context != NULL); g_assert (binding != NULL); /* If the target went away we still have a strong reference to the source * here and can clear it from the binding. Otherwise if the source went away * we can clear the target from the binding. Finalizing an object clears its * signal handlers and all weak references pointing to it before calling * weak notify callbacks. * * If both still exist we clean up everything set up by the binding. */ if (source) { /* We always add/remove the source property notify and the weak notify * of the source at the same time, and should only ever do that once. */ if (binding->source_notify != 0) { g_signal_handler_disconnect (source, binding->source_notify); g_object_weak_unref (source, weak_unbind, context); binding_context_unref (context); binding->source_notify = 0; } g_weak_ref_set (&context->source, NULL); } /* As above, but with the target. If source==target then no weak notify was * installed for the target, which is why that is stored as a separate * boolean inside the binding. */ if (target) { /* There might be a target property notify without a weak notify on the * target or the other way around, so these have to be handled * independently here unlike for the source. */ if (binding->target_notify != 0) { g_signal_handler_disconnect (target, binding->target_notify); binding->target_notify = 0; } g_weak_ref_set (&context->target, NULL); /* Remove the weak notify from the target, at most once */ if (binding->target_weak_notify_installed) { g_object_weak_unref (target, weak_unbind, context); binding_context_unref (context); binding->target_weak_notify_installed = FALSE; } } /* Make sure to remove the binding only once and return to the caller that * this was the call that actually removed it. */ if (!context->binding_removed) { context->binding_removed = TRUE; binding_was_removed = TRUE; } return binding_was_removed; } /* 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. Each weak notify owns a strong reference to the * binding that should be dropped here. */ static void weak_unbind (gpointer user_data, GObject *where_the_object_was) { BindingContext *context = user_data; GBinding *binding; GObject *source, *target; gboolean binding_was_removed = FALSE; TransformFunc *transform_func; binding = g_weak_ref_get (&context->binding); if (!binding) { /* The binding was already destroyed before so there's nothing to do */ binding_context_unref (context); return; } g_mutex_lock (&binding->unbind_lock); transform_func = g_steal_pointer (&binding->transform_func); source = g_weak_ref_get (&context->source); target = g_weak_ref_get (&context->target); /* If this is called then either the source or target or both must be in the * process of being disposed. If this happens as part of g_object_unref() * then the weak references are actually cleared, otherwise if disposing * happens as part of g_object_run_dispose() then they would still point to * the disposed object. * * If the object this is being called for is either the source or the target * and we actually got a strong reference to it nonetheless (see above), * then signal handlers and weak notifies for it are already disconnected * and they must not be disconnected a second time. Instead simply clear the * weak reference and be done with it. * * See https://gitlab.gnome.org/GNOME/glib/-/issues/2266 */ if (source == where_the_object_was) { g_weak_ref_set (&context->source, NULL); g_clear_object (&source); } if (target == where_the_object_was) { g_weak_ref_set (&context->target, NULL); g_clear_object (&target); } binding_was_removed = unbind_internal_locked (context, binding, source, target); g_mutex_unlock (&binding->unbind_lock); /* Unref source, target and transform_func after the mutex is unlocked as it * might release the last reference, which then accesses the mutex again */ g_clear_object (&target); g_clear_object (&source); g_clear_pointer (&transform_func, transform_func_unref); /* This releases the strong reference we got from the weak ref above */ g_object_unref (binding); /* This will take care of the binding itself. */ if (binding_was_removed) g_object_unref (binding); /* Each weak notify owns a reference to the binding context. */ binding_context_unref (context); } static gboolean default_transform (GBinding *binding, const GValue *value_a, GValue *value_b, gpointer user_data G_GNUC_UNUSED) { /* if it's not the same type, try to convert it using the GValue * transformation API; otherwise just copy it */ if (!g_type_is_a (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) { /* are these two types compatible (can be directly copied)? */ if (g_value_type_compatible (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) { g_value_copy (value_a, value_b); return TRUE; } if (g_value_type_transformable (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) { if (g_value_transform (value_a, value_b)) return TRUE; } g_critical ("%s: Unable to convert a value of type %s to a " "value of type %s", G_STRLOC, g_type_name (G_VALUE_TYPE (value_a)), g_type_name (G_VALUE_TYPE (value_b))); return FALSE; } g_value_copy (value_a, value_b); return TRUE; } static gboolean default_invert_boolean_transform (GBinding *binding, const GValue *value_a, GValue *value_b, gpointer user_data G_GNUC_UNUSED) { gboolean value; g_assert (G_VALUE_HOLDS_BOOLEAN (value_a)); g_assert (G_VALUE_HOLDS_BOOLEAN (value_b)); value = g_value_get_boolean (value_a); value = !value; g_value_set_boolean (value_b, value); return TRUE; } static void on_source_notify (GObject *source, GParamSpec *pspec, BindingContext *context) { GBinding *binding; GObject *target; TransformFunc *transform_func; GValue from_value = G_VALUE_INIT; GValue to_value = G_VALUE_INIT; gboolean res; binding = g_weak_ref_get (&context->binding); if (!binding) return; if (binding->is_frozen) { g_object_unref (binding); return; } target = g_weak_ref_get (&context->target); if (!target) { g_object_unref (binding); return; } /* Get the transform function safely */ g_mutex_lock (&binding->unbind_lock); if (!binding->transform_func) { /* it was released already during unbinding, nothing to do here */ g_mutex_unlock (&binding->unbind_lock); return; } transform_func = transform_func_ref (binding->transform_func); g_mutex_unlock (&binding->unbind_lock); g_value_init (&from_value, G_PARAM_SPEC_VALUE_TYPE (binding->source_pspec)); g_value_init (&to_value, G_PARAM_SPEC_VALUE_TYPE (binding->target_pspec)); g_object_get_property (source, binding->source_pspec->name, &from_value); res = transform_func->transform_s2t (binding, &from_value, &to_value, transform_func->transform_data); transform_func_unref (transform_func); if (res) { binding->is_frozen = TRUE; (void) g_param_value_validate (binding->target_pspec, &to_value); g_object_set_property (target, binding->target_pspec->name, &to_value); binding->is_frozen = FALSE; } g_value_unset (&from_value); g_value_unset (&to_value); g_object_unref (target); g_object_unref (binding); } static void on_target_notify (GObject *target, GParamSpec *pspec, BindingContext *context) { GBinding *binding; GObject *source; TransformFunc *transform_func; GValue from_value = G_VALUE_INIT; GValue to_value = G_VALUE_INIT; gboolean res; binding = g_weak_ref_get (&context->binding); if (!binding) return; if (binding->is_frozen) { g_object_unref (binding); return; } source = g_weak_ref_get (&context->source); if (!source) { g_object_unref (binding); return; } /* Get the transform function safely */ g_mutex_lock (&binding->unbind_lock); if (!binding->transform_func) { /* it was released already during unbinding, nothing to do here */ g_mutex_unlock (&binding->unbind_lock); return; } transform_func = transform_func_ref (binding->transform_func); g_mutex_unlock (&binding->unbind_lock); g_value_init (&from_value, G_PARAM_SPEC_VALUE_TYPE (binding->target_pspec)); g_value_init (&to_value, G_PARAM_SPEC_VALUE_TYPE (binding->source_pspec)); g_object_get_property (target, binding->target_pspec->name, &from_value); res = transform_func->transform_t2s (binding, &from_value, &to_value, transform_func->transform_data); transform_func_unref (transform_func); if (res) { binding->is_frozen = TRUE; (void) g_param_value_validate (binding->source_pspec, &to_value); g_object_set_property (source, binding->source_pspec->name, &to_value); binding->is_frozen = FALSE; } g_value_unset (&from_value); g_value_unset (&to_value); g_object_unref (source); g_object_unref (binding); } static inline void g_binding_unbind_internal (GBinding *binding, gboolean unref_binding) { BindingContext *context = binding->context; GObject *source, *target; gboolean binding_was_removed = FALSE; TransformFunc *transform_func; g_mutex_lock (&binding->unbind_lock); transform_func = g_steal_pointer (&binding->transform_func); source = g_weak_ref_get (&context->source); target = g_weak_ref_get (&context->target); binding_was_removed = unbind_internal_locked (context, binding, source, target); g_mutex_unlock (&binding->unbind_lock); /* Unref source, target and transform_func after the mutex is unlocked as it * might release the last reference, which then accesses the mutex again */ g_clear_object (&target); g_clear_object (&source); g_clear_pointer (&transform_func, transform_func_unref); if (binding_was_removed && unref_binding) g_object_unref (binding); } static void g_binding_finalize (GObject *gobject) { GBinding *binding = G_BINDING (gobject); g_binding_unbind_internal (binding, FALSE); binding_context_unref (binding->context); g_mutex_clear (&binding->unbind_lock); G_OBJECT_CLASS (g_binding_parent_class)->finalize (gobject); } /* @key must have already been validated with is_valid() * Modifies @key in place. */ static void canonicalize_key (gchar *key) { gchar *p; for (p = key; *p != 0; p++) { gchar c = *p; if (c == '_') *p = '-'; } } /* @key must have already been validated with is_valid() */ static gboolean is_canonical (const gchar *key) { return (strchr (key, '_') == NULL); } static void g_binding_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { GBinding *binding = G_BINDING (gobject); switch (prop_id) { case PROP_SOURCE: g_weak_ref_set (&binding->context->source, g_value_get_object (value)); break; case PROP_TARGET: g_weak_ref_set (&binding->context->target, g_value_get_object (value)); break; case PROP_SOURCE_PROPERTY: case PROP_TARGET_PROPERTY: { gchar *name_copy = NULL; const gchar *name = g_value_get_string (value); const gchar **dest; /* Ensure the name we intern is canonical. */ if (!is_canonical (name)) { name_copy = g_value_dup_string (value); canonicalize_key (name_copy); name = name_copy; } if (prop_id == PROP_SOURCE_PROPERTY) dest = &binding->source_property; else dest = &binding->target_property; *dest = g_intern_string (name); g_free (name_copy); break; } case PROP_FLAGS: binding->flags = g_value_get_flags (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void g_binding_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { GBinding *binding = G_BINDING (gobject); switch (prop_id) { case PROP_SOURCE: g_value_take_object (value, g_weak_ref_get (&binding->context->source)); break; case PROP_SOURCE_PROPERTY: /* @source_property is interned, so we don’t need to take a copy */ g_value_set_interned_string (value, binding->source_property); break; case PROP_TARGET: g_value_take_object (value, g_weak_ref_get (&binding->context->target)); break; case PROP_TARGET_PROPERTY: /* @target_property is interned, so we don’t need to take a copy */ g_value_set_interned_string (value, binding->target_property); break; case PROP_FLAGS: g_value_set_flags (value, binding->flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void g_binding_constructed (GObject *gobject) { GBinding *binding = G_BINDING (gobject); GBindingTransformFunc transform_func = default_transform; GObject *source, *target; GQuark source_property_detail; GClosure *source_notify_closure; /* assert that we were constructed correctly */ source = g_weak_ref_get (&binding->context->source); target = g_weak_ref_get (&binding->context->target); g_assert (source != NULL); g_assert (target != NULL); g_assert (binding->source_property != NULL); g_assert (binding->target_property != NULL); /* we assume a check was performed prior to construction - since * g_object_bind_property_full() does it; we cannot fail construction * anyway, so it would be hard for use to properly warn here */ binding->source_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), binding->source_property); binding->target_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), binding->target_property); g_assert (binding->source_pspec != NULL); g_assert (binding->target_pspec != NULL); /* switch to the invert boolean transform if needed */ if (binding->flags & G_BINDING_INVERT_BOOLEAN) transform_func = default_invert_boolean_transform; /* set the default transformation functions here */ binding->transform_func = transform_func_new (transform_func, transform_func, NULL, NULL); source_property_detail = g_quark_from_string (binding->source_property); source_notify_closure = g_cclosure_new (G_CALLBACK (on_source_notify), binding_context_ref (binding->context), (GClosureNotify) binding_context_unref); binding->source_notify = g_signal_connect_closure_by_id (source, gobject_notify_signal_id, source_property_detail, source_notify_closure, FALSE); g_object_weak_ref (source, weak_unbind, binding_context_ref (binding->context)); if (binding->flags & G_BINDING_BIDIRECTIONAL) { GQuark target_property_detail; GClosure *target_notify_closure; target_property_detail = g_quark_from_string (binding->target_property); target_notify_closure = g_cclosure_new (G_CALLBACK (on_target_notify), binding_context_ref (binding->context), (GClosureNotify) binding_context_unref); binding->target_notify = g_signal_connect_closure_by_id (target, gobject_notify_signal_id, target_property_detail, target_notify_closure, FALSE); } if (target != source) { g_object_weak_ref (target, weak_unbind, binding_context_ref (binding->context)); /* Need to remember separately if a target weak notify was installed as * unlike for the source it can exist independently of the property * notification callback */ binding->target_weak_notify_installed = TRUE; } g_object_unref (source); g_object_unref (target); } static void g_binding_class_init (GBindingClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_notify_signal_id = g_signal_lookup ("notify", G_TYPE_OBJECT); g_assert (gobject_notify_signal_id != 0); gobject_class->constructed = g_binding_constructed; gobject_class->set_property = g_binding_set_property; gobject_class->get_property = g_binding_get_property; gobject_class->finalize = g_binding_finalize; /** * GBinding:source: * * The #GObject that should be used as the source of the binding * * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_SOURCE, g_param_spec_object ("source", P_("Source"), P_("The source of the binding"), G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GBinding:target: * * The #GObject that should be used as the target of the binding * * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_TARGET, g_param_spec_object ("target", P_("Target"), P_("The target of the binding"), G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GBinding:source-property: * * The name of the property of #GBinding:source that should be used * as the source of the binding. * * This should be in [canonical form][canonical-parameter-names] to get the * best performance. * * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_SOURCE_PROPERTY, g_param_spec_string ("source-property", P_("Source Property"), P_("The property on the source to bind"), NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GBinding:target-property: * * The name of the property of #GBinding:target that should be used * as the target of the binding. * * This should be in [canonical form][canonical-parameter-names] to get the * best performance. * * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_TARGET_PROPERTY, g_param_spec_string ("target-property", P_("Target Property"), P_("The property on the target to bind"), NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GBinding:flags: * * Flags to be used to control the #GBinding * * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_FLAGS, g_param_spec_flags ("flags", P_("Flags"), P_("The binding flags"), G_TYPE_BINDING_FLAGS, G_BINDING_DEFAULT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void g_binding_init (GBinding *binding) { g_mutex_init (&binding->unbind_lock); binding->context = g_atomic_rc_box_new0 (BindingContext); g_weak_ref_init (&binding->context->binding, binding); g_weak_ref_init (&binding->context->source, NULL); g_weak_ref_init (&binding->context->target, NULL); } /** * g_binding_get_flags: * @binding: a #GBinding * * Retrieves the flags passed when constructing the #GBinding. * * Returns: the #GBindingFlags used by the #GBinding * * Since: 2.26 */ GBindingFlags g_binding_get_flags (GBinding *binding) { g_return_val_if_fail (G_IS_BINDING (binding), G_BINDING_DEFAULT); return binding->flags; } /** * g_binding_get_source: * @binding: a #GBinding * * Retrieves the #GObject instance used as the source of the binding. * * A #GBinding can outlive the source #GObject as the binding does not hold a * strong reference to the source. If the source is destroyed before the * binding then this function will return %NULL. * * Use g_binding_dup_source() if the source or binding are used from different * threads as otherwise the pointer returned from this function might become * invalid if the source is finalized from another thread in the meantime. * * Returns: (transfer none) (nullable): the source #GObject, or %NULL if the * source does not exist any more. * * Deprecated: 2.68: Use g_binding_dup_source() for a safer version of this * function. * * Since: 2.26 */ GObject * g_binding_get_source (GBinding *binding) { GObject *source; g_return_val_if_fail (G_IS_BINDING (binding), NULL); source = g_weak_ref_get (&binding->context->source); /* Unref here, this API is not thread-safe * FIXME: Remove this API when we next break API */ if (source) g_object_unref (source); return source; } /** * g_binding_dup_source: * @binding: a #GBinding * * Retrieves the #GObject instance used as the source of the binding. * * A #GBinding can outlive the source #GObject as the binding does not hold a * strong reference to the source. If the source is destroyed before the * binding then this function will return %NULL. * * Returns: (transfer full) (nullable): the source #GObject, or %NULL if the * source does not exist any more. * * Since: 2.68 */ GObject * g_binding_dup_source (GBinding *binding) { g_return_val_if_fail (G_IS_BINDING (binding), NULL); return g_weak_ref_get (&binding->context->source); } /** * g_binding_get_target: * @binding: a #GBinding * * Retrieves the #GObject instance used as the target of the binding. * * A #GBinding can outlive the target #GObject as the binding does not hold a * strong reference to the target. If the target is destroyed before the * binding then this function will return %NULL. * * Use g_binding_dup_target() if the target or binding are used from different * threads as otherwise the pointer returned from this function might become * invalid if the target is finalized from another thread in the meantime. * * Returns: (transfer none) (nullable): the target #GObject, or %NULL if the * target does not exist any more. * * Deprecated: 2.68: Use g_binding_dup_target() for a safer version of this * function. * * Since: 2.26 */ GObject * g_binding_get_target (GBinding *binding) { GObject *target; g_return_val_if_fail (G_IS_BINDING (binding), NULL); target = g_weak_ref_get (&binding->context->target); /* Unref here, this API is not thread-safe * FIXME: Remove this API when we next break API */ if (target) g_object_unref (target); return target; } /** * g_binding_dup_target: * @binding: a #GBinding * * Retrieves the #GObject instance used as the target of the binding. * * A #GBinding can outlive the target #GObject as the binding does not hold a * strong reference to the target. If the target is destroyed before the * binding then this function will return %NULL. * * Returns: (transfer full) (nullable): the target #GObject, or %NULL if the * target does not exist any more. * * Since: 2.68 */ GObject * g_binding_dup_target (GBinding *binding) { g_return_val_if_fail (G_IS_BINDING (binding), NULL); return g_weak_ref_get (&binding->context->target); } /** * g_binding_get_source_property: * @binding: a #GBinding * * Retrieves the name of the property of #GBinding:source used as the source * of the binding. * * Returns: the name of the source property * * Since: 2.26 */ const gchar * g_binding_get_source_property (GBinding *binding) { g_return_val_if_fail (G_IS_BINDING (binding), NULL); return binding->source_property; } /** * g_binding_get_target_property: * @binding: a #GBinding * * Retrieves the name of the property of #GBinding:target used as the target * of the binding. * * Returns: the name of the target property * * Since: 2.26 */ const gchar * g_binding_get_target_property (GBinding *binding) { g_return_val_if_fail (G_IS_BINDING (binding), NULL); return binding->target_property; } /** * g_binding_unbind: * @binding: a #GBinding * * Explicitly releases the binding between the source and the target * property expressed by @binding. * * This function will release the reference that is being held on * the @binding instance if the binding is still bound; if you want to hold on * to the #GBinding instance after calling g_binding_unbind(), you will need * to hold a reference to it. * * Note however that this function does not take ownership of @binding, it * only unrefs the reference that was initially created by * g_object_bind_property() and is owned by the binding. * * Since: 2.38 */ void g_binding_unbind (GBinding *binding) { g_return_if_fail (G_IS_BINDING (binding)); g_binding_unbind_internal (binding, TRUE); } /** * g_object_bind_property_full: * @source: (type GObject.Object): the source #GObject * @source_property: the property on @source to bind * @target: (type GObject.Object): the target #GObject * @target_property: the property on @target to bind * @flags: flags to pass to #GBinding * @transform_to: (scope notified) (nullable): the transformation function * from the @source to the @target, or %NULL to use the default * @transform_from: (scope notified) (nullable): the transformation function * from the @target to the @source, or %NULL to use the default * @user_data: custom data to be passed to the transformation functions, * or %NULL * @notify: (nullable): a function to call when disposing the binding, to free * resources used by the transformation functions, or %NULL if not required * * Complete version of g_object_bind_property(). * * Creates a binding between @source_property on @source and @target_property * on @target, allowing you to set the transformation functions to be used by * the binding. * * If @flags contains %G_BINDING_BIDIRECTIONAL then the binding will be mutual: * if @target_property on @target changes then the @source_property on @source * will be updated as well. The @transform_from function is only used in case * of bidirectional bindings, otherwise it will be ignored * * The binding will automatically be removed when either the @source or the * @target instances are finalized. This will release the reference that is * being held on the #GBinding instance; if you want to hold on to the * #GBinding instance, you will need to hold a reference to it. * * To remove the binding, call g_binding_unbind(). * * A #GObject can have multiple bindings. * * The same @user_data parameter will be used for both @transform_to * and @transform_from transformation functions; the @notify function will * be called once, when the binding is removed. If you need different data * for each transformation function, please use * g_object_bind_property_with_closures() instead. * * Returns: (transfer none): the #GBinding instance representing the * binding between the two #GObject instances. The binding is released * whenever the #GBinding reference count reaches zero. * * Since: 2.26 */ GBinding * g_object_bind_property_full (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags, GBindingTransformFunc transform_to, GBindingTransformFunc transform_from, gpointer user_data, GDestroyNotify notify) { GParamSpec *pspec; GBinding *binding; g_return_val_if_fail (G_IS_OBJECT (source), NULL); g_return_val_if_fail (source_property != NULL, NULL); g_return_val_if_fail (g_param_spec_is_valid_name (source_property), NULL); g_return_val_if_fail (G_IS_OBJECT (target), NULL); g_return_val_if_fail (target_property != NULL, NULL); g_return_val_if_fail (g_param_spec_is_valid_name (target_property), NULL); if (source == target && g_strcmp0 (source_property, target_property) == 0) { g_critical ("Unable to bind the same property on the same instance"); return NULL; } /* remove the G_BINDING_INVERT_BOOLEAN flag in case we have * custom transformation functions */ if ((flags & G_BINDING_INVERT_BOOLEAN) && (transform_to != NULL || transform_from != NULL)) { flags &= ~G_BINDING_INVERT_BOOLEAN; } pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property); if (pspec == NULL) { g_critical ("%s: The source object of type %s has no property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (source), source_property); return NULL; } if (!(pspec->flags & G_PARAM_READABLE)) { g_critical ("%s: The source object of type %s has no readable property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (source), source_property); return NULL; } if ((flags & G_BINDING_BIDIRECTIONAL) && ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) || !(pspec->flags & G_PARAM_WRITABLE))) { g_critical ("%s: The source object of type %s has no writable property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (source), source_property); return NULL; } if ((flags & G_BINDING_INVERT_BOOLEAN) && !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN)) { g_critical ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used " "when binding boolean properties; the source property '%s' " "is of type '%s'", G_STRLOC, source_property, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); return NULL; } pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), target_property); if (pspec == NULL) { g_critical ("%s: The target object of type %s has no property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (target), target_property); return NULL; } if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) || !(pspec->flags & G_PARAM_WRITABLE)) { g_critical ("%s: The target object of type %s has no writable property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (target), target_property); return NULL; } if ((flags & G_BINDING_BIDIRECTIONAL) && !(pspec->flags & G_PARAM_READABLE)) { g_critical ("%s: The target object of type %s has no readable property called '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (target), target_property); return NULL; } if ((flags & G_BINDING_INVERT_BOOLEAN) && !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN)) { g_critical ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used " "when binding boolean properties; the target property '%s' " "is of type '%s'", G_STRLOC, target_property, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); return NULL; } binding = g_object_new (G_TYPE_BINDING, "source", source, "source-property", source_property, "target", target, "target-property", target_property, "flags", flags, NULL); g_assert (binding->transform_func != NULL); /* Use default functions if not provided here */ if (transform_to == NULL) transform_to = binding->transform_func->transform_s2t; if (transform_from == NULL) transform_from = binding->transform_func->transform_t2s; g_clear_pointer (&binding->transform_func, transform_func_unref); binding->transform_func = transform_func_new (transform_to, transform_from, user_data, notify); /* synchronize the target with the source by faking an emission of * the ::notify signal for the source property; this will also take * care of the bidirectional binding case because the eventual change * will emit a notification on the target */ if (flags & G_BINDING_SYNC_CREATE) on_source_notify (source, binding->source_pspec, binding->context); return binding; } /** * g_object_bind_property: * @source: (type GObject.Object): the source #GObject * @source_property: the property on @source to bind * @target: (type GObject.Object): the target #GObject * @target_property: the property on @target to bind * @flags: flags to pass to #GBinding * * Creates a binding between @source_property on @source and @target_property * on @target. * * Whenever the @source_property is changed the @target_property is * updated using the same value. For instance: * * |[ * g_object_bind_property (action, "active", widget, "sensitive", 0); * ]| * * Will result in the "sensitive" property of the widget #GObject instance to be * updated with the same value of the "active" property of the action #GObject * instance. * * If @flags contains %G_BINDING_BIDIRECTIONAL then the binding will be mutual: * if @target_property on @target changes then the @source_property on @source * will be updated as well. * * The binding will automatically be removed when either the @source or the * @target instances are finalized. To remove the binding without affecting the * @source and the @target you can just call g_object_unref() on the returned * #GBinding instance. * * Removing the binding by calling g_object_unref() on it must only be done if * the binding, @source and @target are only used from a single thread and it * is clear that both @source and @target outlive the binding. Especially it * is not safe to rely on this if the binding, @source or @target can be * finalized from different threads. Keep another reference to the binding and * use g_binding_unbind() instead to be on the safe side. * * A #GObject can have multiple bindings. * * Returns: (transfer none): the #GBinding instance representing the * binding between the two #GObject instances. The binding is released * whenever the #GBinding reference count reaches zero. * * Since: 2.26 */ GBinding * g_object_bind_property (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { /* type checking is done in g_object_bind_property_full() */ return g_object_bind_property_full (source, source_property, target, target_property, flags, NULL, NULL, NULL, NULL); } typedef struct _TransformData { GClosure *transform_to_closure; GClosure *transform_from_closure; } TransformData; static gboolean bind_with_closures_transform_to (GBinding *binding, const GValue *source, GValue *target, gpointer data) { TransformData *t_data = data; GValue params[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; GValue retval = G_VALUE_INIT; gboolean res; g_value_init (¶ms[0], G_TYPE_BINDING); g_value_set_object (¶ms[0], binding); g_value_init (¶ms[1], G_TYPE_VALUE); g_value_set_boxed (¶ms[1], source); g_value_init (¶ms[2], G_TYPE_VALUE); g_value_set_boxed (¶ms[2], target); g_value_init (&retval, G_TYPE_BOOLEAN); g_value_set_boolean (&retval, FALSE); g_closure_invoke (t_data->transform_to_closure, &retval, 3, params, NULL); res = g_value_get_boolean (&retval); if (res) { const GValue *out_value = g_value_get_boxed (¶ms[2]); g_assert (out_value != NULL); g_value_copy (out_value, target); } g_value_unset (¶ms[0]); g_value_unset (¶ms[1]); g_value_unset (¶ms[2]); g_value_unset (&retval); return res; } static gboolean bind_with_closures_transform_from (GBinding *binding, const GValue *source, GValue *target, gpointer data) { TransformData *t_data = data; GValue params[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; GValue retval = G_VALUE_INIT; gboolean res; g_value_init (¶ms[0], G_TYPE_BINDING); g_value_set_object (¶ms[0], binding); g_value_init (¶ms[1], G_TYPE_VALUE); g_value_set_boxed (¶ms[1], source); g_value_init (¶ms[2], G_TYPE_VALUE); g_value_set_boxed (¶ms[2], target); g_value_init (&retval, G_TYPE_BOOLEAN); g_value_set_boolean (&retval, FALSE); g_closure_invoke (t_data->transform_from_closure, &retval, 3, params, NULL); res = g_value_get_boolean (&retval); if (res) { const GValue *out_value = g_value_get_boxed (¶ms[2]); g_assert (out_value != NULL); g_value_copy (out_value, target); } g_value_unset (¶ms[0]); g_value_unset (¶ms[1]); g_value_unset (¶ms[2]); g_value_unset (&retval); return res; } static void bind_with_closures_free_func (gpointer data) { TransformData *t_data = data; if (t_data->transform_to_closure != NULL) g_closure_unref (t_data->transform_to_closure); if (t_data->transform_from_closure != NULL) g_closure_unref (t_data->transform_from_closure); g_slice_free (TransformData, t_data); } /** * g_object_bind_property_with_closures: (rename-to g_object_bind_property_full) * @source: (type GObject.Object): the source #GObject * @source_property: the property on @source to bind * @target: (type GObject.Object): the target #GObject * @target_property: the property on @target to bind * @flags: flags to pass to #GBinding * @transform_to: a #GClosure wrapping the transformation function * from the @source to the @target, or %NULL to use the default * @transform_from: a #GClosure wrapping the transformation function * from the @target to the @source, or %NULL to use the default * * Creates a binding between @source_property on @source and @target_property * on @target, allowing you to set the transformation functions to be used by * the binding. * * This function is the language bindings friendly version of * g_object_bind_property_full(), using #GClosures instead of * function pointers. * * Returns: (transfer none): the #GBinding instance representing the * binding between the two #GObject instances. The binding is released * whenever the #GBinding reference count reaches zero. * * Since: 2.26 */ GBinding * g_object_bind_property_with_closures (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags, GClosure *transform_to, GClosure *transform_from) { TransformData *data; data = g_slice_new0 (TransformData); if (transform_to != NULL) { if (G_CLOSURE_NEEDS_MARSHAL (transform_to)) g_closure_set_marshal (transform_to, g_cclosure_marshal_BOOLEAN__BOXED_BOXED); data->transform_to_closure = g_closure_ref (transform_to); g_closure_sink (data->transform_to_closure); } if (transform_from != NULL) { if (G_CLOSURE_NEEDS_MARSHAL (transform_from)) g_closure_set_marshal (transform_from, g_cclosure_marshal_BOOLEAN__BOXED_BOXED); data->transform_from_closure = g_closure_ref (transform_from); g_closure_sink (data->transform_from_closure); } return g_object_bind_property_full (source, source_property, target, target_property, flags, transform_to != NULL ? bind_with_closures_transform_to : NULL, transform_from != NULL ? bind_with_closures_transform_from : NULL, data, bind_with_closures_free_func); }