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 }