4
* Copyright (C) 2008 Stefan Walter
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as
8
* published by the Free Software Foundation; either version 2.1 of
9
* the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24
#include "seahorse-bind.h"
29
value_equal (const GValue *a, const GValue *b)
32
const gchar *stra, *strb;
34
if (G_VALUE_TYPE (a) != G_VALUE_TYPE (b))
37
switch (G_VALUE_TYPE (a))
40
if (g_value_get_boolean (a) < g_value_get_boolean (b))
42
else if (g_value_get_boolean (a) == g_value_get_boolean (b))
48
if (g_value_get_char (a) < g_value_get_char (b))
50
else if (g_value_get_char (a) == g_value_get_char (b))
56
if (g_value_get_uchar (a) < g_value_get_uchar (b))
58
else if (g_value_get_uchar (a) == g_value_get_uchar (b))
64
if (g_value_get_int (a) < g_value_get_int (b))
66
else if (g_value_get_int (a) == g_value_get_int (b))
72
if (g_value_get_uint (a) < g_value_get_uint (b))
74
else if (g_value_get_uint (a) == g_value_get_uint (b))
80
if (g_value_get_long (a) < g_value_get_long (b))
82
else if (g_value_get_long (a) == g_value_get_long (b))
88
if (g_value_get_ulong (a) < g_value_get_ulong (b))
90
else if (g_value_get_ulong (a) == g_value_get_ulong (b))
96
if (g_value_get_int64 (a) < g_value_get_int64 (b))
98
else if (g_value_get_int64 (a) == g_value_get_int64 (b))
104
if (g_value_get_uint64 (a) < g_value_get_uint64 (b))
106
else if (g_value_get_uint64 (a) == g_value_get_uint64 (b))
112
/* this is even more bogus. */
113
if (g_value_get_flags (a) < g_value_get_flags (b))
115
else if (g_value_get_flags (a) == g_value_get_flags (b))
121
if (g_value_get_float (a) < g_value_get_float (b))
123
else if (g_value_get_float (a) == g_value_get_float (b))
129
if (g_value_get_double (a) < g_value_get_double (b))
131
else if (g_value_get_double (a) == g_value_get_double (b))
137
stra = g_value_get_string (a);
138
strb = g_value_get_string (b);
141
else if (!stra || !strb)
144
retval = g_utf8_collate (stra, strb) == 0;
147
retval = (g_value_get_pointer (a) == g_value_get_pointer (b));
150
retval = (g_value_get_boxed (a) == g_value_get_boxed (b));
153
retval = (g_value_get_object (a) == g_value_get_object (b));
156
if (G_VALUE_HOLDS_ENUM (a)) {
157
retval = (g_value_get_enum (a) == g_value_get_enum (b));
159
/* Default case is not equal */
168
static GHashTable *all_bindings = NULL;
170
typedef struct _Binding {
172
GParamSpec *prop_src;
173
GParamSpec *prop_dest;
177
SeahorseTransform transform;
183
static void binding_unref (Binding *binding);
184
static void binding_ref (Binding *binding);
187
binding_src_gone (gpointer data, GObject *was)
189
Binding *binding = (Binding*)data;
190
g_assert (binding->obj_src == was);
191
binding->obj_src = NULL;
192
binding_unref (binding);
196
binding_dest_gone (gpointer data, GObject *was)
198
Binding *binding = (Binding*)data;
201
at = g_slist_find (binding->obj_dests, was);
202
g_assert (at != NULL);
204
/* Remove it from the list */
205
binding->obj_dests = g_slist_delete_link (binding->obj_dests, at);
207
/* If no more destination objects, then go away */
208
if (!binding->obj_dests)
209
binding_unref (binding);
213
binding_ref (Binding *binding)
216
++binding->references;
220
binding_unref (Binding *binding)
226
g_assert (binding->references > 0);
227
--binding->references;
228
if (binding->references > 0)
231
if (G_IS_OBJECT (binding->obj_src)) {
232
g_signal_handler_disconnect (binding->obj_src, binding->connection);
233
g_object_weak_unref (binding->obj_src, binding_src_gone, binding);
234
binding->obj_src = NULL;
237
for (l = binding->obj_dests; l; l = g_slist_next (l)) {
238
if (G_IS_OBJECT (l->data))
239
g_object_weak_unref (l->data, binding_dest_gone, binding);
241
g_slist_free (binding->obj_dests);
242
binding->obj_dests = NULL;
244
g_assert (binding->prop_src);
245
g_param_spec_unref (binding->prop_src);
246
binding->prop_src = NULL;
248
g_assert (binding->prop_dest);
249
g_param_spec_unref (binding->prop_dest);
250
binding->prop_dest = NULL;
254
/* Remove from the list of all bindings */
255
g_assert (all_bindings);
256
g_hash_table_remove (all_bindings, binding);
257
if (g_hash_table_size (all_bindings) == 0) {
258
g_hash_table_destroy (all_bindings);
264
bind_transfer (Binding *binding, gboolean forward)
266
GValue src, dest, check;
269
g_assert (binding->obj_src);
270
g_assert (binding->obj_dests);
272
/* IMPORTANT: No return during this fuction */
273
binding_ref (binding);
274
binding->processing = TRUE;
276
/* Get the value from the source object */
277
memset (&src, 0, sizeof (src));
278
g_value_init (&src, binding->prop_src->value_type);
279
g_object_get_property (binding->obj_src, binding->prop_src->name, &src);
281
/* Transform the value */
282
memset (&dest, 0, sizeof (dest));
283
g_value_init (&dest, binding->prop_dest->value_type);
284
if ((binding->transform) (&src, &dest)) {
287
for (l = binding->obj_dests; l; l = g_slist_next (l)) {
289
/* Get the current value of the destination object */
290
memset (&check, 0, sizeof (check));
291
g_value_init (&check, binding->prop_dest->value_type);
292
g_object_get_property (l->data, binding->prop_dest->name, &check);
294
/* Set the property on the destination object */
295
if (!value_equal (&dest, &check))
296
g_object_set_property (l->data, binding->prop_dest->name, &dest);
298
g_value_unset (&check);
303
g_warning ("couldn't transform value from '%s' to '%s' when trying to "
304
"transfer bound property from %s.%s to %s.%s",
305
g_type_name (binding->prop_src->value_type),
306
g_type_name (binding->prop_dest->value_type),
307
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (binding->obj_src)),
308
binding->prop_src->name,
309
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (binding->obj_dests->data)),
310
binding->prop_dest->name);
313
g_value_unset (&src);
314
g_value_unset (&dest);
316
binding->processing = FALSE;
317
binding_unref (binding);
321
binding_fire (GObject *gobject, GParamSpec *pspec, gpointer data)
323
Binding *binding = (Binding*)data;
324
g_assert (gobject == binding->obj_src);
325
g_assert (pspec == binding->prop_src);
326
g_assert (binding->transform);
328
if (!binding->processing)
329
bind_transfer (binding, TRUE);
333
seahorse_bind_property (const gchar *prop_src, gpointer obj_src,
334
const gchar *prop_dest, gpointer obj_dest)
336
g_return_val_if_fail (G_IS_OBJECT (obj_src), NULL);
337
g_return_val_if_fail (prop_src, NULL);
338
g_return_val_if_fail (G_IS_OBJECT (obj_dest), NULL);
339
g_return_val_if_fail (prop_dest, NULL);
341
return seahorse_bind_property_full (prop_src, obj_src,
343
prop_dest, obj_dest, NULL);
347
seahorse_bind_property_full (const gchar *prop_src, gpointer obj_src,
348
SeahorseTransform transform,
349
const gchar *prop_dest, ...)
352
GParamSpec *spec_src;
353
GParamSpec *spec_dest;
361
g_return_val_if_fail (transform, NULL);
362
g_return_val_if_fail (G_IS_OBJECT (obj_src), NULL);
363
g_return_val_if_fail (prop_src, NULL);
364
g_return_val_if_fail (prop_dest, NULL);
366
cls = G_OBJECT_GET_CLASS (obj_src);
367
spec_src = g_object_class_find_property (cls, prop_src);
369
g_warning ("no property with the name '%s' exists in object of class '%s'",
370
prop_src, G_OBJECT_CLASS_NAME (cls));
376
va_start(va, prop_dest);
378
dest = G_OBJECT (va_arg (va, GObject*));
382
g_return_val_if_fail (G_IS_OBJECT (dest), NULL);
384
cls = G_OBJECT_GET_CLASS (dest);
385
spec = g_object_class_find_property (cls, prop_dest);
387
g_warning ("no property with the name '%s' exists in object of class '%s'",
388
prop_dest, G_OBJECT_CLASS_NAME (cls));
392
if (spec_dest && spec->value_type != spec_dest->value_type) {
393
g_warning ("destination property '%s' has a different type between objects in binding: %s != %s",
394
prop_dest, g_type_name (spec_dest->value_type), g_type_name (spec->value_type));
398
dests = g_slist_prepend (dests, dest);
402
g_return_val_if_fail (spec_dest, NULL);
403
g_return_val_if_fail (dests, NULL);
405
binding = g_new0 (Binding, 1);
407
binding->obj_src = obj_src;
408
g_object_weak_ref (obj_src, binding_src_gone, binding);
409
binding->prop_src = spec_src;
410
g_param_spec_ref (spec_src);
411
binding->transform = transform;
412
detail = g_strdup_printf ("notify::%s", prop_src);
413
binding->connection = g_signal_connect (obj_src, detail, G_CALLBACK (binding_fire), binding);
416
binding->obj_dests = dests;
417
binding->prop_dest = spec_dest;
418
g_param_spec_ref (spec_dest);
419
for (l = binding->obj_dests; l; l = g_slist_next (l))
420
g_object_weak_ref (l->data, binding_dest_gone, binding);
422
binding->references = 1;
424
/* Note this in all bindings */
426
all_bindings = g_hash_table_new (g_direct_hash, g_direct_equal);
427
g_hash_table_insert (all_bindings, binding, binding);
429
/* Transfer the first time */
430
binding_fire (obj_src, spec_src, binding);
435
static GHashTable *all_transfers = NULL;
437
typedef struct _Transfer {
441
SeahorseTransfer callback;
444
static void transfer_free (Transfer *transfer);
447
transfer_gone (gpointer data, GObject *was)
449
Transfer *transfer = (Transfer*)data;
450
if (transfer->object == was)
451
transfer->object = NULL;
452
else if (transfer->dest == was)
453
transfer->dest = NULL;
455
g_assert_not_reached ();
456
transfer_free (transfer);
460
transfer_free (Transfer *transfer)
464
if (G_IS_OBJECT (transfer->object)) {
465
g_object_weak_unref (transfer->object, transfer_gone, transfer);
466
g_signal_handler_disconnect (transfer->object, transfer->connection);
467
transfer->object = NULL;
470
if (G_IS_OBJECT (transfer->dest)) {
471
g_object_weak_unref (transfer->dest, transfer_gone, transfer);
472
transfer->dest = NULL;
477
/* Remove from the list of all notifies */
478
g_assert (all_transfers);
479
g_hash_table_remove (all_transfers, transfer);
480
if (g_hash_table_size (all_transfers) == 0) {
481
g_hash_table_destroy (all_transfers);
482
all_transfers = NULL;
487
transfer_fire (GObject *object, GParamSpec *spec, gpointer data)
489
Transfer *transfer = (Transfer*)data;
490
g_assert (transfer->object == object);
491
(transfer->callback) (object, transfer->dest);
495
seahorse_bind_objects (const gchar *property, gpointer object,
496
SeahorseTransfer callback, gpointer dest)
502
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
503
g_return_val_if_fail (G_IS_OBJECT (dest), NULL);
504
g_return_val_if_fail (callback, NULL);
507
cls = G_OBJECT_GET_CLASS (object);
508
if (!g_object_class_find_property (cls, property)) {
509
g_warning ("no property with the name '%s' exists in object of class '%s'",
510
property, G_OBJECT_CLASS_NAME (cls));
515
transfer = g_new0 (Transfer, 1);
517
transfer->object = object;
518
g_object_weak_ref (object, transfer_gone, transfer);
519
transfer->dest = dest;
520
g_object_weak_ref (dest, transfer_gone, transfer);
521
transfer->callback = callback;
524
detail = g_strdup_printf ("notify::%s", property);
525
transfer->connection = g_signal_connect (object, detail, G_CALLBACK (transfer_fire), transfer);
528
transfer->connection = g_signal_connect (object, "notify", G_CALLBACK (transfer_fire), transfer);
531
/* Note this in all bindings */
533
all_transfers = g_hash_table_new (g_direct_hash, g_direct_equal);
534
g_hash_table_insert (all_transfers, transfer, transfer);
536
/* Transfer the first time */
537
transfer_fire (object, NULL, transfer);
543
seahorse_bind_disconnect (gpointer what)
545
g_return_if_fail (what);
546
if (all_bindings && g_hash_table_lookup (all_bindings, what))
547
binding_unref ((Binding*)what);
548
else if (all_transfers && g_hash_table_lookup (all_transfers, what))
549
transfer_free ((Transfer*)what);