nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* gbinding.c: Binding for object properties |
2 | * |
||
3 | * Copyright (C) 2010 Intel Corp. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General |
||
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: Emmanuele Bassi <ebassi@linux.intel.com> |
||
19 | */ |
||
20 | |||
21 | /** |
||
22 | * SECTION:gbinding |
||
23 | * @Title: GBinding |
||
24 | * @Short_Description: Bind two object properties |
||
25 | * |
||
26 | * #GBinding is the representation of a binding between a property on a |
||
27 | * #GObject instance (or source) and another property on another #GObject |
||
28 | * instance (or target). Whenever the source property changes, the same |
||
29 | * value is applied to the target property; for instance, the following |
||
30 | * binding: |
||
31 | * |
||
32 | * |[<!-- language="C" --> |
||
33 | * g_object_bind_property (object1, "property-a", |
||
34 | * object2, "property-b", |
||
35 | * G_BINDING_DEFAULT); |
||
36 | * ]| |
||
37 | * |
||
38 | * will cause the property named "property-b" of @object2 to be updated |
||
39 | * every time g_object_set() or the specific accessor changes the value of |
||
40 | * the property "property-a" of @object1. |
||
41 | * |
||
42 | * It is possible to create a bidirectional binding between two properties |
||
43 | * of two #GObject instances, so that if either property changes, the |
||
44 | * other is updated as well, for instance: |
||
45 | * |
||
46 | * |[<!-- language="C" --> |
||
47 | * g_object_bind_property (object1, "property-a", |
||
48 | * object2, "property-b", |
||
49 | * G_BINDING_BIDIRECTIONAL); |
||
50 | * ]| |
||
51 | * |
||
52 | * will keep the two properties in sync. |
||
53 | * |
||
54 | * It is also possible to set a custom transformation function (in both |
||
55 | * directions, in case of a bidirectional binding) to apply a custom |
||
56 | * transformation from the source value to the target value before |
||
57 | * applying it; for instance, the following binding: |
||
58 | * |
||
59 | * |[<!-- language="C" --> |
||
60 | * g_object_bind_property_full (adjustment1, "value", |
||
61 | * adjustment2, "value", |
||
62 | * G_BINDING_BIDIRECTIONAL, |
||
63 | * celsius_to_fahrenheit, |
||
64 | * fahrenheit_to_celsius, |
||
65 | * NULL, NULL); |
||
66 | * ]| |
||
67 | * |
||
68 | * will keep the "value" property of the two adjustments in sync; the |
||
69 | * @celsius_to_fahrenheit function will be called whenever the "value" |
||
70 | * property of @adjustment1 changes and will transform the current value |
||
71 | * of the property before applying it to the "value" property of @adjustment2. |
||
72 | * |
||
73 | * Vice versa, the @fahrenheit_to_celsius function will be called whenever |
||
74 | * the "value" property of @adjustment2 changes, and will transform the |
||
75 | * current value of the property before applying it to the "value" property |
||
76 | * of @adjustment1. |
||
77 | * |
||
78 | * Note that #GBinding does not resolve cycles by itself; a cycle like |
||
79 | * |
||
80 | * |[ |
||
81 | * object1:propertyA -> object2:propertyB |
||
82 | * object2:propertyB -> object3:propertyC |
||
83 | * object3:propertyC -> object1:propertyA |
||
84 | * ]| |
||
85 | * |
||
86 | * might lead to an infinite loop. The loop, in this particular case, |
||
87 | * can be avoided if the objects emit the #GObject::notify signal only |
||
88 | * if the value has effectively been changed. A binding is implemented |
||
89 | * using the #GObject::notify signal, so it is susceptible to all the |
||
90 | * various ways of blocking a signal emission, like g_signal_stop_emission() |
||
91 | * or g_signal_handler_block(). |
||
92 | * |
||
93 | * A binding will be severed, and the resources it allocates freed, whenever |
||
94 | * either one of the #GObject instances it refers to are finalized, or when |
||
95 | * the #GBinding instance loses its last reference. |
||
96 | * |
||
97 | * Bindings for languages with garbage collection can use |
||
98 | * g_binding_unbind() to explicitly release a binding between the source |
||
99 | * and target properties, instead of relying on the last reference on the |
||
100 | * binding, source, and target instances to drop. |
||
101 | * |
||
102 | * #GBinding is available since GObject 2.26 |
||
103 | */ |
||
104 | |||
105 | #include "config.h" |
||
106 | |||
107 | #include <string.h> |
||
108 | |||
109 | #include "gbinding.h" |
||
110 | #include "genums.h" |
||
111 | #include "gmarshal.h" |
||
112 | #include "gobject.h" |
||
113 | #include "gsignal.h" |
||
114 | #include "gparamspecs.h" |
||
115 | #include "gvaluetypes.h" |
||
116 | |||
117 | #include "glibintl.h" |
||
118 | |||
119 | |||
120 | GType |
||
121 | g_binding_flags_get_type (void) |
||
122 | { |
||
123 | static volatile gsize g_define_type_id__volatile = 0; |
||
124 | |||
125 | if (g_once_init_enter (&g_define_type_id__volatile)) |
||
126 | { |
||
127 | static const GFlagsValue values[] = { |
||
128 | { G_BINDING_DEFAULT, "G_BINDING_DEFAULT", "default" }, |
||
129 | { G_BINDING_BIDIRECTIONAL, "G_BINDING_BIDIRECTIONAL", "bidirectional" }, |
||
130 | { G_BINDING_SYNC_CREATE, "G_BINDING_SYNC_CREATE", "sync-create" }, |
||
131 | { G_BINDING_INVERT_BOOLEAN, "G_BINDING_INVERT_BOOLEAN", "invert-boolean" }, |
||
132 | { 0, NULL, NULL } |
||
133 | }; |
||
134 | GType g_define_type_id = |
||
135 | g_flags_register_static (g_intern_static_string ("GBindingFlags"), values); |
||
136 | g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); |
||
137 | } |
||
138 | |||
139 | return g_define_type_id__volatile; |
||
140 | } |
||
141 | |||
142 | #define G_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_BINDING, GBindingClass)) |
||
143 | #define G_IS_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_BINDING)) |
||
144 | #define G_BINDING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_BINDING, GBindingClass)) |
||
145 | |||
146 | typedef struct _GBindingClass GBindingClass; |
||
147 | |||
148 | struct _GBinding |
||
149 | { |
||
150 | GObject parent_instance; |
||
151 | |||
152 | /* no reference is held on the objects, to avoid cycles */ |
||
153 | GObject *source; |
||
154 | GObject *target; |
||
155 | |||
156 | /* the property names are interned, so they should not be freed */ |
||
157 | const gchar *source_property; |
||
158 | const gchar *target_property; |
||
159 | |||
160 | GParamSpec *source_pspec; |
||
161 | GParamSpec *target_pspec; |
||
162 | |||
163 | GBindingTransformFunc transform_s2t; |
||
164 | GBindingTransformFunc transform_t2s; |
||
165 | |||
166 | GBindingFlags flags; |
||
167 | |||
168 | guint source_notify; |
||
169 | guint target_notify; |
||
170 | |||
171 | gpointer transform_data; |
||
172 | GDestroyNotify notify; |
||
173 | |||
174 | /* a guard, to avoid loops */ |
||
175 | guint is_frozen : 1; |
||
176 | }; |
||
177 | |||
178 | struct _GBindingClass |
||
179 | { |
||
180 | GObjectClass parent_class; |
||
181 | }; |
||
182 | |||
183 | enum |
||
184 | { |
||
185 | PROP_0, |
||
186 | |||
187 | PROP_SOURCE, |
||
188 | PROP_TARGET, |
||
189 | PROP_SOURCE_PROPERTY, |
||
190 | PROP_TARGET_PROPERTY, |
||
191 | PROP_FLAGS |
||
192 | }; |
||
193 | |||
194 | static guint gobject_notify_signal_id; |
||
195 | |||
196 | G_DEFINE_TYPE (GBinding, g_binding, G_TYPE_OBJECT); |
||
197 | |||
198 | /* the basic assumption is that if either the source or the target |
||
199 | * goes away then the binding does not exist any more and it should |
||
200 | * be reaped as well |
||
201 | */ |
||
202 | static void |
||
203 | weak_unbind (gpointer user_data, |
||
204 | GObject *where_the_object_was) |
||
205 | { |
||
206 | GBinding *binding = user_data; |
||
207 | |||
208 | /* if what went away was the source, unset it so that GBinding::finalize |
||
209 | * does not try to access it; otherwise, disconnect everything and remove |
||
210 | * the GBinding instance from the object's qdata |
||
211 | */ |
||
212 | if (binding->source == where_the_object_was) |
||
213 | binding->source = NULL; |
||
214 | else |
||
215 | { |
||
216 | if (binding->source_notify != 0) |
||
217 | g_signal_handler_disconnect (binding->source, binding->source_notify); |
||
218 | |||
219 | g_object_weak_unref (binding->source, weak_unbind, user_data); |
||
220 | |||
221 | binding->source_notify = 0; |
||
222 | binding->source = NULL; |
||
223 | } |
||
224 | |||
225 | /* as above, but with the target */ |
||
226 | if (binding->target == where_the_object_was) |
||
227 | binding->target = NULL; |
||
228 | else |
||
229 | { |
||
230 | if (binding->target_notify != 0) |
||
231 | g_signal_handler_disconnect (binding->target, binding->target_notify); |
||
232 | |||
233 | g_object_weak_unref (binding->target, weak_unbind, user_data); |
||
234 | |||
235 | binding->target_notify = 0; |
||
236 | binding->target = NULL; |
||
237 | } |
||
238 | |||
239 | /* this will take care of the binding itself */ |
||
240 | g_object_unref (binding); |
||
241 | } |
||
242 | |||
243 | static gboolean |
||
244 | default_transform (GBinding *binding, |
||
245 | const GValue *value_a, |
||
246 | GValue *value_b, |
||
247 | gpointer user_data G_GNUC_UNUSED) |
||
248 | { |
||
249 | /* if it's not the same type, try to convert it using the GValue |
||
250 | * transformation API; otherwise just copy it |
||
251 | */ |
||
252 | if (!g_type_is_a (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) |
||
253 | { |
||
254 | /* are these two types compatible (can be directly copied)? */ |
||
255 | if (g_value_type_compatible (G_VALUE_TYPE (value_a), |
||
256 | G_VALUE_TYPE (value_b))) |
||
257 | { |
||
258 | g_value_copy (value_a, value_b); |
||
259 | return TRUE; |
||
260 | } |
||
261 | |||
262 | if (g_value_type_transformable (G_VALUE_TYPE (value_a), |
||
263 | G_VALUE_TYPE (value_b))) |
||
264 | { |
||
265 | if (g_value_transform (value_a, value_b)) |
||
266 | return TRUE; |
||
267 | } |
||
268 | |||
269 | g_warning ("%s: Unable to convert a value of type %s to a " |
||
270 | "value of type %s", |
||
271 | G_STRLOC, |
||
272 | g_type_name (G_VALUE_TYPE (value_a)), |
||
273 | g_type_name (G_VALUE_TYPE (value_b))); |
||
274 | |||
275 | return FALSE; |
||
276 | } |
||
277 | |||
278 | g_value_copy (value_a, value_b); |
||
279 | return TRUE; |
||
280 | } |
||
281 | |||
282 | static gboolean |
||
283 | default_invert_boolean_transform (GBinding *binding, |
||
284 | const GValue *value_a, |
||
285 | GValue *value_b, |
||
286 | gpointer user_data G_GNUC_UNUSED) |
||
287 | { |
||
288 | gboolean value; |
||
289 | |||
290 | g_assert (G_VALUE_HOLDS_BOOLEAN (value_a)); |
||
291 | g_assert (G_VALUE_HOLDS_BOOLEAN (value_b)); |
||
292 | |||
293 | value = g_value_get_boolean (value_a); |
||
294 | value = !value; |
||
295 | |||
296 | g_value_set_boolean (value_b, value); |
||
297 | |||
298 | return TRUE; |
||
299 | } |
||
300 | |||
301 | static void |
||
302 | on_source_notify (GObject *gobject, |
||
303 | GParamSpec *pspec, |
||
304 | GBinding *binding) |
||
305 | { |
||
306 | GValue from_value = G_VALUE_INIT; |
||
307 | GValue to_value = G_VALUE_INIT; |
||
308 | gboolean res; |
||
309 | |||
310 | if (binding->is_frozen) |
||
311 | return; |
||
312 | |||
313 | g_value_init (&from_value, G_PARAM_SPEC_VALUE_TYPE (binding->source_pspec)); |
||
314 | g_value_init (&to_value, G_PARAM_SPEC_VALUE_TYPE (binding->target_pspec)); |
||
315 | |||
316 | g_object_get_property (binding->source, binding->source_pspec->name, &from_value); |
||
317 | |||
318 | res = binding->transform_s2t (binding, |
||
319 | &from_value, |
||
320 | &to_value, |
||
321 | binding->transform_data); |
||
322 | if (res) |
||
323 | { |
||
324 | binding->is_frozen = TRUE; |
||
325 | |||
326 | g_param_value_validate (binding->target_pspec, &to_value); |
||
327 | g_object_set_property (binding->target, binding->target_pspec->name, &to_value); |
||
328 | |||
329 | binding->is_frozen = FALSE; |
||
330 | } |
||
331 | |||
332 | g_value_unset (&from_value); |
||
333 | g_value_unset (&to_value); |
||
334 | } |
||
335 | |||
336 | static void |
||
337 | on_target_notify (GObject *gobject, |
||
338 | GParamSpec *pspec, |
||
339 | GBinding *binding) |
||
340 | { |
||
341 | GValue from_value = G_VALUE_INIT; |
||
342 | GValue to_value = G_VALUE_INIT; |
||
343 | gboolean res; |
||
344 | |||
345 | if (binding->is_frozen) |
||
346 | return; |
||
347 | |||
348 | g_value_init (&from_value, G_PARAM_SPEC_VALUE_TYPE (binding->target_pspec)); |
||
349 | g_value_init (&to_value, G_PARAM_SPEC_VALUE_TYPE (binding->source_pspec)); |
||
350 | |||
351 | g_object_get_property (binding->target, binding->target_pspec->name, &from_value); |
||
352 | |||
353 | res = binding->transform_t2s (binding, |
||
354 | &from_value, |
||
355 | &to_value, |
||
356 | binding->transform_data); |
||
357 | if (res) |
||
358 | { |
||
359 | binding->is_frozen = TRUE; |
||
360 | |||
361 | g_param_value_validate (binding->source_pspec, &to_value); |
||
362 | g_object_set_property (binding->source, binding->source_pspec->name, &to_value); |
||
363 | |||
364 | binding->is_frozen = FALSE; |
||
365 | } |
||
366 | |||
367 | g_value_unset (&from_value); |
||
368 | g_value_unset (&to_value); |
||
369 | } |
||
370 | |||
371 | static inline void |
||
372 | g_binding_unbind_internal (GBinding *binding, |
||
373 | gboolean unref_binding) |
||
374 | { |
||
375 | gboolean source_is_target = binding->source == binding->target; |
||
376 | |||
377 | /* dispose of the transformation data */ |
||
378 | if (binding->notify != NULL) |
||
379 | { |
||
380 | binding->notify (binding->transform_data); |
||
381 | |||
382 | binding->transform_data = NULL; |
||
383 | binding->notify = NULL; |
||
384 | } |
||
385 | |||
386 | if (binding->source != NULL) |
||
387 | { |
||
388 | if (binding->source_notify != 0) |
||
389 | g_signal_handler_disconnect (binding->source, binding->source_notify); |
||
390 | |||
391 | g_object_weak_unref (binding->source, weak_unbind, binding); |
||
392 | |||
393 | binding->source_notify = 0; |
||
394 | binding->source = NULL; |
||
395 | } |
||
396 | |||
397 | if (binding->target != NULL) |
||
398 | { |
||
399 | if (binding->target_notify != 0) |
||
400 | g_signal_handler_disconnect (binding->target, binding->target_notify); |
||
401 | |||
402 | if (!source_is_target) |
||
403 | g_object_weak_unref (binding->target, weak_unbind, binding); |
||
404 | |||
405 | binding->target_notify = 0; |
||
406 | binding->target = NULL; |
||
407 | } |
||
408 | |||
409 | if (unref_binding) |
||
410 | g_object_unref (binding); |
||
411 | } |
||
412 | |||
413 | static void |
||
414 | g_binding_finalize (GObject *gobject) |
||
415 | { |
||
416 | GBinding *binding = G_BINDING (gobject); |
||
417 | |||
418 | g_binding_unbind_internal (binding, FALSE); |
||
419 | |||
420 | G_OBJECT_CLASS (g_binding_parent_class)->finalize (gobject); |
||
421 | } |
||
422 | |||
423 | static void |
||
424 | g_binding_set_property (GObject *gobject, |
||
425 | guint prop_id, |
||
426 | const GValue *value, |
||
427 | GParamSpec *pspec) |
||
428 | { |
||
429 | GBinding *binding = G_BINDING (gobject); |
||
430 | |||
431 | switch (prop_id) |
||
432 | { |
||
433 | case PROP_SOURCE: |
||
434 | binding->source = g_value_get_object (value); |
||
435 | break; |
||
436 | |||
437 | case PROP_SOURCE_PROPERTY: |
||
438 | binding->source_property = g_intern_string (g_value_get_string (value)); |
||
439 | break; |
||
440 | |||
441 | case PROP_TARGET: |
||
442 | binding->target = g_value_get_object (value); |
||
443 | break; |
||
444 | |||
445 | case PROP_TARGET_PROPERTY: |
||
446 | binding->target_property = g_intern_string (g_value_get_string (value)); |
||
447 | break; |
||
448 | |||
449 | case PROP_FLAGS: |
||
450 | binding->flags = g_value_get_flags (value); |
||
451 | break; |
||
452 | |||
453 | default: |
||
454 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
||
455 | break; |
||
456 | } |
||
457 | } |
||
458 | |||
459 | static void |
||
460 | g_binding_get_property (GObject *gobject, |
||
461 | guint prop_id, |
||
462 | GValue *value, |
||
463 | GParamSpec *pspec) |
||
464 | { |
||
465 | GBinding *binding = G_BINDING (gobject); |
||
466 | |||
467 | switch (prop_id) |
||
468 | { |
||
469 | case PROP_SOURCE: |
||
470 | g_value_set_object (value, binding->source); |
||
471 | break; |
||
472 | |||
473 | case PROP_SOURCE_PROPERTY: |
||
474 | g_value_set_string (value, binding->source_property); |
||
475 | break; |
||
476 | |||
477 | case PROP_TARGET: |
||
478 | g_value_set_object (value, binding->target); |
||
479 | break; |
||
480 | |||
481 | case PROP_TARGET_PROPERTY: |
||
482 | g_value_set_string (value, binding->target_property); |
||
483 | break; |
||
484 | |||
485 | case PROP_FLAGS: |
||
486 | g_value_set_flags (value, binding->flags); |
||
487 | break; |
||
488 | |||
489 | default: |
||
490 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
||
491 | break; |
||
492 | } |
||
493 | } |
||
494 | |||
495 | static void |
||
496 | g_binding_constructed (GObject *gobject) |
||
497 | { |
||
498 | GBinding *binding = G_BINDING (gobject); |
||
499 | GBindingTransformFunc transform_func = default_transform; |
||
500 | GQuark source_property_detail; |
||
501 | GClosure *source_notify_closure; |
||
502 | |||
503 | /* assert that we were constructed correctly */ |
||
504 | g_assert (binding->source != NULL); |
||
505 | g_assert (binding->target != NULL); |
||
506 | g_assert (binding->source_property != NULL); |
||
507 | g_assert (binding->target_property != NULL); |
||
508 | |||
509 | /* we assume a check was performed prior to construction - since |
||
510 | * g_object_bind_property_full() does it; we cannot fail construction |
||
511 | * anyway, so it would be hard for use to properly warn here |
||
512 | */ |
||
513 | binding->source_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (binding->source), binding->source_property); |
||
514 | binding->target_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (binding->target), binding->target_property); |
||
515 | g_assert (binding->source_pspec != NULL); |
||
516 | g_assert (binding->target_pspec != NULL); |
||
517 | |||
518 | /* switch to the invert boolean transform if needed */ |
||
519 | if (binding->flags & G_BINDING_INVERT_BOOLEAN) |
||
520 | transform_func = default_invert_boolean_transform; |
||
521 | |||
522 | /* set the default transformation functions here */ |
||
523 | binding->transform_s2t = transform_func; |
||
524 | binding->transform_t2s = transform_func; |
||
525 | |||
526 | binding->transform_data = NULL; |
||
527 | binding->notify = NULL; |
||
528 | |||
529 | source_property_detail = g_quark_from_string (binding->source_property); |
||
530 | source_notify_closure = g_cclosure_new (G_CALLBACK (on_source_notify), |
||
531 | binding, NULL); |
||
532 | binding->source_notify = g_signal_connect_closure_by_id (binding->source, |
||
533 | gobject_notify_signal_id, |
||
534 | source_property_detail, |
||
535 | source_notify_closure, |
||
536 | FALSE); |
||
537 | |||
538 | g_object_weak_ref (binding->source, weak_unbind, binding); |
||
539 | |||
540 | if (binding->flags & G_BINDING_BIDIRECTIONAL) |
||
541 | { |
||
542 | GQuark target_property_detail; |
||
543 | GClosure *target_notify_closure; |
||
544 | |||
545 | target_property_detail = g_quark_from_string (binding->target_property); |
||
546 | target_notify_closure = g_cclosure_new (G_CALLBACK (on_target_notify), |
||
547 | binding, NULL); |
||
548 | binding->target_notify = g_signal_connect_closure_by_id (binding->target, |
||
549 | gobject_notify_signal_id, |
||
550 | target_property_detail, |
||
551 | target_notify_closure, |
||
552 | FALSE); |
||
553 | } |
||
554 | |||
555 | if (binding->target != binding->source) |
||
556 | g_object_weak_ref (binding->target, weak_unbind, binding); |
||
557 | } |
||
558 | |||
559 | static void |
||
560 | g_binding_class_init (GBindingClass *klass) |
||
561 | { |
||
562 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
||
563 | |||
564 | gobject_notify_signal_id = g_signal_lookup ("notify", G_TYPE_OBJECT); |
||
565 | g_assert (gobject_notify_signal_id != 0); |
||
566 | |||
567 | gobject_class->constructed = g_binding_constructed; |
||
568 | gobject_class->set_property = g_binding_set_property; |
||
569 | gobject_class->get_property = g_binding_get_property; |
||
570 | gobject_class->finalize = g_binding_finalize; |
||
571 | |||
572 | /** |
||
573 | * GBinding:source: |
||
574 | * |
||
575 | * The #GObject that should be used as the source of the binding |
||
576 | * |
||
577 | * Since: 2.26 |
||
578 | */ |
||
579 | g_object_class_install_property (gobject_class, PROP_SOURCE, |
||
580 | g_param_spec_object ("source", |
||
581 | P_("Source"), |
||
582 | P_("The source of the binding"), |
||
583 | G_TYPE_OBJECT, |
||
584 | G_PARAM_CONSTRUCT_ONLY | |
||
585 | G_PARAM_READWRITE | |
||
586 | G_PARAM_STATIC_STRINGS)); |
||
587 | /** |
||
588 | * GBinding:target: |
||
589 | * |
||
590 | * The #GObject that should be used as the target of the binding |
||
591 | * |
||
592 | * Since: 2.26 |
||
593 | */ |
||
594 | g_object_class_install_property (gobject_class, PROP_TARGET, |
||
595 | g_param_spec_object ("target", |
||
596 | P_("Target"), |
||
597 | P_("The target of the binding"), |
||
598 | G_TYPE_OBJECT, |
||
599 | G_PARAM_CONSTRUCT_ONLY | |
||
600 | G_PARAM_READWRITE | |
||
601 | G_PARAM_STATIC_STRINGS)); |
||
602 | /** |
||
603 | * GBinding:source-property: |
||
604 | * |
||
605 | * The name of the property of #GBinding:source that should be used |
||
606 | * as the source of the binding |
||
607 | * |
||
608 | * Since: 2.26 |
||
609 | */ |
||
610 | g_object_class_install_property (gobject_class, PROP_SOURCE_PROPERTY, |
||
611 | g_param_spec_string ("source-property", |
||
612 | P_("Source Property"), |
||
613 | P_("The property on the source to bind"), |
||
614 | NULL, |
||
615 | G_PARAM_CONSTRUCT_ONLY | |
||
616 | G_PARAM_READWRITE | |
||
617 | G_PARAM_STATIC_STRINGS)); |
||
618 | /** |
||
619 | * GBinding:target-property: |
||
620 | * |
||
621 | * The name of the property of #GBinding:target that should be used |
||
622 | * as the target of the binding |
||
623 | * |
||
624 | * Since: 2.26 |
||
625 | */ |
||
626 | g_object_class_install_property (gobject_class, PROP_TARGET_PROPERTY, |
||
627 | g_param_spec_string ("target-property", |
||
628 | P_("Target Property"), |
||
629 | P_("The property on the target to bind"), |
||
630 | NULL, |
||
631 | G_PARAM_CONSTRUCT_ONLY | |
||
632 | G_PARAM_READWRITE | |
||
633 | G_PARAM_STATIC_STRINGS)); |
||
634 | /** |
||
635 | * GBinding:flags: |
||
636 | * |
||
637 | * Flags to be used to control the #GBinding |
||
638 | * |
||
639 | * Since: 2.26 |
||
640 | */ |
||
641 | g_object_class_install_property (gobject_class, PROP_FLAGS, |
||
642 | g_param_spec_flags ("flags", |
||
643 | P_("Flags"), |
||
644 | P_("The binding flags"), |
||
645 | G_TYPE_BINDING_FLAGS, |
||
646 | G_BINDING_DEFAULT, |
||
647 | G_PARAM_CONSTRUCT_ONLY | |
||
648 | G_PARAM_READWRITE | |
||
649 | G_PARAM_STATIC_STRINGS)); |
||
650 | } |
||
651 | |||
652 | static void |
||
653 | g_binding_init (GBinding *binding) |
||
654 | { |
||
655 | } |
||
656 | |||
657 | /** |
||
658 | * g_binding_get_flags: |
||
659 | * @binding: a #GBinding |
||
660 | * |
||
661 | * Retrieves the flags passed when constructing the #GBinding. |
||
662 | * |
||
663 | * Returns: the #GBindingFlags used by the #GBinding |
||
664 | * |
||
665 | * Since: 2.26 |
||
666 | */ |
||
667 | GBindingFlags |
||
668 | g_binding_get_flags (GBinding *binding) |
||
669 | { |
||
670 | g_return_val_if_fail (G_IS_BINDING (binding), G_BINDING_DEFAULT); |
||
671 | |||
672 | return binding->flags; |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * g_binding_get_source: |
||
677 | * @binding: a #GBinding |
||
678 | * |
||
679 | * Retrieves the #GObject instance used as the source of the binding. |
||
680 | * |
||
681 | * Returns: (transfer none): the source #GObject |
||
682 | * |
||
683 | * Since: 2.26 |
||
684 | */ |
||
685 | GObject * |
||
686 | g_binding_get_source (GBinding *binding) |
||
687 | { |
||
688 | g_return_val_if_fail (G_IS_BINDING (binding), NULL); |
||
689 | |||
690 | return binding->source; |
||
691 | } |
||
692 | |||
693 | /** |
||
694 | * g_binding_get_target: |
||
695 | * @binding: a #GBinding |
||
696 | * |
||
697 | * Retrieves the #GObject instance used as the target of the binding. |
||
698 | * |
||
699 | * Returns: (transfer none): the target #GObject |
||
700 | * |
||
701 | * Since: 2.26 |
||
702 | */ |
||
703 | GObject * |
||
704 | g_binding_get_target (GBinding *binding) |
||
705 | { |
||
706 | g_return_val_if_fail (G_IS_BINDING (binding), NULL); |
||
707 | |||
708 | return binding->target; |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * g_binding_get_source_property: |
||
713 | * @binding: a #GBinding |
||
714 | * |
||
715 | * Retrieves the name of the property of #GBinding:source used as the source |
||
716 | * of the binding. |
||
717 | * |
||
718 | * Returns: the name of the source property |
||
719 | * |
||
720 | * Since: 2.26 |
||
721 | */ |
||
722 | const gchar * |
||
723 | g_binding_get_source_property (GBinding *binding) |
||
724 | { |
||
725 | g_return_val_if_fail (G_IS_BINDING (binding), NULL); |
||
726 | |||
727 | return binding->source_property; |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * g_binding_get_target_property: |
||
732 | * @binding: a #GBinding |
||
733 | * |
||
734 | * Retrieves the name of the property of #GBinding:target used as the target |
||
735 | * of the binding. |
||
736 | * |
||
737 | * Returns: the name of the target property |
||
738 | * |
||
739 | * Since: 2.26 |
||
740 | */ |
||
741 | const gchar * |
||
742 | g_binding_get_target_property (GBinding *binding) |
||
743 | { |
||
744 | g_return_val_if_fail (G_IS_BINDING (binding), NULL); |
||
745 | |||
746 | return binding->target_property; |
||
747 | } |
||
748 | |||
749 | /** |
||
750 | * g_binding_unbind: |
||
751 | * @binding: a #GBinding |
||
752 | * |
||
753 | * Explicitly releases the binding between the source and the target |
||
754 | * property expressed by @binding. |
||
755 | * |
||
756 | * This function will release the reference that is being held on |
||
757 | * the @binding instance; if you want to hold on to the #GBinding instance |
||
758 | * after calling g_binding_unbind(), you will need to hold a reference |
||
759 | * to it. |
||
760 | * |
||
761 | * Since: 2.38 |
||
762 | */ |
||
763 | void |
||
764 | g_binding_unbind (GBinding *binding) |
||
765 | { |
||
766 | g_return_if_fail (G_IS_BINDING (binding)); |
||
767 | |||
768 | g_binding_unbind_internal (binding, TRUE); |
||
769 | } |
||
770 | |||
771 | /** |
||
772 | * g_object_bind_property_full: |
||
773 | * @source: (type GObject.Object): the source #GObject |
||
774 | * @source_property: the property on @source to bind |
||
775 | * @target: (type GObject.Object): the target #GObject |
||
776 | * @target_property: the property on @target to bind |
||
777 | * @flags: flags to pass to #GBinding |
||
778 | * @transform_to: (scope notified) (allow-none): the transformation function |
||
779 | * from the @source to the @target, or %NULL to use the default |
||
780 | * @transform_from: (scope notified) (allow-none): the transformation function |
||
781 | * from the @target to the @source, or %NULL to use the default |
||
782 | * @user_data: custom data to be passed to the transformation functions, |
||
783 | * or %NULL |
||
784 | * @notify: function to be called when disposing the binding, to free the |
||
785 | * resources used by the transformation functions |
||
786 | * |
||
787 | * Complete version of g_object_bind_property(). |
||
788 | * |
||
789 | * Creates a binding between @source_property on @source and @target_property |
||
790 | * on @target, allowing you to set the transformation functions to be used by |
||
791 | * the binding. |
||
792 | * |
||
793 | * If @flags contains %G_BINDING_BIDIRECTIONAL then the binding will be mutual: |
||
794 | * if @target_property on @target changes then the @source_property on @source |
||
795 | * will be updated as well. The @transform_from function is only used in case |
||
796 | * of bidirectional bindings, otherwise it will be ignored |
||
797 | * |
||
798 | * The binding will automatically be removed when either the @source or the |
||
799 | * @target instances are finalized. To remove the binding without affecting the |
||
800 | * @source and the @target you can just call g_object_unref() on the returned |
||
801 | * #GBinding instance. |
||
802 | * |
||
803 | * A #GObject can have multiple bindings. |
||
804 | * |
||
805 | * The same @user_data parameter will be used for both @transform_to |
||
806 | * and @transform_from transformation functions; the @notify function will |
||
807 | * be called once, when the binding is removed. If you need different data |
||
808 | * for each transformation function, please use |
||
809 | * g_object_bind_property_with_closures() instead. |
||
810 | * |
||
811 | * Returns: (transfer none): the #GBinding instance representing the |
||
812 | * binding between the two #GObject instances. The binding is released |
||
813 | * whenever the #GBinding reference count reaches zero. |
||
814 | * |
||
815 | * Since: 2.26 |
||
816 | */ |
||
817 | GBinding * |
||
818 | g_object_bind_property_full (gpointer source, |
||
819 | const gchar *source_property, |
||
820 | gpointer target, |
||
821 | const gchar *target_property, |
||
822 | GBindingFlags flags, |
||
823 | GBindingTransformFunc transform_to, |
||
824 | GBindingTransformFunc transform_from, |
||
825 | gpointer user_data, |
||
826 | GDestroyNotify notify) |
||
827 | { |
||
828 | GParamSpec *pspec; |
||
829 | GBinding *binding; |
||
830 | |||
831 | g_return_val_if_fail (G_IS_OBJECT (source), NULL); |
||
832 | g_return_val_if_fail (source_property != NULL, NULL); |
||
833 | g_return_val_if_fail (G_IS_OBJECT (target), NULL); |
||
834 | g_return_val_if_fail (target_property != NULL, NULL); |
||
835 | |||
836 | if (source == target && g_strcmp0 (source_property, target_property) == 0) |
||
837 | { |
||
838 | g_warning ("Unable to bind the same property on the same instance"); |
||
839 | return NULL; |
||
840 | } |
||
841 | |||
842 | /* remove the G_BINDING_INVERT_BOOLEAN flag in case we have |
||
843 | * custom transformation functions |
||
844 | */ |
||
845 | if ((flags & G_BINDING_INVERT_BOOLEAN) && |
||
846 | (transform_to != NULL || transform_from != NULL)) |
||
847 | { |
||
848 | flags &= ~G_BINDING_INVERT_BOOLEAN; |
||
849 | } |
||
850 | |||
851 | pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property); |
||
852 | if (pspec == NULL) |
||
853 | { |
||
854 | g_warning ("%s: The source object of type %s has no property called '%s'", |
||
855 | G_STRLOC, |
||
856 | G_OBJECT_TYPE_NAME (source), |
||
857 | source_property); |
||
858 | return NULL; |
||
859 | } |
||
860 | |||
861 | if (!(pspec->flags & G_PARAM_READABLE)) |
||
862 | { |
||
863 | g_warning ("%s: The source object of type %s has no readable property called '%s'", |
||
864 | G_STRLOC, |
||
865 | G_OBJECT_TYPE_NAME (source), |
||
866 | source_property); |
||
867 | return NULL; |
||
868 | } |
||
869 | |||
870 | if ((flags & G_BINDING_BIDIRECTIONAL) && |
||
871 | ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) || !(pspec->flags & G_PARAM_WRITABLE))) |
||
872 | { |
||
873 | g_warning ("%s: The source object of type %s has no writable property called '%s'", |
||
874 | G_STRLOC, |
||
875 | G_OBJECT_TYPE_NAME (source), |
||
876 | source_property); |
||
877 | return NULL; |
||
878 | } |
||
879 | |||
880 | if ((flags & G_BINDING_INVERT_BOOLEAN) && |
||
881 | !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN)) |
||
882 | { |
||
883 | g_warning ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used " |
||
884 | "when binding boolean properties; the source property '%s' " |
||
885 | "is of type '%s'", |
||
886 | G_STRLOC, |
||
887 | source_property, |
||
888 | g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); |
||
889 | return NULL; |
||
890 | } |
||
891 | |||
892 | pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), target_property); |
||
893 | if (pspec == NULL) |
||
894 | { |
||
895 | g_warning ("%s: The target object of type %s has no property called '%s'", |
||
896 | G_STRLOC, |
||
897 | G_OBJECT_TYPE_NAME (target), |
||
898 | target_property); |
||
899 | return NULL; |
||
900 | } |
||
901 | |||
902 | if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) || !(pspec->flags & G_PARAM_WRITABLE)) |
||
903 | { |
||
904 | g_warning ("%s: The target object of type %s has no writable property called '%s'", |
||
905 | G_STRLOC, |
||
906 | G_OBJECT_TYPE_NAME (target), |
||
907 | target_property); |
||
908 | return NULL; |
||
909 | } |
||
910 | |||
911 | if ((flags & G_BINDING_BIDIRECTIONAL) && |
||
912 | !(pspec->flags & G_PARAM_READABLE)) |
||
913 | { |
||
914 | g_warning ("%s: The target object of type %s has no readable property called '%s'", |
||
915 | G_STRLOC, |
||
916 | G_OBJECT_TYPE_NAME (target), |
||
917 | target_property); |
||
918 | return NULL; |
||
919 | } |
||
920 | |||
921 | if ((flags & G_BINDING_INVERT_BOOLEAN) && |
||
922 | !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN)) |
||
923 | { |
||
924 | g_warning ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used " |
||
925 | "when binding boolean properties; the target property '%s' " |
||
926 | "is of type '%s'", |
||
927 | G_STRLOC, |
||
928 | target_property, |
||
929 | g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); |
||
930 | return NULL; |
||
931 | } |
||
932 | |||
933 | binding = g_object_new (G_TYPE_BINDING, |
||
934 | "source", source, |
||
935 | "source-property", source_property, |
||
936 | "target", target, |
||
937 | "target-property", target_property, |
||
938 | "flags", flags, |
||
939 | NULL); |
||
940 | |||
941 | if (transform_to != NULL) |
||
942 | binding->transform_s2t = transform_to; |
||
943 | |||
944 | if (transform_from != NULL) |
||
945 | binding->transform_t2s = transform_from; |
||
946 | |||
947 | binding->transform_data = user_data; |
||
948 | binding->notify = notify; |
||
949 | |||
950 | /* synchronize the target with the source by faking an emission of |
||
951 | * the ::notify signal for the source property; this will also take |
||
952 | * care of the bidirectional binding case because the eventual change |
||
953 | * will emit a notification on the target |
||
954 | */ |
||
955 | if (flags & G_BINDING_SYNC_CREATE) |
||
956 | on_source_notify (binding->source, binding->source_pspec, binding); |
||
957 | |||
958 | return binding; |
||
959 | } |
||
960 | |||
961 | /** |
||
962 | * g_object_bind_property: |
||
963 | * @source: (type GObject.Object): the source #GObject |
||
964 | * @source_property: the property on @source to bind |
||
965 | * @target: (type GObject.Object): the target #GObject |
||
966 | * @target_property: the property on @target to bind |
||
967 | * @flags: flags to pass to #GBinding |
||
968 | * |
||
969 | * Creates a binding between @source_property on @source and @target_property |
||
970 | * on @target. Whenever the @source_property is changed the @target_property is |
||
971 | * updated using the same value. For instance: |
||
972 | * |
||
973 | * |[ |
||
974 | * g_object_bind_property (action, "active", widget, "sensitive", 0); |
||
975 | * ]| |
||
976 | * |
||
977 | * Will result in the "sensitive" property of the widget #GObject instance to be |
||
978 | * updated with the same value of the "active" property of the action #GObject |
||
979 | * instance. |
||
980 | * |
||
981 | * If @flags contains %G_BINDING_BIDIRECTIONAL then the binding will be mutual: |
||
982 | * if @target_property on @target changes then the @source_property on @source |
||
983 | * will be updated as well. |
||
984 | * |
||
985 | * The binding will automatically be removed when either the @source or the |
||
986 | * @target instances are finalized. To remove the binding without affecting the |
||
987 | * @source and the @target you can just call g_object_unref() on the returned |
||
988 | * #GBinding instance. |
||
989 | * |
||
990 | * A #GObject can have multiple bindings. |
||
991 | * |
||
992 | * Returns: (transfer none): the #GBinding instance representing the |
||
993 | * binding between the two #GObject instances. The binding is released |
||
994 | * whenever the #GBinding reference count reaches zero. |
||
995 | * |
||
996 | * Since: 2.26 |
||
997 | */ |
||
998 | GBinding * |
||
999 | g_object_bind_property (gpointer source, |
||
1000 | const gchar *source_property, |
||
1001 | gpointer target, |
||
1002 | const gchar *target_property, |
||
1003 | GBindingFlags flags) |
||
1004 | { |
||
1005 | /* type checking is done in g_object_bind_property_full() */ |
||
1006 | |||
1007 | return g_object_bind_property_full (source, source_property, |
||
1008 | target, target_property, |
||
1009 | flags, |
||
1010 | NULL, |
||
1011 | NULL, |
||
1012 | NULL, NULL); |
||
1013 | } |
||
1014 | |||
1015 | typedef struct _TransformData |
||
1016 | { |
||
1017 | GClosure *transform_to_closure; |
||
1018 | GClosure *transform_from_closure; |
||
1019 | } TransformData; |
||
1020 | |||
1021 | static gboolean |
||
1022 | bind_with_closures_transform_to (GBinding *binding, |
||
1023 | const GValue *source, |
||
1024 | GValue *target, |
||
1025 | gpointer data) |
||
1026 | { |
||
1027 | TransformData *t_data = data; |
||
1028 | GValue params[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; |
||
1029 | GValue retval = G_VALUE_INIT; |
||
1030 | gboolean res; |
||
1031 | |||
1032 | g_value_init (¶ms[0], G_TYPE_BINDING); |
||
1033 | g_value_set_object (¶ms[0], binding); |
||
1034 | |||
1035 | g_value_init (¶ms[1], G_TYPE_VALUE); |
||
1036 | g_value_set_boxed (¶ms[1], source); |
||
1037 | |||
1038 | g_value_init (¶ms[2], G_TYPE_VALUE); |
||
1039 | g_value_set_boxed (¶ms[2], target); |
||
1040 | |||
1041 | g_value_init (&retval, G_TYPE_BOOLEAN); |
||
1042 | g_value_set_boolean (&retval, FALSE); |
||
1043 | |||
1044 | g_closure_invoke (t_data->transform_to_closure, &retval, 3, params, NULL); |
||
1045 | |||
1046 | res = g_value_get_boolean (&retval); |
||
1047 | if (res) |
||
1048 | { |
||
1049 | const GValue *out_value = g_value_get_boxed (¶ms[2]); |
||
1050 | |||
1051 | g_assert (out_value != NULL); |
||
1052 | |||
1053 | g_value_copy (out_value, target); |
||
1054 | } |
||
1055 | |||
1056 | g_value_unset (¶ms[0]); |
||
1057 | g_value_unset (¶ms[1]); |
||
1058 | g_value_unset (¶ms[2]); |
||
1059 | g_value_unset (&retval); |
||
1060 | |||
1061 | return res; |
||
1062 | } |
||
1063 | |||
1064 | static gboolean |
||
1065 | bind_with_closures_transform_from (GBinding *binding, |
||
1066 | const GValue *source, |
||
1067 | GValue *target, |
||
1068 | gpointer data) |
||
1069 | { |
||
1070 | TransformData *t_data = data; |
||
1071 | GValue params[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; |
||
1072 | GValue retval = G_VALUE_INIT; |
||
1073 | gboolean res; |
||
1074 | |||
1075 | g_value_init (¶ms[0], G_TYPE_BINDING); |
||
1076 | g_value_set_object (¶ms[0], binding); |
||
1077 | |||
1078 | g_value_init (¶ms[1], G_TYPE_VALUE); |
||
1079 | g_value_set_boxed (¶ms[1], source); |
||
1080 | |||
1081 | g_value_init (¶ms[2], G_TYPE_VALUE); |
||
1082 | g_value_set_boxed (¶ms[2], target); |
||
1083 | |||
1084 | g_value_init (&retval, G_TYPE_BOOLEAN); |
||
1085 | g_value_set_boolean (&retval, FALSE); |
||
1086 | |||
1087 | g_closure_invoke (t_data->transform_from_closure, &retval, 3, params, NULL); |
||
1088 | |||
1089 | res = g_value_get_boolean (&retval); |
||
1090 | if (res) |
||
1091 | { |
||
1092 | const GValue *out_value = g_value_get_boxed (¶ms[2]); |
||
1093 | |||
1094 | g_assert (out_value != NULL); |
||
1095 | |||
1096 | g_value_copy (out_value, target); |
||
1097 | } |
||
1098 | |||
1099 | g_value_unset (¶ms[0]); |
||
1100 | g_value_unset (¶ms[1]); |
||
1101 | g_value_unset (¶ms[2]); |
||
1102 | g_value_unset (&retval); |
||
1103 | |||
1104 | return res; |
||
1105 | } |
||
1106 | |||
1107 | static void |
||
1108 | bind_with_closures_free_func (gpointer data) |
||
1109 | { |
||
1110 | TransformData *t_data = data; |
||
1111 | |||
1112 | if (t_data->transform_to_closure != NULL) |
||
1113 | g_closure_unref (t_data->transform_to_closure); |
||
1114 | |||
1115 | if (t_data->transform_from_closure != NULL) |
||
1116 | g_closure_unref (t_data->transform_from_closure); |
||
1117 | |||
1118 | g_slice_free (TransformData, t_data); |
||
1119 | } |
||
1120 | |||
1121 | /** |
||
1122 | * g_object_bind_property_with_closures: (rename-to g_object_bind_property_full) |
||
1123 | * @source: (type GObject.Object): the source #GObject |
||
1124 | * @source_property: the property on @source to bind |
||
1125 | * @target: (type GObject.Object): the target #GObject |
||
1126 | * @target_property: the property on @target to bind |
||
1127 | * @flags: flags to pass to #GBinding |
||
1128 | * @transform_to: a #GClosure wrapping the transformation function |
||
1129 | * from the @source to the @target, or %NULL to use the default |
||
1130 | * @transform_from: a #GClosure wrapping the transformation function |
||
1131 | * from the @target to the @source, or %NULL to use the default |
||
1132 | * |
||
1133 | * Creates a binding between @source_property on @source and @target_property |
||
1134 | * on @target, allowing you to set the transformation functions to be used by |
||
1135 | * the binding. |
||
1136 | * |
||
1137 | * This function is the language bindings friendly version of |
||
1138 | * g_object_bind_property_full(), using #GClosures instead of |
||
1139 | * function pointers. |
||
1140 | * |
||
1141 | * Returns: (transfer none): the #GBinding instance representing the |
||
1142 | * binding between the two #GObject instances. The binding is released |
||
1143 | * whenever the #GBinding reference count reaches zero. |
||
1144 | * |
||
1145 | * Since: 2.26 |
||
1146 | */ |
||
1147 | GBinding * |
||
1148 | g_object_bind_property_with_closures (gpointer source, |
||
1149 | const gchar *source_property, |
||
1150 | gpointer target, |
||
1151 | const gchar *target_property, |
||
1152 | GBindingFlags flags, |
||
1153 | GClosure *transform_to, |
||
1154 | GClosure *transform_from) |
||
1155 | { |
||
1156 | TransformData *data; |
||
1157 | |||
1158 | data = g_slice_new0 (TransformData); |
||
1159 | |||
1160 | if (transform_to != NULL) |
||
1161 | { |
||
1162 | if (G_CLOSURE_NEEDS_MARSHAL (transform_to)) |
||
1163 | g_closure_set_marshal (transform_to, g_cclosure_marshal_BOOLEAN__BOXED_BOXED); |
||
1164 | |||
1165 | data->transform_to_closure = g_closure_ref (transform_to); |
||
1166 | g_closure_sink (data->transform_to_closure); |
||
1167 | } |
||
1168 | |||
1169 | if (transform_from != NULL) |
||
1170 | { |
||
1171 | if (G_CLOSURE_NEEDS_MARSHAL (transform_from)) |
||
1172 | g_closure_set_marshal (transform_from, g_cclosure_marshal_BOOLEAN__BOXED_BOXED); |
||
1173 | |||
1174 | data->transform_from_closure = g_closure_ref (transform_from); |
||
1175 | g_closure_sink (data->transform_from_closure); |
||
1176 | } |
||
1177 | |||
1178 | return g_object_bind_property_full (source, source_property, |
||
1179 | target, target_property, |
||
1180 | flags, |
||
1181 | transform_to != NULL ? bind_with_closures_transform_to : NULL, |
||
1182 | transform_from != NULL ? bind_with_closures_transform_from : NULL, |
||
1183 | data, |
||
1184 | bind_with_closures_free_func); |
||
1185 | } |