Branch data Line data Source code
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 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General
19 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : *
21 : : * SPDX-License-Identifier: LGPL-2.1-or-later
22 : : */
23 : :
24 : : #include <glib-object.h>
25 : :
26 : : /* Copied from glib */
27 : : typedef struct _BindingSource
28 : : {
29 : : GObject parent_instance;
30 : :
31 : : gint foo;
32 : : gint bar;
33 : : gdouble value;
34 : : gboolean toggle;
35 : : } BindingSource;
36 : :
37 : : typedef struct _BindingSourceClass
38 : : {
39 : : GObjectClass parent_class;
40 : : } BindingSourceClass;
41 : :
42 : : enum
43 : : {
44 : : PROP_SOURCE_FOO = 1,
45 : : PROP_SOURCE_BAR,
46 : : PROP_SOURCE_VALUE,
47 : : PROP_SOURCE_TOGGLE
48 : : };
49 : :
50 : : static GType binding_source_get_type (void);
51 : 13 : G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
52 : :
53 : : static void
54 : 26 : binding_source_set_property (GObject *gobject,
55 : : guint prop_id,
56 : : const GValue *value,
57 : : GParamSpec *pspec)
58 : : {
59 : 26 : BindingSource *source = (BindingSource *) gobject;
60 : :
61 : 26 : switch (prop_id)
62 : : {
63 : 8 : case PROP_SOURCE_FOO:
64 : 8 : source->foo = g_value_get_int (value);
65 : 8 : break;
66 : :
67 : 8 : case PROP_SOURCE_BAR:
68 : 8 : source->bar = g_value_get_int (value);
69 : 8 : break;
70 : :
71 : 10 : case PROP_SOURCE_VALUE:
72 : 10 : source->value = g_value_get_double (value);
73 : 10 : break;
74 : :
75 : 0 : case PROP_SOURCE_TOGGLE:
76 : 0 : source->toggle = g_value_get_boolean (value);
77 : 0 : break;
78 : :
79 : 0 : default:
80 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
81 : : }
82 : 26 : }
83 : :
84 : : static void
85 : 73 : binding_source_get_property (GObject *gobject,
86 : : guint prop_id,
87 : : GValue *value,
88 : : GParamSpec *pspec)
89 : : {
90 : 73 : BindingSource *source = (BindingSource *) gobject;
91 : :
92 : 73 : switch (prop_id)
93 : : {
94 : 30 : case PROP_SOURCE_FOO:
95 : 30 : g_value_set_int (value, source->foo);
96 : 30 : break;
97 : :
98 : 2 : case PROP_SOURCE_BAR:
99 : 2 : g_value_set_int (value, source->bar);
100 : 2 : break;
101 : :
102 : 41 : case PROP_SOURCE_VALUE:
103 : 41 : g_value_set_double (value, source->value);
104 : 41 : break;
105 : :
106 : 0 : case PROP_SOURCE_TOGGLE:
107 : 0 : g_value_set_boolean (value, source->toggle);
108 : 0 : break;
109 : :
110 : 0 : default:
111 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
112 : : }
113 : 73 : }
114 : :
115 : : static void
116 : 1 : binding_source_class_init (BindingSourceClass *klass)
117 : : {
118 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119 : :
120 : 1 : gobject_class->set_property = binding_source_set_property;
121 : 1 : gobject_class->get_property = binding_source_get_property;
122 : :
123 : 1 : g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
124 : : g_param_spec_int ("foo", "Foo", "Foo",
125 : : -1, 100,
126 : : 0,
127 : : G_PARAM_READWRITE));
128 : 1 : g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
129 : : g_param_spec_int ("bar", "Bar", "Bar",
130 : : -1, 100,
131 : : 0,
132 : : G_PARAM_READWRITE));
133 : 1 : g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
134 : : g_param_spec_double ("value", "Value", "Value",
135 : : -100.0, 200.0,
136 : : 0.0,
137 : : G_PARAM_READWRITE));
138 : 1 : g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
139 : : g_param_spec_boolean ("toggle", "Toggle", "Toggle",
140 : : FALSE,
141 : : G_PARAM_READWRITE));
142 : 1 : }
143 : :
144 : : static void
145 : 11 : binding_source_init (BindingSource *self)
146 : : {
147 : 11 : }
148 : :
149 : : typedef struct _BindingTarget
150 : : {
151 : : GObject parent_instance;
152 : :
153 : : gint bar;
154 : : gdouble value;
155 : : gboolean toggle;
156 : : } BindingTarget;
157 : :
158 : : typedef struct _BindingTargetClass
159 : : {
160 : : GObjectClass parent_class;
161 : : } BindingTargetClass;
162 : :
163 : : enum
164 : : {
165 : : PROP_TARGET_BAR = 1,
166 : : PROP_TARGET_VALUE,
167 : : PROP_TARGET_TOGGLE
168 : : };
169 : :
170 : : static GType binding_target_get_type (void);
171 : 19 : G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
172 : :
173 : : static void
174 : 74 : binding_target_set_property (GObject *gobject,
175 : : guint prop_id,
176 : : const GValue *value,
177 : : GParamSpec *pspec)
178 : : {
179 : 74 : BindingTarget *target = (BindingTarget *) gobject;
180 : :
181 : 74 : switch (prop_id)
182 : : {
183 : 27 : case PROP_TARGET_BAR:
184 : 27 : target->bar = g_value_get_int (value);
185 : 27 : break;
186 : :
187 : 47 : case PROP_TARGET_VALUE:
188 : 47 : target->value = g_value_get_double (value);
189 : 47 : break;
190 : :
191 : 0 : case PROP_TARGET_TOGGLE:
192 : 0 : target->toggle = g_value_get_boolean (value);
193 : 0 : break;
194 : :
195 : 0 : default:
196 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
197 : : }
198 : 74 : }
199 : :
200 : : static void
201 : 4 : binding_target_get_property (GObject *gobject,
202 : : guint prop_id,
203 : : GValue *value,
204 : : GParamSpec *pspec)
205 : : {
206 : 4 : BindingTarget *target = (BindingTarget *) gobject;
207 : :
208 : 4 : switch (prop_id)
209 : : {
210 : 0 : case PROP_TARGET_BAR:
211 : 0 : g_value_set_int (value, target->bar);
212 : 0 : break;
213 : :
214 : 4 : case PROP_TARGET_VALUE:
215 : 4 : g_value_set_double (value, target->value);
216 : 4 : break;
217 : :
218 : 0 : case PROP_TARGET_TOGGLE:
219 : 0 : g_value_set_boolean (value, target->toggle);
220 : 0 : break;
221 : :
222 : 0 : default:
223 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
224 : : }
225 : 4 : }
226 : :
227 : : static void
228 : 1 : binding_target_class_init (BindingTargetClass *klass)
229 : : {
230 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
231 : :
232 : 1 : gobject_class->set_property = binding_target_set_property;
233 : 1 : gobject_class->get_property = binding_target_get_property;
234 : :
235 : 1 : g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
236 : : g_param_spec_int ("bar", "Bar", "Bar",
237 : : -1, 100,
238 : : 0,
239 : : G_PARAM_READWRITE));
240 : 1 : g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
241 : : g_param_spec_double ("value", "Value", "Value",
242 : : -100.0, 200.0,
243 : : 0.0,
244 : : G_PARAM_READWRITE));
245 : 1 : g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
246 : : g_param_spec_boolean ("toggle", "Toggle", "Toggle",
247 : : FALSE,
248 : : G_PARAM_READWRITE));
249 : 1 : }
250 : :
251 : : static void
252 : 17 : binding_target_init (BindingTarget *self)
253 : : {
254 : 17 : }
255 : :
256 : : static gboolean
257 : 4 : celsius_to_fahrenheit (GBinding *binding,
258 : : const GValue *from_value,
259 : : GValue *to_value,
260 : : gpointer user_data G_GNUC_UNUSED)
261 : : {
262 : : gdouble celsius, fahrenheit;
263 : :
264 : 4 : g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
265 : 4 : g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
266 : :
267 : 4 : celsius = g_value_get_double (from_value);
268 : 4 : fahrenheit = (9 * celsius / 5) + 32.0;
269 : :
270 : 4 : if (g_test_verbose ())
271 : 0 : g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
272 : :
273 : 4 : g_value_set_double (to_value, fahrenheit);
274 : :
275 : 4 : return TRUE;
276 : : }
277 : :
278 : : static gboolean
279 : 2 : fahrenheit_to_celsius (GBinding *binding,
280 : : const GValue *from_value,
281 : : GValue *to_value,
282 : : gpointer user_data G_GNUC_UNUSED)
283 : : {
284 : : gdouble celsius, fahrenheit;
285 : :
286 : 2 : g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
287 : 2 : g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
288 : :
289 : 2 : fahrenheit = g_value_get_double (from_value);
290 : 2 : celsius = 5 * (fahrenheit - 32.0) / 9;
291 : :
292 : 2 : if (g_test_verbose ())
293 : 0 : g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
294 : :
295 : 2 : g_value_set_double (to_value, celsius);
296 : :
297 : 2 : return TRUE;
298 : : }
299 : :
300 : : static void
301 : 1 : test_binding_group_invalid (void)
302 : : {
303 : 1 : GBindingGroup *group = g_binding_group_new ();
304 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
305 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
306 : :
307 : : /* Invalid Target Property */
308 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
309 : : "*find_property*target_property*!=*NULL*");
310 : 1 : g_binding_group_bind (group, "value",
311 : : target, "does-not-exist",
312 : : G_BINDING_DEFAULT);
313 : 1 : g_test_assert_expected_messages ();
314 : :
315 : 1 : g_binding_group_set_source (group, NULL);
316 : :
317 : : /* Invalid Source Property */
318 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
319 : : "*find_property*source_property*!=*NULL*");
320 : 1 : g_binding_group_set_source (group, source);
321 : 1 : g_binding_group_bind (group, "does-not-exist",
322 : : target, "value",
323 : : G_BINDING_DEFAULT);
324 : 1 : g_test_assert_expected_messages ();
325 : :
326 : 1 : g_binding_group_set_source (group, NULL);
327 : :
328 : : /* Invalid Source */
329 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
330 : : "*find_property*->source_property*!=*NULL*");
331 : 1 : g_binding_group_bind (group, "does-not-exist",
332 : : target, "value",
333 : : G_BINDING_DEFAULT);
334 : 1 : g_binding_group_set_source (group, source);
335 : 1 : g_test_assert_expected_messages ();
336 : :
337 : 1 : g_object_unref (target);
338 : 1 : g_object_unref (source);
339 : 1 : g_object_unref (group);
340 : 1 : }
341 : :
342 : : static void
343 : 1 : test_binding_group_default (void)
344 : : {
345 : : gsize i, j;
346 : 1 : GBindingGroup *group = g_binding_group_new ();
347 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
348 : : BindingTarget *targets[5];
349 : : BindingSource *readback;
350 : :
351 : 6 : for (i = 0; i < G_N_ELEMENTS (targets); ++i)
352 : : {
353 : 5 : targets[i] = g_object_new (binding_target_get_type (), NULL);
354 : 5 : g_binding_group_bind (group, "foo",
355 : 5 : targets[i], "bar",
356 : : G_BINDING_DEFAULT);
357 : : }
358 : :
359 : 1 : g_assert_null (g_binding_group_dup_source (group));
360 : 1 : g_binding_group_set_source (group, source);
361 : 1 : readback = g_binding_group_dup_source (group);
362 : 1 : g_assert_true (readback == source);
363 : 1 : g_object_unref (readback);
364 : :
365 : 3 : for (i = 0; i < 2; ++i)
366 : : {
367 : 2 : g_object_set (source, "foo", 42, NULL);
368 : 12 : for (j = 0; j < G_N_ELEMENTS (targets); ++j)
369 : 10 : g_assert_cmpint (source->foo, ==, targets[j]->bar);
370 : :
371 : 2 : g_object_set (targets[0], "bar", 47, NULL);
372 : 2 : g_assert_cmpint (source->foo, !=, targets[0]->bar);
373 : :
374 : : /* Check that we transition the source correctly */
375 : 2 : g_binding_group_set_source (group, NULL);
376 : 2 : g_assert_null (g_binding_group_dup_source (group));
377 : 2 : g_binding_group_set_source (group, source);
378 : 2 : readback = g_binding_group_dup_source (group);
379 : 2 : g_assert_true (readback == source);
380 : 2 : g_object_unref (readback);
381 : : }
382 : :
383 : 1 : g_object_unref (group);
384 : :
385 : 1 : g_object_set (source, "foo", 0, NULL);
386 : 6 : for (i = 0; i < G_N_ELEMENTS (targets); ++i)
387 : 5 : g_assert_cmpint (source->foo, !=, targets[i]->bar);
388 : :
389 : 1 : g_object_unref (source);
390 : 6 : for (i = 0; i < G_N_ELEMENTS (targets); ++i)
391 : 5 : g_object_unref (targets[i]);
392 : 1 : }
393 : :
394 : : static void
395 : 1 : test_binding_group_bidirectional (void)
396 : : {
397 : : gsize i, j;
398 : 1 : GBindingGroup *group = g_binding_group_new ();
399 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
400 : : BindingTarget *targets[5];
401 : : BindingSource *readback;
402 : :
403 : 6 : for (i = 0; i < G_N_ELEMENTS (targets); ++i)
404 : : {
405 : 5 : targets[i] = g_object_new (binding_target_get_type (), NULL);
406 : 5 : g_binding_group_bind (group, "value",
407 : 5 : targets[i], "value",
408 : : G_BINDING_BIDIRECTIONAL);
409 : : }
410 : :
411 : 1 : g_assert_null (g_binding_group_dup_source (group));
412 : 1 : g_binding_group_set_source (group, source);
413 : 1 : readback = g_binding_group_dup_source (group);
414 : 1 : g_assert_true (readback == source);
415 : 1 : g_object_unref (readback);
416 : :
417 : 3 : for (i = 0; i < 2; ++i)
418 : : {
419 : 2 : g_object_set (source, "value", 42.0, NULL);
420 : 12 : for (j = 0; j < G_N_ELEMENTS (targets); ++j)
421 : 10 : g_assert_cmpfloat (source->value, ==, targets[j]->value);
422 : :
423 : 2 : g_object_set (targets[0], "value", 47.0, NULL);
424 : 2 : g_assert_cmpfloat (source->value, ==, targets[0]->value);
425 : :
426 : : /* Check that we transition the source correctly */
427 : 2 : g_binding_group_set_source (group, NULL);
428 : 2 : g_assert_null (g_binding_group_dup_source (group));
429 : 2 : g_binding_group_set_source (group, source);
430 : 2 : readback = g_binding_group_dup_source (group);
431 : 2 : g_assert_true (readback == source);
432 : 2 : g_object_unref (readback);
433 : : }
434 : :
435 : 1 : g_object_unref (group);
436 : :
437 : 1 : g_object_set (targets[0], "value", 0.0, NULL);
438 : 1 : g_assert_cmpfloat (source->value, !=, targets[0]->value);
439 : :
440 : 1 : g_object_unref (source);
441 : 6 : for (i = 0; i < G_N_ELEMENTS (targets); ++i)
442 : 5 : g_object_unref (targets[i]);
443 : 1 : }
444 : :
445 : : static void
446 : 3 : transform_destroy_notify (gpointer data)
447 : : {
448 : 3 : gboolean *transform_destroy_called = data;
449 : :
450 : 3 : *transform_destroy_called = TRUE;
451 : 3 : }
452 : :
453 : : static void
454 : 1 : test_binding_group_transform (void)
455 : : {
456 : 1 : gboolean transform_destroy_called = FALSE;
457 : 1 : GBindingGroup *group = g_binding_group_new ();
458 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
459 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
460 : :
461 : 1 : g_binding_group_set_source (group, source);
462 : 1 : g_binding_group_bind_full (group, "value",
463 : : target, "value",
464 : : G_BINDING_BIDIRECTIONAL,
465 : : celsius_to_fahrenheit,
466 : : fahrenheit_to_celsius,
467 : : &transform_destroy_called,
468 : : transform_destroy_notify);
469 : :
470 : 1 : g_object_set (source, "value", 24.0, NULL);
471 : 1 : g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
472 : :
473 : 1 : g_object_set (target, "value", 69.0, NULL);
474 : 1 : g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
475 : :
476 : : /* The GDestroyNotify should only be called when the
477 : : * set is freed, not when the various GBindings are freed
478 : : */
479 : 1 : g_binding_group_set_source (group, NULL);
480 : 1 : g_assert_false (transform_destroy_called);
481 : :
482 : 1 : g_object_unref (group);
483 : 1 : g_assert_true (transform_destroy_called);
484 : :
485 : 1 : g_object_unref (source);
486 : 1 : g_object_unref (target);
487 : 1 : }
488 : :
489 : : static void
490 : 1 : test_binding_group_transform_closures (void)
491 : : {
492 : 1 : gboolean transform_destroy_called_1 = FALSE;
493 : 1 : gboolean transform_destroy_called_2 = FALSE;
494 : 1 : GBindingGroup *group = g_binding_group_new ();
495 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
496 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
497 : : GClosure *c2f_closure, *f2c_closure;
498 : :
499 : 1 : c2f_closure = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit),
500 : : &transform_destroy_called_1,
501 : : (GClosureNotify) transform_destroy_notify);
502 : 1 : f2c_closure = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius),
503 : : &transform_destroy_called_2,
504 : : (GClosureNotify) transform_destroy_notify);
505 : :
506 : 1 : g_binding_group_set_source (group, source);
507 : 1 : g_binding_group_bind_with_closures (group, "value",
508 : : target, "value",
509 : : G_BINDING_BIDIRECTIONAL,
510 : : c2f_closure,
511 : : f2c_closure);
512 : :
513 : 1 : g_object_set (source, "value", 24.0, NULL);
514 : 1 : g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
515 : :
516 : 1 : g_object_set (target, "value", 69.0, NULL);
517 : 1 : g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
518 : :
519 : : /* The GClsoureNotify should only be called when the
520 : : * set is freed, not when the various GBindings are freed
521 : : */
522 : 1 : g_binding_group_set_source (group, NULL);
523 : 1 : g_assert_false (transform_destroy_called_1);
524 : 1 : g_assert_false (transform_destroy_called_2);
525 : :
526 : 1 : g_object_unref (group);
527 : 1 : g_assert_true (transform_destroy_called_1);
528 : 1 : g_assert_true (transform_destroy_called_2);
529 : :
530 : 1 : g_object_unref (source);
531 : 1 : g_object_unref (target);
532 : 1 : }
533 : :
534 : : static void
535 : 1 : test_binding_group_same_object (void)
536 : : {
537 : : gsize i;
538 : 1 : GBindingGroup *group = g_binding_group_new ();
539 : 1 : BindingSource *source = g_object_new (binding_source_get_type (),
540 : : "foo", 100,
541 : : "bar", 50,
542 : : NULL);
543 : :
544 : 1 : g_binding_group_set_source (group, source);
545 : 1 : g_binding_group_bind (group, "foo",
546 : : source, "bar",
547 : : G_BINDING_BIDIRECTIONAL);
548 : :
549 : 3 : for (i = 0; i < 2; ++i)
550 : : {
551 : 2 : g_object_set (source, "foo", 10, NULL);
552 : 2 : g_assert_cmpint (source->foo, ==, 10);
553 : 2 : g_assert_cmpint (source->bar, ==, 10);
554 : :
555 : 2 : g_object_set (source, "bar", 30, NULL);
556 : 2 : g_assert_cmpint (source->foo, ==, 30);
557 : 2 : g_assert_cmpint (source->bar, ==, 30);
558 : :
559 : : /* Check that it is possible both when initially
560 : : * adding the binding and when changing the source
561 : : */
562 : 2 : g_binding_group_set_source (group, NULL);
563 : 2 : g_binding_group_set_source (group, source);
564 : : }
565 : :
566 : 1 : g_object_unref (source);
567 : 1 : g_object_unref (group);
568 : 1 : }
569 : :
570 : : static void
571 : 1 : test_binding_group_weak_ref_source (void)
572 : : {
573 : 1 : GBindingGroup *group = g_binding_group_new ();
574 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
575 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
576 : : BindingSource *readback;
577 : :
578 : 1 : g_binding_group_set_source (group, source);
579 : 1 : g_binding_group_bind (group, "value",
580 : : target, "value",
581 : : G_BINDING_BIDIRECTIONAL);
582 : :
583 : 1 : g_object_add_weak_pointer (G_OBJECT (source), (gpointer)&source);
584 : 1 : readback = g_binding_group_dup_source (group);
585 : 1 : g_assert_true (readback == source);
586 : 1 : g_object_unref (readback);
587 : 1 : g_object_unref (source);
588 : 1 : g_assert_null (source);
589 : 1 : g_assert_null (g_binding_group_dup_source (group));
590 : :
591 : : /* Hopefully this would explode if the binding was still alive */
592 : 1 : g_object_set (target, "value", 0.0, NULL);
593 : :
594 : 1 : g_object_unref (target);
595 : 1 : g_object_unref (group);
596 : 1 : }
597 : :
598 : : static void
599 : 1 : test_binding_group_weak_ref_target (void)
600 : : {
601 : 1 : GBindingGroup *group = g_binding_group_new ();
602 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
603 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
604 : :
605 : 1 : g_binding_group_set_source (group, source);
606 : 1 : g_binding_group_bind (group, "value",
607 : : target, "value",
608 : : G_BINDING_BIDIRECTIONAL);
609 : :
610 : 1 : g_object_set (source, "value", 47.0, NULL);
611 : 1 : g_assert_cmpfloat (target->value, ==, 47.0);
612 : :
613 : 1 : g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
614 : 1 : g_object_unref (target);
615 : 1 : g_assert_null (target);
616 : :
617 : : /* Hopefully this would explode if the binding was still alive */
618 : 1 : g_object_set (source, "value", 0.0, NULL);
619 : :
620 : 1 : g_object_unref (source);
621 : 1 : g_object_unref (group);
622 : 1 : }
623 : :
624 : : static void
625 : 1 : test_binding_group_properties (void)
626 : : {
627 : 1 : GBindingGroup *group = g_binding_group_new ();
628 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
629 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
630 : : BindingSource *other;
631 : :
632 : 1 : g_binding_group_set_source (group, source);
633 : 1 : g_binding_group_bind (group, "value",
634 : : target, "value",
635 : : G_BINDING_BIDIRECTIONAL);
636 : :
637 : 1 : g_object_get (group, "source", &other, NULL);
638 : 1 : g_assert_true (other == source);
639 : 1 : g_object_unref (other);
640 : :
641 : 1 : g_object_set (group, "source", NULL, NULL);
642 : 1 : g_object_get (group, "source", &other, NULL);
643 : 1 : g_assert_null (other);
644 : :
645 : 1 : g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
646 : 1 : g_object_unref (target);
647 : 1 : g_assert_null (target);
648 : :
649 : 1 : g_object_unref (source);
650 : 1 : g_object_unref (group);
651 : 1 : }
652 : :
653 : : static void
654 : 1 : test_binding_group_weak_notify_no_bindings (void)
655 : : {
656 : 1 : GBindingGroup *group = g_binding_group_new ();
657 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
658 : :
659 : 1 : g_binding_group_set_source (group, source);
660 : 1 : g_assert_finalize_object (source);
661 : 1 : g_assert_finalize_object (group);
662 : 1 : }
663 : :
664 : : static void
665 : 1 : test_binding_group_empty_closures (void)
666 : : {
667 : 1 : GBindingGroup *group = g_binding_group_new ();
668 : 1 : BindingSource *source = g_object_new (binding_source_get_type (), NULL);
669 : 1 : BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
670 : :
671 : 1 : g_binding_group_bind_full (group, "value", target, "value", 0,
672 : : NULL, NULL, NULL, NULL);
673 : :
674 : 1 : g_assert_finalize_object (group);
675 : 1 : g_assert_finalize_object (target);
676 : 1 : g_assert_finalize_object (source);
677 : 1 : }
678 : :
679 : : gint
680 : 1 : main (gint argc,
681 : : gchar *argv[])
682 : : {
683 : 1 : g_test_init (&argc, &argv, NULL);
684 : 1 : g_test_add_func ("/GObject/BindingGroup/invalid", test_binding_group_invalid);
685 : 1 : g_test_add_func ("/GObject/BindingGroup/default", test_binding_group_default);
686 : 1 : g_test_add_func ("/GObject/BindingGroup/bidirectional", test_binding_group_bidirectional);
687 : 1 : g_test_add_func ("/GObject/BindingGroup/transform", test_binding_group_transform);
688 : 1 : g_test_add_func ("/GObject/BindingGroup/transform-closures", test_binding_group_transform_closures);
689 : 1 : g_test_add_func ("/GObject/BindingGroup/same-object", test_binding_group_same_object);
690 : 1 : g_test_add_func ("/GObject/BindingGroup/weak-ref-source", test_binding_group_weak_ref_source);
691 : 1 : g_test_add_func ("/GObject/BindingGroup/weak-ref-target", test_binding_group_weak_ref_target);
692 : 1 : g_test_add_func ("/GObject/BindingGroup/properties", test_binding_group_properties);
693 : 1 : g_test_add_func ("/GObject/BindingGroup/weak-notify-no-bindings", test_binding_group_weak_notify_no_bindings);
694 : 1 : g_test_add_func ("/GObject/BindingGroup/empty-closures", test_binding_group_empty_closures);
695 : 1 : return g_test_run ();
696 : : }
|