3 #include <glib-object.h>
5 typedef struct _BindingSource
7 GObject parent_instance
;
15 typedef struct _BindingSourceClass
17 GObjectClass parent_class
;
30 static GType
binding_source_get_type (void);
31 G_DEFINE_TYPE (BindingSource
, binding_source
, G_TYPE_OBJECT
)
34 binding_source_set_property (GObject
*gobject
,
39 BindingSource
*source
= (BindingSource
*) gobject
;
44 source
->foo
= g_value_get_int (value
);
48 source
->bar
= g_value_get_int (value
);
51 case PROP_SOURCE_VALUE
:
52 source
->value
= g_value_get_double (value
);
55 case PROP_SOURCE_TOGGLE
:
56 source
->toggle
= g_value_get_boolean (value
);
60 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject
, prop_id
, pspec
);
65 binding_source_get_property (GObject
*gobject
,
70 BindingSource
*source
= (BindingSource
*) gobject
;
75 g_value_set_int (value
, source
->foo
);
79 g_value_set_int (value
, source
->bar
);
82 case PROP_SOURCE_VALUE
:
83 g_value_set_double (value
, source
->value
);
86 case PROP_SOURCE_TOGGLE
:
87 g_value_set_boolean (value
, source
->toggle
);
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject
, prop_id
, pspec
);
96 binding_source_class_init (BindingSourceClass
*klass
)
98 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
100 gobject_class
->set_property
= binding_source_set_property
;
101 gobject_class
->get_property
= binding_source_get_property
;
103 g_object_class_install_property (gobject_class
, PROP_SOURCE_FOO
,
104 g_param_spec_int ("foo", "Foo", "Foo",
108 g_object_class_install_property (gobject_class
, PROP_SOURCE_BAR
,
109 g_param_spec_int ("bar", "Bar", "Bar",
113 g_object_class_install_property (gobject_class
, PROP_SOURCE_VALUE
,
114 g_param_spec_double ("value", "Value", "Value",
118 g_object_class_install_property (gobject_class
, PROP_SOURCE_TOGGLE
,
119 g_param_spec_boolean ("toggle", "Toggle", "Toggle",
125 binding_source_init (BindingSource
*self
)
129 typedef struct _BindingTarget
131 GObject parent_instance
;
138 typedef struct _BindingTargetClass
140 GObjectClass parent_class
;
141 } BindingTargetClass
;
152 static GType
binding_target_get_type (void);
153 G_DEFINE_TYPE (BindingTarget
, binding_target
, G_TYPE_OBJECT
)
156 binding_target_set_property (GObject
*gobject
,
161 BindingTarget
*target
= (BindingTarget
*) gobject
;
165 case PROP_TARGET_BAR
:
166 target
->bar
= g_value_get_int (value
);
169 case PROP_TARGET_VALUE
:
170 target
->value
= g_value_get_double (value
);
173 case PROP_TARGET_TOGGLE
:
174 target
->toggle
= g_value_get_boolean (value
);
178 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject
, prop_id
, pspec
);
183 binding_target_get_property (GObject
*gobject
,
188 BindingTarget
*target
= (BindingTarget
*) gobject
;
192 case PROP_TARGET_BAR
:
193 g_value_set_int (value
, target
->bar
);
196 case PROP_TARGET_VALUE
:
197 g_value_set_double (value
, target
->value
);
200 case PROP_TARGET_TOGGLE
:
201 g_value_set_boolean (value
, target
->toggle
);
205 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject
, prop_id
, pspec
);
210 binding_target_class_init (BindingTargetClass
*klass
)
212 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
214 gobject_class
->set_property
= binding_target_set_property
;
215 gobject_class
->get_property
= binding_target_get_property
;
217 g_object_class_install_property (gobject_class
, PROP_TARGET_BAR
,
218 g_param_spec_int ("bar", "Bar", "Bar",
222 g_object_class_install_property (gobject_class
, PROP_TARGET_VALUE
,
223 g_param_spec_double ("value", "Value", "Value",
227 g_object_class_install_property (gobject_class
, PROP_TARGET_TOGGLE
,
228 g_param_spec_boolean ("toggle", "Toggle", "Toggle",
234 binding_target_init (BindingTarget
*self
)
239 celsius_to_fahrenheit (GBinding
*binding
,
240 const GValue
*from_value
,
242 gpointer user_data G_GNUC_UNUSED
)
244 gdouble celsius
, fahrenheit
;
246 g_assert (G_VALUE_HOLDS (from_value
, G_TYPE_DOUBLE
));
247 g_assert (G_VALUE_HOLDS (to_value
, G_TYPE_DOUBLE
));
249 celsius
= g_value_get_double (from_value
);
250 fahrenheit
= (9 * celsius
/ 5) + 32.0;
252 if (g_test_verbose ())
253 g_printerr ("Converting %.2fC to %.2fF\n", celsius
, fahrenheit
);
255 g_value_set_double (to_value
, fahrenheit
);
261 fahrenheit_to_celsius (GBinding
*binding
,
262 const GValue
*from_value
,
264 gpointer user_data G_GNUC_UNUSED
)
266 gdouble celsius
, fahrenheit
;
268 g_assert (G_VALUE_HOLDS (from_value
, G_TYPE_DOUBLE
));
269 g_assert (G_VALUE_HOLDS (to_value
, G_TYPE_DOUBLE
));
271 fahrenheit
= g_value_get_double (from_value
);
272 celsius
= 5 * (fahrenheit
- 32.0) / 9;
274 if (g_test_verbose ())
275 g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit
, celsius
);
277 g_value_set_double (to_value
, celsius
);
283 binding_default (void)
285 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
286 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
289 binding
= g_object_bind_property (source
, "foo",
293 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
294 g_assert ((BindingSource
*) g_binding_get_source (binding
) == source
);
295 g_assert ((BindingTarget
*) g_binding_get_target (binding
) == target
);
296 g_assert_cmpstr (g_binding_get_source_property (binding
), ==, "foo");
297 g_assert_cmpstr (g_binding_get_target_property (binding
), ==, "bar");
298 g_assert_cmpint (g_binding_get_flags (binding
), ==, G_BINDING_DEFAULT
);
300 g_object_set (source
, "foo", 42, NULL
);
301 g_assert_cmpint (source
->foo
, ==, target
->bar
);
303 g_object_set (target
, "bar", 47, NULL
);
304 g_assert_cmpint (source
->foo
, !=, target
->bar
);
306 g_object_unref (binding
);
308 g_object_set (source
, "foo", 0, NULL
);
309 g_assert_cmpint (source
->foo
, !=, target
->bar
);
311 g_object_unref (source
);
312 g_object_unref (target
);
313 g_assert (binding
== NULL
);
317 binding_bidirectional (void)
319 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
320 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
323 binding
= g_object_bind_property (source
, "foo",
325 G_BINDING_BIDIRECTIONAL
);
326 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
328 g_object_set (source
, "foo", 42, NULL
);
329 g_assert_cmpint (source
->foo
, ==, target
->bar
);
331 g_object_set (target
, "bar", 47, NULL
);
332 g_assert_cmpint (source
->foo
, ==, target
->bar
);
334 g_object_unref (binding
);
336 g_object_set (source
, "foo", 0, NULL
);
337 g_assert_cmpint (source
->foo
, !=, target
->bar
);
339 g_object_unref (source
);
340 g_object_unref (target
);
341 g_assert (binding
== NULL
);
345 data_free (gpointer data
)
353 binding_transform_default (void)
355 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
356 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
359 gchar
*src_prop
, *trg_prop
;
362 binding
= g_object_bind_property (source
, "foo",
364 G_BINDING_BIDIRECTIONAL
);
366 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
368 g_object_get (binding
,
370 "source-property", &src_prop
,
372 "target-property", &trg_prop
,
375 g_assert (src
== source
);
376 g_assert (trg
== target
);
377 g_assert_cmpstr (src_prop
, ==, "foo");
378 g_assert_cmpstr (trg_prop
, ==, "value");
379 g_assert_cmpint (flags
, ==, G_BINDING_BIDIRECTIONAL
);
380 g_object_unref (src
);
381 g_object_unref (trg
);
385 g_object_set (source
, "foo", 24, NULL
);
386 g_assert_cmpfloat (target
->value
, ==, 24.0);
388 g_object_set (target
, "value", 69.0, NULL
);
389 g_assert_cmpint (source
->foo
, ==, 69);
391 g_object_unref (target
);
392 g_object_unref (source
);
393 g_assert (binding
== NULL
);
397 binding_transform (void)
399 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
400 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
401 GBinding
*binding G_GNUC_UNUSED
;
402 gboolean unused_data
= FALSE
;
404 binding
= g_object_bind_property_full (source
, "value",
406 G_BINDING_BIDIRECTIONAL
,
407 celsius_to_fahrenheit
,
408 fahrenheit_to_celsius
,
409 &unused_data
, data_free
);
411 g_object_set (source
, "value", 24.0, NULL
);
412 g_assert_cmpfloat (target
->value
, ==, ((9 * 24.0 / 5) + 32.0));
414 g_object_set (target
, "value", 69.0, NULL
);
415 g_assert_cmpfloat (source
->value
, ==, (5 * (69.0 - 32.0) / 9));
417 g_object_unref (source
);
418 g_object_unref (target
);
420 g_assert (unused_data
);
424 binding_transform_closure (void)
426 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
427 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
428 GBinding
*binding G_GNUC_UNUSED
;
429 gboolean unused_data_1
= FALSE
, unused_data_2
= FALSE
;
430 GClosure
*c2f_clos
, *f2c_clos
;
432 c2f_clos
= g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit
), &unused_data_1
, (GClosureNotify
) data_free
);
434 f2c_clos
= g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius
), &unused_data_2
, (GClosureNotify
) data_free
);
436 binding
= g_object_bind_property_with_closures (source
, "value",
438 G_BINDING_BIDIRECTIONAL
,
442 g_object_set (source
, "value", 24.0, NULL
);
443 g_assert_cmpfloat (target
->value
, ==, ((9 * 24.0 / 5) + 32.0));
445 g_object_set (target
, "value", 69.0, NULL
);
446 g_assert_cmpfloat (source
->value
, ==, (5 * (69.0 - 32.0) / 9));
448 g_object_unref (source
);
449 g_object_unref (target
);
451 g_assert (unused_data_1
);
452 g_assert (unused_data_2
);
458 BindingSource
*a
= g_object_new (binding_source_get_type (), NULL
);
459 BindingSource
*b
= g_object_new (binding_source_get_type (), NULL
);
460 BindingSource
*c
= g_object_new (binding_source_get_type (), NULL
);
461 GBinding
*binding_1
, *binding_2
;
463 g_test_bug_base ("http://bugzilla.gnome.org/");
464 g_test_bug ("621782");
467 binding_1
= g_object_bind_property (a
, "foo", b
, "foo", G_BINDING_BIDIRECTIONAL
);
468 g_object_add_weak_pointer (G_OBJECT (binding_1
), (gpointer
*) &binding_1
);
470 binding_2
= g_object_bind_property (b
, "foo", c
, "foo", G_BINDING_BIDIRECTIONAL
);
471 g_object_add_weak_pointer (G_OBJECT (binding_2
), (gpointer
*) &binding_2
);
473 /* verify the chain */
474 g_object_set (a
, "foo", 42, NULL
);
475 g_assert_cmpint (a
->foo
, ==, b
->foo
);
476 g_assert_cmpint (b
->foo
, ==, c
->foo
);
478 /* unbind A -> B and B -> C */
479 g_object_unref (binding_1
);
480 g_assert (binding_1
== NULL
);
481 g_object_unref (binding_2
);
482 g_assert (binding_2
== NULL
);
484 /* bind A -> C directly */
485 binding_2
= g_object_bind_property (a
, "foo", c
, "foo", G_BINDING_BIDIRECTIONAL
);
487 /* verify the chain is broken */
488 g_object_set (a
, "foo", 47, NULL
);
489 g_assert_cmpint (a
->foo
, !=, b
->foo
);
490 g_assert_cmpint (a
->foo
, ==, c
->foo
);
498 binding_sync_create (void)
500 BindingSource
*source
= g_object_new (binding_source_get_type (),
503 BindingTarget
*target
= g_object_new (binding_target_get_type (),
508 binding
= g_object_bind_property (source
, "foo",
510 G_BINDING_DEFAULT
| G_BINDING_SYNC_CREATE
);
512 g_assert_cmpint (source
->foo
, ==, 42);
513 g_assert_cmpint (target
->bar
, ==, 42);
515 g_object_set (source
, "foo", 47, NULL
);
516 g_assert_cmpint (source
->foo
, ==, target
->bar
);
518 g_object_unref (binding
);
520 g_object_set (target
, "bar", 49, NULL
);
522 binding
= g_object_bind_property (source
, "foo",
524 G_BINDING_BIDIRECTIONAL
| G_BINDING_SYNC_CREATE
);
525 g_assert_cmpint (source
->foo
, ==, 47);
526 g_assert_cmpint (target
->bar
, ==, 47);
528 g_object_unref (source
);
529 g_object_unref (target
);
533 binding_invert_boolean (void)
535 BindingSource
*source
= g_object_new (binding_source_get_type (),
538 BindingTarget
*target
= g_object_new (binding_target_get_type (),
543 binding
= g_object_bind_property (source
, "toggle",
545 G_BINDING_BIDIRECTIONAL
| G_BINDING_INVERT_BOOLEAN
);
547 g_assert (source
->toggle
);
548 g_assert (!target
->toggle
);
550 g_object_set (source
, "toggle", FALSE
, NULL
);
551 g_assert (!source
->toggle
);
552 g_assert (target
->toggle
);
554 g_object_set (target
, "toggle", FALSE
, NULL
);
555 g_assert (source
->toggle
);
556 g_assert (!target
->toggle
);
558 g_object_unref (binding
);
559 g_object_unref (source
);
560 g_object_unref (target
);
564 binding_same_object (void)
566 BindingSource
*source
= g_object_new (binding_source_get_type (),
572 binding
= g_object_bind_property (source
, "foo",
574 G_BINDING_BIDIRECTIONAL
);
575 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
577 g_object_set (source
, "foo", 10, NULL
);
578 g_assert_cmpint (source
->foo
, ==, 10);
579 g_assert_cmpint (source
->bar
, ==, 10);
580 g_object_set (source
, "bar", 30, NULL
);
581 g_assert_cmpint (source
->foo
, ==, 30);
582 g_assert_cmpint (source
->bar
, ==, 30);
584 g_object_unref (source
);
585 g_assert (binding
== NULL
);
589 binding_unbind (void)
591 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
592 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
595 binding
= g_object_bind_property (source
, "foo",
598 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
600 g_object_set (source
, "foo", 42, NULL
);
601 g_assert_cmpint (source
->foo
, ==, target
->bar
);
603 g_object_set (target
, "bar", 47, NULL
);
604 g_assert_cmpint (source
->foo
, !=, target
->bar
);
606 g_binding_unbind (binding
);
607 g_assert (binding
== NULL
);
609 g_object_set (source
, "foo", 0, NULL
);
610 g_assert_cmpint (source
->foo
, !=, target
->bar
);
612 g_object_unref (source
);
613 g_object_unref (target
);
616 /* g_binding_unbind() has a special case for this */
617 source
= g_object_new (binding_source_get_type (), NULL
);
618 binding
= g_object_bind_property (source
, "foo",
621 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
623 g_binding_unbind (binding
);
624 g_assert (binding
== NULL
);
626 g_object_unref (source
);
629 /* When source or target die, so does the binding if there is no other ref */
631 binding_unbind_weak (void)
634 BindingSource
*source
;
635 BindingTarget
*target
;
637 /* first source, then target */
638 source
= g_object_new (binding_source_get_type (), NULL
);
639 target
= g_object_new (binding_target_get_type (), NULL
);
640 binding
= g_object_bind_property (source
, "foo",
643 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
644 g_assert_nonnull (binding
);
645 g_object_unref (source
);
646 g_assert_null (binding
);
647 g_object_unref (target
);
648 g_assert_null (binding
);
650 /* first target, then source */
651 source
= g_object_new (binding_source_get_type (), NULL
);
652 target
= g_object_new (binding_target_get_type (), NULL
);
653 binding
= g_object_bind_property (source
, "foo",
656 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
657 g_assert_nonnull (binding
);
658 g_object_unref (target
);
659 g_assert_null (binding
);
660 g_object_unref (source
);
661 g_assert_null (binding
);
663 /* target and source are the same */
664 source
= g_object_new (binding_source_get_type (), NULL
);
665 binding
= g_object_bind_property (source
, "foo",
668 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
669 g_assert_nonnull (binding
);
670 g_object_unref (source
);
671 g_assert_null (binding
);
674 /* Test that every call to unbind() after the first is a noop */
676 binding_unbind_multiple (void)
678 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
679 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
685 binding
= g_object_bind_property (source
, "foo",
688 g_object_ref (binding
);
689 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
690 g_assert_nonnull (binding
);
692 /* this shouldn't crash */
693 for (i
= 0; i
< 50; i
++)
695 g_binding_unbind (binding
);
696 g_assert_nonnull (binding
);
699 g_object_unref (binding
);
700 g_assert_null (binding
);
702 g_object_unref (source
);
703 g_object_unref (target
);
709 BindingSource
*source
= g_object_new (binding_source_get_type (), NULL
);
710 BindingTarget
*target
= g_object_new (binding_target_get_type (), NULL
);
713 /* double -> boolean is not supported */
714 binding
= g_object_bind_property (source
, "value",
717 g_object_add_weak_pointer (G_OBJECT (binding
), (gpointer
*) &binding
);
719 g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING
,
720 "*Unable to convert*double*boolean*");
721 g_object_set (source
, "value", 1.0, NULL
);
722 g_test_assert_expected_messages ();
724 g_object_unref (source
);
725 g_object_unref (target
);
726 g_assert (binding
== NULL
);
730 main (int argc
, char *argv
[])
732 g_test_init (&argc
, &argv
, NULL
);
734 g_test_bug_base ("https://gitlab.gnome.org/GNOME/glib/issues/");
736 g_test_add_func ("/binding/default", binding_default
);
737 g_test_add_func ("/binding/bidirectional", binding_bidirectional
);
738 g_test_add_func ("/binding/transform", binding_transform
);
739 g_test_add_func ("/binding/transform-default", binding_transform_default
);
740 g_test_add_func ("/binding/transform-closure", binding_transform_closure
);
741 g_test_add_func ("/binding/chain", binding_chain
);
742 g_test_add_func ("/binding/sync-create", binding_sync_create
);
743 g_test_add_func ("/binding/invert-boolean", binding_invert_boolean
);
744 g_test_add_func ("/binding/same-object", binding_same_object
);
745 g_test_add_func ("/binding/unbind", binding_unbind
);
746 g_test_add_func ("/binding/unbind-weak", binding_unbind_weak
);
747 g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple
);
748 g_test_add_func ("/binding/fail", binding_fail
);
750 return g_test_run ();