Mercurial > pidgin.yaz
annotate src/prefs.c @ 10683:e11f3e1599d4
[gaim-migrate @ 12230]
A guy named Martin noticed that if you read the info of an ICQ user,
and they have more than one email address listed, the mailto link
for all addresses after the first are missing a colon.
And I also removed some no-longer-needed \n's that were causing some
extra white space in the output. I'm glad those aren't needed--yay
for improvements to gtkimhtml.
Let me go crazy on ya
Crazy on you
Let me go crazy, crazy on you, ohhh
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Fri, 11 Mar 2005 04:13:27 +0000 |
parents | b6ca0e1b19d0 |
children | fa06fda62868 |
rev | line source |
---|---|
1 | 1 /* |
2 * gaim | |
3 * | |
8046 | 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. | |
1 | 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 | |
349
b402a23f35df
[gaim-migrate @ 359]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
340
diff
changeset
|
24 #ifdef HAVE_CONFIG_H |
2090
b66aca8e8dce
[gaim-migrate @ 2100]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2074
diff
changeset
|
25 #include <config.h> |
349
b402a23f35df
[gaim-migrate @ 359]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
340
diff
changeset
|
26 #endif |
5440 | 27 |
1 | 28 #include <string.h> |
29 #include <stdio.h> | |
30 #include <stdlib.h> | |
5440 | 31 #include <sys/stat.h> |
32 #include <sys/types.h> | |
33 #include <glib.h> | |
6216 | 34 #include "internal.h" |
5440 | 35 #include "prefs.h" |
36 #include "debug.h" | |
37 #include "util.h" | |
3366 | 38 |
4026
a997156437b6
[gaim-migrate @ 4230]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
4010
diff
changeset
|
39 #ifdef _WIN32 |
a997156437b6
[gaim-migrate @ 4230]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
4010
diff
changeset
|
40 #include "win32dep.h" |
a997156437b6
[gaim-migrate @ 4230]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
4010
diff
changeset
|
41 #endif |
a997156437b6
[gaim-migrate @ 4230]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
4010
diff
changeset
|
42 |
5440 | 43 struct pref_cb { |
44 GaimPrefCallback func; | |
45 gpointer data; | |
46 guint id; | |
10087 | 47 void *handle; |
5440 | 48 }; |
49 | |
10443 | 50 /* TODO: This should use GaimValues? */ |
5440 | 51 struct gaim_pref { |
52 GaimPrefType type; | |
53 char *name; | |
54 union { | |
55 gpointer generic; | |
56 gboolean boolean; | |
57 int integer; | |
58 char *string; | |
5561 | 59 GList *stringlist; |
5440 | 60 } value; |
61 GSList *callbacks; | |
62 struct gaim_pref *parent; | |
63 struct gaim_pref *sibling; | |
64 struct gaim_pref *first_child; | |
65 }; | |
3366 | 66 |
5440 | 67 |
10443 | 68 static struct gaim_pref prefs = { |
69 GAIM_PREF_NONE, | |
70 NULL, | |
71 { NULL }, | |
72 NULL, | |
73 NULL, | |
74 NULL, | |
75 NULL | |
76 }; | |
5440 | 77 |
10443 | 78 static GHashTable *prefs_hash = NULL; |
79 static guint save_timer = 0; | |
80 static gboolean prefs_loaded = FALSE; | |
5534 | 81 |
82 | |
10443 | 83 /********************************************************************* |
84 * Private utility functions * | |
85 *********************************************************************/ | |
8235 | 86 |
10443 | 87 static struct |
88 gaim_pref *find_pref(const char *name) | |
5787
2adc29c88a45
[gaim-migrate @ 6212]
Christian Hammond <chipx86@chipx86.com>
parents:
5684
diff
changeset
|
89 { |
10443 | 90 if (!name || name[0] != '/') |
5440 | 91 return NULL; |
10443 | 92 else if (name[1] == '\0') |
5440 | 93 return &prefs; |
10443 | 94 else |
5440 | 95 return g_hash_table_lookup(prefs_hash, name); |
96 } | |
97 | |
10443 | 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 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; | |
5561 | 114 |
10443 | 115 /* Create a new node */ |
116 node = xmlnode_new_child(parent, "pref"); | |
117 xmlnode_set_attrib(node, "name", pref->name); | |
5440 | 118 |
10443 | 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); | |
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); | |
5440 | 135 } |
136 } | |
10443 | 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); | |
5440 | 141 } |
142 | |
10443 | 143 /* All My Children */ |
144 for (child = pref->first_child; child != NULL; child = child->sibling) | |
145 pref_to_xmlnode(node, child); | |
5440 | 146 } |
147 | |
10443 | 148 static xmlnode * |
149 prefs_to_xmlnode(void) | |
150 { | |
151 xmlnode *node; | |
152 struct gaim_pref *pref, *child; | |
5440 | 153 |
10443 | 154 pref = &prefs; |
5440 | 155 |
10443 | 156 /* Create the root preference node */ |
157 node = xmlnode_new("pref"); | |
158 xmlnode_set_attrib(node, "version", "1"); | |
159 xmlnode_set_attrib(node, "name", "/"); | |
5561 | 160 |
10443 | 161 /* All My Children */ |
162 for (child = pref->first_child; child != NULL; child = child->sibling) | |
163 pref_to_xmlnode(node, child); | |
5561 | 164 |
10443 | 165 return node; |
5561 | 166 } |
167 | |
10443 | 168 static void |
169 sync_prefs(void) | |
170 { | |
171 xmlnode *node; | |
172 char *data; | |
5205
fefad67de2c7
[gaim-migrate @ 5573]
Christian Hammond <chipx86@chipx86.com>
parents:
5105
diff
changeset
|
173 |
10443 | 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"); | |
5814 | 182 return; |
183 } | |
3500 | 184 |
10443 | 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); | |
4326 | 190 } |
191 | |
10443 | 192 static gboolean |
193 save_cb(gpointer data) | |
194 { | |
195 sync_prefs(); | |
196 save_timer = 0; | |
9594 | 197 return FALSE; |
4288 | 198 } |
199 | |
10443 | 200 static void |
201 schedule_prefs_save(void) | |
5440 | 202 { |
10443 | 203 if (save_timer == 0) |
204 save_timer = gaim_timeout_add(5000, save_cb, NULL); | |
3366 | 205 } |
206 | |
2254 | 207 |
10443 | 208 /********************************************************************* |
209 * Reading from disk * | |
210 *********************************************************************/ | |
3551 | 211 |
5440 | 212 static GList *prefs_stack = NULL; |
873
789df4b47508
[gaim-migrate @ 883]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
864
diff
changeset
|
213 |
10443 | 214 static void |
215 prefs_start_element_handler (GMarkupParseContext *context, | |
5440 | 216 const gchar *element_name, |
217 const gchar **attribute_names, | |
218 const gchar **attribute_values, | |
219 gpointer user_data, | |
10443 | 220 GError **error) |
221 { | |
5440 | 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; | |
3366 | 227 |
5561 | 228 if(strcmp(element_name, "pref") && strcmp(element_name, "item")) |
5440 | 229 return; |
3500 | 230 |
5440 | 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; | |
5561 | 241 else if(!strcmp(attribute_values[i], "stringlist")) |
242 pref_type = GAIM_PREF_STRING_LIST; | |
5440 | 243 else |
244 return; | |
245 } else if(!strcmp(attribute_names[i], "value")) { | |
246 pref_value = attribute_values[i]; | |
247 } | |
248 } | |
873
789df4b47508
[gaim-migrate @ 883]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
864
diff
changeset
|
249 |
5561 | 250 if(!strcmp(element_name, "item")) { |
5838 | 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 | |
5561 | 262 if(pref) { |
263 pref->value.stringlist = g_list_append(pref->value.stringlist, | |
264 g_strdup(pref_value)); | |
265 } | |
5838 | 266 } else { |
267 if(!pref_name || !strcmp(pref_name, "/")) | |
268 return; | |
652
4d3285caa191
[gaim-migrate @ 662]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
619
diff
changeset
|
269 |
5838 | 270 pref_name_full = g_string_new(pref_name); |
652
4d3285caa191
[gaim-migrate @ 662]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
619
diff
changeset
|
271 |
5838 | 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 | |
5440 | 277 pref_name_full = g_string_prepend_c(pref_name_full, '/'); |
1253
8342d3aab1f1
[gaim-migrate @ 1263]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
1250
diff
changeset
|
278 |
5838 | 279 switch(pref_type) { |
280 case GAIM_PREF_NONE: | |
7785 | 281 gaim_prefs_add_none(pref_name_full->str); |
5838 | 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); | |
5440 | 298 } |
1170 | 299 } |
300 | |
10443 | 301 static void |
302 prefs_end_element_handler(GMarkupParseContext *context, | |
303 const gchar *element_name, | |
304 gpointer user_data, GError **error) | |
305 { | |
5940 | 306 if(prefs_stack && !strcmp(element_name, "pref")) { |
307 g_free(prefs_stack->data); | |
5440 | 308 prefs_stack = g_list_delete_link(prefs_stack, prefs_stack); |
309 } | |
1170 | 310 } |
311 | |
5440 | 312 static GMarkupParser prefs_parser = { |
313 prefs_start_element_handler, | |
314 prefs_end_element_handler, | |
315 NULL, | |
316 NULL, | |
317 NULL | |
318 }; | |
1170 | 319 |
10443 | 320 gboolean |
321 gaim_prefs_load() | |
322 { | |
5440 | 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; | |
5314
1f901484599d
[gaim-migrate @ 5686]
Christian Hammond <chipx86@chipx86.com>
parents:
5297
diff
changeset
|
328 |
7561
cdfdbabd3266
[gaim-migrate @ 8175]
Christian Hammond <chipx86@chipx86.com>
parents:
7555
diff
changeset
|
329 if (!filename) { |
10443 | 330 prefs_loaded = TRUE; |
5545
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
331 return FALSE; |
5534 | 332 } |
5440 | 333 |
10443 | 334 gaim_debug_info("prefs", "Reading %s\n", filename); |
5314
1f901484599d
[gaim-migrate @ 5686]
Christian Hammond <chipx86@chipx86.com>
parents:
5297
diff
changeset
|
335 |
5440 | 336 if(!g_file_get_contents(filename, &contents, &length, &error)) { |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
337 #ifndef _WIN32 |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
338 g_free(filename); |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
339 g_error_free(error); |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
340 |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
341 error = NULL; |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
342 |
8702 | 343 filename = g_build_filename(SYSCONFDIR, "gaim", "prefs.xml", NULL); |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
344 |
10443 | 345 gaim_debug_info("prefs", "Reading %s\n", filename); |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
346 |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
347 if (!g_file_get_contents(filename, &contents, &length, &error)) { |
10443 | 348 gaim_debug_error("prefs", "Error reading prefs: %s\n", |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
349 error->message); |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
350 g_error_free(error); |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
351 g_free(filename); |
10443 | 352 prefs_loaded = TRUE; |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
353 |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
354 return FALSE; |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
355 } |
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
356 #else /* _WIN32 */ |
10443 | 357 gaim_debug_error("prefs", "Error reading prefs: %s\n", |
5440 | 358 error->message); |
359 g_error_free(error); | |
6040 | 360 g_free(filename); |
10443 | 361 prefs_loaded = TRUE; |
6040 | 362 |
5545
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
363 return FALSE; |
8671
d99d2572d1a9
[gaim-migrate @ 9423]
Christian Hammond <chipx86@chipx86.com>
parents:
8549
diff
changeset
|
364 #endif /* _WIN32 */ |
1170 | 365 } |
366 | |
5440 | 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); | |
6040 | 372 g_free(filename); |
10443 | 373 prefs_loaded = TRUE; |
6040 | 374 |
5545
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
375 return FALSE; |
5440 | 376 } |
377 | |
378 if(!g_markup_parse_context_end_parse(context, NULL)) { | |
10443 | 379 gaim_debug_error("prefs", "Error parsing %s\n", filename); |
5440 | 380 g_markup_parse_context_free(context); |
381 g_free(contents); | |
6040 | 382 g_free(filename); |
10443 | 383 prefs_loaded = TRUE; |
6040 | 384 |
5545
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
385 return FALSE; |
5440 | 386 } |
387 | |
10443 | 388 gaim_debug_info("prefs", "Finished reading %s\n", filename); |
5440 | 389 g_markup_parse_context_free(context); |
390 g_free(contents); | |
391 g_free(filename); | |
10443 | 392 prefs_loaded = TRUE; |
5545
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
393 |
7a64114641c3
[gaim-migrate @ 5946]
Christian Hammond <chipx86@chipx86.com>
parents:
5539
diff
changeset
|
394 return TRUE; |
1006
0a4d0ed65e17
[gaim-migrate @ 1016]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
1002
diff
changeset
|
395 } |
0a4d0ed65e17
[gaim-migrate @ 1016]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
1002
diff
changeset
|
396 |
10443 | 397 |
398 | |
399 static void | |
400 prefs_save_cb(const char *name, GaimPrefType type, gpointer val, | |
401 gpointer user_data) | |
402 { | |
403 | |
404 if(!prefs_loaded) | |
405 return; | |
406 | |
407 gaim_debug_misc("prefs", "%s changed, scheduling save.\n", name); | |
408 | |
409 schedule_prefs_save(); | |
410 } | |
411 | |
412 static char * | |
413 get_path_dirname(const char *name) | |
414 { | |
415 char *c, *str; | |
416 | |
417 str = g_strdup(name); | |
418 | |
419 if ((c = strrchr(str, '/')) != NULL) { | |
420 *c = '\0'; | |
421 | |
422 if (*str == '\0') { | |
423 g_free(str); | |
424 | |
425 str = g_strdup("/"); | |
426 } | |
427 } | |
428 else { | |
429 g_free(str); | |
430 | |
431 str = g_strdup("."); | |
432 } | |
433 | |
434 return str; | |
435 } | |
436 | |
437 static char * | |
438 get_path_basename(const char *name) | |
439 { | |
440 const char *c; | |
441 | |
442 if ((c = strrchr(name, '/')) != NULL) | |
443 return g_strdup(c + 1); | |
444 | |
445 return g_strdup(name); | |
446 } | |
447 | |
448 static char * | |
449 pref_full_name(struct gaim_pref *pref) | |
450 { | |
451 GString *name; | |
452 struct gaim_pref *parent; | |
453 char *ret; | |
454 | |
455 if(!pref) | |
456 return NULL; | |
457 | |
458 if(pref == &prefs) | |
459 return g_strdup("/"); | |
460 | |
461 name = g_string_new(pref->name); | |
462 parent = pref->parent; | |
463 | |
464 for(parent = pref->parent; parent && parent->name; parent = parent->parent) { | |
465 name = g_string_prepend_c(name, '/'); | |
466 name = g_string_prepend(name, parent->name); | |
467 } | |
468 ret = name->str; | |
469 g_string_free(name, FALSE); | |
470 return ret; | |
471 } | |
472 | |
473 static struct gaim_pref * | |
474 find_pref_parent(const char *name) | |
475 { | |
476 char *parent_name = get_path_dirname(name); | |
477 struct gaim_pref *ret = &prefs; | |
478 | |
479 if(strcmp(parent_name, "/")) { | |
480 ret = find_pref(parent_name); | |
481 } | |
482 | |
483 g_free(parent_name); | |
484 return ret; | |
485 } | |
486 | |
487 static void | |
488 free_pref_value(struct gaim_pref *pref) | |
489 { | |
490 switch(pref->type) { | |
491 case GAIM_PREF_BOOLEAN: | |
492 pref->value.boolean = FALSE; | |
493 break; | |
494 case GAIM_PREF_INT: | |
495 pref->value.integer = 0; | |
496 break; | |
497 case GAIM_PREF_STRING: | |
498 g_free(pref->value.string); | |
499 pref->value.string = NULL; | |
500 break; | |
501 case GAIM_PREF_STRING_LIST: | |
502 { | |
503 GList *tmp; | |
504 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next) | |
505 g_free(tmp->data); | |
506 | |
507 g_list_free(pref->value.stringlist); | |
508 } break; | |
509 case GAIM_PREF_NONE: | |
510 break; | |
511 } | |
512 } | |
513 | |
514 static struct gaim_pref * | |
515 add_pref(GaimPrefType type, const char *name) | |
516 { | |
517 struct gaim_pref *parent; | |
518 struct gaim_pref *me; | |
519 struct gaim_pref *sibling; | |
520 char *my_name; | |
521 | |
522 parent = find_pref_parent(name); | |
523 | |
524 if(!parent) | |
525 return NULL; | |
526 | |
527 my_name = get_path_basename(name); | |
528 | |
529 for(sibling = parent->first_child; sibling; sibling = sibling->sibling) { | |
530 if(!strcmp(sibling->name, my_name)) { | |
531 g_free(my_name); | |
532 return NULL; | |
533 } | |
534 } | |
535 | |
536 me = g_new0(struct gaim_pref, 1); | |
537 me->type = type; | |
538 me->name = my_name; | |
539 | |
540 me->parent = parent; | |
541 if(parent->first_child) { | |
542 /* blatant abuse of a for loop */ | |
543 for(sibling = parent->first_child; sibling->sibling; | |
544 sibling = sibling->sibling); | |
545 sibling->sibling = me; | |
546 } else { | |
547 parent->first_child = me; | |
548 } | |
549 | |
550 g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me); | |
551 | |
552 return me; | |
553 } | |
554 | |
555 void | |
556 gaim_prefs_add_none(const char *name) | |
557 { | |
558 add_pref(GAIM_PREF_NONE, name); | |
559 } | |
560 | |
561 void | |
562 gaim_prefs_add_bool(const char *name, gboolean value) | |
563 { | |
564 struct gaim_pref *pref = add_pref(GAIM_PREF_BOOLEAN, name); | |
565 | |
566 if(!pref) | |
567 return; | |
568 | |
569 pref->value.boolean = value; | |
570 } | |
571 | |
572 void | |
573 gaim_prefs_add_int(const char *name, int value) | |
574 { | |
575 struct gaim_pref *pref = add_pref(GAIM_PREF_INT, name); | |
576 | |
577 if(!pref) | |
578 return; | |
579 | |
580 pref->value.integer = value; | |
581 } | |
582 | |
583 void | |
584 gaim_prefs_add_string(const char *name, const char *value) | |
585 { | |
586 struct gaim_pref *pref = add_pref(GAIM_PREF_STRING, name); | |
587 | |
588 if(!pref) | |
589 return; | |
590 | |
591 pref->value.string = g_strdup(value); | |
592 } | |
593 | |
594 void | |
595 gaim_prefs_add_string_list(const char *name, GList *value) | |
596 { | |
597 struct gaim_pref *pref = add_pref(GAIM_PREF_STRING_LIST, name); | |
598 GList *tmp; | |
599 | |
600 if(!pref) | |
601 return; | |
602 | |
603 for(tmp = value; tmp; tmp = tmp->next) | |
604 pref->value.stringlist = g_list_append(pref->value.stringlist, | |
605 g_strdup(tmp->data)); | |
606 } | |
607 | |
608 void | |
609 remove_pref(struct gaim_pref *pref) | |
610 { | |
611 char *name; | |
612 | |
613 if(!pref || pref == &prefs) | |
614 return; | |
615 | |
616 while(pref->first_child) | |
617 remove_pref(pref->first_child); | |
618 | |
619 if(pref->parent->first_child == pref) { | |
620 pref->parent->first_child = pref->sibling; | |
621 } else { | |
622 struct gaim_pref *sib = pref->parent->first_child; | |
623 while(sib->sibling != pref) | |
624 sib = sib->sibling; | |
625 sib->sibling = pref->sibling; | |
626 } | |
627 | |
628 name = pref_full_name(pref); | |
629 | |
630 gaim_debug_info("prefs", "removing pref /%s\n", name); | |
631 | |
632 g_hash_table_remove(prefs_hash, name); | |
633 g_free(name); | |
634 | |
635 free_pref_value(pref); | |
636 | |
637 g_slist_free(pref->callbacks); | |
638 g_free(pref->name); | |
639 g_free(pref); | |
640 } | |
641 | |
642 void | |
643 gaim_prefs_remove(const char *name) | |
644 { | |
645 struct gaim_pref *pref = find_pref(name); | |
646 | |
647 if(!pref) | |
648 return; | |
649 | |
650 remove_pref(pref); | |
651 } | |
652 | |
653 void | |
654 gaim_prefs_destroy() | |
655 { | |
656 gaim_prefs_remove("/"); | |
657 } | |
658 | |
659 static void | |
660 do_callbacks(const char* name, struct gaim_pref *pref) | |
661 { | |
662 GSList *cbs; | |
663 struct gaim_pref *cb_pref; | |
664 for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) { | |
665 for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) { | |
666 struct pref_cb *cb = cbs->data; | |
667 cb->func(name, pref->type, pref->value.generic, cb->data); | |
668 } | |
669 } | |
670 } | |
671 | |
672 void | |
673 gaim_prefs_trigger_callback(const char *name) | |
674 { | |
675 struct gaim_pref *pref = find_pref(name); | |
676 | |
677 if(!pref) { | |
678 gaim_debug_error("prefs", | |
679 "gaim_prefs_trigger_callback: Unknown pref %s\n", name); | |
680 return; | |
681 } | |
682 | |
683 do_callbacks(name, pref); | |
684 } | |
685 | |
686 void | |
687 gaim_prefs_set_generic(const char *name, gpointer value) | |
688 { | |
689 struct gaim_pref *pref = find_pref(name); | |
690 | |
691 if(!pref) { | |
692 gaim_debug_error("prefs", | |
693 "gaim_prefs_set_generic: Unknown pref %s\n", name); | |
694 return; | |
695 } | |
696 | |
697 pref->value.generic = value; | |
698 do_callbacks(name, pref); | |
699 } | |
700 | |
701 void | |
702 gaim_prefs_set_bool(const char *name, gboolean value) | |
703 { | |
704 struct gaim_pref *pref = find_pref(name); | |
705 | |
706 if(pref) { | |
707 if(pref->type != GAIM_PREF_BOOLEAN) { | |
708 gaim_debug_error("prefs", | |
709 "gaim_prefs_set_bool: %s not a boolean pref\n", name); | |
710 return; | |
711 } | |
712 | |
713 if(pref->value.boolean != value) { | |
714 pref->value.boolean = value; | |
715 do_callbacks(name, pref); | |
716 } | |
717 } else { | |
718 gaim_prefs_add_bool(name, value); | |
719 } | |
720 } | |
721 | |
722 void | |
723 gaim_prefs_set_int(const char *name, int value) | |
724 { | |
725 struct gaim_pref *pref = find_pref(name); | |
726 | |
727 if(pref) { | |
728 if(pref->type != GAIM_PREF_INT) { | |
729 gaim_debug_error("prefs", | |
730 "gaim_prefs_set_int: %s not an integer pref\n", name); | |
731 return; | |
732 } | |
733 | |
734 if(pref->value.integer != value) { | |
735 pref->value.integer = value; | |
736 do_callbacks(name, pref); | |
737 } | |
738 } else { | |
739 gaim_prefs_add_int(name, value); | |
740 } | |
741 } | |
742 | |
743 void | |
744 gaim_prefs_set_string(const char *name, const char *value) | |
745 { | |
746 struct gaim_pref *pref = find_pref(name); | |
747 | |
748 if(pref) { | |
749 if(pref->type != GAIM_PREF_STRING) { | |
750 gaim_debug_error("prefs", | |
751 "gaim_prefs_set_string: %s not a string pref\n", name); | |
752 return; | |
753 } | |
754 | |
755 if((value && !pref->value.string) || | |
756 (!value && pref->value.string) || | |
757 strcmp(pref->value.string, value)) { | |
758 g_free(pref->value.string); | |
759 pref->value.string = g_strdup(value); | |
760 do_callbacks(name, pref); | |
761 } | |
762 } else { | |
763 gaim_prefs_add_string(name, value); | |
764 } | |
765 } | |
766 | |
767 void | |
768 gaim_prefs_set_string_list(const char *name, GList *value) | |
769 { | |
770 struct gaim_pref *pref = find_pref(name); | |
771 if(pref) { | |
772 GList *tmp; | |
773 | |
774 if(pref->type != GAIM_PREF_STRING_LIST) { | |
775 gaim_debug_error("prefs", | |
776 "gaim_prefs_set_string_list: %s not a string list pref\n", | |
777 name); | |
778 return; | |
779 } | |
780 | |
781 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next) | |
782 g_free(tmp->data); | |
783 | |
784 g_list_free(pref->value.stringlist); | |
785 pref->value.stringlist = NULL; | |
786 | |
787 for(tmp = value; tmp; tmp = tmp->next) | |
788 pref->value.stringlist = g_list_append(pref->value.stringlist, | |
789 g_strdup(tmp->data)); | |
790 | |
791 do_callbacks(name, pref); | |
792 | |
793 } else { | |
794 gaim_prefs_add_string_list(name, value); | |
795 } | |
796 } | |
797 | |
798 gboolean | |
799 gaim_prefs_exists(const char *name) | |
800 { | |
801 struct gaim_pref *pref = find_pref(name); | |
802 | |
803 if (pref != NULL) | |
804 return TRUE; | |
805 | |
806 return FALSE; | |
807 } | |
808 | |
809 GaimPrefType | |
810 gaim_prefs_get_type(const char *name) | |
811 { | |
812 struct gaim_pref *pref = find_pref(name); | |
813 | |
814 if (pref == NULL) | |
815 return GAIM_PREF_NONE; | |
816 | |
817 return (pref->type); | |
818 } | |
819 | |
820 gboolean | |
821 gaim_prefs_get_bool(const char *name) | |
822 { | |
823 struct gaim_pref *pref = find_pref(name); | |
824 | |
825 if(!pref) { | |
826 gaim_debug_error("prefs", | |
827 "gaim_prefs_get_bool: Unknown pref %s\n", name); | |
828 return FALSE; | |
829 } else if(pref->type != GAIM_PREF_BOOLEAN) { | |
830 gaim_debug_error("prefs", | |
831 "gaim_prefs_get_bool: %s not a boolean pref\n", name); | |
832 return FALSE; | |
833 } | |
834 | |
835 return pref->value.boolean; | |
836 } | |
837 | |
838 int | |
839 gaim_prefs_get_int(const char *name) | |
840 { | |
841 struct gaim_pref *pref = find_pref(name); | |
842 | |
843 if(!pref) { | |
844 gaim_debug_error("prefs", | |
845 "gaim_prefs_get_int: Unknown pref %s\n", name); | |
846 return 0; | |
847 } else if(pref->type != GAIM_PREF_INT) { | |
848 gaim_debug_error("prefs", | |
849 "gaim_prefs_get_int: %s not an integer pref\n", name); | |
850 return 0; | |
851 } | |
852 | |
853 return pref->value.integer; | |
854 } | |
855 | |
856 const char * | |
857 gaim_prefs_get_string(const char *name) | |
858 { | |
859 struct gaim_pref *pref = find_pref(name); | |
860 | |
861 if(!pref) { | |
862 gaim_debug_error("prefs", | |
863 "gaim_prefs_get_string: Unknown pref %s\n", name); | |
864 return NULL; | |
865 } else if(pref->type != GAIM_PREF_STRING) { | |
866 gaim_debug_error("prefs", | |
867 "gaim_prefs_get_string: %s not a string pref\n", name); | |
868 return NULL; | |
869 } | |
870 | |
871 return pref->value.string; | |
872 } | |
873 | |
874 GList * | |
875 gaim_prefs_get_string_list(const char *name) | |
876 { | |
877 struct gaim_pref *pref = find_pref(name); | |
878 GList *ret = NULL, *tmp; | |
879 | |
880 if(!pref) { | |
881 gaim_debug_error("prefs", | |
882 "gaim_prefs_get_string_list: Unknown pref %s\n", name); | |
883 return NULL; | |
884 } else if(pref->type != GAIM_PREF_STRING_LIST) { | |
885 gaim_debug_error("prefs", | |
886 "gaim_prefs_get_string_list: %s not a string list pref\n", name); | |
887 return NULL; | |
888 } | |
889 | |
890 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next) | |
891 ret = g_list_append(ret, g_strdup(tmp->data)); | |
892 | |
893 return ret; | |
894 } | |
895 | |
896 void | |
897 gaim_prefs_rename(const char *oldname, const char *newname) | |
898 { | |
899 struct gaim_pref *oldpref, *newpref; | |
900 | |
901 oldpref = find_pref(oldname); | |
902 newpref = find_pref(newname); | |
903 | |
904 /* it's already been renamed, call off the dogs */ | |
905 if(!oldpref) | |
906 return; | |
907 | |
908 gaim_debug_info("prefs", "Renaming %s to %s\n", oldname, newname); | |
909 | |
910 g_return_if_fail(newpref != NULL); /* the new one needs to be created first */ | |
911 g_return_if_fail(oldpref->type == newpref->type); | |
912 g_return_if_fail(oldpref->first_child == NULL); /* can't rename parents */ | |
913 | |
914 switch(oldpref->type) { | |
915 case GAIM_PREF_NONE: | |
916 break; | |
917 case GAIM_PREF_BOOLEAN: | |
918 gaim_prefs_set_bool(newname, oldpref->value.boolean); | |
919 break; | |
920 case GAIM_PREF_INT: | |
921 gaim_prefs_set_int(newname, oldpref->value.integer); | |
922 break; | |
923 case GAIM_PREF_STRING: | |
924 gaim_prefs_set_string(newname, oldpref->value.string); | |
925 break; | |
926 case GAIM_PREF_STRING_LIST: | |
927 gaim_prefs_set_string_list(newname, oldpref->value.stringlist); | |
928 break; | |
929 } | |
930 | |
931 remove_pref(oldpref); | |
932 } | |
933 | |
934 void | |
935 gaim_prefs_rename_boolean_toggle(const char *oldname, const char *newname) | |
936 { | |
937 struct gaim_pref *oldpref, *newpref; | |
938 | |
939 gaim_debug_info("prefs", "Attempting to rename and toggle %s to %s\n", oldname, newname); | |
940 | |
941 oldpref = find_pref(oldname); | |
942 newpref = find_pref(newname); | |
943 | |
944 /* it's already been renamed, call off the cats */ | |
945 if(!oldpref) | |
946 return; | |
947 | |
948 g_return_if_fail(newpref != NULL); /* the new one needs to be created */ | |
949 g_return_if_fail(oldpref->type == newpref->type); | |
950 g_return_if_fail(oldpref->type == GAIM_PREF_BOOLEAN); | |
951 g_return_if_fail(oldpref->first_child == NULL); /* can't rename parents */ | |
952 | |
953 gaim_prefs_set_bool(newname, !(oldpref->value.boolean)); | |
954 | |
955 remove_pref(oldpref); | |
956 | |
957 } | |
958 | |
959 guint | |
960 gaim_prefs_connect_callback(void *handle, const char *name, GaimPrefCallback func, gpointer data) | |
961 { | |
962 struct gaim_pref *pref; | |
963 struct pref_cb *cb; | |
964 static guint cb_id = 0; | |
965 | |
966 pref = find_pref(name); | |
967 if (pref == NULL) | |
968 return 0; | |
969 | |
970 cb = g_new0(struct pref_cb, 1); | |
971 | |
972 cb->func = func; | |
973 cb->data = data; | |
974 cb->id = ++cb_id; | |
975 cb->handle = handle; | |
976 | |
977 pref->callbacks = g_slist_append(pref->callbacks, cb); | |
978 | |
979 return cb->id; | |
980 } | |
981 | |
982 static gboolean | |
983 disco_callback_helper(struct gaim_pref *pref, guint callback_id) | |
984 { | |
985 GSList *cbs; | |
986 struct gaim_pref *child; | |
987 | |
988 if(!pref) | |
989 return FALSE; | |
990 | |
991 for(cbs = pref->callbacks; cbs; cbs = cbs->next) { | |
992 struct pref_cb *cb = cbs->data; | |
993 if(cb->id == callback_id) { | |
994 pref->callbacks = g_slist_remove(pref->callbacks, cb); | |
995 g_free(cb); | |
996 return TRUE; | |
997 } | |
998 } | |
999 | |
1000 for(child = pref->first_child; child; child = child->sibling) { | |
1001 if(disco_callback_helper(child, callback_id)) | |
1002 return TRUE; | |
1003 } | |
1004 | |
1005 return FALSE; | |
1006 } | |
1007 | |
1008 void | |
1009 gaim_prefs_disconnect_callback(guint callback_id) | |
1010 { | |
1011 disco_callback_helper(&prefs, callback_id); | |
1012 } | |
1013 | |
1014 static void | |
1015 disco_callback_helper_handle(struct gaim_pref *pref, void *handle) | |
1016 { | |
1017 GSList *cbs; | |
1018 struct gaim_pref *child; | |
1019 | |
1020 if(!pref) | |
1021 return; | |
1022 | |
1023 cbs = pref->callbacks; | |
1024 while (cbs != NULL) { | |
1025 struct pref_cb *cb = cbs->data; | |
1026 if(cb->handle == handle) { | |
1027 pref->callbacks = g_slist_remove(pref->callbacks, cb); | |
1028 g_free(cb); | |
1029 cbs = pref->callbacks; | |
1030 } else | |
1031 cbs = cbs->next; | |
1032 } | |
1033 | |
1034 for(child = pref->first_child; child; child = child->sibling) | |
1035 disco_callback_helper_handle(child, handle); | |
1036 } | |
1037 | |
1038 void | |
1039 gaim_prefs_disconnect_by_handle(void *handle) | |
1040 { | |
1041 g_return_if_fail(handle != NULL); | |
1042 | |
1043 disco_callback_helper_handle(&prefs, handle); | |
1044 } | |
1045 | |
1046 void | |
1047 gaim_prefs_update_old() | |
1048 { | |
8900 | 1049 /* Remove some no-longer-used prefs */ |
9594 | 1050 gaim_prefs_remove("/core/away/auto_response/enabled"); |
1051 gaim_prefs_remove("/core/away/auto_response/idle_only"); | |
8948 | 1052 gaim_prefs_remove("/core/away/auto_response/in_active_conv"); |
1053 gaim_prefs_remove("/core/away/auto_response/sec_before_resend"); | |
9594 | 1054 gaim_prefs_remove("/core/away/auto_response"); |
10353 | 1055 gaim_prefs_remove("/core/buddies/use_server_alias"); |
8942 | 1056 gaim_prefs_remove("/core/conversations/away_back_on_send"); |
8900 | 1057 gaim_prefs_remove("/core/conversations/send_urls_as_links"); |
8942 | 1058 gaim_prefs_remove("/core/conversations/im/show_login"); |
8998 | 1059 gaim_prefs_remove("/core/conversations/chat/show_join"); |
1060 gaim_prefs_remove("/core/conversations/chat/show_leave"); | |
9251 | 1061 gaim_prefs_remove("/core/conversations/combine_chat_im"); |
10389 | 1062 gaim_prefs_remove("/core/conversations/use_alias_for_title"); |
8900 | 1063 } |
10443 | 1064 |
1065 void * | |
1066 gaim_prefs_get_handle(void) | |
1067 { | |
1068 static int handle; | |
1069 | |
1070 return &handle; | |
1071 } | |
1072 | |
1073 void | |
1074 gaim_prefs_init(void) | |
1075 { | |
1076 void *handle = gaim_prefs_get_handle(); | |
1077 | |
1078 prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); | |
1079 | |
1080 gaim_prefs_connect_callback(handle, "/", prefs_save_cb, NULL); | |
1081 | |
1082 gaim_prefs_add_none("/core"); | |
1083 gaim_prefs_add_none("/plugins"); | |
1084 gaim_prefs_add_none("/plugins/core"); | |
1085 gaim_prefs_add_none("/plugins/lopl"); | |
1086 gaim_prefs_add_none("/plugins/prpl"); | |
1087 | |
1088 /* Away */ | |
1089 gaim_prefs_add_none("/core/away"); | |
1090 gaim_prefs_add_bool("/core/away/away_when_idle", TRUE); | |
1091 gaim_prefs_add_int("/core/away/mins_before_away", 5); | |
1092 /* XXX: internationalized string in prefs...evil */ | |
1093 gaim_prefs_add_string("/core/away/default_message", | |
1094 _("Slightly less boring default")); | |
1095 | |
1096 /* Away -> Auto-Reply */ | |
1097 if (!gaim_prefs_exists("/core/away/auto_response/enabled") || | |
1098 !gaim_prefs_exists("/core/away/auto_response/idle_only")) { | |
1099 gaim_prefs_add_string("/core/away/auto_reply", "awayidle"); | |
1100 } else { | |
1101 if (!gaim_prefs_get_bool("/core/away/auto_response/enabled")) { | |
1102 gaim_prefs_add_string("/core/away/auto_reply", "never"); | |
1103 } else { | |
1104 if (gaim_prefs_get_bool("/core/away/auto_response/idle_only")) { | |
1105 gaim_prefs_add_string("/core/away/auto_reply", "awayidle"); | |
1106 } else { | |
1107 gaim_prefs_add_string("/core/away/auto_reply", "away"); | |
1108 } | |
1109 } | |
1110 } | |
1111 | |
1112 /* Buddies */ | |
1113 gaim_prefs_add_none("/core/buddies"); | |
1114 | |
1115 /* Contact Priority Settings */ | |
1116 gaim_prefs_add_none("/core/contact"); | |
1117 gaim_prefs_add_bool("/core/contact/last_match", FALSE); | |
1118 gaim_prefs_add_int("/core/contact/offline_score", 4); | |
1119 gaim_prefs_add_int("/core/contact/away_score", 2); | |
1120 gaim_prefs_add_int("/core/contact/idle_score", 1); | |
1121 } | |
1122 | |
1123 void | |
1124 gaim_prefs_uninit() | |
1125 { | |
1126 if (save_timer != 0) | |
1127 { | |
1128 gaim_timeout_remove(save_timer); | |
1129 save_timer = 0; | |
1130 sync_prefs(); | |
1131 } | |
1132 | |
1133 gaim_prefs_disconnect_by_handle(gaim_prefs_get_handle()); | |
1134 } |