(root)/
glib-2.79.0/
gobject/
gbindinggroup.c
       1  /* GObject - GLib Type, Object, Parameter and Signal Library
       2   *
       3   * Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
       4   * Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General
      17   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * SPDX-License-Identifier: LGPL-2.1-or-later
      20   */
      21  
      22  #include "config.h"
      23  #include "glib.h"
      24  #include "glibintl.h"
      25  
      26  #include "gbindinggroup.h"
      27  #include "gparamspecs.h"
      28  
      29  /**
      30   * GBindingGroup:
      31   *
      32   * `GBindingGroup` can be used to bind multiple properties
      33   * from an object collectively.
      34   *
      35   * Use the various methods to bind properties from a single source
      36   * object to multiple destination objects. Properties can be bound
      37   * bidirectionally and are connected when the source object is set
      38   * with [method@GObject.BindingGroup.set_source].
      39   *
      40   * Since: 2.72
      41   */
      42  
      43  #if 0
      44  # define DEBUG_BINDINGS
      45  #endif
      46  
      47  struct _GBindingGroup
      48  {
      49    GObject    parent_instance;
      50    GMutex     mutex;
      51    GObject   *source;         /* (owned weak) */
      52    GPtrArray *lazy_bindings;  /* (owned) (element-type LazyBinding) */
      53  };
      54  
      55  typedef struct _GBindingGroupClass
      56  {
      57    GObjectClass parent_class;
      58  } GBindingGroupClass;
      59  
      60  typedef struct
      61  {
      62    GBindingGroup      *group;  /* (unowned) */
      63    const char         *source_property;  /* (interned) */
      64    const char         *target_property;  /* (interned) */
      65    GObject            *target;  /* (owned weak) */
      66    GBinding           *binding;  /* (unowned) */
      67    gpointer            user_data;
      68    GDestroyNotify      user_data_destroy;
      69    gpointer            transform_to;  /* (nullable) (owned) */
      70    gpointer            transform_from;  /* (nullable) (owned) */
      71    GBindingFlags       binding_flags;
      72    guint               using_closures : 1;
      73  } LazyBinding;
      74  
      75  G_DEFINE_TYPE (GBindingGroup, g_binding_group, G_TYPE_OBJECT)
      76  
      77  typedef enum
      78  {
      79    PROP_SOURCE = 1,
      80    N_PROPS
      81  } GBindingGroupProperty;
      82  
      83  static void lazy_binding_free (gpointer data);
      84  
      85  static GParamSpec *properties[N_PROPS];
      86  
      87  static void
      88  g_binding_group_connect (GBindingGroup *self,
      89                           LazyBinding   *lazy_binding)
      90  {
      91    GBinding *binding;
      92  
      93    g_assert (G_IS_BINDING_GROUP (self));
      94    g_assert (self->source != NULL);
      95    g_assert (lazy_binding != NULL);
      96    g_assert (lazy_binding->binding == NULL);
      97    g_assert (lazy_binding->target != NULL);
      98    g_assert (lazy_binding->target_property != NULL);
      99    g_assert (lazy_binding->source_property != NULL);
     100  
     101  #ifdef DEBUG_BINDINGS
     102    {
     103      GFlagsClass *flags_class;
     104      g_autofree gchar *flags_str = NULL;
     105  
     106      flags_class = g_type_class_ref (G_TYPE_BINDING_FLAGS);
     107      flags_str = g_flags_to_string (flags_class, lazy_binding->binding_flags);
     108  
     109      g_print ("Binding %s(%p):%s to %s(%p):%s (flags=%s)\n",
     110               G_OBJECT_TYPE_NAME (self->source),
     111               self->source,
     112               lazy_binding->source_property,
     113               G_OBJECT_TYPE_NAME (lazy_binding->target),
     114               lazy_binding->target,
     115               lazy_binding->target_property,
     116               flags_str);
     117  
     118      g_type_class_unref (flags_class);
     119    }
     120  #endif
     121  
     122    if (!lazy_binding->using_closures)
     123      binding = g_object_bind_property_full (self->source,
     124                                             lazy_binding->source_property,
     125                                             lazy_binding->target,
     126                                             lazy_binding->target_property,
     127                                             lazy_binding->binding_flags,
     128                                             lazy_binding->transform_to,
     129                                             lazy_binding->transform_from,
     130                                             lazy_binding->user_data,
     131                                             NULL);
     132    else
     133      binding = g_object_bind_property_with_closures (self->source,
     134                                                      lazy_binding->source_property,
     135                                                      lazy_binding->target,
     136                                                      lazy_binding->target_property,
     137                                                      lazy_binding->binding_flags,
     138                                                      lazy_binding->transform_to,
     139                                                      lazy_binding->transform_from);
     140  
     141    lazy_binding->binding = binding;
     142  }
     143  
     144  static void
     145  g_binding_group_disconnect (LazyBinding *lazy_binding)
     146  {
     147    g_assert (lazy_binding != NULL);
     148  
     149    if (lazy_binding->binding != NULL)
     150      {
     151        g_binding_unbind (lazy_binding->binding);
     152        lazy_binding->binding = NULL;
     153      }
     154  }
     155  
     156  static void
     157  g_binding_group__source_weak_notify (gpointer  data,
     158                                       GObject  *where_object_was)
     159  {
     160    GBindingGroup *self = data;
     161    guint i;
     162  
     163    g_assert (G_IS_BINDING_GROUP (self));
     164  
     165    g_mutex_lock (&self->mutex);
     166  
     167    self->source = NULL;
     168  
     169    for (i = 0; i < self->lazy_bindings->len; i++)
     170      {
     171        LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
     172  
     173        lazy_binding->binding = NULL;
     174      }
     175  
     176    g_mutex_unlock (&self->mutex);
     177  }
     178  
     179  static void
     180  g_binding_group__target_weak_notify (gpointer  data,
     181                                       GObject  *where_object_was)
     182  {
     183    GBindingGroup *self = data;
     184    LazyBinding *to_free = NULL;
     185    guint i;
     186  
     187    g_assert (G_IS_BINDING_GROUP (self));
     188  
     189    g_mutex_lock (&self->mutex);
     190  
     191    for (i = 0; i < self->lazy_bindings->len; i++)
     192      {
     193        LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
     194  
     195        if (lazy_binding->target == where_object_was)
     196          {
     197            lazy_binding->target = NULL;
     198            lazy_binding->binding = NULL;
     199  
     200            to_free = g_ptr_array_steal_index_fast (self->lazy_bindings, i);
     201            break;
     202          }
     203      }
     204  
     205    g_mutex_unlock (&self->mutex);
     206  
     207    if (to_free != NULL)
     208      lazy_binding_free (to_free);
     209  }
     210  
     211  static void
     212  lazy_binding_free (gpointer data)
     213  {
     214    LazyBinding *lazy_binding = data;
     215  
     216    if (lazy_binding->target != NULL)
     217      {
     218        g_object_weak_unref (lazy_binding->target,
     219                             g_binding_group__target_weak_notify,
     220                             lazy_binding->group);
     221        lazy_binding->target = NULL;
     222      }
     223  
     224    g_binding_group_disconnect (lazy_binding);
     225  
     226    lazy_binding->group = NULL;
     227    lazy_binding->source_property = NULL;
     228    lazy_binding->target_property = NULL;
     229  
     230    if (lazy_binding->user_data_destroy)
     231      lazy_binding->user_data_destroy (lazy_binding->user_data);
     232  
     233    if (lazy_binding->using_closures)
     234      {
     235        g_clear_pointer (&lazy_binding->transform_to, g_closure_unref);
     236        g_clear_pointer (&lazy_binding->transform_from, g_closure_unref);
     237      }
     238  
     239    g_slice_free (LazyBinding, lazy_binding);
     240  }
     241  
     242  static void
     243  g_binding_group_dispose (GObject *object)
     244  {
     245    GBindingGroup *self = (GBindingGroup *)object;
     246    LazyBinding **lazy_bindings = NULL;
     247    gsize len = 0;
     248    gsize i;
     249  
     250    g_assert (G_IS_BINDING_GROUP (self));
     251  
     252    g_mutex_lock (&self->mutex);
     253  
     254    if (self->source != NULL)
     255      {
     256        g_object_weak_unref (self->source,
     257                             g_binding_group__source_weak_notify,
     258                             self);
     259        self->source = NULL;
     260      }
     261  
     262    if (self->lazy_bindings->len > 0)
     263      lazy_bindings = (LazyBinding **)g_ptr_array_steal (self->lazy_bindings, &len);
     264  
     265    g_mutex_unlock (&self->mutex);
     266  
     267    /* Free bindings without holding self->mutex to avoid re-entrancy
     268     * from collateral damage through release of binding closure data,
     269     * GDataList, etc.
     270     */
     271    for (i = 0; i < len; i++)
     272      lazy_binding_free (lazy_bindings[i]);
     273    g_free (lazy_bindings);
     274  
     275    G_OBJECT_CLASS (g_binding_group_parent_class)->dispose (object);
     276  }
     277  
     278  static void
     279  g_binding_group_finalize (GObject *object)
     280  {
     281    GBindingGroup *self = (GBindingGroup *)object;
     282  
     283    g_assert (self->lazy_bindings != NULL);
     284    g_assert (self->lazy_bindings->len == 0);
     285  
     286    g_clear_pointer (&self->lazy_bindings, g_ptr_array_unref);
     287    g_mutex_clear (&self->mutex);
     288  
     289    G_OBJECT_CLASS (g_binding_group_parent_class)->finalize (object);
     290  }
     291  
     292  static void
     293  g_binding_group_get_property (GObject    *object,
     294                                guint       prop_id,
     295                                GValue     *value,
     296                                GParamSpec *pspec)
     297  {
     298    GBindingGroup *self = G_BINDING_GROUP (object);
     299  
     300    switch ((GBindingGroupProperty) prop_id)
     301      {
     302      case PROP_SOURCE:
     303        g_value_take_object (value, g_binding_group_dup_source (self));
     304        break;
     305  
     306      default:
     307        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     308      }
     309  }
     310  
     311  static void
     312  g_binding_group_set_property (GObject      *object,
     313                                guint         prop_id,
     314                                const GValue *value,
     315                                GParamSpec   *pspec)
     316  {
     317    GBindingGroup *self = G_BINDING_GROUP (object);
     318  
     319    switch ((GBindingGroupProperty) prop_id)
     320      {
     321      case PROP_SOURCE:
     322        g_binding_group_set_source (self, g_value_get_object (value));
     323        break;
     324  
     325      default:
     326        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     327      }
     328  }
     329  
     330  static void
     331  g_binding_group_class_init (GBindingGroupClass *klass)
     332  {
     333    GObjectClass *object_class = G_OBJECT_CLASS (klass);
     334  
     335    object_class->dispose = g_binding_group_dispose;
     336    object_class->finalize = g_binding_group_finalize;
     337    object_class->get_property = g_binding_group_get_property;
     338    object_class->set_property = g_binding_group_set_property;
     339  
     340    /**
     341     * GBindingGroup:source:
     342     *
     343     * The source object used for binding properties.
     344     *
     345     * Since: 2.72
     346     */
     347    properties[PROP_SOURCE] =
     348        g_param_spec_object ("source", NULL, NULL,
     349                             G_TYPE_OBJECT,
     350                             (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
     351  
     352    g_object_class_install_properties (object_class, N_PROPS, properties);
     353  }
     354  
     355  static void
     356  g_binding_group_init (GBindingGroup *self)
     357  {
     358    g_mutex_init (&self->mutex);
     359    self->lazy_bindings = g_ptr_array_new_with_free_func (lazy_binding_free);
     360  }
     361  
     362  /**
     363   * g_binding_group_new:
     364   *
     365   * Creates a new #GBindingGroup.
     366   *
     367   * Returns: (transfer full): a new #GBindingGroup
     368   *
     369   * Since: 2.72
     370   */
     371  GBindingGroup *
     372  g_binding_group_new (void)
     373  {
     374    return g_object_new (G_TYPE_BINDING_GROUP, NULL);
     375  }
     376  
     377  /**
     378   * g_binding_group_dup_source:
     379   * @self: the #GBindingGroup
     380   *
     381   * Gets the source object used for binding properties.
     382   *
     383   * Returns: (transfer none) (nullable) (type GObject): a #GObject or %NULL.
     384   *
     385   * Since: 2.72
     386   */
     387  gpointer
     388  g_binding_group_dup_source (GBindingGroup *self)
     389  {
     390    GObject *source;
     391  
     392    g_return_val_if_fail (G_IS_BINDING_GROUP (self), NULL);
     393  
     394    g_mutex_lock (&self->mutex);
     395    source = self->source ? g_object_ref (self->source) : NULL;
     396    g_mutex_unlock (&self->mutex);
     397  
     398    return source;
     399  }
     400  
     401  static gboolean
     402  g_binding_group_check_source (GBindingGroup *self,
     403                                gpointer       source)
     404  {
     405    guint i;
     406  
     407    g_assert (G_IS_BINDING_GROUP (self));
     408    g_assert (!source || G_IS_OBJECT (source));
     409  
     410    for (i = 0; i < self->lazy_bindings->len; i++)
     411      {
     412        LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
     413  
     414        g_return_val_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (source),
     415                                                            lazy_binding->source_property) != NULL,
     416                              FALSE);
     417      }
     418  
     419    return TRUE;
     420  }
     421  
     422  /**
     423   * g_binding_group_set_source:
     424   * @self: the #GBindingGroup
     425   * @source: (type GObject) (nullable) (transfer none): the source #GObject,
     426   *   or %NULL to clear it
     427   *
     428   * Sets @source as the source object used for creating property
     429   * bindings. If there is already a source object all bindings from it
     430   * will be removed.
     431   *
     432   * Note that all properties that have been bound must exist on @source.
     433   *
     434   * Since: 2.72
     435   */
     436  void
     437  g_binding_group_set_source (GBindingGroup *self,
     438                              gpointer       source)
     439  {
     440    gboolean notify = FALSE;
     441  
     442    g_return_if_fail (G_IS_BINDING_GROUP (self));
     443    g_return_if_fail (!source || G_IS_OBJECT (source));
     444    g_return_if_fail (source != (gpointer) self);
     445  
     446    g_mutex_lock (&self->mutex);
     447  
     448    if (source == (gpointer) self->source)
     449      goto unlock;
     450  
     451    if (self->source != NULL)
     452      {
     453        guint i;
     454  
     455        g_object_weak_unref (self->source,
     456                             g_binding_group__source_weak_notify,
     457                             self);
     458        self->source = NULL;
     459  
     460        for (i = 0; i < self->lazy_bindings->len; i++)
     461          {
     462            LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
     463  
     464            g_binding_group_disconnect (lazy_binding);
     465          }
     466      }
     467  
     468    if (source != NULL && g_binding_group_check_source (self, source))
     469      {
     470        guint i;
     471  
     472        self->source = source;
     473        g_object_weak_ref (self->source,
     474                           g_binding_group__source_weak_notify,
     475                           self);
     476  
     477        for (i = 0; i < self->lazy_bindings->len; i++)
     478          {
     479            LazyBinding *lazy_binding;
     480  
     481            lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
     482            g_binding_group_connect (self, lazy_binding);
     483          }
     484      }
     485  
     486    notify = TRUE;
     487  
     488  unlock:
     489    g_mutex_unlock (&self->mutex);
     490  
     491    if (notify)
     492      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SOURCE]);
     493  }
     494  
     495  static void
     496  g_binding_group_bind_helper (GBindingGroup  *self,
     497                               const gchar    *source_property,
     498                               gpointer        target,
     499                               const gchar    *target_property,
     500                               GBindingFlags   flags,
     501                               gpointer        transform_to,
     502                               gpointer        transform_from,
     503                               gpointer        user_data,
     504                               GDestroyNotify  user_data_destroy,
     505                               gboolean        using_closures)
     506  {
     507    LazyBinding *lazy_binding;
     508  
     509    g_return_if_fail (G_IS_BINDING_GROUP (self));
     510    g_return_if_fail (source_property != NULL);
     511    g_return_if_fail (self->source == NULL ||
     512                      g_object_class_find_property (G_OBJECT_GET_CLASS (self->source),
     513                                                    source_property) != NULL);
     514    g_return_if_fail (G_IS_OBJECT (target));
     515    g_return_if_fail (target_property != NULL);
     516    g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (target),
     517                                                    target_property) != NULL);
     518    g_return_if_fail (target != (gpointer) self ||
     519                      strcmp (source_property, target_property) != 0);
     520  
     521    g_mutex_lock (&self->mutex);
     522  
     523    lazy_binding = g_slice_new0 (LazyBinding);
     524    lazy_binding->group = self;
     525    lazy_binding->source_property = g_intern_string (source_property);
     526    lazy_binding->target_property = g_intern_string (target_property);
     527    lazy_binding->target = target;
     528    lazy_binding->binding_flags = flags | G_BINDING_SYNC_CREATE;
     529    lazy_binding->user_data = user_data;
     530    lazy_binding->user_data_destroy = user_data_destroy;
     531    lazy_binding->transform_to = transform_to;
     532    lazy_binding->transform_from = transform_from;
     533  
     534    if (using_closures)
     535      {
     536        lazy_binding->using_closures = TRUE;
     537  
     538        if (transform_to != NULL)
     539          g_closure_sink (g_closure_ref (transform_to));
     540  
     541        if (transform_from != NULL)
     542          g_closure_sink (g_closure_ref (transform_from));
     543      }
     544  
     545    g_object_weak_ref (target,
     546                       g_binding_group__target_weak_notify,
     547                       self);
     548  
     549    g_ptr_array_add (self->lazy_bindings, lazy_binding);
     550  
     551    if (self->source != NULL)
     552      g_binding_group_connect (self, lazy_binding);
     553  
     554    g_mutex_unlock (&self->mutex);
     555  }
     556  
     557  /**
     558   * g_binding_group_bind:
     559   * @self: the #GBindingGroup
     560   * @source_property: the property on the source to bind
     561   * @target: (type GObject) (transfer none) (not nullable): the target #GObject
     562   * @target_property: the property on @target to bind
     563   * @flags: the flags used to create the #GBinding
     564   *
     565   * Creates a binding between @source_property on the source object
     566   * and @target_property on @target. Whenever the @source_property
     567   * is changed the @target_property is updated using the same value.
     568   * The binding flag %G_BINDING_SYNC_CREATE is automatically specified.
     569   *
     570   * See g_object_bind_property() for more information.
     571   *
     572   * Since: 2.72
     573   */
     574  void
     575  g_binding_group_bind (GBindingGroup *self,
     576                        const gchar   *source_property,
     577                        gpointer       target,
     578                        const gchar   *target_property,
     579                        GBindingFlags  flags)
     580  {
     581    g_binding_group_bind_full (self, source_property,
     582                               target, target_property,
     583                               flags,
     584                               NULL, NULL,
     585                               NULL, NULL);
     586  }
     587  
     588  /**
     589   * g_binding_group_bind_full:
     590   * @self: the #GBindingGroup
     591   * @source_property: the property on the source to bind
     592   * @target: (type GObject) (transfer none) (not nullable): the target #GObject
     593   * @target_property: the property on @target to bind
     594   * @flags: the flags used to create the #GBinding
     595   * @transform_to: (scope notified) (nullable): the transformation function
     596   *     from the source object to the @target, or %NULL to use the default
     597   * @transform_from: (scope notified) (nullable): the transformation function
     598   *     from the @target to the source object, or %NULL to use the default
     599   * @user_data: custom data to be passed to the transformation
     600   *             functions, or %NULL
     601   * @user_data_destroy: function to be called when disposing the binding,
     602   *     to free the resources used by the transformation functions
     603   *
     604   * Creates a binding between @source_property on the source object and
     605   * @target_property on @target, allowing you to set the transformation
     606   * functions to be used by the binding. The binding flag
     607   * %G_BINDING_SYNC_CREATE is automatically specified.
     608   *
     609   * See g_object_bind_property_full() for more information.
     610   *
     611   * Since: 2.72
     612   */
     613  void
     614  g_binding_group_bind_full (GBindingGroup         *self,
     615                             const gchar           *source_property,
     616                             gpointer               target,
     617                             const gchar           *target_property,
     618                             GBindingFlags          flags,
     619                             GBindingTransformFunc  transform_to,
     620                             GBindingTransformFunc  transform_from,
     621                             gpointer               user_data,
     622                             GDestroyNotify         user_data_destroy)
     623  {
     624    g_binding_group_bind_helper (self, source_property,
     625                                 target, target_property,
     626                                 flags,
     627                                 transform_to, transform_from,
     628                                 user_data, user_data_destroy,
     629                                 FALSE);
     630  }
     631  
     632  /**
     633   * g_binding_group_bind_with_closures: (rename-to g_binding_group_bind_full)
     634   * @self: the #GBindingGroup
     635   * @source_property: the property on the source to bind
     636   * @target: (type GObject) (transfer none) (not nullable): the target #GObject
     637   * @target_property: the property on @target to bind
     638   * @flags: the flags used to create the #GBinding
     639   * @transform_to: (nullable) (transfer none): a #GClosure wrapping the
     640   *     transformation function from the source object to the @target,
     641   *     or %NULL to use the default
     642   * @transform_from: (nullable) (transfer none): a #GClosure wrapping the
     643   *     transformation function from the @target to the source object,
     644   *     or %NULL to use the default
     645   *
     646   * Creates a binding between @source_property on the source object and
     647   * @target_property on @target, allowing you to set the transformation
     648   * functions to be used by the binding. The binding flag
     649   * %G_BINDING_SYNC_CREATE is automatically specified.
     650   *
     651   * This function is the language bindings friendly version of
     652   * g_binding_group_bind_property_full(), using #GClosures
     653   * instead of function pointers.
     654   *
     655   * See g_object_bind_property_with_closures() for more information.
     656   *
     657   * Since: 2.72
     658   */
     659  void
     660  g_binding_group_bind_with_closures (GBindingGroup *self,
     661                                      const gchar   *source_property,
     662                                      gpointer       target,
     663                                      const gchar   *target_property,
     664                                      GBindingFlags  flags,
     665                                      GClosure      *transform_to,
     666                                      GClosure      *transform_from)
     667  {
     668    g_binding_group_bind_helper (self, source_property,
     669                                 target, target_property,
     670                                 flags,
     671                                 transform_to, transform_from,
     672                                 NULL, NULL,
     673                                 TRUE);
     674  }