binding: Add SYNC_CREATE to the flags
[platform/upstream/glib.git] / gobject / tests / binding.c
1 #include <stdlib.h>
2 #include <gstdio.h>
3 #include <glib-object.h>
4
5 typedef struct _BindingSource
6 {
7   GObject parent_instance;
8
9   gint foo;
10   gdouble value;
11 } BindingSource;
12
13 typedef struct _BindingSourceClass
14 {
15   GObjectClass parent_class;
16 } BindingSourceClass;
17
18 enum
19 {
20   PROP_SOURCE_0,
21
22   PROP_SOURCE_FOO,
23
24   PROP_SOURCE_VALUE
25 };
26
27 G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
28
29 static void
30 binding_source_set_property (GObject      *gobject,
31                              guint         prop_id,
32                              const GValue *value,
33                              GParamSpec   *pspec)
34 {
35   BindingSource *source = (BindingSource *) gobject;
36
37   switch (prop_id)
38     {
39     case PROP_SOURCE_FOO:
40       source->foo = g_value_get_int (value);
41       break;
42
43     case PROP_SOURCE_VALUE:
44       source->value = g_value_get_double (value);
45       break;
46
47     default:
48       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
49     }
50 }
51
52 static void
53 binding_source_get_property (GObject    *gobject,
54                              guint       prop_id,
55                              GValue     *value,
56                              GParamSpec *pspec)
57 {
58   BindingSource *source = (BindingSource *) gobject;
59
60   switch (prop_id)
61     {
62     case PROP_SOURCE_FOO:
63       g_value_set_int (value, source->foo);
64       break;
65
66     case PROP_SOURCE_VALUE:
67       g_value_set_double (value, source->value);
68       break;
69
70     default:
71       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
72     }
73 }
74
75 static void
76 binding_source_class_init (BindingSourceClass *klass)
77 {
78   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
79
80   gobject_class->set_property = binding_source_set_property;
81   gobject_class->get_property = binding_source_get_property;
82
83   g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
84                                    g_param_spec_int ("foo", "Foo", "Foo",
85                                                      -1, 100,
86                                                      0,
87                                                      G_PARAM_READWRITE));
88   g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
89                                    g_param_spec_double ("value", "Value", "Value",
90                                                         -100.0, 200.0,
91                                                         0.0,
92                                                         G_PARAM_READWRITE));
93 }
94
95 static void
96 binding_source_init (BindingSource *self)
97 {
98 }
99
100 typedef struct _BindingTarget
101 {
102   GObject parent_instance;
103
104   gint bar;
105   gdouble value;
106 } BindingTarget;
107
108 typedef struct _BindingTargetClass
109 {
110   GObjectClass parent_class;
111 } BindingTargetClass;
112
113 enum
114 {
115   PROP_TARGET_0,
116
117   PROP_TARGET_BAR,
118
119   PROP_TARGET_VALUE
120 };
121
122 G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
123
124 static void
125 binding_target_set_property (GObject      *gobject,
126                              guint         prop_id,
127                              const GValue *value,
128                              GParamSpec   *pspec)
129 {
130   BindingTarget *target = (BindingTarget *) gobject;
131
132   switch (prop_id)
133     {
134     case PROP_TARGET_BAR:
135       target->bar = g_value_get_int (value);
136       break;
137
138     case PROP_TARGET_VALUE:
139       target->value = g_value_get_double (value);
140       break;
141
142     default:
143       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
144     }
145 }
146
147 static void
148 binding_target_get_property (GObject    *gobject,
149                              guint       prop_id,
150                              GValue     *value,
151                              GParamSpec *pspec)
152 {
153   BindingTarget *target = (BindingTarget *) gobject;
154
155   switch (prop_id)
156     {
157     case PROP_TARGET_BAR:
158       g_value_set_int (value, target->bar);
159       break;
160
161     case PROP_TARGET_VALUE:
162       g_value_set_double (value, target->value);
163       break;
164
165     default:
166       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
167     }
168 }
169
170 static void
171 binding_target_class_init (BindingTargetClass *klass)
172 {
173   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
174
175   gobject_class->set_property = binding_target_set_property;
176   gobject_class->get_property = binding_target_get_property;
177
178   g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
179                                    g_param_spec_int ("bar", "Bar", "Bar",
180                                                      -1, 100,
181                                                      0,
182                                                      G_PARAM_READWRITE));
183   g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
184                                    g_param_spec_double ("value", "Value", "Value",
185                                                         -100.0, 200.0,
186                                                         0.0,
187                                                         G_PARAM_READWRITE));
188 }
189
190 static void
191 binding_target_init (BindingTarget *self)
192 {
193 }
194
195 static gboolean
196 celsius_to_fahrenheit (GBinding     *binding,
197                        const GValue *source_value,
198                        GValue       *target_value,
199                        gpointer      user_data G_GNUC_UNUSED)
200 {
201   gdouble celsius, fahrenheit;
202
203   g_assert (G_VALUE_HOLDS (source_value, G_TYPE_DOUBLE));
204   g_assert (G_VALUE_HOLDS (target_value, G_TYPE_DOUBLE));
205
206   celsius = g_value_get_double (source_value);
207   fahrenheit = (9 * celsius / 5) + 32.0;
208
209   if (g_test_verbose ())
210     g_print ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
211
212   g_value_set_double (target_value, fahrenheit);
213
214   return TRUE;
215 }
216
217 static gboolean
218 fahrenheit_to_celsius (GBinding     *binding,
219                        const GValue *source_value,
220                        GValue       *target_value,
221                        gpointer      user_data G_GNUC_UNUSED)
222 {
223   gdouble celsius, fahrenheit;
224
225   g_assert (G_VALUE_HOLDS (source_value, G_TYPE_DOUBLE));
226   g_assert (G_VALUE_HOLDS (target_value, G_TYPE_DOUBLE));
227
228   fahrenheit = g_value_get_double (source_value);
229   celsius = 5 * (fahrenheit - 32.0) / 9;
230
231   if (g_test_verbose ())
232     g_print ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
233
234   g_value_set_double (target_value, celsius);
235
236   return TRUE;
237 }
238
239 static void
240 binding_default (void)
241 {
242   BindingSource *source = g_object_new (binding_source_get_type (), NULL);
243   BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
244   GBinding *binding;
245
246   binding = g_object_bind_property (source, "foo",
247                                     target, "bar",
248                                     G_BINDING_DEFAULT);
249
250   g_object_set (source, "foo", 42, NULL);
251   g_assert_cmpint (source->foo, ==, target->bar);
252
253   g_object_set (target, "bar", 47, NULL);
254   g_assert_cmpint (source->foo, !=, target->bar);
255
256   g_object_unref (binding);
257
258   g_object_set (source, "foo", 0, NULL);
259   g_assert_cmpint (source->foo, !=, target->bar);
260
261   g_object_unref (source);
262   g_object_unref (target);
263 }
264
265 static void
266 binding_bidirectional (void)
267 {
268   BindingSource *source = g_object_new (binding_source_get_type (), NULL);
269   BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
270   GBinding *binding;
271
272   binding = g_object_bind_property (source, "foo",
273                                     target, "bar",
274                                     G_BINDING_BIDIRECTIONAL);
275
276   g_object_set (source, "foo", 42, NULL);
277   g_assert_cmpint (source->foo, ==, target->bar);
278
279   g_object_set (target, "bar", 47, NULL);
280   g_assert_cmpint (source->foo, ==, target->bar);
281
282   g_object_unref (binding);
283
284   g_object_set (source, "foo", 0, NULL);
285   g_assert_cmpint (source->foo, !=, target->bar);
286
287   g_object_unref (source);
288   g_object_unref (target);
289 }
290
291 static void
292 binding_transform (void)
293 {
294   BindingSource *source = g_object_new (binding_source_get_type (), NULL);
295   BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
296   GBinding *binding;
297
298   binding = g_object_bind_property_full (source, "value",
299                                          target, "value",
300                                          G_BINDING_BIDIRECTIONAL,
301                                          celsius_to_fahrenheit,
302                                          fahrenheit_to_celsius,
303                                          NULL, NULL);
304
305   g_object_set (source, "value", 24.0, NULL);
306   g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
307
308   g_object_set (target, "value", 69.0, NULL);
309   g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
310
311   g_object_unref (source);
312   g_object_unref (target);
313 }
314
315 static void
316 binding_chain (void)
317 {
318   BindingSource *a = g_object_new (binding_source_get_type (), NULL);
319   BindingSource *b = g_object_new (binding_source_get_type (), NULL);
320   BindingSource *c = g_object_new (binding_source_get_type (), NULL);
321   GBinding *binding_1, *binding_2;
322
323   /* A -> B, B -> C */
324   binding_1 = g_object_bind_property (a, "foo", b, "foo", G_BINDING_BIDIRECTIONAL);
325   binding_2 = g_object_bind_property (b, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
326
327   /* verify the chain */
328   g_object_set (a, "foo", 42, NULL);
329   g_assert_cmpint (a->foo, ==, b->foo);
330   g_assert_cmpint (b->foo, ==, c->foo);
331
332   /* unbind A -> B and B -> C */
333   g_object_unref (binding_1);
334   g_object_unref (binding_2);
335
336   /* bind A -> C directly */
337   binding_2 = g_object_bind_property (a, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
338
339   /* verify the chain is broken */
340   g_object_set (a, "foo", 47, NULL);
341   g_assert_cmpint (a->foo, !=, b->foo);
342   g_assert_cmpint (a->foo, ==, c->foo);
343
344   g_object_unref (a);
345   g_object_unref (b);
346   g_object_unref (c);
347 }
348
349 static void
350 binding_sync_create (void)
351 {
352   BindingSource *source = g_object_new (binding_source_get_type (),
353                                         "foo", 42,
354                                         NULL);
355   BindingTarget *target = g_object_new (binding_target_get_type (),
356                                         "bar", 47,
357                                         NULL);
358   GBinding *binding;
359
360   binding = g_object_bind_property (source, "foo",
361                                     target, "bar",
362                                     G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
363
364   g_assert_cmpint (source->foo, ==, 42);
365   g_assert_cmpint (target->bar, ==, 42);
366
367   g_object_set (source, "foo", 47, NULL);
368   g_assert_cmpint (source->foo, ==, target->bar);
369
370   g_object_unref (binding);
371
372   g_object_set (target, "bar", 49, NULL);
373
374   binding = g_object_bind_property (source, "foo",
375                                     target, "bar",
376                                     G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
377   g_assert_cmpint (source->foo, ==, 47);
378   g_assert_cmpint (target->bar, ==, 47);
379
380   g_object_unref (source);
381   g_object_unref (target);
382 }
383
384 int
385 main (int argc, char *argv[])
386 {
387   g_type_init ();
388   g_test_init (&argc, &argv, NULL);
389
390   g_test_add_func ("/binding/default", binding_default);
391   g_test_add_func ("/binding/bidirectional", binding_bidirectional);
392   g_test_add_func ("/binding/transform", binding_transform);
393   g_test_add_func ("/binding/chain", binding_chain);
394   g_test_add_func ("/binding/sync-create", binding_sync_create);
395
396   return g_test_run ();
397 }