Standardize all protocol header guard macros.
[pidgin-git.git] / libpurple / prefs.c
blob0f3706ea9ed59b0968491e4d50f13f32f0ad505a
1 /*
2 * purple
4 * Purple 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.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <glib.h>
33 #include "internal.h"
34 #include "prefs.h"
35 #include "debug.h"
36 #include "util.h"
38 static PurplePrefsUiOps *prefs_ui_ops = NULL;
40 struct _PurplePrefCallbackData {
41 PurplePrefCallback func;
42 gpointer data;
43 guint id;
44 void *handle;
45 void *ui_data;
46 char *name;
49 struct pref_cb {
50 PurplePrefCallback func;
51 gpointer data;
52 guint id;
53 void *handle;
54 void *ui_data;
55 char *name;
58 /* TODO: This should use PurpleValues? */
59 struct purple_pref {
60 PurplePrefType type;
61 char *name;
62 union {
63 /* 'generic' is kind of ugly. We use it as an elegant way to refer to
64 the value of this pref when calling callback functions. We could
65 use 'boolean' or 'integer' or any other field... but it feels
66 mildly cleaner to use a gpointer. Maybe it would be best to use a
67 GValue? */
68 gpointer generic;
69 gboolean boolean;
70 int integer;
71 char *string;
72 GList *stringlist;
73 } value;
74 GSList *callbacks;
75 struct purple_pref *parent;
76 struct purple_pref *sibling;
77 struct purple_pref *first_child;
81 static struct purple_pref prefs = {
82 PURPLE_PREF_NONE,
83 NULL,
84 { NULL },
85 NULL,
86 NULL,
87 NULL,
88 NULL
91 static GHashTable *prefs_hash = NULL;
92 static guint save_timer = 0;
93 static gboolean prefs_loaded = FALSE;
94 static GSList *ui_callbacks = NULL;
96 #define PURPLE_PREFS_UI_OP_CALL(member, ...) \
97 { \
98 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \
99 if (uiop && uiop->member) { \
100 uiop->member(__VA_ARGS__); \
101 return; \
105 #define PURPLE_PREFS_UI_OP_CALL_RETURN(member, ...) \
107 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \
108 if (uiop && uiop->member) { \
109 return uiop->member(__VA_ARGS__); \
114 /*********************************************************************
115 * Private utility functions *
116 *********************************************************************/
118 static struct
119 purple_pref *find_pref(const char *name)
121 g_return_val_if_fail(name != NULL && name[0] == '/', NULL);
123 if (name[1] == '\0')
124 return &prefs;
125 else
127 /* When we're initializing, the debug system is
128 * initialized before the prefs system, but debug
129 * calls will end up calling prefs functions, so we
130 * need to deal cleanly here. */
131 if (prefs_hash)
132 return g_hash_table_lookup(prefs_hash, name);
133 else
134 return NULL;
139 /*********************************************************************
140 * Writing to disk *
141 *********************************************************************/
144 * This function recursively creates the PurpleXmlNode tree from the prefs
145 * tree structure. Yay recursion!
147 static void
148 pref_to_xmlnode(PurpleXmlNode *parent, struct purple_pref *pref)
150 PurpleXmlNode *node, *childnode;
151 struct purple_pref *child;
152 char buf[21];
153 GList *cur;
155 /* Create a new node */
156 node = purple_xmlnode_new_child(parent, "pref");
157 purple_xmlnode_set_attrib(node, "name", pref->name);
159 /* Set the type of this node (if type == PURPLE_PREF_NONE then do nothing) */
160 if (pref->type == PURPLE_PREF_INT) {
161 purple_xmlnode_set_attrib(node, "type", "int");
162 g_snprintf(buf, sizeof(buf), "%d", pref->value.integer);
163 purple_xmlnode_set_attrib(node, "value", buf);
165 else if (pref->type == PURPLE_PREF_STRING) {
166 purple_xmlnode_set_attrib(node, "type", "string");
167 purple_xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
169 else if (pref->type == PURPLE_PREF_STRING_LIST) {
170 purple_xmlnode_set_attrib(node, "type", "stringlist");
171 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
173 childnode = purple_xmlnode_new_child(node, "item");
174 purple_xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
177 else if (pref->type == PURPLE_PREF_PATH) {
178 char *encoded = g_filename_to_utf8(pref->value.string ? pref->value.string : "", -1, NULL, NULL, NULL);
179 purple_xmlnode_set_attrib(node, "type", "path");
180 purple_xmlnode_set_attrib(node, "value", encoded);
181 g_free(encoded);
183 else if (pref->type == PURPLE_PREF_PATH_LIST) {
184 purple_xmlnode_set_attrib(node, "type", "pathlist");
185 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
187 char *encoded = g_filename_to_utf8(cur->data ? cur->data : "", -1, NULL, NULL, NULL);
188 childnode = purple_xmlnode_new_child(node, "item");
189 purple_xmlnode_set_attrib(childnode, "value", encoded);
190 g_free(encoded);
193 else if (pref->type == PURPLE_PREF_BOOLEAN) {
194 purple_xmlnode_set_attrib(node, "type", "bool");
195 g_snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
196 purple_xmlnode_set_attrib(node, "value", buf);
199 /* All My Children */
200 for (child = pref->first_child; child != NULL; child = child->sibling)
201 pref_to_xmlnode(node, child);
204 static PurpleXmlNode *
205 prefs_to_xmlnode(void)
207 PurpleXmlNode *node;
208 struct purple_pref *pref, *child;
210 pref = &prefs;
212 /* Create the root preference node */
213 node = purple_xmlnode_new("pref");
214 purple_xmlnode_set_attrib(node, "version", "1");
215 purple_xmlnode_set_attrib(node, "name", "/");
217 /* All My Children */
218 for (child = pref->first_child; child != NULL; child = child->sibling)
219 pref_to_xmlnode(node, child);
221 return node;
224 static void
225 sync_prefs(void)
227 PurpleXmlNode *node;
228 char *data;
230 if (!prefs_loaded)
233 * TODO: Call schedule_prefs_save()? Ideally we wouldn't need to.
234 * (prefs.xml should be loaded when purple_prefs_init is called)
236 purple_debug_error("prefs", "Attempted to save prefs before "
237 "they were read!\n");
238 return;
241 PURPLE_PREFS_UI_OP_CALL(save);
243 node = prefs_to_xmlnode();
244 data = purple_xmlnode_to_formatted_str(node, NULL);
245 purple_util_write_data_to_config_file("prefs.xml", data, -1);
246 g_free(data);
247 purple_xmlnode_free(node);
250 static gboolean
251 save_cb(gpointer data)
253 sync_prefs();
254 save_timer = 0;
255 return FALSE;
258 static void
259 schedule_prefs_save(void)
261 PURPLE_PREFS_UI_OP_CALL(schedule_save);
263 if (save_timer == 0)
264 save_timer = g_timeout_add_seconds(5, save_cb, NULL);
268 /*********************************************************************
269 * Reading from disk *
270 *********************************************************************/
272 static GList *prefs_stack = NULL;
274 static void
275 prefs_start_element_handler (GMarkupParseContext *context,
276 const gchar *element_name,
277 const gchar **attribute_names,
278 const gchar **attribute_values,
279 gpointer user_data,
280 GError **error)
282 PurplePrefType pref_type = PURPLE_PREF_NONE;
283 int i;
284 const char *pref_name = NULL, *pref_value = NULL;
285 GString *pref_name_full;
286 GList *tmp;
288 if(!purple_strequal(element_name, "pref") &&
289 !purple_strequal(element_name, "item"))
290 return;
292 for(i = 0; attribute_names[i]; i++) {
293 if(purple_strequal(attribute_names[i], "name")) {
294 pref_name = attribute_values[i];
295 } else if(purple_strequal(attribute_names[i], "type")) {
296 if(purple_strequal(attribute_values[i], "bool"))
297 pref_type = PURPLE_PREF_BOOLEAN;
298 else if(purple_strequal(attribute_values[i], "int"))
299 pref_type = PURPLE_PREF_INT;
300 else if(purple_strequal(attribute_values[i], "string"))
301 pref_type = PURPLE_PREF_STRING;
302 else if(purple_strequal(attribute_values[i], "stringlist"))
303 pref_type = PURPLE_PREF_STRING_LIST;
304 else if(purple_strequal(attribute_values[i], "path"))
305 pref_type = PURPLE_PREF_PATH;
306 else if(purple_strequal(attribute_values[i], "pathlist"))
307 pref_type = PURPLE_PREF_PATH_LIST;
308 else
309 return;
310 } else if(purple_strequal(attribute_names[i], "value")) {
311 pref_value = attribute_values[i];
315 if ((pref_type == PURPLE_PREF_BOOLEAN || pref_type == PURPLE_PREF_INT) &&
316 pref_value == NULL) {
317 /* Missing a value attribute */
318 return;
321 if(purple_strequal(element_name, "item")) {
322 struct purple_pref *pref;
324 pref_name_full = g_string_new("");
326 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
327 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
328 pref_name_full = g_string_prepend_c(pref_name_full, '/');
331 pref = find_pref(pref_name_full->str);
333 if(pref) {
334 if(pref->type == PURPLE_PREF_STRING_LIST) {
335 pref->value.stringlist = g_list_append(pref->value.stringlist,
336 g_strdup(pref_value));
337 } else if(pref->type == PURPLE_PREF_PATH_LIST) {
338 pref->value.stringlist = g_list_append(pref->value.stringlist,
339 g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL));
342 g_string_free(pref_name_full, TRUE);
343 } else {
344 char *decoded;
346 if(!pref_name || purple_strequal(pref_name, "/"))
347 return;
349 pref_name_full = g_string_new(pref_name);
351 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
352 pref_name_full = g_string_prepend_c(pref_name_full, '/');
353 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
356 pref_name_full = g_string_prepend_c(pref_name_full, '/');
358 switch(pref_type) {
359 case PURPLE_PREF_NONE:
360 purple_prefs_add_none(pref_name_full->str);
361 break;
362 case PURPLE_PREF_BOOLEAN:
363 purple_prefs_set_bool(pref_name_full->str, atoi(pref_value));
364 break;
365 case PURPLE_PREF_INT:
366 purple_prefs_set_int(pref_name_full->str, atoi(pref_value));
367 break;
368 case PURPLE_PREF_STRING:
369 purple_prefs_set_string(pref_name_full->str, pref_value);
370 break;
371 case PURPLE_PREF_STRING_LIST:
372 purple_prefs_set_string_list(pref_name_full->str, NULL);
373 break;
374 case PURPLE_PREF_PATH:
375 if (pref_value) {
376 decoded = g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL);
377 purple_prefs_set_path(pref_name_full->str, decoded);
378 g_free(decoded);
379 } else {
380 purple_prefs_set_path(pref_name_full->str, NULL);
382 break;
383 case PURPLE_PREF_PATH_LIST:
384 purple_prefs_set_path_list(pref_name_full->str, NULL);
385 break;
387 prefs_stack = g_list_prepend(prefs_stack, g_strdup(pref_name));
388 g_string_free(pref_name_full, TRUE);
392 static void
393 prefs_end_element_handler(GMarkupParseContext *context,
394 const gchar *element_name,
395 gpointer user_data, GError **error)
397 if(prefs_stack && purple_strequal(element_name, "pref")) {
398 g_free(prefs_stack->data);
399 prefs_stack = g_list_delete_link(prefs_stack, prefs_stack);
403 static GMarkupParser prefs_parser = {
404 prefs_start_element_handler,
405 prefs_end_element_handler,
406 NULL,
407 NULL,
408 NULL
411 gboolean
412 purple_prefs_load()
414 gchar *filename;
415 gchar *contents = NULL;
416 gsize length;
417 GMarkupParseContext *context;
418 GError *error = NULL;
420 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
422 if (uiop && uiop->load) {
423 prefs_loaded = TRUE;
424 return uiop->load();
427 filename = g_build_filename(purple_config_dir(), "prefs.xml", NULL);
429 if (!filename) {
430 prefs_loaded = TRUE;
431 return FALSE;
434 purple_debug_misc("prefs", "Reading %s", filename);
436 if(!g_file_get_contents(filename, &contents, &length, &error)) {
437 const gchar *sysconfdir = PURPLE_SYSCONFDIR;
438 g_free(filename);
439 g_error_free(error);
441 error = NULL;
443 #ifndef __COVERITY__
444 /* coverity dead_error_line false positive */
445 if (sysconfdir == NULL)
446 sysconfdir = "";
447 #endif
448 filename = g_build_filename(sysconfdir, "purple", "prefs.xml", NULL);
450 purple_debug_info("prefs", "Reading %s\n", filename);
452 if (!g_file_get_contents(filename, &contents, &length, &error)) {
453 purple_debug_error("prefs", "Error reading prefs: %s\n",
454 error->message);
455 g_error_free(error);
456 g_free(filename);
457 prefs_loaded = TRUE;
459 return FALSE;
463 context = g_markup_parse_context_new(&prefs_parser, 0, NULL, NULL);
465 if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
466 g_markup_parse_context_free(context);
467 g_free(contents);
468 g_free(filename);
469 prefs_loaded = TRUE;
471 return FALSE;
474 if(!g_markup_parse_context_end_parse(context, NULL)) {
475 purple_debug_error("prefs", "Error parsing %s\n", filename);
476 g_markup_parse_context_free(context);
477 g_free(contents);
478 g_free(filename);
479 prefs_loaded = TRUE;
481 return FALSE;
484 if (purple_debug_is_verbose())
485 purple_debug_misc("prefs", "Finished reading %s", filename);
486 g_markup_parse_context_free(context);
487 g_free(contents);
488 g_free(filename);
489 prefs_loaded = TRUE;
491 return TRUE;
496 static void
497 prefs_save_cb(const char *name, PurplePrefType type, gconstpointer val,
498 gpointer user_data)
501 if(!prefs_loaded)
502 return;
504 purple_debug_misc("prefs", "%s changed, scheduling save.\n", name);
506 schedule_prefs_save();
509 static char *
510 get_path_dirname(const char *name)
512 char *c, *str;
514 str = g_strdup(name);
516 if ((c = strrchr(str, '/')) != NULL) {
517 *c = '\0';
519 if (*str == '\0') {
520 g_free(str);
522 str = g_strdup("/");
525 else {
526 g_free(str);
528 str = g_strdup(".");
531 return str;
534 static char *
535 get_path_basename(const char *name)
537 const char *c;
539 if ((c = strrchr(name, '/')) != NULL)
540 return g_strdup(c + 1);
542 return g_strdup(name);
545 static char *
546 pref_full_name(struct purple_pref *pref)
548 GString *name;
549 struct purple_pref *parent;
551 if(!pref)
552 return NULL;
554 if(pref == &prefs)
555 return g_strdup("/");
557 name = g_string_new(pref->name);
559 for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
560 name = g_string_prepend_c(name, '/');
561 name = g_string_prepend(name, parent->name);
563 name = g_string_prepend_c(name, '/');
564 return g_string_free(name, FALSE);
567 static struct purple_pref *
568 find_pref_parent(const char *name)
570 char *parent_name = get_path_dirname(name);
571 struct purple_pref *ret = &prefs;
573 if(!purple_strequal(parent_name, "/")) {
574 ret = find_pref(parent_name);
577 g_free(parent_name);
578 return ret;
581 static void
582 free_pref_value(struct purple_pref *pref)
584 switch(pref->type) {
585 case PURPLE_PREF_BOOLEAN:
586 pref->value.boolean = FALSE;
587 break;
588 case PURPLE_PREF_INT:
589 pref->value.integer = 0;
590 break;
591 case PURPLE_PREF_STRING:
592 case PURPLE_PREF_PATH:
593 g_free(pref->value.string);
594 pref->value.string = NULL;
595 break;
596 case PURPLE_PREF_STRING_LIST:
597 case PURPLE_PREF_PATH_LIST:
599 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
600 g_list_free(pref->value.stringlist);
601 } break;
602 case PURPLE_PREF_NONE:
603 break;
607 static struct purple_pref *
608 add_pref(PurplePrefType type, const char *name)
610 struct purple_pref *parent;
611 struct purple_pref *me;
612 struct purple_pref *sibling;
613 char *my_name;
615 parent = find_pref_parent(name);
617 g_return_val_if_fail(parent, NULL);
619 my_name = get_path_basename(name);
621 for(sibling = parent->first_child; sibling; sibling = sibling->sibling) {
622 if(purple_strequal(sibling->name, my_name)) {
623 g_free(my_name);
624 return NULL;
628 me = g_new0(struct purple_pref, 1);
629 me->type = type;
630 me->name = my_name;
632 me->parent = parent;
633 if(parent->first_child) {
634 /* blatant abuse of a for loop */
635 for(sibling = parent->first_child; sibling->sibling;
636 sibling = sibling->sibling);
637 sibling->sibling = me;
638 } else {
639 parent->first_child = me;
642 g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me);
644 return me;
647 void
648 purple_prefs_add_none(const char *name)
650 PURPLE_PREFS_UI_OP_CALL(add_none, name);
652 add_pref(PURPLE_PREF_NONE, name);
655 void
656 purple_prefs_add_bool(const char *name, gboolean value)
658 struct purple_pref *pref;
660 PURPLE_PREFS_UI_OP_CALL(add_bool, name, value);
662 pref = add_pref(PURPLE_PREF_BOOLEAN, name);
664 if(!pref)
665 return;
667 pref->value.boolean = value;
670 void
671 purple_prefs_add_int(const char *name, int value)
673 struct purple_pref *pref;
675 PURPLE_PREFS_UI_OP_CALL(add_int, name, value);
677 pref = add_pref(PURPLE_PREF_INT, name);
679 if(!pref)
680 return;
682 pref->value.integer = value;
685 void
686 purple_prefs_add_string(const char *name, const char *value)
688 struct purple_pref *pref;
690 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
691 purple_debug_error("prefs", "purple_prefs_add_string: Cannot store invalid UTF8 for string pref %s\n", name);
692 return;
695 PURPLE_PREFS_UI_OP_CALL(add_string, name, value);
697 pref = add_pref(PURPLE_PREF_STRING, name);
699 if(!pref)
700 return;
702 pref->value.string = g_strdup(value);
705 void
706 purple_prefs_add_string_list(const char *name, GList *value)
708 struct purple_pref *pref;
709 GList *tmp;
711 PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value);
713 pref = add_pref(PURPLE_PREF_STRING_LIST, name);
715 if(!pref)
716 return;
718 for(tmp = value; tmp; tmp = tmp->next) {
719 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
720 purple_debug_error("prefs", "purple_prefs_add_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
721 continue;
723 pref->value.stringlist = g_list_append(pref->value.stringlist,
724 g_strdup(tmp->data));
728 void
729 purple_prefs_add_path(const char *name, const char *value)
731 struct purple_pref *pref;
733 /* re-use the string UI OP */
734 PURPLE_PREFS_UI_OP_CALL(add_string, name, value);
736 pref = add_pref(PURPLE_PREF_PATH, name);
738 if(!pref)
739 return;
741 pref->value.string = g_strdup(value);
744 void
745 purple_prefs_add_path_list(const char *name, GList *value)
747 struct purple_pref *pref;
748 GList *tmp;
750 /* re-use the string list UI OP */
751 PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value);
753 pref = add_pref(PURPLE_PREF_PATH_LIST, name);
755 if(!pref)
756 return;
758 for(tmp = value; tmp; tmp = tmp->next)
759 pref->value.stringlist = g_list_append(pref->value.stringlist,
760 g_strdup(tmp->data));
764 static void
765 remove_pref(struct purple_pref *pref)
767 char *name;
768 GSList *l;
770 if(!pref)
771 return;
773 while(pref->first_child)
774 remove_pref(pref->first_child);
776 if(pref == &prefs)
777 return;
779 if(pref->parent->first_child == pref) {
780 pref->parent->first_child = pref->sibling;
781 } else {
782 struct purple_pref *sib = pref->parent->first_child;
783 while(sib && sib->sibling != pref)
784 sib = sib->sibling;
785 if(sib)
786 sib->sibling = pref->sibling;
789 name = pref_full_name(pref);
791 if (prefs_loaded)
792 purple_debug_info("prefs", "removing pref %s\n", name);
794 g_hash_table_remove(prefs_hash, name);
795 g_free(name);
797 free_pref_value(pref);
799 while((l = pref->callbacks) != NULL) {
800 pref->callbacks = pref->callbacks->next;
801 g_free(l->data);
802 g_slist_free_1(l);
804 g_free(pref->name);
805 g_free(pref);
808 void
809 purple_prefs_remove(const char *name)
811 struct purple_pref *pref;
813 PURPLE_PREFS_UI_OP_CALL(remove, name);
815 pref = find_pref(name);
817 if(!pref)
818 return;
820 remove_pref(pref);
823 void
824 purple_prefs_destroy()
826 purple_prefs_remove("/");
829 static void
830 do_callbacks(const char* name, struct purple_pref *pref)
832 GSList *cbs;
833 struct purple_pref *cb_pref;
834 for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) {
835 for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) {
836 PurplePrefCallbackData *cb = cbs->data;
837 cb->func(name, pref->type, pref->value.generic, cb->data);
842 static void
843 do_ui_callbacks(const char *name)
845 GSList *cbs;
847 purple_debug_misc("prefs", "trigger callback %s\n", name);
849 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
850 PurplePrefCallbackData *cb = cbs->data;
851 const char *cb_name = cb->name;
852 size_t len = strlen(cb_name);
853 if (!strncmp(cb_name, name, len) &&
854 (name[len] == 0 || name[len] == '/' ||
855 (len && name[len - 1] == '/'))) {
856 /* This test should behave like this:
857 * name = /toto/tata
858 * cb_name = /toto/tata --> true
859 * cb_name = /toto/tatatiti --> false
860 * cb_name = / --> true
861 * cb_name = /toto --> true
862 * cb_name = /toto/ --> true
864 purple_prefs_trigger_callback_object(cbs->data);
869 void
870 purple_prefs_trigger_callback(const char *name)
872 struct purple_pref *pref;
873 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
875 if (uiop && uiop->connect_callback) {
876 do_ui_callbacks(name);
877 return;
880 pref = find_pref(name);
882 if(!pref) {
883 purple_debug_error("prefs",
884 "purple_prefs_trigger_callback: Unknown pref %s\n", name);
885 return;
888 do_callbacks(name, pref);
891 /* this function is deprecated, so it doesn't get the new UI ops */
892 void
893 purple_prefs_set_bool(const char *name, gboolean value)
895 struct purple_pref *pref;
897 PURPLE_PREFS_UI_OP_CALL(set_bool, name, value);
899 pref = find_pref(name);
901 if(pref) {
902 if(pref->type != PURPLE_PREF_BOOLEAN) {
903 purple_debug_error("prefs",
904 "purple_prefs_set_bool: %s not a boolean pref\n", name);
905 return;
908 if(pref->value.boolean != value) {
909 pref->value.boolean = value;
910 do_callbacks(name, pref);
912 } else {
913 purple_prefs_add_bool(name, value);
917 void
918 purple_prefs_set_int(const char *name, int value)
920 struct purple_pref *pref;
922 PURPLE_PREFS_UI_OP_CALL(set_int, name, value);
924 pref = find_pref(name);
926 if(pref) {
927 if(pref->type != PURPLE_PREF_INT) {
928 purple_debug_error("prefs",
929 "purple_prefs_set_int: %s not an integer pref\n", name);
930 return;
933 if(pref->value.integer != value) {
934 pref->value.integer = value;
935 do_callbacks(name, pref);
937 } else {
938 purple_prefs_add_int(name, value);
942 void
943 purple_prefs_set_string(const char *name, const char *value)
945 struct purple_pref *pref;
947 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
948 purple_debug_error("prefs", "purple_prefs_set_string: Cannot store invalid UTF8 for string pref %s\n", name);
949 return;
952 PURPLE_PREFS_UI_OP_CALL(set_string, name, value);
954 pref = find_pref(name);
956 if(pref) {
957 if(pref->type != PURPLE_PREF_STRING && pref->type != PURPLE_PREF_PATH) {
958 purple_debug_error("prefs",
959 "purple_prefs_set_string: %s not a string pref\n", name);
960 return;
963 if (!purple_strequal(pref->value.string, value)) {
964 g_free(pref->value.string);
965 pref->value.string = g_strdup(value);
966 do_callbacks(name, pref);
968 } else {
969 purple_prefs_add_string(name, value);
973 void
974 purple_prefs_set_string_list(const char *name, GList *value)
976 struct purple_pref *pref;
978 PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value);
980 pref = find_pref(name);
982 if(pref) {
983 GList *tmp;
985 if(pref->type != PURPLE_PREF_STRING_LIST) {
986 purple_debug_error("prefs",
987 "purple_prefs_set_string_list: %s not a string list pref\n",
988 name);
989 return;
992 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
993 g_list_free(pref->value.stringlist);
994 pref->value.stringlist = NULL;
996 for(tmp = value; tmp; tmp = tmp->next) {
997 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
998 purple_debug_error("prefs", "purple_prefs_set_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
999 continue;
1001 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
1002 g_strdup(tmp->data));
1004 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
1006 do_callbacks(name, pref);
1008 } else {
1009 purple_prefs_add_string_list(name, value);
1013 void
1014 purple_prefs_set_path(const char *name, const char *value)
1016 struct purple_pref *pref;
1018 PURPLE_PREFS_UI_OP_CALL(set_string, name, value);
1020 pref = find_pref(name);
1022 if(pref) {
1023 if(pref->type != PURPLE_PREF_PATH) {
1024 purple_debug_error("prefs",
1025 "purple_prefs_set_path: %s not a path pref\n", name);
1026 return;
1029 if (!purple_strequal(pref->value.string, value)) {
1030 g_free(pref->value.string);
1031 pref->value.string = g_strdup(value);
1032 do_callbacks(name, pref);
1034 } else {
1035 purple_prefs_add_path(name, value);
1039 void
1040 purple_prefs_set_path_list(const char *name, GList *value)
1042 struct purple_pref *pref;
1044 PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value);
1046 pref = find_pref(name);
1048 if(pref) {
1049 GList *tmp;
1051 if(pref->type != PURPLE_PREF_PATH_LIST) {
1052 purple_debug_error("prefs",
1053 "purple_prefs_set_path_list: %s not a path list pref\n",
1054 name);
1055 return;
1058 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
1059 g_list_free(pref->value.stringlist);
1060 pref->value.stringlist = NULL;
1062 for(tmp = value; tmp; tmp = tmp->next)
1063 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
1064 g_strdup(tmp->data));
1065 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
1067 do_callbacks(name, pref);
1069 } else {
1070 purple_prefs_add_path_list(name, value);
1075 gboolean
1076 purple_prefs_exists(const char *name)
1078 struct purple_pref *pref;
1080 PURPLE_PREFS_UI_OP_CALL_RETURN(exists, name);
1082 pref = find_pref(name);
1084 if (pref != NULL)
1085 return TRUE;
1087 return FALSE;
1090 PurplePrefType
1091 purple_prefs_get_pref_type(const char *name)
1093 struct purple_pref *pref;
1095 PURPLE_PREFS_UI_OP_CALL_RETURN(get_type, name);
1097 pref = find_pref(name);
1099 if (pref == NULL)
1100 return PURPLE_PREF_NONE;
1102 return (pref->type);
1105 gboolean
1106 purple_prefs_get_bool(const char *name)
1108 struct purple_pref *pref;
1110 PURPLE_PREFS_UI_OP_CALL_RETURN(get_bool, name);
1112 pref = find_pref(name);
1114 if(!pref) {
1115 purple_debug_error("prefs",
1116 "purple_prefs_get_bool: Unknown pref %s\n", name);
1117 return FALSE;
1118 } else if(pref->type != PURPLE_PREF_BOOLEAN) {
1119 purple_debug_error("prefs",
1120 "purple_prefs_get_bool: %s not a boolean pref\n", name);
1121 return FALSE;
1124 return pref->value.boolean;
1128 purple_prefs_get_int(const char *name)
1130 struct purple_pref *pref;
1132 PURPLE_PREFS_UI_OP_CALL_RETURN(get_int, name);
1134 pref = find_pref(name);
1136 if(!pref) {
1137 purple_debug_error("prefs",
1138 "purple_prefs_get_int: Unknown pref %s\n", name);
1139 return 0;
1140 } else if(pref->type != PURPLE_PREF_INT) {
1141 purple_debug_error("prefs",
1142 "purple_prefs_get_int: %s not an integer pref\n", name);
1143 return 0;
1146 return pref->value.integer;
1149 const char *
1150 purple_prefs_get_string(const char *name)
1152 struct purple_pref *pref;
1154 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name);
1156 pref = find_pref(name);
1158 if(!pref) {
1159 purple_debug_error("prefs",
1160 "purple_prefs_get_string: Unknown pref %s\n", name);
1161 return NULL;
1162 } else if(pref->type != PURPLE_PREF_STRING) {
1163 purple_debug_error("prefs",
1164 "purple_prefs_get_string: %s not a string pref\n", name);
1165 return NULL;
1168 return pref->value.string;
1171 GList *
1172 purple_prefs_get_string_list(const char *name)
1174 struct purple_pref *pref;
1175 GList *ret = NULL, *tmp;
1177 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name);
1179 pref = find_pref(name);
1181 if(!pref) {
1182 purple_debug_error("prefs",
1183 "purple_prefs_get_string_list: Unknown pref %s\n", name);
1184 return NULL;
1185 } else if(pref->type != PURPLE_PREF_STRING_LIST) {
1186 purple_debug_error("prefs",
1187 "purple_prefs_get_string_list: %s not a string list pref\n", name);
1188 return NULL;
1191 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1192 ret = g_list_prepend(ret, g_strdup(tmp->data));
1193 ret = g_list_reverse(ret);
1195 return ret;
1198 const char *
1199 purple_prefs_get_path(const char *name)
1201 struct purple_pref *pref;
1203 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name);
1205 pref = find_pref(name);
1207 if(!pref) {
1208 purple_debug_error("prefs",
1209 "purple_prefs_get_path: Unknown pref %s\n", name);
1210 return NULL;
1211 } else if(pref->type != PURPLE_PREF_PATH) {
1212 purple_debug_error("prefs",
1213 "purple_prefs_get_path: %s not a path pref\n", name);
1214 return NULL;
1217 return pref->value.string;
1220 GList *
1221 purple_prefs_get_path_list(const char *name)
1223 struct purple_pref *pref;
1224 GList *ret = NULL, *tmp;
1226 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name);
1228 pref = find_pref(name);
1230 if(!pref) {
1231 purple_debug_error("prefs",
1232 "purple_prefs_get_path_list: Unknown pref %s\n", name);
1233 return NULL;
1234 } else if(pref->type != PURPLE_PREF_PATH_LIST) {
1235 purple_debug_error("prefs",
1236 "purple_prefs_get_path_list: %s not a path list pref\n", name);
1237 return NULL;
1240 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1241 ret = g_list_prepend(ret, g_strdup(tmp->data));
1242 ret = g_list_reverse(ret);
1244 return ret;
1247 static void
1248 purple_prefs_rename_node(struct purple_pref *oldpref, struct purple_pref *newpref)
1250 struct purple_pref *child, *next;
1251 char *oldname, *newname;
1253 /* if we're a parent, rename the kids first */
1254 for(child = oldpref->first_child; child != NULL; child = next)
1256 struct purple_pref *newchild;
1257 next = child->sibling;
1258 for(newchild = newpref->first_child; newchild != NULL; newchild = newchild->sibling)
1260 if(purple_strequal(child->name, newchild->name))
1262 purple_prefs_rename_node(child, newchild);
1263 break;
1266 if(newchild == NULL) {
1267 /* no rename happened, we weren't able to find the new pref */
1268 char *tmpname = pref_full_name(child);
1269 purple_debug_error("prefs", "Unable to find rename pref for %s\n", tmpname);
1270 g_free(tmpname);
1274 oldname = pref_full_name(oldpref);
1275 newname = pref_full_name(newpref);
1277 if (oldpref->type != newpref->type)
1279 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1280 g_free(oldname);
1281 g_free(newname);
1282 return;
1285 purple_debug_info("prefs", "Renaming %s to %s\n", oldname, newname);
1286 g_free(oldname);
1288 switch(oldpref->type) {
1289 case PURPLE_PREF_NONE:
1290 break;
1291 case PURPLE_PREF_BOOLEAN:
1292 purple_prefs_set_bool(newname, oldpref->value.boolean);
1293 break;
1294 case PURPLE_PREF_INT:
1295 purple_prefs_set_int(newname, oldpref->value.integer);
1296 break;
1297 case PURPLE_PREF_STRING:
1298 purple_prefs_set_string(newname, oldpref->value.string);
1299 break;
1300 case PURPLE_PREF_STRING_LIST:
1301 purple_prefs_set_string_list(newname, oldpref->value.stringlist);
1302 break;
1303 case PURPLE_PREF_PATH:
1304 purple_prefs_set_path(newname, oldpref->value.string);
1305 break;
1306 case PURPLE_PREF_PATH_LIST:
1307 purple_prefs_set_path_list(newname, oldpref->value.stringlist);
1308 break;
1310 g_free(newname);
1312 remove_pref(oldpref);
1315 void
1316 purple_prefs_rename(const char *oldname, const char *newname)
1318 struct purple_pref *oldpref, *newpref;
1320 /* win32dep.h causes rename to be defined as wpurple_rename, so we need to undefine it here */
1321 #if defined(_WIN32) && defined(rename)
1322 #undef rename
1323 #endif
1325 PURPLE_PREFS_UI_OP_CALL(rename, oldname, newname);
1327 oldpref = find_pref(oldname);
1329 /* it's already been renamed, call off the dogs */
1330 if(!oldpref)
1331 return;
1333 newpref = find_pref(newname);
1335 if (newpref == NULL)
1337 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1338 return;
1341 purple_prefs_rename_node(oldpref, newpref);
1344 void
1345 purple_prefs_rename_boolean_toggle(const char *oldname, const char *newname)
1347 struct purple_pref *oldpref, *newpref;
1349 PURPLE_PREFS_UI_OP_CALL(rename_boolean_toggle, oldname, newname);
1351 oldpref = find_pref(oldname);
1353 /* it's already been renamed, call off the cats */
1354 if(!oldpref)
1355 return;
1357 if (oldpref->type != PURPLE_PREF_BOOLEAN)
1359 purple_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname, newname);
1360 return;
1363 if (oldpref->first_child != NULL) /* can't rename parents */
1365 purple_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
1366 return;
1370 newpref = find_pref(newname);
1372 if (newpref == NULL)
1374 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1375 return;
1378 if (oldpref->type != newpref->type)
1380 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1381 return;
1384 purple_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname, newname);
1385 purple_prefs_set_bool(newname, !(oldpref->value.boolean));
1387 remove_pref(oldpref);
1390 guint
1391 purple_prefs_connect_callback(void *handle, const char *name, PurplePrefCallback func, gpointer data)
1393 struct purple_pref *pref = NULL;
1394 PurplePrefCallbackData *cb;
1395 static guint cb_id = 0;
1396 PurplePrefsUiOps *uiop = NULL;
1398 g_return_val_if_fail(name != NULL, 0);
1399 g_return_val_if_fail(func != NULL, 0);
1401 uiop = purple_prefs_get_ui_ops();
1403 if (!(uiop && uiop->connect_callback)) {
1404 pref = find_pref(name);
1405 if (pref == NULL) {
1406 purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name);
1407 return 0;
1411 cb = g_new0(PurplePrefCallbackData, 1);
1413 cb->func = func;
1414 cb->data = data;
1415 cb->id = ++cb_id;
1416 cb->handle = handle;
1417 cb->name = g_strdup(name);
1419 if (uiop && uiop->connect_callback) {
1420 cb->ui_data = uiop->connect_callback(name, cb);
1422 if (cb->ui_data == NULL) {
1423 purple_debug_error("prefs", "purple_prefs_connect_callback: connect failed for %s\n", name);
1424 g_free(cb->name);
1425 g_free(cb);
1426 return 0;
1429 ui_callbacks = g_slist_append(ui_callbacks, cb);
1430 } else {
1431 pref->callbacks = g_slist_append(pref->callbacks, cb);
1434 return cb->id;
1437 static void
1438 purple_prefs_trigger_ui_callback_object(PurplePrefCallbackData *cb)
1440 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1441 gconstpointer value = NULL;
1442 PurplePrefType type = PURPLE_PREF_NONE;
1444 type = uiop->get_type(cb->name);
1446 switch (type) {
1447 case PURPLE_PREF_INT:
1448 if (uiop->get_int) {
1449 value = GINT_TO_POINTER(uiop->get_int(cb->name));
1451 break;
1452 case PURPLE_PREF_BOOLEAN:
1453 if (uiop->get_bool) {
1454 value = GINT_TO_POINTER(uiop->get_bool(cb->name));
1456 break;
1457 case PURPLE_PREF_STRING:
1458 case PURPLE_PREF_PATH:
1459 if (uiop->get_string) {
1460 value = uiop->get_string(cb->name);
1462 break;
1463 case PURPLE_PREF_STRING_LIST:
1464 case PURPLE_PREF_PATH_LIST:
1465 if (uiop->get_string_list) {
1466 value = uiop->get_string_list(cb->name);
1468 break;
1469 case PURPLE_PREF_NONE:
1470 break;
1471 default:
1472 purple_debug_error("prefs", "Unexpected type = %i\n", type);
1475 cb->func(cb->name, type, value, cb->data);
1478 void
1479 purple_prefs_trigger_callback_object(PurplePrefCallbackData *cb)
1481 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1483 if (uiop && uiop->connect_callback && uiop->get_type) {
1484 purple_prefs_trigger_ui_callback_object(cb);
1485 } else {
1486 purple_prefs_trigger_callback(cb->name);
1490 static gboolean
1491 disco_callback_helper(struct purple_pref *pref, guint callback_id)
1493 GSList *cbs;
1494 struct purple_pref *child;
1496 if(!pref)
1497 return FALSE;
1499 for(cbs = pref->callbacks; cbs; cbs = cbs->next) {
1500 PurplePrefCallbackData *cb = cbs->data;
1501 if(cb->id == callback_id) {
1502 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1503 g_free(cb->name);
1504 g_free(cb);
1505 return TRUE;
1509 for(child = pref->first_child; child; child = child->sibling) {
1510 if(disco_callback_helper(child, callback_id))
1511 return TRUE;
1514 return FALSE;
1517 static void
1518 disco_ui_callback_helper(guint callback_id)
1520 GSList *cbs;
1521 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1523 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
1524 PurplePrefCallbackData *cb = cbs->data;
1525 if (cb->id == callback_id) {
1526 uiop->disconnect_callback(cb->name, cb->ui_data);
1528 ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
1529 g_free(cb->name);
1530 g_free(cb);
1531 return;
1536 void
1537 purple_prefs_disconnect_callback(guint callback_id)
1539 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1541 if (uiop && uiop->disconnect_callback) {
1542 disco_ui_callback_helper(callback_id);
1543 } else {
1544 disco_callback_helper(&prefs, callback_id);
1548 static void
1549 disco_callback_helper_handle(struct purple_pref *pref, void *handle)
1551 GSList *cbs;
1552 struct purple_pref *child;
1554 if(!pref)
1555 return;
1557 cbs = pref->callbacks;
1558 while (cbs != NULL) {
1559 PurplePrefCallbackData *cb = cbs->data;
1560 if(cb->handle == handle) {
1561 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1562 g_free(cb->name);
1563 g_free(cb);
1564 cbs = pref->callbacks;
1565 } else
1566 cbs = cbs->next;
1569 for(child = pref->first_child; child; child = child->sibling)
1570 disco_callback_helper_handle(child, handle);
1573 static void
1574 disco_ui_callback_helper_handle(void *handle)
1576 GSList *cbs;
1577 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1579 cbs = ui_callbacks;
1580 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
1581 PurplePrefCallbackData *cb = cbs->data;
1582 if (cb->handle != handle) {
1583 cbs = cbs->next;
1584 continue;
1587 uiop->disconnect_callback(cb->name, cb->ui_data);
1589 ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
1590 g_free(cb->name);
1591 g_free(cb);
1592 cbs = ui_callbacks;
1596 void
1597 purple_prefs_disconnect_by_handle(void *handle)
1599 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1601 g_return_if_fail(handle != NULL);
1603 if (uiop && uiop->disconnect_callback) {
1604 disco_ui_callback_helper_handle(handle);
1605 } else {
1606 disco_callback_helper_handle(&prefs, handle);
1610 GList *
1611 purple_prefs_get_children_names(const char *name)
1613 GList * list = NULL;
1614 struct purple_pref *pref, *child;
1615 char sep[2] = "\0\0";;
1617 PURPLE_PREFS_UI_OP_CALL_RETURN(get_children_names, name);
1619 pref = find_pref(name);
1621 if (pref == NULL)
1622 return NULL;
1624 if (name[strlen(name) - 1] != '/')
1625 sep[0] = '/';
1626 for (child = pref->first_child; child; child = child->sibling) {
1627 list = g_list_append(list, g_strdup_printf("%s%s%s", name, sep, child->name));
1629 return list;
1632 static void
1633 prefs_update_old(void)
1635 purple_prefs_rename("/core", "/purple");
1637 /* Remove some no-longer-used prefs */
1638 purple_prefs_remove("/purple/away/auto_response/enabled");
1639 purple_prefs_remove("/purple/away/auto_response/idle_only");
1640 purple_prefs_remove("/purple/away/auto_response/in_active_conv");
1641 purple_prefs_remove("/purple/away/auto_response/sec_before_resend");
1642 purple_prefs_remove("/purple/away/auto_response");
1643 purple_prefs_remove("/purple/away/default_message");
1644 purple_prefs_remove("/purple/buddies/use_server_alias");
1645 purple_prefs_remove("/purple/conversations/away_back_on_send");
1646 purple_prefs_remove("/purple/conversations/send_urls_as_links");
1647 purple_prefs_remove("/purple/conversations/im/show_login");
1648 purple_prefs_remove("/purple/conversations/chat/show_join");
1649 purple_prefs_remove("/purple/conversations/chat/show_leave");
1650 purple_prefs_remove("/purple/conversations/combine_chat_im");
1651 purple_prefs_remove("/purple/conversations/use_alias_for_title");
1652 purple_prefs_remove("/purple/debug/timestamps");
1653 purple_prefs_remove("/purple/logging/log_signon_signoff");
1654 purple_prefs_remove("/purple/logging/log_idle_state");
1655 purple_prefs_remove("/purple/logging/log_away_state");
1656 purple_prefs_remove("/purple/logging/log_own_states");
1657 purple_prefs_remove("/purple/status/scores/hidden");
1658 purple_prefs_remove("/plugins/core/autorecon/hide_connected_error");
1659 purple_prefs_remove("/plugins/core/autorecon/hide_connecting_error");
1660 purple_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog");
1661 purple_prefs_remove("/plugins/core/autorecon/restore_state");
1662 purple_prefs_remove("/plugins/core/autorecon");
1663 purple_prefs_remove("/plugins/lopl");
1665 /* Convert old sounds while_away pref to new 3-way pref. */
1666 if (purple_prefs_exists("/purple/sound/while_away") &&
1667 purple_prefs_get_bool("/purple/sound/while_away"))
1669 purple_prefs_set_int("/purple/sound/while_status", 3);
1671 purple_prefs_remove("/purple/sound/while_away");
1674 void *
1675 purple_prefs_get_handle(void)
1677 static int handle;
1679 return &handle;
1682 void
1683 purple_prefs_init(void)
1685 void *handle = purple_prefs_get_handle();
1687 prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1689 purple_prefs_connect_callback(handle, "/", prefs_save_cb, NULL);
1691 purple_prefs_add_none("/purple");
1692 purple_prefs_add_none("/plugins");
1693 purple_prefs_add_none("/plugins/core");
1694 purple_prefs_add_none("/plugins/prpl");
1696 /* Away */
1697 purple_prefs_add_none("/purple/away");
1698 purple_prefs_add_string("/purple/away/idle_reporting", "system");
1699 purple_prefs_add_bool("/purple/away/away_when_idle", TRUE);
1700 purple_prefs_add_int("/purple/away/mins_before_away", 5);
1702 /* Away -> Auto-Reply */
1703 if (!purple_prefs_exists("/purple/away/auto_response/enabled") ||
1704 !purple_prefs_exists("/purple/away/auto_response/idle_only"))
1706 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1708 else
1710 if (!purple_prefs_get_bool("/purple/away/auto_response/enabled"))
1712 purple_prefs_add_string("/purple/away/auto_reply", "never");
1714 else
1716 if (purple_prefs_get_bool("/purple/away/auto_response/idle_only"))
1718 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1720 else
1722 purple_prefs_add_string("/purple/away/auto_reply", "away");
1727 /* Buddies */
1728 purple_prefs_add_none("/purple/buddies");
1730 /* Contact Priority Settings */
1731 purple_prefs_add_none("/purple/contact");
1732 purple_prefs_add_bool("/purple/contact/last_match", FALSE);
1733 purple_prefs_remove("/purple/contact/offline_score");
1734 purple_prefs_remove("/purple/contact/away_score");
1735 purple_prefs_remove("/purple/contact/idle_score");
1737 purple_prefs_load();
1738 prefs_update_old();
1741 void
1742 purple_prefs_uninit()
1744 if (save_timer != 0)
1746 g_source_remove(save_timer);
1747 save_cb(NULL);
1750 purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
1752 prefs_loaded = FALSE;
1753 purple_prefs_destroy();
1754 g_hash_table_destroy(prefs_hash);
1755 prefs_hash = NULL;
1759 void
1760 purple_prefs_set_ui_ops(PurplePrefsUiOps *ops)
1762 prefs_ui_ops = ops;
1765 PurplePrefsUiOps *
1766 purple_prefs_get_ui_ops(void)
1768 return prefs_ui_ops;