comparison libgaim/prefs.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children 71149a751439
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
1 /*
2 * gaim
3 *
4 * Gaim is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <glib.h>
34 #include "internal.h"
35 #include "prefs.h"
36 #include "debug.h"
37 #include "util.h"
38
39 #ifdef _WIN32
40 #include "win32dep.h"
41 #endif
42
43 struct pref_cb {
44 GaimPrefCallback func;
45 gpointer data;
46 guint id;
47 void *handle;
48 };
49
50 /* TODO: This should use GaimValues? */
51 struct gaim_pref {
52 GaimPrefType type;
53 char *name;
54 union {
55 gpointer generic;
56 gboolean boolean;
57 int integer;
58 char *string;
59 GList *stringlist;
60 } value;
61 GSList *callbacks;
62 struct gaim_pref *parent;
63 struct gaim_pref *sibling;
64 struct gaim_pref *first_child;
65 };
66
67
68 static struct gaim_pref prefs = {
69 GAIM_PREF_NONE,
70 NULL,
71 { NULL },
72 NULL,
73 NULL,
74 NULL,
75 NULL
76 };
77
78 static GHashTable *prefs_hash = NULL;
79 static guint save_timer = 0;
80 static gboolean prefs_loaded = FALSE;
81
82
83 /*********************************************************************
84 * Private utility functions *
85 *********************************************************************/
86
87 static struct
88 gaim_pref *find_pref(const char *name)
89 {
90 if (!name || name[0] != '/')
91 return NULL;
92 else if (name[1] == '\0')
93 return &prefs;
94 else
95 return g_hash_table_lookup(prefs_hash, name);
96 }
97
98
99 /*********************************************************************
100 * Writing to disk *
101 *********************************************************************/
102
103 /*
104 * This function recursively creates the xmlnode tree from the prefs
105 * tree structure. Yay recursion!
106 */
107 static void
108 pref_to_xmlnode(xmlnode *parent, struct gaim_pref *pref)
109 {
110 xmlnode *node, *childnode;
111 struct gaim_pref *child;
112 char buf[20];
113 GList *cur;
114
115 /* Create a new node */
116 node = xmlnode_new_child(parent, "pref");
117 xmlnode_set_attrib(node, "name", pref->name);
118
119 /* Set the type of this node (if type == GAIM_PREF_NONE then do nothing) */
120 if (pref->type == GAIM_PREF_INT) {
121 xmlnode_set_attrib(node, "type", "int");
122 snprintf(buf, sizeof(buf), "%d", pref->value.integer);
123 xmlnode_set_attrib(node, "value", buf);
124 }
125 else if (pref->type == GAIM_PREF_STRING) {
126 xmlnode_set_attrib(node, "type", "string");
127 xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
128 }
129 else if (pref->type == GAIM_PREF_STRING_LIST) {
130 xmlnode_set_attrib(node, "type", "stringlist");
131 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
132 {
133 childnode = xmlnode_new_child(node, "item");
134 xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
135 }
136 }
137 else if (pref->type == GAIM_PREF_BOOLEAN) {
138 xmlnode_set_attrib(node, "type", "bool");
139 snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
140 xmlnode_set_attrib(node, "value", buf);
141 }
142
143 /* All My Children */
144 for (child = pref->first_child; child != NULL; child = child->sibling)
145 pref_to_xmlnode(node, child);
146 }
147
148 static xmlnode *
149 prefs_to_xmlnode(void)
150 {
151 xmlnode *node;
152 struct gaim_pref *pref, *child;
153
154 pref = &prefs;
155
156 /* Create the root preference node */
157 node = xmlnode_new("pref");
158 xmlnode_set_attrib(node, "version", "1");
159 xmlnode_set_attrib(node, "name", "/");
160
161 /* All My Children */
162 for (child = pref->first_child; child != NULL; child = child->sibling)
163 pref_to_xmlnode(node, child);
164
165 return node;
166 }
167
168 static void
169 sync_prefs(void)
170 {
171 xmlnode *node;
172 char *data;
173
174 if (!prefs_loaded)
175 {
176 /*
177 * TODO: Call schedule_prefs_save()? Ideally we wouldn't need to.
178 * (prefs.xml should be loaded when gaim_prefs_init is called)
179 */
180 gaim_debug_error("prefs", "Attempted to save prefs before "
181 "they were read!\n");
182 return;
183 }
184
185 node = prefs_to_xmlnode();
186 data = xmlnode_to_formatted_str(node, NULL);
187 gaim_util_write_data_to_file("prefs.xml", data, -1);
188 g_free(data);
189 xmlnode_free(node);
190 }
191
192 static gboolean
193 save_cb(gpointer data)
194 {
195 sync_prefs();
196 save_timer = 0;
197 return FALSE;
198 }
199
200 static void
201 schedule_prefs_save(void)
202 {
203 if (save_timer == 0)
204 save_timer = gaim_timeout_add(5000, save_cb, NULL);
205 }
206
207
208 /*********************************************************************
209 * Reading from disk *
210 *********************************************************************/
211
212 static GList *prefs_stack = NULL;
213
214 static void
215 prefs_start_element_handler (GMarkupParseContext *context,
216 const gchar *element_name,
217 const gchar **attribute_names,
218 const gchar **attribute_values,
219 gpointer user_data,
220 GError **error)
221 {
222 GaimPrefType pref_type = GAIM_PREF_NONE;
223 int i;
224 const char *pref_name = NULL, *pref_value = NULL;
225 GString *pref_name_full;
226 GList *tmp;
227
228 if(strcmp(element_name, "pref") && strcmp(element_name, "item"))
229 return;
230
231 for(i = 0; attribute_names[i]; i++) {
232 if(!strcmp(attribute_names[i], "name")) {
233 pref_name = attribute_values[i];
234 } else if(!strcmp(attribute_names[i], "type")) {
235 if(!strcmp(attribute_values[i], "bool"))
236 pref_type = GAIM_PREF_BOOLEAN;
237 else if(!strcmp(attribute_values[i], "int"))
238 pref_type = GAIM_PREF_INT;
239 else if(!strcmp(attribute_values[i], "string"))
240 pref_type = GAIM_PREF_STRING;
241 else if(!strcmp(attribute_values[i], "stringlist"))
242 pref_type = GAIM_PREF_STRING_LIST;
243 else
244 return;
245 } else if(!strcmp(attribute_names[i], "value")) {
246 pref_value = attribute_values[i];
247 }
248 }
249
250 if(!strcmp(element_name, "item")) {
251 struct gaim_pref *pref;
252
253 pref_name_full = g_string_new("");
254
255 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
256 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
257 pref_name_full = g_string_prepend_c(pref_name_full, '/');
258 }
259
260 pref = find_pref(pref_name_full->str);
261
262 if(pref) {
263 pref->value.stringlist = g_list_append(pref->value.stringlist,
264 g_strdup(pref_value));
265 }
266 } else {
267 if(!pref_name || !strcmp(pref_name, "/"))
268 return;
269
270 pref_name_full = g_string_new(pref_name);
271
272 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
273 pref_name_full = g_string_prepend_c(pref_name_full, '/');
274 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
275 }
276
277 pref_name_full = g_string_prepend_c(pref_name_full, '/');
278
279 switch(pref_type) {
280 case GAIM_PREF_NONE:
281 gaim_prefs_add_none(pref_name_full->str);
282 break;
283 case GAIM_PREF_BOOLEAN:
284 gaim_prefs_set_bool(pref_name_full->str, atoi(pref_value));
285 break;
286 case GAIM_PREF_INT:
287 gaim_prefs_set_int(pref_name_full->str, atoi(pref_value));
288 break;
289 case GAIM_PREF_STRING:
290 gaim_prefs_set_string(pref_name_full->str, pref_value);
291 break;
292 case GAIM_PREF_STRING_LIST:
293 gaim_prefs_set_string_list(pref_name_full->str, NULL);
294 break;
295 }
296 prefs_stack = g_list_prepend(prefs_stack, g_strdup(pref_name));
297 g_string_free(pref_name_full, TRUE);
298 }
299 }
300
301 static void
302 prefs_end_element_handler(GMarkupParseContext *context,
303 const gchar *element_name,
304 gpointer user_data, GError **error)
305 {
306 if(prefs_stack && !strcmp(element_name, "pref")) {
307 g_free(prefs_stack->data);
308 prefs_stack = g_list_delete_link(prefs_stack, prefs_stack);
309 }
310 }
311
312 static GMarkupParser prefs_parser = {
313 prefs_start_element_handler,
314 prefs_end_element_handler,
315 NULL,
316 NULL,
317 NULL
318 };
319
320 gboolean
321 gaim_prefs_load()
322 {
323 gchar *filename = g_build_filename(gaim_user_dir(), "prefs.xml", NULL);
324 gchar *contents = NULL;
325 gsize length;
326 GMarkupParseContext *context;
327 GError *error = NULL;
328
329 if (!filename) {
330 prefs_loaded = TRUE;
331 return FALSE;
332 }
333
334 gaim_debug_info("prefs", "Reading %s\n", filename);
335
336 if(!g_file_get_contents(filename, &contents, &length, &error)) {
337 #ifndef _WIN32
338 g_free(filename);
339 g_error_free(error);
340
341 error = NULL;
342
343 filename = g_build_filename(SYSCONFDIR, "gaim", "prefs.xml", NULL);
344
345 gaim_debug_info("prefs", "Reading %s\n", filename);
346
347 if (!g_file_get_contents(filename, &contents, &length, &error)) {
348 gaim_debug_error("prefs", "Error reading prefs: %s\n",
349 error->message);
350 g_error_free(error);
351 g_free(filename);
352 prefs_loaded = TRUE;
353
354 return FALSE;
355 }
356 #else /* _WIN32 */
357 gaim_debug_error("prefs", "Error reading prefs: %s\n",
358 error->message);
359 g_error_free(error);
360 g_free(filename);
361 prefs_loaded = TRUE;
362
363 return FALSE;
364 #endif /* _WIN32 */
365 }
366
367 context = g_markup_parse_context_new(&prefs_parser, 0, NULL, NULL);
368
369 if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
370 g_markup_parse_context_free(context);
371 g_free(contents);
372 g_free(filename);
373 prefs_loaded = TRUE;
374
375 return FALSE;
376 }
377
378 if(!g_markup_parse_context_end_parse(context, NULL)) {
379 gaim_debug_error("prefs", "Error parsing %s\n", filename);
380 g_markup_parse_context_free(context);
381 g_free(contents);
382 g_free(filename);
383 prefs_loaded = TRUE;
384
385 return FALSE;
386 }
387
388 gaim_debug_info("prefs", "Finished reading %s\n", filename);
389 g_markup_parse_context_free(context);
390 g_free(contents);
391 g_free(filename);
392 prefs_loaded = TRUE;
393
394 /* I introduced a bug in 2.0.0beta2. This fixes the broken
395 * scores on upgrade. This can be removed sometime shortly
396 * after 2.0.0 final is released. -- rlaager */
397 if (gaim_prefs_get_int("/core/status/scores/offline") == -500 &&
398 gaim_prefs_get_int("/core/status/scores/available") == 100 &&
399 gaim_prefs_get_int("/core/status/scores/invisible") == -50 &&
400 gaim_prefs_get_int("/core/status/scores/away") == -100 &&
401 gaim_prefs_get_int("/core/status/scores/extended_away") == -200 &&
402 gaim_prefs_get_int("/core/status/scores/idle") == -400)
403 {
404 gaim_prefs_set_int("/core/status/scores/idle", -10);
405 }
406
407 return TRUE;
408 }
409
410
411
412 static void
413 prefs_save_cb(const char *name, GaimPrefType type, gconstpointer val,
414 gpointer user_data)
415 {
416
417 if(!prefs_loaded)
418 return;
419
420 gaim_debug_misc("prefs", "%s changed, scheduling save.\n", name);
421
422 schedule_prefs_save();
423 }
424
425 static char *
426 get_path_dirname(const char *name)
427 {
428 char *c, *str;
429
430 str = g_strdup(name);
431
432 if ((c = strrchr(str, '/')) != NULL) {
433 *c = '\0';
434
435 if (*str == '\0') {
436 g_free(str);
437
438 str = g_strdup("/");
439 }
440 }
441 else {
442 g_free(str);
443
444 str = g_strdup(".");
445 }
446
447 return str;
448 }
449
450 static char *
451 get_path_basename(const char *name)
452 {
453 const char *c;
454
455 if ((c = strrchr(name, '/')) != NULL)
456 return g_strdup(c + 1);
457
458 return g_strdup(name);
459 }
460
461 static char *
462 pref_full_name(struct gaim_pref *pref)
463 {
464 GString *name;
465 struct gaim_pref *parent;
466
467 if(!pref)
468 return NULL;
469
470 if(pref == &prefs)
471 return g_strdup("/");
472
473 name = g_string_new(pref->name);
474 parent = pref->parent;
475
476 for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
477 name = g_string_prepend_c(name, '/');
478 name = g_string_prepend(name, parent->name);
479 }
480 name = g_string_prepend_c(name, '/');
481 return g_string_free(name, FALSE);
482 }
483
484 static struct gaim_pref *
485 find_pref_parent(const char *name)
486 {
487 char *parent_name = get_path_dirname(name);
488 struct gaim_pref *ret = &prefs;
489
490 if(strcmp(parent_name, "/")) {
491 ret = find_pref(parent_name);
492 }
493
494 g_free(parent_name);
495 return ret;
496 }
497
498 static void
499 free_pref_value(struct gaim_pref *pref)
500 {
501 switch(pref->type) {
502 case GAIM_PREF_BOOLEAN:
503 pref->value.boolean = FALSE;
504 break;
505 case GAIM_PREF_INT:
506 pref->value.integer = 0;
507 break;
508 case GAIM_PREF_STRING:
509 g_free(pref->value.string);
510 pref->value.string = NULL;
511 break;
512 case GAIM_PREF_STRING_LIST:
513 {
514 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
515 g_list_free(pref->value.stringlist);
516 } break;
517 case GAIM_PREF_NONE:
518 break;
519 }
520 }
521
522 static struct gaim_pref *
523 add_pref(GaimPrefType type, const char *name)
524 {
525 struct gaim_pref *parent;
526 struct gaim_pref *me;
527 struct gaim_pref *sibling;
528 char *my_name;
529
530 parent = find_pref_parent(name);
531
532 if(!parent)
533 return NULL;
534
535 my_name = get_path_basename(name);
536
537 for(sibling = parent->first_child; sibling; sibling = sibling->sibling) {
538 if(!strcmp(sibling->name, my_name)) {
539 g_free(my_name);
540 return NULL;
541 }
542 }
543
544 me = g_new0(struct gaim_pref, 1);
545 me->type = type;
546 me->name = my_name;
547
548 me->parent = parent;
549 if(parent->first_child) {
550 /* blatant abuse of a for loop */
551 for(sibling = parent->first_child; sibling->sibling;
552 sibling = sibling->sibling);
553 sibling->sibling = me;
554 } else {
555 parent->first_child = me;
556 }
557
558 g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me);
559
560 return me;
561 }
562
563 void
564 gaim_prefs_add_none(const char *name)
565 {
566 add_pref(GAIM_PREF_NONE, name);
567 }
568
569 void
570 gaim_prefs_add_bool(const char *name, gboolean value)
571 {
572 struct gaim_pref *pref = add_pref(GAIM_PREF_BOOLEAN, name);
573
574 if(!pref)
575 return;
576
577 pref->value.boolean = value;
578 }
579
580 void
581 gaim_prefs_add_int(const char *name, int value)
582 {
583 struct gaim_pref *pref = add_pref(GAIM_PREF_INT, name);
584
585 if(!pref)
586 return;
587
588 pref->value.integer = value;
589 }
590
591 void
592 gaim_prefs_add_string(const char *name, const char *value)
593 {
594 struct gaim_pref *pref = add_pref(GAIM_PREF_STRING, name);
595
596 if(!pref)
597 return;
598
599 pref->value.string = g_strdup(value);
600 }
601
602 void
603 gaim_prefs_add_string_list(const char *name, GList *value)
604 {
605 struct gaim_pref *pref = add_pref(GAIM_PREF_STRING_LIST, name);
606 GList *tmp;
607
608 if(!pref)
609 return;
610
611 for(tmp = value; tmp; tmp = tmp->next)
612 pref->value.stringlist = g_list_append(pref->value.stringlist,
613 g_strdup(tmp->data));
614 }
615
616 static void
617 remove_pref(struct gaim_pref *pref)
618 {
619 char *name;
620
621 if(!pref || pref == &prefs)
622 return;
623
624 while(pref->first_child)
625 remove_pref(pref->first_child);
626
627 if(pref->parent->first_child == pref) {
628 pref->parent->first_child = pref->sibling;
629 } else {
630 struct gaim_pref *sib = pref->parent->first_child;
631 while(sib && sib->sibling != pref)
632 sib = sib->sibling;
633 if(sib)
634 sib->sibling = pref->sibling;
635 }
636
637 name = pref_full_name(pref);
638
639 gaim_debug_info("prefs", "removing pref %s\n", name);
640
641 g_hash_table_remove(prefs_hash, name);
642 g_free(name);
643
644 free_pref_value(pref);
645
646 g_slist_free(pref->callbacks);
647 g_free(pref->name);
648 g_free(pref);
649 }
650
651 void
652 gaim_prefs_remove(const char *name)
653 {
654 struct gaim_pref *pref = find_pref(name);
655
656 if(!pref)
657 return;
658
659 remove_pref(pref);
660 }
661
662 void
663 gaim_prefs_destroy()
664 {
665 gaim_prefs_remove("/");
666 }
667
668 static void
669 do_callbacks(const char* name, struct gaim_pref *pref)
670 {
671 GSList *cbs;
672 struct gaim_pref *cb_pref;
673 for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) {
674 for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) {
675 struct pref_cb *cb = cbs->data;
676 cb->func(name, pref->type, pref->value.generic, cb->data);
677 }
678 }
679 }
680
681 void
682 gaim_prefs_trigger_callback(const char *name)
683 {
684 struct gaim_pref *pref = find_pref(name);
685
686 if(!pref) {
687 gaim_debug_error("prefs",
688 "gaim_prefs_trigger_callback: Unknown pref %s\n", name);
689 return;
690 }
691
692 do_callbacks(name, pref);
693 }
694
695 void
696 gaim_prefs_set_generic(const char *name, gpointer value)
697 {
698 struct gaim_pref *pref = find_pref(name);
699
700 if(!pref) {
701 gaim_debug_error("prefs",
702 "gaim_prefs_set_generic: Unknown pref %s\n", name);
703 return;
704 }
705
706 pref->value.generic = value;
707 do_callbacks(name, pref);
708 }
709
710 void
711 gaim_prefs_set_bool(const char *name, gboolean value)
712 {
713 struct gaim_pref *pref = find_pref(name);
714
715 if(pref) {
716 if(pref->type != GAIM_PREF_BOOLEAN) {
717 gaim_debug_error("prefs",
718 "gaim_prefs_set_bool: %s not a boolean pref\n", name);
719 return;
720 }
721
722 if(pref->value.boolean != value) {
723 pref->value.boolean = value;
724 do_callbacks(name, pref);
725 }
726 } else {
727 gaim_prefs_add_bool(name, value);
728 }
729 }
730
731 void
732 gaim_prefs_set_int(const char *name, int value)
733 {
734 struct gaim_pref *pref = find_pref(name);
735
736 if(pref) {
737 if(pref->type != GAIM_PREF_INT) {
738 gaim_debug_error("prefs",
739 "gaim_prefs_set_int: %s not an integer pref\n", name);
740 return;
741 }
742
743 if(pref->value.integer != value) {
744 pref->value.integer = value;
745 do_callbacks(name, pref);
746 }
747 } else {
748 gaim_prefs_add_int(name, value);
749 }
750 }
751
752 void
753 gaim_prefs_set_string(const char *name, const char *value)
754 {
755 struct gaim_pref *pref = find_pref(name);
756
757 if(pref) {
758 if(pref->type != GAIM_PREF_STRING) {
759 gaim_debug_error("prefs",
760 "gaim_prefs_set_string: %s not a string pref\n", name);
761 return;
762 }
763
764 if((value && !pref->value.string) ||
765 (!value && pref->value.string) ||
766 (value && pref->value.string &&
767 strcmp(pref->value.string, value))) {
768 g_free(pref->value.string);
769 pref->value.string = g_strdup(value);
770 do_callbacks(name, pref);
771 }
772 } else {
773 gaim_prefs_add_string(name, value);
774 }
775 }
776
777 void
778 gaim_prefs_set_string_list(const char *name, GList *value)
779 {
780 struct gaim_pref *pref = find_pref(name);
781 if(pref) {
782 GList *tmp;
783
784 if(pref->type != GAIM_PREF_STRING_LIST) {
785 gaim_debug_error("prefs",
786 "gaim_prefs_set_string_list: %s not a string list pref\n",
787 name);
788 return;
789 }
790
791 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
792 g_list_free(pref->value.stringlist);
793 pref->value.stringlist = NULL;
794
795 for(tmp = value; tmp; tmp = tmp->next)
796 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
797 g_strdup(tmp->data));
798 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
799
800 do_callbacks(name, pref);
801
802 } else {
803 gaim_prefs_add_string_list(name, value);
804 }
805 }
806
807 gboolean
808 gaim_prefs_exists(const char *name)
809 {
810 struct gaim_pref *pref = find_pref(name);
811
812 if (pref != NULL)
813 return TRUE;
814
815 return FALSE;
816 }
817
818 GaimPrefType
819 gaim_prefs_get_type(const char *name)
820 {
821 struct gaim_pref *pref = find_pref(name);
822
823 if (pref == NULL)
824 return GAIM_PREF_NONE;
825
826 return (pref->type);
827 }
828
829 gboolean
830 gaim_prefs_get_bool(const char *name)
831 {
832 struct gaim_pref *pref = find_pref(name);
833
834 if(!pref) {
835 gaim_debug_error("prefs",
836 "gaim_prefs_get_bool: Unknown pref %s\n", name);
837 return FALSE;
838 } else if(pref->type != GAIM_PREF_BOOLEAN) {
839 gaim_debug_error("prefs",
840 "gaim_prefs_get_bool: %s not a boolean pref\n", name);
841 return FALSE;
842 }
843
844 return pref->value.boolean;
845 }
846
847 int
848 gaim_prefs_get_int(const char *name)
849 {
850 struct gaim_pref *pref = find_pref(name);
851
852 if(!pref) {
853 gaim_debug_error("prefs",
854 "gaim_prefs_get_int: Unknown pref %s\n", name);
855 return 0;
856 } else if(pref->type != GAIM_PREF_INT) {
857 gaim_debug_error("prefs",
858 "gaim_prefs_get_int: %s not an integer pref\n", name);
859 return 0;
860 }
861
862 return pref->value.integer;
863 }
864
865 const char *
866 gaim_prefs_get_string(const char *name)
867 {
868 struct gaim_pref *pref = find_pref(name);
869
870 if(!pref) {
871 gaim_debug_error("prefs",
872 "gaim_prefs_get_string: Unknown pref %s\n", name);
873 return NULL;
874 } else if(pref->type != GAIM_PREF_STRING) {
875 gaim_debug_error("prefs",
876 "gaim_prefs_get_string: %s not a string pref\n", name);
877 return NULL;
878 }
879
880 return pref->value.string;
881 }
882
883 GList *
884 gaim_prefs_get_string_list(const char *name)
885 {
886 struct gaim_pref *pref = find_pref(name);
887 GList *ret = NULL, *tmp;
888
889 if(!pref) {
890 gaim_debug_error("prefs",
891 "gaim_prefs_get_string_list: Unknown pref %s\n", name);
892 return NULL;
893 } else if(pref->type != GAIM_PREF_STRING_LIST) {
894 gaim_debug_error("prefs",
895 "gaim_prefs_get_string_list: %s not a string list pref\n", name);
896 return NULL;
897 }
898
899 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
900 ret = g_list_prepend(ret, g_strdup(tmp->data));
901 ret = g_list_reverse(ret);
902
903 return ret;
904 }
905
906 void
907 gaim_prefs_rename(const char *oldname, const char *newname)
908 {
909 struct gaim_pref *oldpref, *newpref;
910
911 oldpref = find_pref(oldname);
912
913 /* it's already been renamed, call off the dogs */
914 if(!oldpref)
915 return;
916
917 if (oldpref->first_child != NULL) /* can't rename parents */
918 {
919 gaim_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
920 return;
921 }
922
923
924 newpref = find_pref(newname);
925
926 if (newpref == NULL)
927 {
928 gaim_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
929 return;
930 }
931
932 if (oldpref->type != newpref->type)
933 {
934 gaim_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
935 return;
936 }
937
938 gaim_debug_info("prefs", "Renaming %s to %s\n", oldname, newname);
939
940 switch(oldpref->type) {
941 case GAIM_PREF_NONE:
942 break;
943 case GAIM_PREF_BOOLEAN:
944 gaim_prefs_set_bool(newname, oldpref->value.boolean);
945 break;
946 case GAIM_PREF_INT:
947 gaim_prefs_set_int(newname, oldpref->value.integer);
948 break;
949 case GAIM_PREF_STRING:
950 gaim_prefs_set_string(newname, oldpref->value.string);
951 break;
952 case GAIM_PREF_STRING_LIST:
953 gaim_prefs_set_string_list(newname, oldpref->value.stringlist);
954 break;
955 }
956
957 remove_pref(oldpref);
958 }
959
960 void
961 gaim_prefs_rename_boolean_toggle(const char *oldname, const char *newname)
962 {
963 struct gaim_pref *oldpref, *newpref;
964
965 oldpref = find_pref(oldname);
966
967 /* it's already been renamed, call off the cats */
968 if(!oldpref)
969 return;
970
971 if (oldpref->type != GAIM_PREF_BOOLEAN)
972 {
973 gaim_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname, newname);
974 return;
975 }
976
977 if (oldpref->first_child != NULL) /* can't rename parents */
978 {
979 gaim_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
980 return;
981 }
982
983
984 newpref = find_pref(newname);
985
986 if (newpref == NULL)
987 {
988 gaim_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
989 return;
990 }
991
992 if (oldpref->type != newpref->type)
993 {
994 gaim_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
995 return;
996 }
997
998 gaim_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname, newname);
999 gaim_prefs_set_bool(newname, !(oldpref->value.boolean));
1000
1001 remove_pref(oldpref);
1002 }
1003
1004 guint
1005 gaim_prefs_connect_callback(void *handle, const char *name, GaimPrefCallback func, gpointer data)
1006 {
1007 struct gaim_pref *pref;
1008 struct pref_cb *cb;
1009 static guint cb_id = 0;
1010
1011 pref = find_pref(name);
1012 if (pref == NULL)
1013 return 0;
1014
1015 cb = g_new0(struct pref_cb, 1);
1016
1017 cb->func = func;
1018 cb->data = data;
1019 cb->id = ++cb_id;
1020 cb->handle = handle;
1021
1022 pref->callbacks = g_slist_append(pref->callbacks, cb);
1023
1024 return cb->id;
1025 }
1026
1027 static gboolean
1028 disco_callback_helper(struct gaim_pref *pref, guint callback_id)
1029 {
1030 GSList *cbs;
1031 struct gaim_pref *child;
1032
1033 if(!pref)
1034 return FALSE;
1035
1036 for(cbs = pref->callbacks; cbs; cbs = cbs->next) {
1037 struct pref_cb *cb = cbs->data;
1038 if(cb->id == callback_id) {
1039 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1040 g_free(cb);
1041 return TRUE;
1042 }
1043 }
1044
1045 for(child = pref->first_child; child; child = child->sibling) {
1046 if(disco_callback_helper(child, callback_id))
1047 return TRUE;
1048 }
1049
1050 return FALSE;
1051 }
1052
1053 void
1054 gaim_prefs_disconnect_callback(guint callback_id)
1055 {
1056 disco_callback_helper(&prefs, callback_id);
1057 }
1058
1059 static void
1060 disco_callback_helper_handle(struct gaim_pref *pref, void *handle)
1061 {
1062 GSList *cbs;
1063 struct gaim_pref *child;
1064
1065 if(!pref)
1066 return;
1067
1068 cbs = pref->callbacks;
1069 while (cbs != NULL) {
1070 struct pref_cb *cb = cbs->data;
1071 if(cb->handle == handle) {
1072 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1073 g_free(cb);
1074 cbs = pref->callbacks;
1075 } else
1076 cbs = cbs->next;
1077 }
1078
1079 for(child = pref->first_child; child; child = child->sibling)
1080 disco_callback_helper_handle(child, handle);
1081 }
1082
1083 void
1084 gaim_prefs_disconnect_by_handle(void *handle)
1085 {
1086 g_return_if_fail(handle != NULL);
1087
1088 disco_callback_helper_handle(&prefs, handle);
1089 }
1090
1091 void
1092 gaim_prefs_update_old()
1093 {
1094 /* Remove some no-longer-used prefs */
1095 gaim_prefs_remove("/core/away/auto_response/enabled");
1096 gaim_prefs_remove("/core/away/auto_response/idle_only");
1097 gaim_prefs_remove("/core/away/auto_response/in_active_conv");
1098 gaim_prefs_remove("/core/away/auto_response/sec_before_resend");
1099 gaim_prefs_remove("/core/away/auto_response");
1100 gaim_prefs_remove("/core/away/default_message");
1101 gaim_prefs_remove("/core/buddies/use_server_alias");
1102 gaim_prefs_remove("/core/conversations/away_back_on_send");
1103 gaim_prefs_remove("/core/conversations/send_urls_as_links");
1104 gaim_prefs_remove("/core/conversations/im/show_login");
1105 gaim_prefs_remove("/core/conversations/chat/show_join");
1106 gaim_prefs_remove("/core/conversations/chat/show_leave");
1107 gaim_prefs_remove("/core/conversations/combine_chat_im");
1108 gaim_prefs_remove("/core/conversations/use_alias_for_title");
1109 gaim_prefs_remove("/core/logging/log_signon_signoff");
1110 gaim_prefs_remove("/core/logging/log_idle_state");
1111 gaim_prefs_remove("/core/logging/log_away_state");
1112 gaim_prefs_remove("/core/logging/log_own_states");
1113 gaim_prefs_remove("/core/status/scores/hidden");
1114 gaim_prefs_remove("/plugins/core/autorecon/hide_connected_error");
1115 gaim_prefs_remove("/plugins/core/autorecon/hide_connecting_error");
1116 gaim_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog");
1117 gaim_prefs_remove("/plugins/core/autorecon/restore_state");
1118 gaim_prefs_remove("/plugins/core/autorecon");
1119 }
1120
1121 void *
1122 gaim_prefs_get_handle(void)
1123 {
1124 static int handle;
1125
1126 return &handle;
1127 }
1128
1129 void
1130 gaim_prefs_init(void)
1131 {
1132 void *handle = gaim_prefs_get_handle();
1133
1134 prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1135
1136 gaim_prefs_connect_callback(handle, "/", prefs_save_cb, NULL);
1137
1138 gaim_prefs_add_none("/core");
1139 gaim_prefs_add_none("/plugins");
1140 gaim_prefs_add_none("/plugins/core");
1141 gaim_prefs_add_none("/plugins/lopl");
1142 gaim_prefs_add_none("/plugins/prpl");
1143
1144 /* Away */
1145 gaim_prefs_add_none("/core/away");
1146 gaim_prefs_add_string("/core/away/idle_reporting", "system");
1147 gaim_prefs_add_bool("/core/away/away_when_idle", TRUE);
1148 gaim_prefs_add_int("/core/away/mins_before_away", 5);
1149
1150 /* Away -> Auto-Reply */
1151 if (!gaim_prefs_exists("/core/away/auto_response/enabled") ||
1152 !gaim_prefs_exists("/core/away/auto_response/idle_only"))
1153 {
1154 gaim_prefs_add_string("/core/away/auto_reply", "awayidle");
1155 }
1156 else
1157 {
1158 if (!gaim_prefs_get_bool("/core/away/auto_response/enabled"))
1159 {
1160 gaim_prefs_add_string("/core/away/auto_reply", "never");
1161 }
1162 else
1163 {
1164 if (gaim_prefs_get_bool("/core/away/auto_response/idle_only"))
1165 {
1166 gaim_prefs_add_string("/core/away/auto_reply", "awayidle");
1167 }
1168 else
1169 {
1170 gaim_prefs_add_string("/core/away/auto_reply", "away");
1171 }
1172 }
1173 }
1174
1175 /* Buddies */
1176 gaim_prefs_add_none("/core/buddies");
1177
1178 /* Contact Priority Settings */
1179 gaim_prefs_add_none("/core/contact");
1180 gaim_prefs_add_bool("/core/contact/last_match", FALSE);
1181 gaim_prefs_remove("/core/contact/offline_score");
1182 gaim_prefs_remove("/core/contact/away_score");
1183 gaim_prefs_remove("/core/contact/idle_score");
1184 }
1185
1186 void
1187 gaim_prefs_uninit()
1188 {
1189 if (save_timer != 0)
1190 {
1191 gaim_timeout_remove(save_timer);
1192 save_timer = 0;
1193 sync_prefs();
1194 }
1195
1196 gaim_prefs_disconnect_by_handle(gaim_prefs_get_handle());
1197 }