1 /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GConf
3 * Copyright (C) 1999, 2000, 2000 Red Hat Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "gconf-client.h"
26 #include "gconf/gconf-internals.h"
27
28 #include "gconfmarshal.h"
29 #include "gconfmarshal.c"
30
31 static gboolean do_trace = FALSE;
32
33 static void
trace(const char * format,...)34 trace (const char *format, ...)
35 {
36 va_list args;
37 gchar *str;
38
39 if (!do_trace)
40 return;
41
42 g_return_if_fail (format != NULL);
43
44 va_start (args, format);
45 str = g_strdup_vprintf (format, args);
46 va_end (args);
47
48 g_message ("%s", str);
49
50 g_free (str);
51 }
52
53 /*
54 * Error handler override
55 */
56
57 static GConfClientErrorHandlerFunc global_error_handler = NULL;
58
59 /**
60 * gconf_client_set_global_default_error_handler: (skip)
61 * @func: pointer to the function to be called for error handling.
62 *
63 * Set @func as the default error handler for the #GConfClient. This handler would be called
64 * for all #GConfClient internal errors.
65 */
66 void
gconf_client_set_global_default_error_handler(GConfClientErrorHandlerFunc func)67 gconf_client_set_global_default_error_handler(GConfClientErrorHandlerFunc func)
68 {
69 global_error_handler = func;
70 }
71
72 /*
73 * Dir object (for list of directories we're watching)
74 */
75
76 typedef struct _Dir Dir;
77
78 struct _Dir {
79 gchar* name;
80 guint notify_id;
81 /* number of times this dir has been added */
82 guint add_count;
83 };
84
85 static Dir* dir_new(const gchar* name, guint notify_id);
86 static void dir_destroy(Dir* d);
87
88 /*
89 * Listener object
90 */
91
92 typedef struct _Listener Listener;
93
94 struct _Listener {
95 GConfClientNotifyFunc func;
96 gpointer data;
97 GFreeFunc destroy_notify;
98 };
99
100 static Listener* listener_new(GConfClientNotifyFunc func,
101 GFreeFunc destroy_notify,
102 gpointer data);
103
104 static void listener_destroy(Listener* l);
105
106 /*
107 * GConfClient proper
108 */
109
110 #define PUSH_USE_ENGINE(client) do { if ((client)->engine) gconf_engine_push_owner_usage ((client)->engine, client); } while (0)
111 #define POP_USE_ENGINE(client) do { if ((client)->engine) gconf_engine_pop_owner_usage ((client)->engine, client); } while (0)
112
113 enum {
114 VALUE_CHANGED,
115 UNRETURNED_ERROR,
116 ERROR,
117 LAST_SIGNAL
118 };
119
120 static void register_client (GConfClient *client);
121 static GConfClient *lookup_client (GConfEngine *engine);
122 static void unregister_client (GConfClient *client);
123
124 static void gconf_client_class_init (GConfClientClass *klass);
125 static void gconf_client_init (GConfClient *client);
126 static void gconf_client_real_unreturned_error (GConfClient* client, GError* error);
127 static void gconf_client_real_error (GConfClient* client, GError* error);
128 static void gconf_client_finalize (GObject* object);
129
130 static gboolean gconf_client_cache (GConfClient *client,
131 gboolean take_ownership,
132 GConfEntry *entry,
133 gboolean preserve_schema_name);
134
135 static gboolean gconf_client_lookup (GConfClient *client,
136 const char *key,
137 GConfEntry **entryp);
138
139 static void gconf_client_real_remove_dir (GConfClient* client,
140 Dir* d,
141 GError** err);
142
143 static void gconf_client_queue_notify (GConfClient *client,
144 const char *key);
145 static void gconf_client_flush_notifies (GConfClient *client);
146 static void gconf_client_unqueue_notifies (GConfClient *client);
147 static void notify_one_entry (GConfClient *client, GConfEntry *entry);
148
149 static GConfEntry* get (GConfClient *client,
150 const gchar *key,
151 gboolean use_default,
152 GError **error);
153
154
155 static gpointer parent_class = NULL;
156 static guint client_signals[LAST_SIGNAL] = { 0 };
157
158 GType
gconf_client_get_type(void)159 gconf_client_get_type (void)
160 {
161 static GType client_type = 0;
162
163 if (!client_type)
164 {
165 static const GTypeInfo client_info =
166 {
167 sizeof (GConfClientClass),
168 (GBaseInitFunc) NULL,
169 (GBaseFinalizeFunc) NULL,
170 (GClassInitFunc) gconf_client_class_init,
171 NULL, /* class_finalize */
172 NULL, /* class_data */
173 sizeof (GConfClient),
174 0, /* n_preallocs */
175 (GInstanceInitFunc) gconf_client_init
176 };
177
178 client_type = g_type_register_static (G_TYPE_OBJECT,
179 "GConfClient",
180 &client_info,
181 0);
182 }
183
184 return client_type;
185 }
186
187 static void
gconf_client_class_init(GConfClientClass * class)188 gconf_client_class_init (GConfClientClass *class)
189 {
190 GObjectClass *object_class;
191
192 object_class = (GObjectClass*) class;
193
194 parent_class = g_type_class_peek_parent (class);
195
196 client_signals[VALUE_CHANGED] =
197 g_signal_new ("value_changed",
198 G_TYPE_FROM_CLASS (object_class),
199 G_SIGNAL_RUN_LAST,
200 G_STRUCT_OFFSET (GConfClientClass, value_changed),
201 NULL, NULL,
202 gconf_marshal_VOID__STRING_POINTER,
203 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
204
205 client_signals[UNRETURNED_ERROR] =
206 g_signal_new ("unreturned_error",
207 G_TYPE_FROM_CLASS (object_class),
208 G_SIGNAL_RUN_LAST,
209 G_STRUCT_OFFSET (GConfClientClass, unreturned_error),
210 NULL, NULL,
211 gconf_marshal_VOID__POINTER,
212 G_TYPE_NONE, 1, G_TYPE_POINTER);
213
214 client_signals[ERROR] =
215 g_signal_new ("error",
216 G_TYPE_FROM_CLASS (object_class),
217 G_SIGNAL_RUN_LAST,
218 G_STRUCT_OFFSET (GConfClientClass, error),
219 NULL, NULL,
220 gconf_marshal_VOID__POINTER,
221 G_TYPE_NONE, 1, G_TYPE_POINTER);
222
223 class->value_changed = NULL;
224 class->unreturned_error = gconf_client_real_unreturned_error;
225 class->error = gconf_client_real_error;
226
227 object_class->finalize = gconf_client_finalize;
228
229 if (g_getenv ("GCONF_DEBUG_TRACE_CLIENT") != NULL)
230 do_trace = TRUE;
231 }
232
233 static void
gconf_client_init(GConfClient * client)234 gconf_client_init (GConfClient *client)
235 {
236 client->engine = NULL;
237 client->error_mode = GCONF_CLIENT_HANDLE_UNRETURNED;
238 client->dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
239 client->cache_hash = g_hash_table_new (g_str_hash, g_str_equal);
240 client->cache_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
241 g_free, NULL);
242 client->cache_recursive_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
243 g_free, NULL);
244 /* We create the listeners only if they're actually used */
245 client->listeners = NULL;
246 client->notify_list = NULL;
247 client->notify_handler = 0;
248 }
249
250 static gboolean
destroy_dir_foreach_remove(gpointer key,gpointer value,gpointer user_data)251 destroy_dir_foreach_remove(gpointer key, gpointer value, gpointer user_data)
252 {
253 GConfClient *client = user_data;
254 Dir* d = value;
255
256 /* remove notify for this dir */
257
258 if (d->notify_id != 0)
259 {
260 trace ("REMOTED: Removing notify ID %u from engine", d->notify_id);
261 PUSH_USE_ENGINE (client);
262 gconf_engine_notify_remove (client->engine, d->notify_id);
263 POP_USE_ENGINE (client);
264 }
265
266 d->notify_id = 0;
267
268 dir_destroy(value);
269
270 return TRUE;
271 }
272
273 static void
set_engine(GConfClient * client,GConfEngine * engine)274 set_engine (GConfClient *client,
275 GConfEngine *engine)
276 {
277 if (engine == client->engine)
278 return;
279
280 if (engine)
281 {
282 gconf_engine_ref (engine);
283
284 gconf_engine_set_owner (engine, client);
285 }
286
287 if (client->engine)
288 {
289 gconf_engine_set_owner (client->engine, NULL);
290
291 gconf_engine_unref (client->engine);
292 }
293
294 client->engine = engine;
295 }
296
297 static void
gconf_client_finalize(GObject * object)298 gconf_client_finalize (GObject* object)
299 {
300 GConfClient* client = GCONF_CLIENT(object);
301
302 gconf_client_unqueue_notifies (client);
303
304 g_hash_table_foreach_remove (client->dir_hash,
305 destroy_dir_foreach_remove, client);
306
307 gconf_client_clear_cache (client);
308
309 if (client->listeners != NULL)
310 {
311 gconf_listeners_free (client->listeners);
312 client->listeners = NULL;
313 }
314
315 g_hash_table_destroy (client->dir_hash);
316 client->dir_hash = NULL;
317
318 g_hash_table_destroy (client->cache_hash);
319 client->cache_hash = NULL;
320
321 g_hash_table_destroy (client->cache_recursive_dirs);
322 client->cache_recursive_dirs = NULL;
323
324 g_hash_table_destroy (client->cache_dirs);
325 client->cache_dirs = NULL;
326
327 unregister_client (client);
328
329 set_engine (client, NULL);
330
331 if (G_OBJECT_CLASS (parent_class)->finalize)
332 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
333 }
334
335 /*
336 * Default error handlers
337 */
338
339 static void
gconf_client_real_unreturned_error(GConfClient * client,GError * error)340 gconf_client_real_unreturned_error (GConfClient* client, GError* error)
341 {
342 trace ("Unreturned error '%s'", error->message);
343 if (client->error_mode == GCONF_CLIENT_HANDLE_UNRETURNED)
344 {
345 if (global_error_handler != NULL)
346 {
347 (*global_error_handler) (client, error);
348 }
349 else
350 {
351 /* We silently ignore this, since it probably isn't
352 * really an error per se
353 */
354 if (error->code == GCONF_ERROR_OVERRIDDEN ||
355 error->code == GCONF_ERROR_NO_WRITABLE_DATABASE)
356 return;
357
358 g_printerr (_("GConf Error: %s\n"),
359 error->message);
360 }
361 }
362 }
363
364 static void
gconf_client_real_error(GConfClient * client,GError * error)365 gconf_client_real_error (GConfClient* client, GError* error)
366 {
367 trace ("Error '%s'", error->message);
368 if (client->error_mode == GCONF_CLIENT_HANDLE_ALL)
369 {
370 if (global_error_handler != NULL)
371 {
372 (*global_error_handler) (client, error);
373 }
374 else
375 {
376 g_printerr (_("GConf Error: %s\n"),
377 error->message);
378 }
379 }
380 }
381
382 /* Emit the proper signals for the error, and fill in err */
383 static gboolean
handle_error(GConfClient * client,GError * error,GError ** err)384 handle_error(GConfClient* client, GError* error, GError** err)
385 {
386 if (error != NULL)
387 {
388 gconf_client_error(client, error);
389
390 if (err == NULL)
391 {
392 gconf_client_unreturned_error(client, error);
393
394 g_error_free(error);
395 }
396 else
397 *err = error;
398
399 return TRUE;
400 }
401 else
402 return FALSE;
403 }
404
405 static void
notify_from_server_callback(GConfEngine * conf,guint cnxn_id,GConfEntry * entry,gpointer user_data)406 notify_from_server_callback (GConfEngine* conf, guint cnxn_id,
407 GConfEntry *entry,
408 gpointer user_data)
409 {
410 GConfClient* client = user_data;
411 gboolean changed;
412
413 g_return_if_fail (client != NULL);
414 g_return_if_fail (GCONF_IS_CLIENT(client));
415 g_return_if_fail (client->engine == conf);
416
417 trace ("Received notify of change to '%s' from server",
418 entry->key);
419
420 /* First do the caching, so that state is sane for the
421 * listeners or functions connected to value_changed.
422 * We know this key is under a directory in our dir list.
423 */
424 changed = gconf_client_cache (client, FALSE, entry, TRUE);
425
426 if (!changed)
427 return; /* don't do the notify */
428
429 gconf_client_queue_notify (client, entry->key);
430 }
431
432 /*
433 * Public API
434 */
435
436
437 /**
438 * gconf_client_get_default:
439 *
440 * Creates a new #GConfClient using the default #GConfEngine. Normally this is the
441 * engine you want. If someone else is already using the default
442 * #GConfClient, this function returns the same one they're using, but
443 * with the reference count incremented. So you have to unref either way.
444 *
445 * It's important to call g_type_init() before using this GObject, to initialize the type system.
446 *
447 * Return value: (transfer full): a new #GConfClient. g_object_unref() when you're done.
448 */
449 GConfClient*
gconf_client_get_default(void)450 gconf_client_get_default (void)
451 {
452 GConfClient *client;
453 GConfEngine *engine;
454
455 g_return_val_if_fail(gconf_is_initialized(), NULL);
456
457 engine = gconf_engine_get_default ();
458
459 client = lookup_client (engine);
460 if (client)
461 {
462 g_assert (client->engine == engine);
463 g_object_ref (G_OBJECT (client));
464 gconf_engine_unref (engine);
465 return client;
466 }
467 else
468 {
469 client = g_object_new (gconf_client_get_type (), NULL);
470 g_object_ref (G_OBJECT (client));
471 set_engine (client, engine);
472 register_client (client);
473 }
474
475 return client;
476 }
477
478 /**
479 * gconf_client_get_for_engine:
480 * @engine: the #GConfEngine to use.
481 *
482 * Creates a new #GConfClient with a specific #GConfEngine. Only specialized
483 * configuration-related programs should need to call this function. The
484 * returned #GConfClient should be unref'd when you're done with g_object_unref().
485 * Remember to avoid using the #GConfEngine directly once you have a #GConfClient
486 * wrapper.
487 *
488 * Return value: (transfer full): a new #GConfClient.
489 */
490 GConfClient*
gconf_client_get_for_engine(GConfEngine * engine)491 gconf_client_get_for_engine (GConfEngine* engine)
492 {
493 GConfClient *client;
494
495 g_return_val_if_fail(gconf_is_initialized(), NULL);
496
497 client = lookup_client (engine);
498 if (client)
499 {
500 g_assert (client->engine == engine);
501 g_object_ref (G_OBJECT (client));
502 return client;
503 }
504 else
505 {
506 client = g_object_new (gconf_client_get_type (), NULL);
507
508 set_engine (client, engine);
509
510 register_client (client);
511 }
512
513 return client;
514 }
515
516 typedef struct {
517 GConfClient *client;
518 Dir *lower_dir;
519 const char *dirname;
520 } OverlapData;
521
522 static void
foreach_setup_overlap(gpointer key,gpointer value,gpointer user_data)523 foreach_setup_overlap(gpointer key, gpointer value, gpointer user_data)
524 {
525 GConfClient *client;
526 Dir *dir = value;
527 OverlapData * od = user_data;
528
529 client = od->client;
530
531 /* if we have found the first (well there is only one anyway) directory
532 * that includes us that has a notify handler
533 */
534 #ifdef GCONF_ENABLE_DEBUG
535 if (dir->notify_id != 0 &&
536 gconf_key_is_below(dir->name, od->dirname))
537 {
538 g_assert(od->lower_dir == NULL);
539 od->lower_dir = dir;
540 }
541 #else
542 if (od->lower_dir == NULL &&
543 dir->notify_id != 0 &&
544 gconf_key_is_below(dir->name, od->dirname))
545 od->lower_dir = dir;
546 #endif
547 /* if we have found a directory that we include and it has
548 * a notify_id, remove the notify handler now
549 * FIXME: this is a race, from now on we can miss notifies, it is
550 * not an incredible amount of time so this is not a showstopper */
551 else if (dir->notify_id != 0 &&
552 gconf_key_is_below (od->dirname, dir->name))
553 {
554 PUSH_USE_ENGINE (client);
555 gconf_engine_notify_remove (client->engine, dir->notify_id);
556 POP_USE_ENGINE (client);
557 dir->notify_id = 0;
558 }
559 }
560
561 static Dir *
setup_overlaps(GConfClient * client,const gchar * dirname)562 setup_overlaps(GConfClient* client, const gchar* dirname)
563 {
564 OverlapData od;
565
566 od.client = client;
567 od.lower_dir = NULL;
568 od.dirname = dirname;
569
570 g_hash_table_foreach(client->dir_hash, foreach_setup_overlap, &od);
571
572 return od.lower_dir;
573 }
574
575 void
gconf_client_add_dir(GConfClient * client,const gchar * dirname,GConfClientPreloadType preload,GError ** err)576 gconf_client_add_dir (GConfClient* client,
577 const gchar* dirname,
578 GConfClientPreloadType preload,
579 GError** err)
580 {
581 Dir* d;
582 guint notify_id = 0;
583 GError* error = NULL;
584
585 g_return_if_fail (gconf_valid_key (dirname, NULL));
586
587 trace ("Adding directory '%s'", dirname);
588
589 d = g_hash_table_lookup (client->dir_hash, dirname);
590
591 if (d == NULL)
592 {
593 Dir *overlap_dir;
594
595 overlap_dir = setup_overlaps (client, dirname);
596
597 /* only if there is no directory that includes us
598 * already add a notify
599 */
600 if (overlap_dir == NULL)
601 {
602 trace ("REMOTE: Adding notify to engine at '%s'",
603 dirname);
604 PUSH_USE_ENGINE (client);
605 notify_id = gconf_engine_notify_add (client->engine,
606 dirname,
607 notify_from_server_callback,
608 client,
609 &error);
610 POP_USE_ENGINE (client);
611
612 /* We got a notify ID or we got an error, not both */
613 g_return_if_fail ( (notify_id != 0 && error == NULL) ||
614 (notify_id == 0 && error != NULL) );
615
616
617 if (handle_error (client, error, err))
618 return;
619
620 g_assert (error == NULL);
621 }
622 else
623 {
624 notify_id = 0;
625 }
626
627 d = dir_new (dirname, notify_id);
628
629 g_hash_table_insert (client->dir_hash, d->name, d);
630
631 gconf_client_preload (client, dirname, preload, &error);
632
633 handle_error (client, error, err);
634 }
635
636 g_assert (d != NULL);
637
638 d->add_count += 1;
639 }
640
641 typedef struct {
642 GConfClient *client;
643 GError *error;
644 } AddNotifiesData;
645
646 static void
foreach_add_notifies(gpointer key,gpointer value,gpointer user_data)647 foreach_add_notifies(gpointer key, gpointer value, gpointer user_data)
648 {
649 AddNotifiesData *ad = user_data;
650 GConfClient *client;
651 Dir *dir = value;
652
653 client = ad->client;
654
655 if (ad->error != NULL)
656 return;
657
658 if (dir->notify_id == 0)
659 {
660 Dir *overlap_dir;
661 overlap_dir = setup_overlaps(client, dir->name);
662
663 /* only if there is no directory that includes us
664 * already add a notify */
665 if (overlap_dir == NULL)
666 {
667 trace ("REMOTE: Adding notify to engine at '%s'",
668 dir->name);
669 PUSH_USE_ENGINE (client);
670 dir->notify_id = gconf_engine_notify_add(client->engine,
671 dir->name,
672 notify_from_server_callback,
673 client,
674 &ad->error);
675 POP_USE_ENGINE (client);
676
677 /* We got a notify ID or we got an error, not both */
678 g_return_if_fail( (dir->notify_id != 0 && ad->error == NULL) ||
679 (dir->notify_id == 0 && ad->error != NULL) );
680
681 /* if error is returned, then we'll just ignore
682 * things until the end */
683 }
684 }
685 }
686
687 static gboolean
clear_dir_cache_foreach(char * key,GConfEntry * entry,char * dir)688 clear_dir_cache_foreach (char* key, GConfEntry* entry, char *dir)
689 {
690 if (gconf_key_is_below (dir, key))
691 {
692 gconf_entry_free (entry);
693 return TRUE;
694 }
695 else
696 return FALSE;
697 }
698
699 static gboolean
clear_cache_dirs_foreach(char * key,gpointer value,char * dir)700 clear_cache_dirs_foreach (char *key, gpointer value, char *dir)
701 {
702 if (strcmp (dir, key) == 0 ||
703 gconf_key_is_below (dir, key))
704 {
705 trace ("'%s' no longer fully cached", dir);
706 return TRUE;
707 }
708
709 return FALSE;
710 }
711
712 static void
gconf_client_real_remove_dir(GConfClient * client,Dir * d,GError ** err)713 gconf_client_real_remove_dir (GConfClient* client,
714 Dir* d,
715 GError** err)
716 {
717 AddNotifiesData ad;
718
719 g_return_if_fail(d != NULL);
720 g_return_if_fail(d->add_count == 0);
721
722 g_hash_table_remove(client->dir_hash, d->name);
723
724 /* remove notify for this dir */
725
726 if (d->notify_id != 0)
727 {
728 trace ("REMOTE: Removing notify from engine at '%s'", d->name);
729 PUSH_USE_ENGINE (client);
730 gconf_engine_notify_remove (client->engine, d->notify_id);
731 POP_USE_ENGINE (client);
732 d->notify_id = 0;
733 }
734
735 g_hash_table_foreach_remove (client->cache_hash,
736 (GHRFunc)clear_dir_cache_foreach,
737 d->name);
738 g_hash_table_foreach_remove (client->cache_dirs,
739 (GHRFunc)clear_cache_dirs_foreach,
740 d->name);
741 dir_destroy(d);
742
743 ad.client = client;
744 ad.error = NULL;
745
746 g_hash_table_foreach(client->dir_hash, foreach_add_notifies, &ad);
747
748 handle_error(client, ad.error, err);
749 }
750
751 void
gconf_client_remove_dir(GConfClient * client,const gchar * dirname,GError ** err)752 gconf_client_remove_dir (GConfClient* client,
753 const gchar* dirname,
754 GError** err)
755 {
756 Dir* found = NULL;
757
758 trace ("Removing directory '%s'", dirname);
759
760 found = g_hash_table_lookup (client->dir_hash,
761 dirname);
762
763 if (found != NULL)
764 {
765 g_return_if_fail(found->add_count > 0);
766
767 found->add_count -= 1;
768
769 if (found->add_count == 0)
770 gconf_client_real_remove_dir (client, found, err);
771 }
772 #ifndef G_DISABLE_CHECKS
773 else
774 g_warning("Directory `%s' was not being monitored by GConfClient %p",
775 dirname, client);
776 #endif
777 }
778
779 /**
780 * gconf_client_notify_add:
781 * @client:
782 * @namespace_section
783 * @func: (scope notified) (closure user_data) (destroy destroy_notify):
784 * @user_data:
785 * @destroy_notify:
786 * @err:
787 */
788 guint
gconf_client_notify_add(GConfClient * client,const gchar * namespace_section,GConfClientNotifyFunc func,gpointer user_data,GFreeFunc destroy_notify,GError ** err)789 gconf_client_notify_add (GConfClient* client,
790 const gchar* namespace_section,
791 GConfClientNotifyFunc func,
792 gpointer user_data,
793 GFreeFunc destroy_notify,
794 GError** err)
795 {
796 guint cnxn_id = 0;
797
798 g_return_val_if_fail(client != NULL, 0);
799 g_return_val_if_fail(GCONF_IS_CLIENT(client), 0);
800
801 if (client->listeners == NULL)
802 client->listeners = gconf_listeners_new();
803
804 cnxn_id = gconf_listeners_add (client->listeners,
805 namespace_section,
806 listener_new (func, destroy_notify, user_data),
807 (GFreeFunc)listener_destroy);
808
809 return cnxn_id;
810 }
811
812 void
gconf_client_notify_remove(GConfClient * client,guint cnxn)813 gconf_client_notify_remove (GConfClient* client,
814 guint cnxn)
815 {
816 g_return_if_fail(client != NULL);
817 g_return_if_fail(GCONF_IS_CLIENT(client));
818 g_return_if_fail(client->listeners != NULL);
819
820 gconf_listeners_remove(client->listeners, cnxn);
821
822 if (gconf_listeners_count(client->listeners) == 0)
823 {
824 gconf_listeners_free(client->listeners);
825 client->listeners = NULL;
826 }
827 }
828
829 void
gconf_client_notify(GConfClient * client,const char * key)830 gconf_client_notify (GConfClient* client, const char* key)
831 {
832 GConfEntry *entry;
833
834 g_return_if_fail (client != NULL);
835 g_return_if_fail (GCONF_IS_CLIENT(client));
836 g_return_if_fail (key != NULL);
837
838 entry = gconf_client_get_entry (client, key, NULL, TRUE, NULL);
839 if (entry != NULL)
840 {
841 notify_one_entry (client, entry);
842 gconf_entry_unref (entry);
843 }
844 }
845
846 void
gconf_client_set_error_handling(GConfClient * client,GConfClientErrorHandlingMode mode)847 gconf_client_set_error_handling(GConfClient* client,
848 GConfClientErrorHandlingMode mode)
849 {
850 g_return_if_fail(client != NULL);
851 g_return_if_fail(GCONF_IS_CLIENT(client));
852
853 client->error_mode = mode;
854 }
855
856 static gboolean
clear_cache_foreach(char * key,GConfEntry * entry,GConfClient * client)857 clear_cache_foreach (char* key, GConfEntry* entry, GConfClient* client)
858 {
859 gconf_entry_free (entry);
860
861 return TRUE;
862 }
863
864 void
gconf_client_clear_cache(GConfClient * client)865 gconf_client_clear_cache(GConfClient* client)
866 {
867 g_return_if_fail(client != NULL);
868 g_return_if_fail(GCONF_IS_CLIENT(client));
869
870 trace ("Clearing cache");
871
872 g_hash_table_foreach_remove (client->cache_hash, (GHRFunc)clear_cache_foreach,
873 client);
874
875 g_hash_table_remove_all (client->cache_dirs);
876 }
877
878 static void
879 cache_pairs_in_dir(GConfClient* client, const gchar* path, gboolean recursive);
880
881 static void
recurse_subdir_list(GConfClient * client,GSList * subdirs)882 recurse_subdir_list(GConfClient* client, GSList* subdirs)
883 {
884 GSList* tmp;
885
886 tmp = subdirs;
887
888 while (tmp != NULL)
889 {
890 gchar* s = tmp->data;
891
892 cache_pairs_in_dir(client, s, TRUE);
893
894 trace ("REMOTE: All dirs at '%s'", s);
895 PUSH_USE_ENGINE (client);
896 recurse_subdir_list(client,
897 gconf_engine_all_dirs (client->engine, s, NULL));
898 POP_USE_ENGINE (client);
899
900 g_free(s);
901
902 tmp = g_slist_next(tmp);
903 }
904
905 g_slist_free(subdirs);
906 }
907
908 static gboolean
key_being_monitored(GConfClient * client,const char * key)909 key_being_monitored (GConfClient *client,
910 const char *key)
911 {
912 gboolean retval = FALSE;
913 char* parent = g_strdup (key);
914 char* end;
915
916 end = parent + strlen (parent);
917
918 while (end)
919 {
920 if (end == parent)
921 *(end + 1) = '\0'; /* special-case "/" root dir */
922 else
923 *end = '\0'; /* chop '/' off of dir */
924
925 if (g_hash_table_lookup (client->dir_hash, parent) != NULL)
926 {
927 retval = TRUE;
928 break;
929 }
930
931 if (end != parent)
932 end = strrchr (parent, '/');
933 else
934 end = NULL;
935 }
936
937 g_free (parent);
938
939 return retval;
940 }
941
942 static void
cache_entry_list_destructively(GConfClient * client,GSList * entries)943 cache_entry_list_destructively (GConfClient *client,
944 GSList *entries)
945 {
946 GSList *tmp;
947
948 tmp = entries;
949
950 while (tmp != NULL)
951 {
952 GConfEntry* entry = tmp->data;
953
954 gconf_client_cache (client, TRUE, entry, FALSE);
955
956 tmp = g_slist_next (tmp);
957 }
958
959 g_slist_free (entries);
960 }
961
962 static void
cache_pairs_in_dir(GConfClient * client,const gchar * dir,gboolean recursive)963 cache_pairs_in_dir(GConfClient* client, const gchar* dir, gboolean recursive)
964 {
965 GSList* pairs;
966 GError* error = NULL;
967
968 trace ("REMOTE: Caching values in '%s'", dir);
969
970 PUSH_USE_ENGINE (client);
971 pairs = gconf_engine_all_entries(client->engine, dir, &error);
972 POP_USE_ENGINE (client);
973
974 if (error != NULL)
975 {
976 g_printerr (_("GConf warning: failure listing pairs in `%s': %s"),
977 dir, error->message);
978 g_error_free(error);
979 error = NULL;
980 }
981
982 cache_entry_list_destructively (client, pairs);
983 trace ("Mark '%s' as fully cached", dir);
984 g_hash_table_insert (client->cache_dirs, g_strdup (dir), GINT_TO_POINTER (1));
985
986 if (recursive)
987 g_hash_table_insert (client->cache_recursive_dirs, g_strdup (dir), GINT_TO_POINTER (1));
988 }
989
990 void
gconf_client_preload(GConfClient * client,const gchar * dirname,GConfClientPreloadType type,GError ** err)991 gconf_client_preload (GConfClient* client,
992 const gchar* dirname,
993 GConfClientPreloadType type,
994 GError** err)
995 {
996
997 g_return_if_fail(client != NULL);
998 g_return_if_fail(GCONF_IS_CLIENT(client));
999 g_return_if_fail(dirname != NULL);
1000
1001 #ifdef GCONF_ENABLE_DEBUG
1002 if (!key_being_monitored (client, dirname))
1003 {
1004 g_warning("Can only preload directories you've added with gconf_client_add_dir() (tried to preload '%s')",
1005 dirname);
1006 return;
1007 }
1008 #endif
1009
1010 switch (type)
1011 {
1012 case GCONF_CLIENT_PRELOAD_NONE:
1013 /* nothing */
1014 break;
1015
1016 case GCONF_CLIENT_PRELOAD_ONELEVEL:
1017 {
1018 trace ("Onelevel preload of '%s'", dirname);
1019
1020 cache_pairs_in_dir (client, dirname, FALSE);
1021 }
1022 break;
1023
1024 case GCONF_CLIENT_PRELOAD_RECURSIVE:
1025 {
1026 GSList* subdirs;
1027
1028 trace ("Recursive preload of '%s'", dirname);
1029
1030 trace ("REMOTE: All dirs at '%s'", dirname);
1031 PUSH_USE_ENGINE (client);
1032 subdirs = gconf_engine_all_dirs(client->engine, dirname, NULL);
1033 POP_USE_ENGINE (client);
1034
1035 cache_pairs_in_dir(client, dirname, TRUE);
1036
1037 recurse_subdir_list(client, subdirs);
1038 }
1039 break;
1040
1041 default:
1042 g_assert_not_reached();
1043 break;
1044 }
1045 }
1046
1047 #ifdef HAVE_DBUS
1048
1049 /*
1050 * Add functions for manipulating the internal cache of values - under the
1051 * D-BUS environment we update the internal cache when changes happen to
1052 * ensure a consistent state
1053 */
1054 static gboolean
remove_key_from_cache_foreach(const gchar * cached_key,GConfEntry * entry,const gchar * key)1055 remove_key_from_cache_foreach (const gchar *cached_key,
1056 GConfEntry *entry,
1057 const gchar *key)
1058 {
1059 if (strcmp (cached_key, key) == 0)
1060 {
1061 gconf_entry_free (entry);
1062 return TRUE;
1063 }
1064
1065 return FALSE;
1066 }
1067
1068 static gboolean
remove_key_from_cache_recursively_foreach(const gchar * cached_key,GConfEntry * entry,const gchar * key)1069 remove_key_from_cache_recursively_foreach (const gchar *cached_key,
1070 GConfEntry *entry,
1071 const gchar *key)
1072 {
1073 if (gconf_key_is_below (cached_key, key) == 0 || strcmp (cached_key, key) == 0)
1074 {
1075 /* Not sure why a null entry would be in the cache, but it happens
1076 * sometimes, workaround for now to avoid crashing.
1077 */
1078 if (entry)
1079 gconf_entry_free (entry);
1080 return TRUE;
1081 }
1082
1083 return FALSE;
1084 }
1085
1086 static gboolean
remove_dir_from_cache_foreach(char * cached_dir,gpointer value,char * dir)1087 remove_dir_from_cache_foreach (char *cached_dir,
1088 gpointer value,
1089 char *dir)
1090 {
1091 if (strcmp (dir, cached_dir) == 0)
1092 {
1093 trace ("'%s' no longer fully cached", dir);
1094 return TRUE;
1095 }
1096
1097 return FALSE;
1098 }
1099
1100 /* The dbus version cleans the cache after modifying a value. So we will
1101 * remove current dir (where the key is in) from the cache_dirs
1102 * when removing the key from the cache_hash.
1103 * This is a workaround. It will degrade the performance of querying negative keys in the same dir.
1104 */
remove_dir_from_cache(GConfClient * client,const gchar * key)1105 static void remove_dir_from_cache (GConfClient *client,
1106 const gchar *key)
1107 {
1108 char *dir, *last_slash;
1109 dir = g_strdup (key);
1110 last_slash = strrchr (dir, '/');
1111 g_assert (last_slash != NULL);
1112 *last_slash = 0;
1113 trace ("Remove dir '%s' from cache since one of keys is changed", dir);
1114 g_hash_table_foreach_remove (client->cache_dirs,
1115 (GHRFunc) remove_dir_from_cache_foreach,
1116 dir);
1117 g_free (dir);
1118 }
1119
1120 static void
remove_key_from_cache(GConfClient * client,const gchar * key)1121 remove_key_from_cache (GConfClient *client,
1122 const gchar *key)
1123 {
1124 g_hash_table_foreach_remove (client->cache_hash,
1125 (GHRFunc) remove_key_from_cache_foreach,
1126 (char *) key);
1127 remove_dir_from_cache (client, key);
1128 }
1129
1130 static void
remove_key_from_cache_recursively(GConfClient * client,const gchar * key)1131 remove_key_from_cache_recursively (GConfClient *client,
1132 const gchar *key)
1133 {
1134 g_hash_table_foreach_remove (client->cache_hash,
1135 (GHRFunc) remove_key_from_cache_recursively_foreach,
1136 (char *) key);
1137 remove_dir_from_cache (client, key);
1138 }
1139
1140 static gboolean
cache_key_value(GConfClient * client,const gchar * key,const GConfValue * val)1141 cache_key_value (GConfClient *client,
1142 const gchar *key,
1143 const GConfValue *val)
1144 {
1145 GConfEntry *entry;
1146
1147 entry = gconf_entry_new (key, val);
1148 return gconf_client_cache (client, TRUE, entry, TRUE);
1149 }
1150
1151 static void
cache_key_value_and_notify(GConfClient * client,const gchar * key,GConfValue * val,gboolean free_value)1152 cache_key_value_and_notify (GConfClient *client,
1153 const gchar *key,
1154 GConfValue *val,
1155 gboolean free_value)
1156 {
1157 if (cache_key_value (client, key, val))
1158 {
1159 if (key_being_monitored (client, key))
1160 gconf_client_queue_notify (client, key);
1161 }
1162
1163 if (free_value)
1164 gconf_value_free (val);
1165 }
1166
1167 #endif
1168
1169 /*
1170 * Basic key-manipulation facilities
1171 */
1172
1173 void
gconf_client_set(GConfClient * client,const gchar * key,const GConfValue * val,GError ** err)1174 gconf_client_set (GConfClient* client,
1175 const gchar* key,
1176 const GConfValue* val,
1177 GError** err)
1178 {
1179 GError* error = NULL;
1180
1181 trace ("REMOTE: Setting value of '%s'", key);
1182 PUSH_USE_ENGINE (client);
1183 gconf_engine_set (client->engine, key, val, &error);
1184 POP_USE_ENGINE (client);
1185
1186 #ifdef HAVE_DBUS
1187 if (!error)
1188 cache_key_value_and_notify (client, key, (GConfValue *) val, FALSE);
1189 #endif
1190
1191 handle_error(client, error, err);
1192 }
1193
1194 gboolean
gconf_client_unset(GConfClient * client,const gchar * key,GError ** err)1195 gconf_client_unset (GConfClient* client,
1196 const gchar* key, GError** err)
1197 {
1198 GError* error = NULL;
1199
1200 trace ("REMOTE: Unsetting '%s'", key);
1201 PUSH_USE_ENGINE (client);
1202 gconf_engine_unset(client->engine, key, &error);
1203 POP_USE_ENGINE (client);
1204
1205 #ifdef HAVE_DBUS
1206 if (!error)
1207 remove_key_from_cache (client, key);
1208 #endif
1209
1210 handle_error(client, error, err);
1211
1212 if (error != NULL)
1213 return FALSE;
1214 else
1215 return TRUE;
1216 }
1217
1218 gboolean
gconf_client_recursive_unset(GConfClient * client,const char * key,GConfUnsetFlags flags,GError ** err)1219 gconf_client_recursive_unset (GConfClient *client,
1220 const char *key,
1221 GConfUnsetFlags flags,
1222 GError **err)
1223 {
1224 GError* error = NULL;
1225
1226 trace ("REMOTE: Recursive unsetting '%s'", key);
1227
1228 PUSH_USE_ENGINE (client);
1229 gconf_engine_recursive_unset(client->engine, key, flags, &error);
1230 POP_USE_ENGINE (client);
1231
1232 #ifdef HAVE_DBUS
1233 if (!error)
1234 remove_key_from_cache_recursively (client, key);
1235 #endif
1236
1237 handle_error(client, error, err);
1238
1239 if (error != NULL)
1240 return FALSE;
1241 else
1242 return TRUE;
1243 }
1244
1245 static GSList*
copy_entry_list(GSList * list)1246 copy_entry_list (GSList *list)
1247 {
1248 GSList *copy;
1249 GSList *tmp;
1250
1251 copy = NULL;
1252 tmp = list;
1253 while (tmp != NULL)
1254 {
1255 copy = g_slist_prepend (copy,
1256 gconf_entry_copy (tmp->data));
1257 tmp = tmp->next;
1258 }
1259
1260 copy = g_slist_reverse (copy);
1261
1262 return copy;
1263 }
1264
1265 /**
1266 * gconf_client_all_entries:
1267 * @client: a #GConfClient.
1268 * @dir: directory to list.
1269 * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1270 *
1271 * Lists the key-value pairs in @dir. Does not list subdirectories; for
1272 * that use gconf_client_all_dirs(). The returned list contains #GConfEntry
1273 * objects. A #GConfEntry contains an <emphasis>absolute</emphasis> key
1274 * and a value. The list is not recursive, it contains only the immediate
1275 * children of @dir. To free the returned list, gconf_entry_free()
1276 * each list element, then g_slist_free() the list itself.
1277 * Just like gconf_engine_all_entries (), but uses #GConfClient caching and error-handling features.
1278 *
1279 * Return value: (element-type GConfEntry) (transfer full): List of #GConfEntry.
1280 */
1281 GSList*
gconf_client_all_entries(GConfClient * client,const gchar * dir,GError ** err)1282 gconf_client_all_entries (GConfClient* client,
1283 const gchar* dir,
1284 GError** err)
1285 {
1286 GError *error = NULL;
1287 GSList *retval;
1288 int dirlen;
1289
1290 if (g_hash_table_lookup (client->cache_dirs, dir))
1291 {
1292 GHashTableIter iter;
1293 gpointer key, value;
1294
1295 trace ("CACHED: Getting all values in '%s'", dir);
1296
1297 dirlen = strlen (dir);
1298 retval = NULL;
1299 g_hash_table_iter_init (&iter, client->cache_hash);
1300 while (g_hash_table_iter_next (&iter, &key, &value))
1301 {
1302 const gchar *id = key;
1303 GConfEntry *entry = value;
1304 if (g_str_has_prefix (id, dir) &&
1305 id + dirlen == strrchr (id, '/'))
1306 retval = g_slist_prepend (retval, gconf_entry_copy (entry));
1307 }
1308
1309 return retval;
1310 }
1311
1312 trace ("REMOTE: Getting all values in '%s'", dir);
1313
1314 PUSH_USE_ENGINE (client);
1315 retval = gconf_engine_all_entries (client->engine, dir, &error);
1316 POP_USE_ENGINE (client);
1317
1318 handle_error (client, error, err);
1319
1320 if (error != NULL)
1321 return NULL;
1322
1323 if (key_being_monitored (client, dir))
1324 {
1325 cache_entry_list_destructively (client, copy_entry_list (retval));
1326 trace ("Mark '%s' as fully cached", dir);
1327 g_hash_table_insert (client->cache_dirs, g_strdup (dir), GINT_TO_POINTER (1));
1328 }
1329
1330 return retval;
1331 }
1332
1333 /**
1334 * gconf_client_all_dirs:
1335 * @client: a #GConfClient.
1336 * @dir: directory to get subdirectories from.
1337 * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1338 *
1339 * Lists the subdirectories in @dir. The returned list contains
1340 * allocated strings. Each string is the absolute path of a
1341 * subdirectory. You should g_free() each string in the list, then
1342 * g_slist_free() the list itself. Just like gconf_engine_all_dirs(),
1343 * but uses #GConfClient caching and error-handling features.
1344 *
1345 * Return value: (element-type utf8) (transfer full): List of allocated subdirectory names.
1346 */
1347 GSList*
gconf_client_all_dirs(GConfClient * client,const gchar * dir,GError ** err)1348 gconf_client_all_dirs (GConfClient* client,
1349 const gchar* dir, GError** err)
1350 {
1351 GError* error = NULL;
1352 GSList* retval;
1353
1354 trace ("REMOTE: Getting all dirs in '%s'", dir);
1355
1356 PUSH_USE_ENGINE (client);
1357 retval = gconf_engine_all_dirs(client->engine, dir, &error);
1358 POP_USE_ENGINE (client);
1359
1360 handle_error(client, error, err);
1361
1362 return retval;
1363 }
1364
1365 void
gconf_client_suggest_sync(GConfClient * client,GError ** err)1366 gconf_client_suggest_sync (GConfClient* client,
1367 GError** err)
1368 {
1369 GError* error = NULL;
1370
1371 trace ("REMOTE: Suggesting sync");
1372
1373 PUSH_USE_ENGINE (client);
1374 gconf_engine_suggest_sync(client->engine, &error);
1375 POP_USE_ENGINE (client);
1376
1377 handle_error(client, error, err);
1378 }
1379
1380 gboolean
gconf_client_dir_exists(GConfClient * client,const gchar * dir,GError ** err)1381 gconf_client_dir_exists(GConfClient* client,
1382 const gchar* dir, GError** err)
1383 {
1384 GError* error = NULL;
1385 gboolean retval;
1386
1387 trace ("REMOTE: Checking whether directory '%s' exists...", dir);
1388
1389 PUSH_USE_ENGINE (client);
1390 retval = gconf_engine_dir_exists (client->engine, dir, &error);
1391 POP_USE_ENGINE (client);
1392
1393 handle_error (client, error, err);
1394
1395 if (retval)
1396 trace ("'%s' exists", dir);
1397 else
1398 trace ("'%s' doesn't exist", dir);
1399
1400 return retval;
1401 }
1402
1403 gboolean
gconf_client_key_is_writable(GConfClient * client,const gchar * key,GError ** err)1404 gconf_client_key_is_writable (GConfClient* client,
1405 const gchar* key,
1406 GError** err)
1407 {
1408 GError* error = NULL;
1409 GConfEntry *entry = NULL;
1410 gboolean is_writable;
1411
1412 g_return_val_if_fail (key != NULL, FALSE);
1413 g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1414
1415 if (gconf_client_lookup (client, key, &entry))
1416 {
1417 if (!entry)
1418 return FALSE;
1419
1420 trace ("CACHED: Checking whether key '%s' is writable", key);
1421 return gconf_entry_get_is_writable (entry);
1422 }
1423
1424 trace ("REMOTE: Checking whether key '%s' is writable", key);
1425
1426 entry = get (client, key, TRUE, &error);
1427
1428 if (entry == NULL && error != NULL)
1429 handle_error (client, error, err);
1430 else
1431 g_assert (error == NULL);
1432
1433 if (entry == NULL)
1434 is_writable = FALSE;
1435 else
1436 is_writable = gconf_entry_get_is_writable (entry);
1437
1438 if (entry)
1439 gconf_entry_free (entry);
1440
1441 return is_writable;
1442 }
1443
1444 static gboolean
check_type(const gchar * key,GConfValue * val,GConfValueType t,GError ** err)1445 check_type(const gchar* key, GConfValue* val, GConfValueType t, GError** err)
1446 {
1447 if (val->type != t)
1448 {
1449 gconf_set_error(err, GCONF_ERROR_TYPE_MISMATCH,
1450 _("Expected `%s' got `%s' for key %s"),
1451 gconf_value_type_to_string(t),
1452 gconf_value_type_to_string(val->type),
1453 key);
1454 return FALSE;
1455 }
1456 else
1457 return TRUE;
1458 }
1459
1460 static GConfEntry*
get(GConfClient * client,const gchar * key,gboolean use_default,GError ** error)1461 get (GConfClient *client,
1462 const gchar *key,
1463 gboolean use_default,
1464 GError **error)
1465 {
1466 GConfEntry *entry = NULL;
1467
1468 g_return_val_if_fail (client != NULL, NULL);
1469 g_return_val_if_fail (GCONF_IS_CLIENT(client), NULL);
1470 g_return_val_if_fail (error != NULL, NULL);
1471 g_return_val_if_fail (*error == NULL, NULL);
1472
1473 /* Check our client-side cache */
1474 if (gconf_client_lookup (client, key, &entry))
1475
1476 {
1477 trace ("CACHED: Query for '%s'", key);
1478
1479 if (entry == NULL)
1480 return NULL;
1481
1482 if (gconf_entry_get_is_default (entry) && !use_default)
1483 return NULL;
1484 else
1485 return gconf_entry_copy (entry);
1486 }
1487
1488 g_assert (entry == NULL); /* if it was in the cache we should have returned */
1489
1490 /* Check the GConfEngine */
1491 trace ("REMOTE: Query for '%s'", key);
1492 PUSH_USE_ENGINE (client);
1493 entry = gconf_engine_get_entry (client->engine, key,
1494 gconf_current_locale(),
1495 TRUE /* always use default here */,
1496 error);
1497 POP_USE_ENGINE (client);
1498
1499 if (*error != NULL)
1500 {
1501 g_return_val_if_fail (entry == NULL, NULL);
1502 return NULL;
1503 }
1504 else
1505 {
1506 g_assert (entry != NULL); /* gconf_engine_get_entry shouldn't return NULL ever */
1507
1508 /* Cache this value, if it's in our directory list. */
1509 if (key_being_monitored (client, key))
1510 {
1511 /* cache a copy of val */
1512 gconf_client_cache (client, FALSE, entry, FALSE);
1513 }
1514
1515 /* We don't own the entry, we're returning this copy belonging
1516 * to the caller
1517 */
1518 if (gconf_entry_get_is_default (entry) && !use_default)
1519 {
1520 gconf_entry_free (entry);
1521 return NULL;
1522 }
1523 else
1524 return entry;
1525 }
1526 }
1527
1528 static GConfValue*
gconf_client_get_full(GConfClient * client,const gchar * key,const gchar * locale,gboolean use_schema_default,GError ** err)1529 gconf_client_get_full (GConfClient* client,
1530 const gchar* key, const gchar* locale,
1531 gboolean use_schema_default,
1532 GError** err)
1533 {
1534 GError* error = NULL;
1535 GConfEntry *entry;
1536 GConfValue *retval;
1537
1538 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1539
1540 if (locale != NULL)
1541 g_warning ("haven't implemented getting a specific locale in GConfClient");
1542
1543 entry = get (client, key, use_schema_default,
1544 &error);
1545
1546 if (entry == NULL && error != NULL)
1547 handle_error(client, error, err);
1548 else
1549 g_assert (error == NULL);
1550
1551 retval = NULL;
1552
1553 if (entry && gconf_entry_get_value (entry))
1554 retval = gconf_value_copy (gconf_entry_get_value (entry));
1555
1556 if (entry != NULL)
1557 gconf_entry_free (entry);
1558
1559 return retval;
1560 }
1561
1562 GConfEntry*
gconf_client_get_entry(GConfClient * client,const gchar * key,const gchar * locale,gboolean use_schema_default,GError ** err)1563 gconf_client_get_entry (GConfClient* client,
1564 const gchar* key,
1565 const gchar* locale,
1566 gboolean use_schema_default,
1567 GError** err)
1568 {
1569 GError* error = NULL;
1570 GConfEntry *entry;
1571
1572 g_return_val_if_fail(err == NULL || *err == NULL, NULL);
1573
1574 if (locale != NULL)
1575 g_warning("haven't implemented getting a specific locale in GConfClient");
1576
1577 entry = get (client, key, use_schema_default,
1578 &error);
1579
1580 if (entry == NULL && error != NULL)
1581 handle_error (client, error, err);
1582 else
1583 g_assert (error == NULL);
1584
1585 return entry;
1586 }
1587
1588 GConfValue*
gconf_client_get(GConfClient * client,const gchar * key,GError ** err)1589 gconf_client_get (GConfClient* client,
1590 const gchar* key,
1591 GError** err)
1592 {
1593 g_return_val_if_fail (GCONF_IS_CLIENT (client), NULL);
1594 g_return_val_if_fail (key != NULL, NULL);
1595 return gconf_client_get_full (client, key, NULL, TRUE, err);
1596 }
1597
1598 GConfValue*
gconf_client_get_without_default(GConfClient * client,const gchar * key,GError ** err)1599 gconf_client_get_without_default (GConfClient* client,
1600 const gchar* key,
1601 GError** err)
1602 {
1603 g_return_val_if_fail (GCONF_IS_CLIENT (client), NULL);
1604 g_return_val_if_fail (key != NULL, NULL);
1605 return gconf_client_get_full (client, key, NULL, FALSE, err);
1606 }
1607
1608 GConfValue*
gconf_client_get_default_from_schema(GConfClient * client,const gchar * key,GError ** err)1609 gconf_client_get_default_from_schema (GConfClient* client,
1610 const gchar* key,
1611 GError** err)
1612 {
1613 GError* error = NULL;
1614 GConfEntry *entry = NULL;
1615 GConfValue *val = NULL;
1616
1617 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1618 g_return_val_if_fail (client != NULL, NULL);
1619 g_return_val_if_fail (GCONF_IS_CLIENT(client), NULL);
1620 g_return_val_if_fail (key != NULL, NULL);
1621
1622 /* Check our client-side cache to see if the default is the same as
1623 * the regular value (FIXME put a default_value field in the
1624 * cache and store both, lose the is_default flag)
1625 */
1626 if (gconf_client_lookup (client, key, &entry))
1627 {
1628 if (!entry)
1629 return NULL;
1630
1631 if (gconf_entry_get_is_default (entry))
1632 {
1633 trace ("CACHED: Getting schema default for '%s'", key);
1634
1635 return gconf_entry_get_value (entry) ?
1636 gconf_value_copy (gconf_entry_get_value (entry)) :
1637 NULL;
1638 }
1639 }
1640
1641 /* Check the GConfEngine */
1642 trace ("REMOTE: Getting schema default for '%s'", key);
1643 PUSH_USE_ENGINE (client);
1644 val = gconf_engine_get_default_from_schema (client->engine, key,
1645 &error);
1646 POP_USE_ENGINE (client);
1647
1648 if (error != NULL)
1649 {
1650 g_assert (val == NULL);
1651 handle_error (client, error, err);
1652 return NULL;
1653 }
1654 else
1655 {
1656 /* FIXME eventually we'll cache the value
1657 * by adding a field to the cache
1658 */
1659 return val;
1660 }
1661 }
1662
1663 gdouble
gconf_client_get_float(GConfClient * client,const gchar * key,GError ** err)1664 gconf_client_get_float (GConfClient* client, const gchar* key,
1665 GError** err)
1666 {
1667 static const gdouble def = 0.0;
1668 GError* error = NULL;
1669 GConfValue *val;
1670
1671 g_return_val_if_fail (err == NULL || *err == NULL, 0.0);
1672
1673 val = gconf_client_get (client, key, &error);
1674
1675 if (val != NULL)
1676 {
1677 gdouble retval = def;
1678
1679 g_assert(error == NULL);
1680
1681 if (check_type (key, val, GCONF_VALUE_FLOAT, &error))
1682 retval = gconf_value_get_float (val);
1683 else
1684 handle_error (client, error, err);
1685
1686 gconf_value_free (val);
1687
1688 return retval;
1689 }
1690 else
1691 {
1692 if (error != NULL)
1693 handle_error (client, error, err);
1694 return def;
1695 }
1696 }
1697
1698 gint
gconf_client_get_int(GConfClient * client,const gchar * key,GError ** err)1699 gconf_client_get_int (GConfClient* client, const gchar* key,
1700 GError** err)
1701 {
1702 static const gint def = 0;
1703 GError* error = NULL;
1704 GConfValue* val;
1705
1706 g_return_val_if_fail (err == NULL || *err == NULL, 0);
1707
1708 val = gconf_client_get (client, key, &error);
1709
1710 if (val != NULL)
1711 {
1712 gint retval = def;
1713
1714 g_assert(error == NULL);
1715
1716 if (check_type (key, val, GCONF_VALUE_INT, &error))
1717 retval = gconf_value_get_int(val);
1718 else
1719 handle_error (client, error, err);
1720
1721 gconf_value_free (val);
1722
1723 return retval;
1724 }
1725 else
1726 {
1727 if (error != NULL)
1728 handle_error (client, error, err);
1729 return def;
1730 }
1731 }
1732
1733 gchar*
gconf_client_get_string(GConfClient * client,const gchar * key,GError ** err)1734 gconf_client_get_string(GConfClient* client, const gchar* key,
1735 GError** err)
1736 {
1737 GError* error = NULL;
1738 GConfValue* val;
1739
1740 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1741
1742 val = gconf_client_get (client, key, &error);
1743
1744 if (val != NULL)
1745 {
1746 gchar* retval = NULL;
1747
1748 g_assert(error == NULL);
1749
1750 if (check_type (key, val, GCONF_VALUE_STRING, &error))
1751 retval = gconf_value_steal_string (val);
1752 else
1753 handle_error (client, error, err);
1754
1755 gconf_value_free (val);
1756
1757 return retval;
1758 }
1759 else
1760 {
1761 if (error != NULL)
1762 handle_error (client, error, err);
1763 return NULL;
1764 }
1765 }
1766
1767
1768 gboolean
gconf_client_get_bool(GConfClient * client,const gchar * key,GError ** err)1769 gconf_client_get_bool (GConfClient* client, const gchar* key,
1770 GError** err)
1771 {
1772 static const gboolean def = FALSE;
1773 GError* error = NULL;
1774 GConfValue* val;
1775
1776 g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1777
1778 val = gconf_client_get (client, key, &error);
1779
1780 if (val != NULL)
1781 {
1782 gboolean retval = def;
1783
1784 g_assert (error == NULL);
1785
1786 if (check_type (key, val, GCONF_VALUE_BOOL, &error))
1787 retval = gconf_value_get_bool (val);
1788 else
1789 handle_error (client, error, err);
1790
1791 gconf_value_free (val);
1792
1793 return retval;
1794 }
1795 else
1796 {
1797 if (error != NULL)
1798 handle_error (client, error, err);
1799 return def;
1800 }
1801 }
1802
1803 GConfSchema*
gconf_client_get_schema(GConfClient * client,const gchar * key,GError ** err)1804 gconf_client_get_schema (GConfClient* client,
1805 const gchar* key, GError** err)
1806 {
1807 GError* error = NULL;
1808 GConfValue* val;
1809
1810 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1811
1812 val = gconf_client_get (client, key, &error);
1813
1814 if (val != NULL)
1815 {
1816 GConfSchema* retval = NULL;
1817
1818 g_assert(error == NULL);
1819
1820 if (check_type (key, val, GCONF_VALUE_SCHEMA, &error))
1821 retval = gconf_value_steal_schema (val);
1822 else
1823 handle_error (client, error, err);
1824
1825 gconf_value_free (val);
1826
1827 return retval;
1828 }
1829 else
1830 {
1831 if (error != NULL)
1832 handle_error (client, error, err);
1833 return NULL;
1834 }
1835 }
1836
1837 /**
1838 * gconf_client_get_list: (skip)
1839 * @client: a #GConfClient.
1840 * @key: key you want the value of.
1841 * @list_type: type of each list element.
1842 * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1843 *
1844 * Requests the list (%GCONF_VALUE_LIST) stored at @key. Automatically
1845 * performs type-checking, so if a non-list is stored at @key, or the
1846 * list does not contain elements of type @list_type, an error is
1847 * returned. If no value is set or an error occurs, <symbol>NULL</symbol>
1848 * is returned. Note that <symbol>NULL</symbol> is also the empty list,
1849 * so if you need to distinguish the empty list from an unset value, you
1850 * must use gconf_client_get () to obtain a raw #GConfValue.
1851 *
1852 * <emphasis>Remember that GConf lists can only store primitive types:
1853 * %GCONF_VALUE_FLOAT, %GCONF_VALUE_INT, %GCONF_VALUE_BOOL,
1854 * %GCONF_VALUE_STRING, %GCONF_VALUE_SCHEMA.</emphasis> Also remember
1855 * that lists must be uniform, you may not mix types in the same list.
1856 *
1857 * The type of the list elements depends on @list_type. A #GConfValue
1858 * with type %GCONF_VALUE_LIST normally stores a list of more #GConfValue
1859 * objects. gconf_client_get_list() automatically converts to primitive C
1860 * types. Thus, the list->data fields in the returned list
1861 * contain:
1862 *
1863 * <informaltable pgwide="1" frame="none">
1864 * <tgroup cols="2"><colspec colwidth="2*"/><colspec colwidth="8*"/>
1865 * <tbody>
1866 *
1867 * <row>
1868 * <entry>%GCONF_VALUE_INT</entry>
1869 * <entry>The integer itself, converted with GINT_TO_POINTER()</entry>
1870 * </row>
1871 *
1872 * <row>
1873 * <entry>%GCONF_VALUE_BOOL</entry>
1874 * <entry>The bool itself, converted with GINT_TO_POINTER()</entry>
1875 * </row>
1876 *
1877 * <row>
1878 * <entry>%GCONF_VALUE_FLOAT</entry>
1879 * <entry>A pointer to #gdouble, which should be freed with g_free()</entry>
1880 * </row>
1881 *
1882 * <row>
1883 * <entry>%GCONF_VALUE_STRING</entry>
1884 * <entry>A pointer to #gchar, which should be freed with g_free()</entry>
1885 * </row>
1886 *
1887 * <row>
1888 * <entry>%GCONF_VALUE_SCHEMA</entry>
1889 * <entry>A pointer to #GConfSchema, which should be freed with gconf_schema_free()</entry>
1890 * </row>
1891 *
1892 * </tbody></tgroup></informaltable>
1893 *
1894 * In the %GCONF_VALUE_FLOAT and %GCONF_VALUE_STRING cases, you must
1895 * g_free() each list element. In the %GCONF_VALUE_SCHEMA case you must
1896 * gconf_schema_free() each element. In all cases you must free the
1897 * list itself with g_slist_free().
1898 *
1899 * Just like gconf_engine_get_list (), but uses #GConfClient caching and error-handling features.
1900 *
1901 * Return value: an allocated list, with elements as described above.
1902 */
1903 GSList*
gconf_client_get_list(GConfClient * client,const gchar * key,GConfValueType list_type,GError ** err)1904 gconf_client_get_list (GConfClient* client, const gchar* key,
1905 GConfValueType list_type, GError** err)
1906 {
1907 GError* error = NULL;
1908 GConfValue* val;
1909
1910 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1911
1912 val = gconf_client_get (client, key, &error);
1913
1914 if (val != NULL)
1915 {
1916 GSList* retval;
1917
1918 g_assert (error == NULL);
1919
1920 /* This function checks the type and destroys "val" */
1921 retval = gconf_value_list_to_primitive_list_destructive (val, list_type, &error);
1922
1923 if (error != NULL)
1924 {
1925 g_assert (retval == NULL);
1926 handle_error (client, error, err);
1927 return NULL;
1928 }
1929 else
1930 return retval;
1931 }
1932 else
1933 {
1934 if (error != NULL)
1935 handle_error (client, error, err);
1936 return NULL;
1937 }
1938 }
1939
1940 gboolean
gconf_client_get_pair(GConfClient * client,const gchar * key,GConfValueType car_type,GConfValueType cdr_type,gpointer car_retloc,gpointer cdr_retloc,GError ** err)1941 gconf_client_get_pair (GConfClient* client, const gchar* key,
1942 GConfValueType car_type, GConfValueType cdr_type,
1943 gpointer car_retloc, gpointer cdr_retloc,
1944 GError** err)
1945 {
1946 GError* error = NULL;
1947 GConfValue* val;
1948
1949 g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1950
1951 val = gconf_client_get (client, key, &error);
1952
1953 if (val != NULL)
1954 {
1955 g_assert(error == NULL);
1956
1957 /* This function checks the type and destroys "val" */
1958 if (gconf_value_pair_to_primitive_pair_destructive (val, car_type, cdr_type,
1959 car_retloc, cdr_retloc,
1960 &error))
1961 {
1962 g_assert (error == NULL);
1963 return TRUE;
1964 }
1965 else
1966 {
1967 g_assert (error != NULL);
1968 handle_error (client, error, err);
1969 return FALSE;
1970 }
1971 }
1972 else
1973 {
1974 if (error != NULL)
1975 {
1976 handle_error (client, error, err);
1977 return FALSE;
1978 }
1979 else
1980 return TRUE;
1981 }
1982 }
1983
1984 #ifdef HAVE_DBUS
1985 #define MAKE_VALUE(v, TYPE, type, val) GConfValue *v = gconf_value_new (GCONF_VALUE_##TYPE); gconf_value_set_##type (v, val)
1986 #endif
1987
1988 /*
1989 * For the set functions, we just set normally, and wait for the
1990 * notification to come back from the server before we update
1991 * our cache. This may be the wrong thing; maybe we should
1992 * update immediately?
1993 * Problem with delayed update: user calls set() then get(),
1994 * results in weirdness
1995 * Problem with with regular update: get() before the notify
1996 * is out of sync with the listening parts of the application
1997 *
1998 */
1999
2000 gboolean
gconf_client_set_float(GConfClient * client,const gchar * key,gdouble val,GError ** err)2001 gconf_client_set_float (GConfClient* client, const gchar* key,
2002 gdouble val, GError** err)
2003 {
2004 GError* error = NULL;
2005 gboolean result;
2006
2007 g_return_val_if_fail(client != NULL, FALSE);
2008 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2009 g_return_val_if_fail(key != NULL, FALSE);
2010
2011 trace ("REMOTE: Setting float '%s'", key);
2012 PUSH_USE_ENGINE (client);
2013 result = gconf_engine_set_float (client->engine, key, val, &error);
2014 POP_USE_ENGINE (client);
2015
2016 #ifdef HAVE_DBUS
2017 if (result)
2018 {
2019 MAKE_VALUE (v, FLOAT, float, val);
2020 cache_key_value_and_notify (client, key, v, TRUE);
2021 }
2022 #endif
2023
2024 if (result)
2025 return TRUE;
2026 else
2027 {
2028 handle_error(client, error, err);
2029 return FALSE;
2030 }
2031 }
2032
2033 gboolean
gconf_client_set_int(GConfClient * client,const gchar * key,gint val,GError ** err)2034 gconf_client_set_int (GConfClient* client, const gchar* key,
2035 gint val, GError** err)
2036 {
2037 GError* error = NULL;
2038 gboolean result;
2039
2040 g_return_val_if_fail(client != NULL, FALSE);
2041 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2042 g_return_val_if_fail(key != NULL, FALSE);
2043
2044 trace ("REMOTE: Setting int '%s'", key);
2045 PUSH_USE_ENGINE (client);
2046 result = gconf_engine_set_int (client->engine, key, val, &error);
2047 POP_USE_ENGINE (client);
2048
2049 #ifdef HAVE_DBUS
2050 if (result)
2051 {
2052 MAKE_VALUE (v, INT, int, val);
2053 cache_key_value_and_notify (client, key, v, TRUE);
2054 }
2055 #endif
2056
2057 if (result)
2058 return TRUE;
2059 else
2060 {
2061 handle_error(client, error, err);
2062 return FALSE;
2063 }
2064 }
2065
2066 gboolean
gconf_client_set_string(GConfClient * client,const gchar * key,const gchar * val,GError ** err)2067 gconf_client_set_string (GConfClient* client, const gchar* key,
2068 const gchar* val, GError** err)
2069 {
2070 GError* error = NULL;
2071 gboolean result;
2072
2073 g_return_val_if_fail(client != NULL, FALSE);
2074 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2075 g_return_val_if_fail(key != NULL, FALSE);
2076 g_return_val_if_fail(val != NULL, FALSE);
2077
2078 trace ("REMOTE: Setting string '%s'", key);
2079 PUSH_USE_ENGINE (client);
2080 result = gconf_engine_set_string(client->engine, key, val, &error);
2081 POP_USE_ENGINE (client);
2082
2083 #ifdef HAVE_DBUS
2084 if (result)
2085 {
2086 MAKE_VALUE (v, STRING, string, val);
2087 cache_key_value_and_notify (client, key, v, TRUE);
2088 }
2089 #endif
2090
2091 if (result)
2092 return TRUE;
2093 else
2094 {
2095 handle_error(client, error, err);
2096 return FALSE;
2097 }
2098 }
2099
2100 gboolean
gconf_client_set_bool(GConfClient * client,const gchar * key,gboolean val,GError ** err)2101 gconf_client_set_bool (GConfClient* client, const gchar* key,
2102 gboolean val, GError** err)
2103 {
2104 GError* error = NULL;
2105 gboolean result;
2106
2107 g_return_val_if_fail(client != NULL, FALSE);
2108 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2109 g_return_val_if_fail(key != NULL, FALSE);
2110
2111 trace ("REMOTE: Setting bool '%s'", key);
2112 PUSH_USE_ENGINE (client);
2113 result = gconf_engine_set_bool (client->engine, key, val, &error);
2114 POP_USE_ENGINE (client);
2115
2116 #ifdef HAVE_DBUS
2117 if (result)
2118 {
2119 MAKE_VALUE (v, BOOL, bool, val);
2120 cache_key_value_and_notify (client, key, v, TRUE);
2121 }
2122 #endif
2123
2124 if (result)
2125 return TRUE;
2126 else
2127 {
2128 handle_error(client, error, err);
2129 return FALSE;
2130 }
2131 }
2132
2133 gboolean
gconf_client_set_schema(GConfClient * client,const gchar * key,const GConfSchema * val,GError ** err)2134 gconf_client_set_schema (GConfClient* client, const gchar* key,
2135 const GConfSchema* val, GError** err)
2136 {
2137 GError* error = NULL;
2138 gboolean result;
2139
2140 g_return_val_if_fail(client != NULL, FALSE);
2141 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2142 g_return_val_if_fail(key != NULL, FALSE);
2143 g_return_val_if_fail(val != NULL, FALSE);
2144
2145 trace ("REMOTE: Setting schema '%s'", key);
2146 PUSH_USE_ENGINE (client);
2147 result = gconf_engine_set_schema(client->engine, key, val, &error);
2148 POP_USE_ENGINE (client);
2149
2150 #ifdef HAVE_DBUS
2151 if (result)
2152 {
2153 MAKE_VALUE (v, SCHEMA, schema, val);
2154 cache_key_value_and_notify (client, key, v, TRUE);
2155 }
2156 #endif
2157
2158 if (result)
2159 return TRUE;
2160 else
2161 {
2162 handle_error(client, error, err);
2163 return FALSE;
2164 }
2165 }
2166
2167 gboolean
gconf_client_set_list(GConfClient * client,const gchar * key,GConfValueType list_type,GSList * list,GError ** err)2168 gconf_client_set_list (GConfClient* client, const gchar* key,
2169 GConfValueType list_type,
2170 GSList* list,
2171 GError** err)
2172 {
2173 GError* error = NULL;
2174 gboolean result;
2175
2176 g_return_val_if_fail(client != NULL, FALSE);
2177 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2178 g_return_val_if_fail(key != NULL, FALSE);
2179
2180 trace ("REMOTE: Setting list '%s'", key);
2181 PUSH_USE_ENGINE (client);
2182 result = gconf_engine_set_list(client->engine, key, list_type, list, &error);
2183 POP_USE_ENGINE (client);
2184
2185 #ifdef HAVE_DBUS
2186 if (result)
2187 {
2188 GConfValue *v = gconf_value_list_from_primitive_list (list_type, list, err);
2189 cache_key_value_and_notify (client, key, v, TRUE);
2190 }
2191 #endif
2192
2193 if (result)
2194 return TRUE;
2195 else
2196 {
2197 handle_error(client, error, err);
2198 return FALSE;
2199 }
2200 }
2201
2202 gboolean
gconf_client_set_pair(GConfClient * client,const gchar * key,GConfValueType car_type,GConfValueType cdr_type,gconstpointer address_of_car,gconstpointer address_of_cdr,GError ** err)2203 gconf_client_set_pair (GConfClient* client, const gchar* key,
2204 GConfValueType car_type, GConfValueType cdr_type,
2205 gconstpointer address_of_car,
2206 gconstpointer address_of_cdr,
2207 GError** err)
2208 {
2209 GError* error = NULL;
2210 gboolean result;
2211
2212 g_return_val_if_fail(client != NULL, FALSE);
2213 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2214 g_return_val_if_fail(key != NULL, FALSE);
2215
2216 trace ("REMOTE: Setting pair '%s'", key);
2217 PUSH_USE_ENGINE (client);
2218 result = gconf_engine_set_pair (client->engine, key, car_type, cdr_type,
2219 address_of_car, address_of_cdr, &error);
2220 POP_USE_ENGINE (client);
2221
2222 #ifdef HAVE_DBUS
2223 if (result)
2224 {
2225 GConfValue *v = gconf_value_pair_from_primitive_pair (car_type, cdr_type, address_of_car, address_of_cdr, &error);
2226 if (!v)
2227 {
2228 handle_error (client, error, err);
2229 return FALSE;
2230 }
2231
2232 cache_key_value_and_notify (client, key, v, TRUE);
2233 }
2234 #endif
2235
2236 if (result)
2237 return TRUE;
2238 else
2239 {
2240 handle_error(client, error, err);
2241 return FALSE;
2242 }
2243 }
2244
2245
2246 /*
2247 * Functions to emit signals
2248 */
2249
2250 void
gconf_client_error(GConfClient * client,GError * error)2251 gconf_client_error (GConfClient* client, GError* error)
2252 {
2253 g_return_if_fail(client != NULL);
2254 g_return_if_fail(GCONF_IS_CLIENT(client));
2255
2256 g_signal_emit (G_OBJECT(client), client_signals[ERROR], 0,
2257 error);
2258 }
2259
2260 void
gconf_client_unreturned_error(GConfClient * client,GError * error)2261 gconf_client_unreturned_error (GConfClient* client, GError* error)
2262 {
2263 g_return_if_fail(client != NULL);
2264 g_return_if_fail(GCONF_IS_CLIENT(client));
2265
2266 g_signal_emit (G_OBJECT(client), client_signals[UNRETURNED_ERROR], 0,
2267 error);
2268 }
2269
2270 void
gconf_client_value_changed(GConfClient * client,const gchar * key,GConfValue * value)2271 gconf_client_value_changed (GConfClient* client,
2272 const gchar* key,
2273 GConfValue* value)
2274 {
2275 g_return_if_fail(client != NULL);
2276 g_return_if_fail(GCONF_IS_CLIENT(client));
2277 g_return_if_fail(key != NULL);
2278
2279 g_signal_emit(G_OBJECT(client), client_signals[VALUE_CHANGED], 0,
2280 key, value);
2281 }
2282
2283 /*
2284 * Internal utility
2285 */
2286
2287 static gboolean
gconf_client_cache(GConfClient * client,gboolean take_ownership,GConfEntry * new_entry,gboolean preserve_schema_name)2288 gconf_client_cache (GConfClient *client,
2289 gboolean take_ownership,
2290 GConfEntry *new_entry,
2291 gboolean preserve_schema_name)
2292 {
2293 gpointer oldkey = NULL, oldval = NULL;
2294
2295 if (g_hash_table_lookup_extended (client->cache_hash, new_entry->key, &oldkey, &oldval))
2296 {
2297 /* Already have a value, update it */
2298 GConfEntry *entry = oldval;
2299 gboolean changed;
2300
2301 g_assert (entry != NULL);
2302
2303 changed = ! gconf_entry_equal (entry, new_entry);
2304
2305 if (changed)
2306 {
2307 trace ("Updating value of '%s' in the cache",
2308 new_entry->key);
2309
2310 if (!take_ownership)
2311 new_entry = gconf_entry_copy (new_entry);
2312
2313 if (preserve_schema_name)
2314 gconf_entry_set_schema_name (new_entry,
2315 gconf_entry_get_schema_name (entry));
2316
2317 g_hash_table_replace (client->cache_hash,
2318 new_entry->key,
2319 new_entry);
2320
2321 /* oldkey is inside entry */
2322 gconf_entry_free (entry);
2323 }
2324 else
2325 {
2326 trace ("Value of '%s' hasn't actually changed, would have updated in cache if it had",
2327 new_entry->key);
2328
2329 if (take_ownership)
2330 gconf_entry_free (new_entry);
2331 }
2332
2333 return changed;
2334 }
2335 else
2336 {
2337 /* Create a new entry */
2338 if (!take_ownership)
2339 new_entry = gconf_entry_copy (new_entry);
2340
2341 g_hash_table_insert (client->cache_hash, new_entry->key, new_entry);
2342 trace ("Added value of '%s' to the cache",
2343 new_entry->key);
2344
2345 return TRUE; /* changed */
2346 }
2347 }
2348
2349 static gboolean
gconf_client_lookup(GConfClient * client,const char * key,GConfEntry ** entryp)2350 gconf_client_lookup (GConfClient *client,
2351 const char *key,
2352 GConfEntry **entryp)
2353 {
2354 GConfEntry *entry;
2355
2356 g_return_val_if_fail (entryp != NULL, FALSE);
2357 g_return_val_if_fail (*entryp == NULL, FALSE);
2358
2359 entry = g_hash_table_lookup (client->cache_hash, key);
2360
2361 *entryp = entry;
2362
2363 if (!entry)
2364 {
2365 char *dir, *last_slash;
2366
2367 dir = g_strdup (key);
2368 last_slash = strrchr (dir, '/');
2369 g_assert (last_slash != NULL);
2370 *last_slash = 0;
2371
2372 if (g_hash_table_lookup (client->cache_dirs, dir))
2373 {
2374 g_free (dir);
2375 trace ("Negative cache hit on %s", key);
2376 return TRUE;
2377 }
2378 else
2379 {
2380 gboolean not_cached = FALSE;
2381 while(not_cached || (!g_hash_table_lookup (client->cache_recursive_dirs, dir)))
2382 {
2383 last_slash = strrchr (dir, '/');
2384 if (last_slash == NULL)
2385 break;
2386 else
2387 *last_slash = 0;
2388 if (g_hash_table_lookup (client->cache_recursive_dirs, dir))
2389 {
2390 g_free (dir);
2391 trace ("Non-existing dir for %s", key);
2392 return TRUE;
2393 }
2394 not_cached = TRUE;
2395 }
2396 }
2397 g_free (dir);
2398 }
2399
2400 return entry != NULL;
2401 }
2402
2403 /*
2404 * Dir
2405 */
2406
2407 static Dir*
dir_new(const gchar * name,guint notify_id)2408 dir_new(const gchar* name, guint notify_id)
2409 {
2410 Dir* d;
2411
2412 d = g_new(Dir, 1);
2413
2414 d->name = g_strdup(name);
2415 d->notify_id = notify_id;
2416 d->add_count = 0;
2417
2418 return d;
2419 }
2420
2421 static void
dir_destroy(Dir * d)2422 dir_destroy(Dir* d)
2423 {
2424 g_return_if_fail(d != NULL);
2425 g_return_if_fail(d->notify_id == 0);
2426
2427 g_free(d->name);
2428 g_free(d);
2429 }
2430
2431 /*
2432 * Listener
2433 */
2434
2435 static Listener*
listener_new(GConfClientNotifyFunc func,GFreeFunc destroy_notify,gpointer data)2436 listener_new(GConfClientNotifyFunc func,
2437 GFreeFunc destroy_notify,
2438 gpointer data)
2439 {
2440 Listener* l;
2441
2442 l = g_new(Listener, 1);
2443
2444 l->func = func;
2445 l->data = data;
2446 l->destroy_notify = destroy_notify;
2447
2448 return l;
2449 }
2450
2451 static void
listener_destroy(Listener * l)2452 listener_destroy(Listener* l)
2453 {
2454 g_return_if_fail(l != NULL);
2455
2456 if (l->destroy_notify)
2457 (* l->destroy_notify) (l->data);
2458
2459 g_free(l);
2460 }
2461
2462 /*
2463 * Change sets
2464 */
2465
2466
2467 struct CommitData {
2468 GConfClient* client;
2469 GError* error;
2470 GSList* remove_list;
2471 gboolean remove_committed;
2472 };
2473
2474 static void
commit_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)2475 commit_foreach (GConfChangeSet* cs,
2476 const gchar* key,
2477 GConfValue* value,
2478 gpointer user_data)
2479 {
2480 struct CommitData* cd = user_data;
2481
2482 g_assert(cd != NULL);
2483
2484 if (cd->error != NULL)
2485 return;
2486
2487 if (value)
2488 gconf_client_set (cd->client, key, value, &cd->error);
2489 else
2490 gconf_client_unset (cd->client, key, &cd->error);
2491
2492 if (cd->error == NULL && cd->remove_committed)
2493 {
2494 /* Bad bad bad; we keep the key reference, knowing that it's
2495 valid until we modify the change set, to avoid string copies. */
2496 cd->remove_list = g_slist_prepend(cd->remove_list, (gchar*)key);
2497 }
2498 }
2499
2500 gboolean
gconf_client_commit_change_set(GConfClient * client,GConfChangeSet * cs,gboolean remove_committed,GError ** err)2501 gconf_client_commit_change_set (GConfClient* client,
2502 GConfChangeSet* cs,
2503 gboolean remove_committed,
2504 GError** err)
2505 {
2506 struct CommitData cd;
2507 GSList* tmp;
2508
2509 g_return_val_if_fail(client != NULL, FALSE);
2510 g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2511 g_return_val_if_fail(cs != NULL, FALSE);
2512 g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
2513
2514 cd.client = client;
2515 cd.error = NULL;
2516 cd.remove_list = NULL;
2517 cd.remove_committed = remove_committed;
2518
2519 /* Because the commit could have lots of side
2520 effects, this makes it safer */
2521 gconf_change_set_ref(cs);
2522 g_object_ref(G_OBJECT(client));
2523
2524 gconf_change_set_foreach(cs, commit_foreach, &cd);
2525
2526 tmp = cd.remove_list;
2527 while (tmp != NULL)
2528 {
2529 const gchar* key = tmp->data;
2530
2531 gconf_change_set_remove(cs, key);
2532
2533 /* key is now invalid due to our little evil trick */
2534
2535 tmp = g_slist_next(tmp);
2536 }
2537
2538 g_slist_free(cd.remove_list);
2539
2540 gconf_change_set_unref(cs);
2541 g_object_unref(G_OBJECT(client));
2542
2543 if (cd.error != NULL)
2544 {
2545 if (err != NULL)
2546 *err = cd.error;
2547 else
2548 g_error_free(cd.error);
2549
2550 return FALSE;
2551 }
2552 else
2553 {
2554 g_assert((!remove_committed) ||
2555 (gconf_change_set_size(cs) == 0));
2556
2557 return TRUE;
2558 }
2559 }
2560
2561 struct RevertData {
2562 GConfClient* client;
2563 GError* error;
2564 GConfChangeSet* revert_set;
2565 };
2566
2567 static void
revert_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)2568 revert_foreach (GConfChangeSet* cs,
2569 const gchar* key,
2570 GConfValue* value,
2571 gpointer user_data)
2572 {
2573 struct RevertData* rd = user_data;
2574 GConfValue* old_value;
2575 GError* error = NULL;
2576
2577 g_assert(rd != NULL);
2578
2579 if (rd->error != NULL)
2580 return;
2581
2582 old_value = gconf_client_get_without_default(rd->client, key, &error);
2583
2584 if (error != NULL)
2585 {
2586 /* FIXME */
2587 g_warning("error creating revert set: %s", error->message);
2588 g_error_free(error);
2589 error = NULL;
2590 }
2591
2592 if (old_value == NULL &&
2593 value == NULL)
2594 return; /* this commit will have no effect. */
2595
2596 if (old_value == NULL)
2597 gconf_change_set_unset(rd->revert_set, key);
2598 else
2599 gconf_change_set_set_nocopy(rd->revert_set, key, old_value);
2600 }
2601
2602
2603 GConfChangeSet*
gconf_client_reverse_change_set(GConfClient * client,GConfChangeSet * cs,GError ** err)2604 gconf_client_reverse_change_set (GConfClient* client,
2605 GConfChangeSet* cs,
2606 GError** err)
2607 {
2608 struct RevertData rd;
2609
2610 rd.error = NULL;
2611 rd.client = client;
2612 rd.revert_set = gconf_change_set_new();
2613
2614 /* we're emitting signals and such, avoid
2615 nasty side effects with these.
2616 */
2617 g_object_ref(G_OBJECT(rd.client));
2618 gconf_change_set_ref(cs);
2619
2620 gconf_change_set_foreach(cs, revert_foreach, &rd);
2621
2622 if (rd.error != NULL)
2623 {
2624 if (err != NULL)
2625 *err = rd.error;
2626 else
2627 g_error_free(rd.error);
2628 }
2629
2630 g_object_unref(G_OBJECT(rd.client));
2631 gconf_change_set_unref(cs);
2632
2633 return rd.revert_set;
2634 }
2635
2636
2637 GConfChangeSet*
gconf_client_change_set_from_currentv(GConfClient * client,const gchar ** keys,GError ** err)2638 gconf_client_change_set_from_currentv (GConfClient* client,
2639 const gchar** keys,
2640 GError** err)
2641 {
2642 GConfValue* old_value;
2643 GConfChangeSet* new_set;
2644 const gchar** keyp;
2645
2646 g_return_val_if_fail(err == NULL || *err == NULL, NULL);
2647
2648 new_set = gconf_change_set_new();
2649
2650 keyp = keys;
2651
2652 while (*keyp != NULL)
2653 {
2654 GError* error = NULL;
2655 const gchar* key = *keyp;
2656
2657 old_value = gconf_client_get_without_default(client, key, &error);
2658
2659 if (error != NULL)
2660 {
2661 /* FIXME */
2662 g_warning("error creating change set from current keys: %s", error->message);
2663 g_error_free(error);
2664 error = NULL;
2665 }
2666
2667 if (old_value == NULL)
2668 gconf_change_set_unset(new_set, key);
2669 else
2670 gconf_change_set_set_nocopy(new_set, key, old_value);
2671
2672 ++keyp;
2673 }
2674
2675 return new_set;
2676 }
2677
2678 GConfChangeSet*
gconf_client_change_set_from_current(GConfClient * client,GError ** err,const gchar * first_key,...)2679 gconf_client_change_set_from_current (GConfClient* client,
2680 GError** err,
2681 const gchar* first_key,
2682 ...)
2683 {
2684 GSList* keys = NULL;
2685 va_list args;
2686 const gchar* arg;
2687 const gchar** vec;
2688 GConfChangeSet* retval;
2689 GSList* tmp;
2690 guint i;
2691
2692 g_return_val_if_fail(err == NULL || *err == NULL, NULL);
2693
2694 va_start (args, first_key);
2695
2696 arg = first_key;
2697
2698 while (arg != NULL)
2699 {
2700 keys = g_slist_prepend(keys, (/*non-const*/gchar*)arg);
2701
2702 arg = va_arg (args, const gchar*);
2703 }
2704
2705 va_end (args);
2706
2707 vec = g_new0(const gchar*, g_slist_length(keys) + 1);
2708
2709 i = 0;
2710 tmp = keys;
2711
2712 while (tmp != NULL)
2713 {
2714 vec[i] = tmp->data;
2715
2716 ++i;
2717 tmp = g_slist_next(tmp);
2718 }
2719
2720 g_slist_free(keys);
2721
2722 retval = gconf_client_change_set_from_currentv(client, vec, err);
2723
2724 g_free(vec);
2725
2726 return retval;
2727 }
2728
2729 static GHashTable * clients = NULL;
2730
2731 static void
register_client(GConfClient * client)2732 register_client (GConfClient *client)
2733 {
2734 if (clients == NULL)
2735 clients = g_hash_table_new (NULL, NULL);
2736
2737 g_hash_table_insert (clients, client->engine, client);
2738 }
2739
2740 static GConfClient *
lookup_client(GConfEngine * engine)2741 lookup_client (GConfEngine *engine)
2742 {
2743 if (clients == NULL)
2744 return NULL;
2745 else
2746 return g_hash_table_lookup (clients, engine);
2747 }
2748
2749 static void
unregister_client(GConfClient * client)2750 unregister_client (GConfClient *client)
2751 {
2752 g_return_if_fail (clients != NULL);
2753
2754 g_hash_table_remove (clients, client->engine);
2755 }
2756
2757
2758 /*
2759 * Notification
2760 */
2761
2762 static gboolean
notify_idle_callback(gpointer data)2763 notify_idle_callback (gpointer data)
2764 {
2765 GConfClient *client = data;
2766
2767 client->notify_handler = 0; /* avoid g_source_remove */
2768
2769 gconf_client_flush_notifies (client);
2770
2771 /* remove handler */
2772 return FALSE;
2773 }
2774
2775 static void
gconf_client_queue_notify(GConfClient * client,const char * key)2776 gconf_client_queue_notify (GConfClient *client,
2777 const char *key)
2778 {
2779 trace ("Queing notify on '%s', %d pending already", key,
2780 client->pending_notify_count);
2781
2782 if (client->notify_handler == 0)
2783 client->notify_handler = g_idle_add (notify_idle_callback, client);
2784
2785 client->notify_list = g_slist_prepend (client->notify_list, g_strdup (key));
2786 client->pending_notify_count += 1;
2787 }
2788
2789 struct ClientAndEntry {
2790 GConfClient* client;
2791 GConfEntry* entry;
2792 };
2793
2794 static void
notify_listeners_callback(GConfListeners * listeners,const gchar * key,guint cnxn_id,gpointer listener_data,gpointer user_data)2795 notify_listeners_callback(GConfListeners* listeners,
2796 const gchar* key,
2797 guint cnxn_id,
2798 gpointer listener_data,
2799 gpointer user_data)
2800 {
2801 Listener* l = listener_data;
2802 struct ClientAndEntry* cae = user_data;
2803
2804 g_return_if_fail (cae != NULL);
2805 g_return_if_fail (cae->client != NULL);
2806 g_return_if_fail (GCONF_IS_CLIENT (cae->client));
2807 g_return_if_fail (l != NULL);
2808 g_return_if_fail (l->func != NULL);
2809
2810 (*l->func) (cae->client, cnxn_id, cae->entry, l->data);
2811 }
2812
2813 static void
notify_one_entry(GConfClient * client,GConfEntry * entry)2814 notify_one_entry (GConfClient *client,
2815 GConfEntry *entry)
2816 {
2817 g_object_ref (G_OBJECT (client));
2818 gconf_entry_ref (entry);
2819
2820 /* Emit the value_changed signal before notifying specific listeners;
2821 * I'm not sure there's a reason this matters though
2822 */
2823 gconf_client_value_changed (client,
2824 entry->key,
2825 gconf_entry_get_value (entry));
2826
2827 /* Now notify our listeners, if any */
2828 if (client->listeners != NULL)
2829 {
2830 struct ClientAndEntry cae;
2831
2832 cae.client = client;
2833 cae.entry = entry;
2834
2835 gconf_listeners_notify (client->listeners,
2836 entry->key,
2837 notify_listeners_callback,
2838 &cae);
2839 }
2840
2841 gconf_entry_unref (entry);
2842 g_object_unref (G_OBJECT (client));
2843 }
2844
2845 static void
gconf_client_flush_notifies(GConfClient * client)2846 gconf_client_flush_notifies (GConfClient *client)
2847 {
2848 GSList *tmp;
2849 GSList *to_notify;
2850 GConfEntry *last_entry;
2851
2852 trace ("Flushing notify queue");
2853
2854 /* Adopt notify list and clear it, to avoid reentrancy concerns.
2855 * Sort it to compress duplicates, and keep people from relying on
2856 * the notify order.
2857 */
2858 to_notify = g_slist_sort (client->notify_list, (GCompareFunc) strcmp);
2859 client->notify_list = NULL;
2860 client->pending_notify_count = 0;
2861
2862 gconf_client_unqueue_notifies (client);
2863
2864 last_entry = NULL;
2865 tmp = to_notify;
2866 while (tmp != NULL)
2867 {
2868 GConfEntry *entry = NULL;
2869
2870 if (gconf_client_lookup (client, tmp->data, &entry) && entry != NULL)
2871 {
2872 if (entry != last_entry)
2873 {
2874 trace ("Doing notification for '%s'", entry->key);
2875 notify_one_entry (client, entry);
2876 last_entry = entry;
2877 }
2878 else
2879 {
2880 trace ("Ignoring duplicate notify for '%s'", entry->key);
2881 }
2882 }
2883 else
2884 {
2885 #ifdef HAVE_DBUS
2886 /* The dbus version cleans the cache after modifying a value so a get
2887 * directly after a set doesn't return a stale value. That means we
2888 * have to check if the key is supposed to be monitored here, we can't
2889 * just rely on it being in the cache.
2890 */
2891 if (key_being_monitored (client, tmp->data))
2892 {
2893 trace ("Key %s was in notify queue but not in cache, but is being monitored",
2894 tmp->data);
2895
2896 entry = gconf_client_get_entry (client, tmp->data, NULL, TRUE, NULL);
2897 if (entry != NULL)
2898 {
2899 notify_one_entry (client, entry);
2900 gconf_entry_unref (entry);
2901 last_entry = NULL;
2902 }
2903 }
2904 else
2905 {
2906 trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
2907 tmp->data);
2908 }
2909 #else
2910 trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
2911 tmp->data);
2912 #endif
2913 }
2914
2915 tmp = tmp->next;
2916 }
2917
2918 g_slist_foreach (to_notify, (GFunc) g_free, NULL);
2919 g_slist_free (to_notify);
2920 }
2921
2922 static void
gconf_client_unqueue_notifies(GConfClient * client)2923 gconf_client_unqueue_notifies (GConfClient *client)
2924 {
2925 if (client->notify_handler != 0)
2926 {
2927 g_source_remove (client->notify_handler);
2928 client->notify_handler = 0;
2929 }
2930
2931 if (client->notify_list != NULL)
2932 {
2933 g_slist_foreach (client->notify_list, (GFunc) g_free, NULL);
2934 g_slist_free (client->notify_list);
2935 client->notify_list = NULL;
2936 client->pending_notify_count = 0;
2937 }
2938 }
2939