1 /*
2 * prefs.c: configuration routines
3 * Copyright (C) 2002-2004 Saulius Menkevicius
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: prefs.c,v 1.35 2004/12/21 15:11:37 bobas Exp $
20 */
21
22 #include <stdarg.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <glib.h>
29 #include <gtk/gtk.h>
30
31 #include "main.h"
32 #include "prefs.h"
33 #include "net.h"
34 #include "user.h"
35 #include "util.h"
36 #include "gui_tray.h"
37
38 #define LOG_PREFS N_("Preferences")
39 #define PREFS_FILE_NAME ".vqcc.conf.xml"
40
41 enum prefs_parser_tag {
42 PREFS_TAG_NO_TAG,
43 PREFS_TAG_VQCC_GTK,
44 PREFS_TAG_VQCC_GTK_SETTINGS,
45 PREFS_TAG_VQCC_GTK_SETTINGS_PREF,
46 PREFS_TAG_VQCC_GTK_SETTINGS_PREF_ENTRY
47 };
48
49 struct prefs_parser_state {
50 enum prefs_parser_tag tag;
51 gboolean pref_valid;
52 GString * pref_name;
53 enum prefs_type pref_type;
54 };
55
56 struct prefs_value {
57 gchar * description;
58 enum prefs_type type;
59 union prefs_value_union {
60 gboolean boolean;
61 gchar * string;
62 guint integer;
63 GList * list;
64 } value;
65
66 prefs_validator_func * validator_func;
67 gpointer validator_user_data;
68
69 GHookList change_hook;
70 };
71
72 /* static variables */
73 static gchar * prefs_file_path;
74 static GHashTable * prefs_hash;
75 static gboolean prefs_values_saved;
76
77 /* forward references */
78 static gboolean prefs_load();
79 static gboolean prefs_store();
80
81 /** static routines
82 *****************/
83
84 /* prefs_free_value:
85 * frees data in the internal value struct
86 */
87 static void
prefs_free_value(enum prefs_type value_type,union prefs_value_union * value)88 prefs_free_value(
89 enum prefs_type value_type, union prefs_value_union * value)
90 {
91 switch(value_type) {
92 case PREFS_TYPE_LIST:
93 util_list_free_with_data(value->list, (GDestroyNotify)g_free);
94 break;
95 case PREFS_TYPE_STR:
96 g_free(value->string);
97 break;
98 default:
99 break;
100 }
101 }
102
103 /* prefs_destroy_value_cb:
104 * destroys preference value descriptor in prefs hash table
105 */
106 static void
prefs_destroy_value_cb(struct prefs_value * value)107 prefs_destroy_value_cb(struct prefs_value * value)
108 {
109 g_assert(value && value->description);
110
111 g_free(value->description);
112 prefs_free_value(value->type, &value->value);
113 g_hook_list_clear(&value->change_hook);
114 g_free(value);
115 }
116
117 /* prefs_init:
118 * initializes preference module
119 */
120 static void
prefs_init()121 prefs_init()
122 {
123 prefs_values_saved = FALSE;
124
125 /* setup prefs hash */
126 prefs_hash = g_hash_table_new_full(
127 (GHashFunc)g_str_hash, (GEqualFunc)g_str_equal,
128 (GDestroyNotify)g_free, (GDestroyNotify)prefs_destroy_value_cb);
129 }
130
131 /* prefs_free:
132 * free any variables we might have allocated in
133 * config module since the start of application
134 */
135 static void
prefs_free()136 prefs_free()
137 {
138 g_hash_table_destroy(prefs_hash);
139 prefs_hash = NULL;
140
141 if(prefs_file_path) {
142 g_free(prefs_file_path);
143 prefs_file_path = NULL;
144 }
145 }
146
147 static void
prefs_event_cb(enum app_event_enum e,gpointer p,gint i)148 prefs_event_cb(
149 enum app_event_enum e,
150 gpointer p, gint i)
151 {
152 switch(e) {
153 case EVENT_MAIN_INIT:
154 prefs_init();
155 break;
156
157 case EVENT_MAIN_REGISTER_PREFS:
158 prefs_register(PREFS_PREFS_AUTO_SAVE, PREFS_TYPE_BOOL,
159 _("Save settings on exit"), NULL, NULL);
160 break;
161
162 case EVENT_MAIN_PRESET_PREFS:
163 prefs_set(PREFS_PREFS_AUTO_SAVE, TRUE);
164 break;
165
166 case EVENT_MAIN_LOAD_PREFS:
167 prefs_load();
168 break;
169
170 case EVENT_MAIN_CLOSE:
171 /* save configuration */
172 if(prefs_bool(PREFS_PREFS_AUTO_SAVE))
173 prefs_store();
174 prefs_free();
175 break;
176
177 case EVENT_IFACE_RELOAD_CONFIG:
178 /* force reload of config */
179 prefs_load();
180 break;
181
182 case EVENT_IFACE_STORE_CONFIG:
183 prefs_store();
184 break;
185 default:
186 break;
187 }
188 }
189
190 /** exported routines
191 *******************/
192
prefs_register_module(gint argc,gchar ** argv,gchar ** env)193 void prefs_register_module(gint argc, gchar ** argv, gchar ** env)
194 {
195 const gchar * home;
196 gchar ** p;
197
198 register_event_cb(prefs_event_cb, EVENT_MAIN|EVENT_IFACE);
199
200 /* get home path */
201 prefs_file_path = NULL;
202
203 /* check if configuration file was specified */
204 for(p=argv+1; *p; p++)
205 if(!g_strcasecmp(*p, "-c") && *(p+1)!=NULL) {
206 prefs_file_path = g_strdup(*(p+1));
207 break;
208 }
209
210 /* get & store UNIX home path */
211 home = g_get_home_dir();
212 if(!prefs_file_path)
213 prefs_file_path = g_strdup_printf("%s/"PREFS_FILE_NAME, home ? home: "");
214 }
215
prefs_register(const gchar * name,enum prefs_type type,const gchar * description,prefs_validator_func * validator_func,gpointer validator_user_data)216 void prefs_register(
217 const gchar * name,
218 enum prefs_type type,
219 const gchar * description,
220 prefs_validator_func * validator_func,
221 gpointer validator_user_data)
222 {
223 struct prefs_value * value;
224
225 g_assert(name && description);
226 g_assert(prefs_hash != NULL);
227
228 /* setup preference value struct */
229 value = g_new(struct prefs_value, 1);
230
231 value->description = g_strdup(description);
232 value->type = type;
233 switch(type) {
234 case PREFS_TYPE_BOOL:
235 value->value.boolean = FALSE;
236 break;
237 case PREFS_TYPE_STR:
238 value->value.string = g_strdup("");
239 break;
240 case PREFS_TYPE_UINT:
241 value->value.integer = 0;
242 break;
243 case PREFS_TYPE_LIST:
244 value->value.list = NULL;
245 break;
246 }
247
248 value->validator_func = validator_func;
249 value->validator_user_data = validator_user_data;
250
251 g_hook_list_init(&value->change_hook, sizeof(GHook));
252
253 /* add it into the prefs hash */
254 g_hash_table_insert(prefs_hash, g_strdup(name), value);
255 }
256
257 /* prefs_add_notifier:
258 * registers pref change notifier
259 */
prefs_add_notifier(const gchar * pref_name,GHookFunc func)260 void prefs_add_notifier(const gchar * pref_name, GHookFunc func)
261 {
262 GHook * hook;
263 struct prefs_value * value;
264 const gchar * value_key;
265 gboolean found_pref;
266
267 found_pref = g_hash_table_lookup_extended(
268 prefs_hash, pref_name, (gpointer*)&value_key, (gpointer*)&value);
269 g_assert(found_pref);
270
271 /* setup new hook for this preference */
272 hook = g_hook_alloc(&value->change_hook);
273 hook->func = func;
274 hook->data = (gpointer)value_key;
275
276 g_hook_prepend(&value->change_hook, hook);
277 }
278
279 /* prefs_write_xml_pref:
280 * stores specified value to the disk
281 */
282 static void
prefs_write_xml_pref(const gchar * prefs_name,struct prefs_value * value,FILE * prefs_f)283 prefs_write_xml_pref(const gchar * prefs_name, struct prefs_value * value, FILE * prefs_f)
284 {
285 gchar * esc;
286 GList * entry;
287
288 g_assert(prefs_name && value && prefs_f);
289
290 #define WRITE_ESCAPED(format, ...) \
291 do { \
292 esc = g_markup_printf_escaped(format, __VA_ARGS__); \
293 fputs(esc, prefs_f); \
294 g_free(esc); \
295 } while(0);
296
297 WRITE_ESCAPED("\t\t<pref name=\"%s\"", prefs_name);
298
299 switch(value->type) {
300 case PREFS_TYPE_UINT:
301 WRITE_ESCAPED(" type=\"uint\">%u</pref>\n", value->value.integer);
302 break;
303
304 case PREFS_TYPE_BOOL:
305 WRITE_ESCAPED(" type=\"bool\">%s</pref>\n",
306 value->value.boolean ? "TRUE": "FALSE");
307 break;
308
309 case PREFS_TYPE_STR:
310 WRITE_ESCAPED(" type=\"string\">%s</pref>\n", value->value.string);
311 break;
312
313 case PREFS_TYPE_LIST:
314 fputs(" type=\"list\">", prefs_f);
315 if(value->value.list) {
316 fputs("\n", prefs_f);
317
318 for(entry = value->value.list; entry; entry = entry->next)
319 WRITE_ESCAPED("\t\t\t<entry>%s</entry>\n",
320 (const gchar*)entry->data);
321
322 fputs("\t\t", prefs_f);
323 }
324 fputs("</pref>\n", prefs_f);
325 break;
326 }
327 }
328
329 static void
prefs_write_xml_sort_prefs_cb(const gchar * prefs_name,struct prefs_value * value,GList ** list)330 prefs_write_xml_sort_prefs_cb(const gchar * prefs_name, struct prefs_value * value, GList ** list)
331 {
332 *list = g_list_insert_sorted(*list, (gpointer)prefs_name, (GCompareFunc)g_utf8_collate);
333 }
334
335 static void
prefs_write_xml_to(FILE * prefs_f)336 prefs_write_xml_to(FILE * prefs_f)
337 {
338 GList * sorted, * entry;
339
340 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
341 "<vqcc_gtk>\n"
342 "\t<settings>\n",
343 prefs_f);
344
345 /* sort the preferences */
346 sorted = NULL;
347 g_hash_table_foreach(prefs_hash, (GHFunc)prefs_write_xml_sort_prefs_cb, (gpointer)&sorted);
348
349 for(entry = sorted; entry; entry = entry->next)
350 prefs_write_xml_pref(
351 (const gchar*) entry->data,
352 g_hash_table_lookup(prefs_hash, entry->data),
353 prefs_f);
354
355 g_list_free(sorted);
356
357 fputs("\t</settings>\n"
358 "</vqcc_gtk>",
359 prefs_f);
360 }
361
362
363 /* prefs_store:
364 * save configuration settings to configuration file
365 */
366 static gboolean
prefs_store()367 prefs_store()
368 {
369 FILE * cf;
370
371 /* open config file */
372 cf = fopen(prefs_file_path, "wb");
373 if(!cf) {
374 log_ferror(_(LOG_PREFS), g_strdup_printf(
375 _("Cannot save configuration settings to file \"%s\": %s"),
376 prefs_file_path, strerror(errno)));
377 } else {
378 /* store settings to file */
379 prefs_write_xml_to(cf);
380 fclose(cf);
381
382 log_fevent(_(LOG_PREFS), g_strdup_printf(
383 _("Configuration settings were stored in \"%s\""),
384 prefs_file_path));
385 }
386
387 /* config values now are in sync with those on the disk */
388 prefs_values_saved = TRUE;
389 raise_event(EVENT_PREFS_SAVED, NULL, 0);
390
391 return TRUE;
392 }
393
394 static gboolean
prefs_get_type_by_name(const gchar * type_name,enum prefs_type * type)395 prefs_get_type_by_name(const gchar * type_name, enum prefs_type * type)
396 {
397 gboolean found = TRUE;
398
399 if(!g_utf8_collate(type_name, "uint"))
400 *type = PREFS_TYPE_UINT;
401 else if(!g_utf8_collate(type_name, "bool"))
402 *type = PREFS_TYPE_BOOL;
403 else if(!g_utf8_collate(type_name, "string"))
404 *type = PREFS_TYPE_STR;
405 else if(!g_utf8_collate(type_name, "list"))
406 *type = PREFS_TYPE_LIST;
407 else
408 found = FALSE;
409
410 return found;
411 }
412
413 static void
prefs_load_xml_start_element(GMarkupParseContext * context,const gchar * element,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)414 prefs_load_xml_start_element(
415 GMarkupParseContext * context,
416 const gchar * element,
417 const gchar ** attribute_names, const gchar ** attribute_values,
418 gpointer user_data,
419 GError ** error)
420 {
421 struct prefs_parser_state * state = (struct prefs_parser_state*)user_data;
422
423 if(!g_utf8_collate(element, "vqcc_gtk") && state->tag==PREFS_TAG_NO_TAG) {
424 state->tag = PREFS_TAG_VQCC_GTK;
425 }
426 else if(!g_utf8_collate(element, "settings") && state->tag==PREFS_TAG_VQCC_GTK) {
427 state->tag = PREFS_TAG_VQCC_GTK_SETTINGS;
428 }
429 else if(!g_utf8_collate(element, "pref") && state->tag==PREFS_TAG_VQCC_GTK_SETTINGS) {
430 struct prefs_value * val;
431 const gchar ** attr, ** value;
432 const gchar * prefs_type_attr;
433
434 state->tag = PREFS_TAG_VQCC_GTK_SETTINGS_PREF;
435
436 /* fetch pref attributes */
437 g_string_assign(state->pref_name, "");
438 for(attr = attribute_names, value = attribute_values; *attr; attr++, value++) {
439 if(!g_utf8_collate(*attr, "name"))
440 g_string_assign(state->pref_name, *value);
441
442 if(!g_utf8_collate(*attr, "type"))
443 prefs_type_attr = *value;
444 }
445
446 /* check if we have a valid preference name and type */
447 state->pref_valid = FALSE;
448 val = (struct prefs_value*)g_hash_table_lookup(prefs_hash, state->pref_name->str);
449
450 if(val && prefs_get_type_by_name(prefs_type_attr, &state->pref_type)) {
451 if(state->pref_type==val->type)
452 state->pref_valid = TRUE;
453 }
454 }
455 else if(!g_utf8_collate(element, "entry") && state->tag==PREFS_TAG_VQCC_GTK_SETTINGS_PREF) {
456 state->tag = PREFS_TAG_VQCC_GTK_SETTINGS_PREF_ENTRY;
457 }
458 }
459
460 static void
prefs_load_xml_end_element(GMarkupParseContext * context,const gchar * element,gpointer user_data,GError ** error)461 prefs_load_xml_end_element(
462 GMarkupParseContext * context,
463 const gchar * element,
464 gpointer user_data,
465 GError ** error)
466 {
467 struct prefs_parser_state * state = (struct prefs_parser_state*)user_data;
468
469 if(!g_utf8_collate(element, "vqcc_gtk")
470 && state->tag==PREFS_TAG_VQCC_GTK) {
471 state->tag = PREFS_TAG_NO_TAG;
472 }
473 else if(!g_utf8_collate(element, "settings")
474 && state->tag==PREFS_TAG_VQCC_GTK_SETTINGS) {
475 state->tag = PREFS_TAG_VQCC_GTK;
476 }
477 else if(!g_utf8_collate(element, "pref")
478 && state->tag==PREFS_TAG_VQCC_GTK_SETTINGS_PREF) {
479 state->tag = PREFS_TAG_VQCC_GTK_SETTINGS;
480 }
481 else if(!g_utf8_collate(element, "entry")
482 && state->tag==PREFS_TAG_VQCC_GTK_SETTINGS_PREF_ENTRY) {
483 state->tag = PREFS_TAG_VQCC_GTK_SETTINGS_PREF;
484 }
485 }
486
487 static void
prefs_load_xml_text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)488 prefs_load_xml_text(
489 GMarkupParseContext * context,
490 const gchar * text,
491 gsize text_len,
492 gpointer user_data,
493 GError ** error)
494 {
495 struct prefs_parser_state * state = (struct prefs_parser_state*)user_data;
496
497 if(!state->pref_valid)
498 return;
499
500 if(state->tag==PREFS_TAG_VQCC_GTK_SETTINGS_PREF) {
501 gchar * stripped;
502 guint uint_read;
503
504 switch(state->pref_type) {
505 case PREFS_TYPE_UINT:
506 if(sscanf(text, "%u", &uint_read)==1) {
507 prefs_set(state->pref_name->str, uint_read);
508 } else {
509 g_set_error(error, G_MARKUP_ERROR,
510 G_MARKUP_ERROR_INVALID_CONTENT,
511 _("Could not read value for preference \"%s\""),
512 state->pref_name->str);
513 }
514 break;
515 case PREFS_TYPE_BOOL:
516 stripped = g_strstrip(g_strdup(text));
517 if(!g_ascii_strncasecmp(text, "FALSE", sizeof("FALSE")-1)) {
518 prefs_set(state->pref_name->str, FALSE);
519 }
520 else if(!g_ascii_strncasecmp(text, "TRUE", sizeof("TRUE")-1)) {
521 prefs_set(state->pref_name->str, TRUE);
522 }
523 else {
524 g_set_error(error, G_MARKUP_ERROR,
525 G_MARKUP_ERROR_INVALID_CONTENT,
526 _("Could not read value for preference \"%s\""),
527 state->pref_name->str);
528 }
529 g_free(stripped);
530 break;
531 case PREFS_TYPE_STR:
532 prefs_set(state->pref_name->str, text);
533 break;
534 default:
535 break;
536 }
537 } else if(state->tag==PREFS_TAG_VQCC_GTK_SETTINGS_PREF_ENTRY
538 && state->pref_type==PREFS_TYPE_LIST) {
539 /* read list data */
540 prefs_list_add(state->pref_name->str, text);
541 }
542 }
543
544 static void
prefs_free_parser_state(struct prefs_parser_state * state)545 prefs_free_parser_state(struct prefs_parser_state * state)
546 {
547 g_string_free(state->pref_name, TRUE);
548 g_free(state);
549 }
550
551 /* prefs_load_xml_from:
552 * creates GMarkupParserContext for parsing the prefs
553 */
554 static void
prefs_load_xml_from(const gchar * prefs_filename,FILE * prefs_f)555 prefs_load_xml_from(const gchar * prefs_filename, FILE * prefs_f)
556 {
557 GMarkupParser parser;
558 GMarkupParseContext * context;
559 gchar * buf;
560 gsize buf_bytes;
561 GError * error = NULL;
562 struct prefs_parser_state * state;
563
564 parser.start_element = prefs_load_xml_start_element;
565 parser.end_element = prefs_load_xml_end_element;
566 parser.text = prefs_load_xml_text;
567 parser.passthrough = NULL;
568 parser.error = NULL;
569
570 state = g_new(struct prefs_parser_state, 1);
571 state->tag = PREFS_TAG_NO_TAG;
572 state->pref_name = g_string_new(NULL);
573
574 context = g_markup_parse_context_new(
575 &parser, 0, (gpointer)state,
576 (GDestroyNotify)prefs_free_parser_state);
577
578 buf = g_malloc(4096);
579
580 while(!feof(prefs_f)) {
581 buf_bytes = fread(buf, 1, 4096, prefs_f);
582 if(buf_bytes < 4096 && ferror(prefs_f)) {
583 log_ferror(_(LOG_PREFS),
584 g_strdup_printf(_("Error while reading "
585 "configuration file \"%s\": %s"),
586 prefs_filename, strerror(errno)));
587
588 goto bail_out;
589 }
590
591 if(buf_bytes)
592 if(g_markup_parse_context_parse(context, buf, buf_bytes, &error)==FALSE)
593 goto parse_error;
594 }
595
596 if(g_markup_parse_context_end_parse(context, &error)==TRUE) {
597 /* parse ok */
598 goto bail_out;
599 }
600
601 parse_error:
602 log_ferror(_(LOG_PREFS),
603 g_strdup_printf(_("Error parsing configuration from \"%s\": %s"),
604 prefs_filename, error->message));
605
606 g_error_free(error);
607
608 bail_out:
609 g_free(buf);
610 g_markup_parse_context_free(context);
611
612 }
613
614 /* prefs_load:
615 * load configuration settings from file
616 */
617 static gboolean
prefs_load()618 prefs_load()
619 {
620 FILE * config_file;
621
622 /* parse main file */
623 config_file = fopen(prefs_file_path, "r");
624 if(!config_file) {
625 log_ferror(_(LOG_PREFS), g_strdup_printf(
626 _("Cannot open configuration file \"%s\" for reading: %s"),
627 prefs_file_path, strerror(errno)));
628 return FALSE;
629 }
630
631 prefs_load_xml_from(prefs_file_path, config_file);
632 fclose(config_file);
633
634 /* config values are in sync with those on disk */
635 prefs_values_saved = TRUE;
636
637 return TRUE;
638 }
639
640 gboolean
prefs_in_sync()641 prefs_in_sync()
642 {
643 return prefs_values_saved;
644 }
645
646 const gchar *
prefs_description(const gchar * prefs_name)647 prefs_description(const gchar * prefs_name)
648 {
649 struct prefs_value * value;
650 value = g_hash_table_lookup(prefs_hash, prefs_name);
651
652 g_return_val_if_fail(value!=NULL, "<Unknown preference>");
653
654 return value->description;
655 }
656
prefs_set(const gchar * prefs_name,...)657 void prefs_set(const gchar * prefs_name, ...)
658 {
659 va_list ap;
660 struct prefs_value * value;
661 guint new_guint;
662 gboolean new_gboolean;
663 const gchar * new_string;
664
665 va_start(ap, prefs_name);
666
667 value = g_hash_table_lookup(prefs_hash, prefs_name);
668 if(value) {
669 gboolean validated;
670 union prefs_value_union value_backup;
671
672 /* backup the current value, if the validator decides the new one is invalid */
673 value_backup = value->value;
674
675 /* check if the old value hasn't changed
676 * and if so, set the new value */
677 switch(value->type) {
678 case PREFS_TYPE_UINT:
679 new_guint = va_arg(ap, guint);
680 if(value->value.integer!=new_guint)
681 value->value.integer = new_guint;
682 else
683 goto no_change;
684 break;
685 case PREFS_TYPE_BOOL:
686 new_gboolean = va_arg(ap, gboolean);
687 if(value->value.boolean!=new_gboolean)
688 value->value.boolean = new_gboolean;
689 else
690 goto no_change;
691 break;
692 case PREFS_TYPE_STR:
693 new_string = va_arg(ap, gchar*);
694 if(strcmp(value->value.string, new_string))
695 value->value.string = g_strdup(new_string);
696 else
697 goto no_change;
698 break;
699 case PREFS_TYPE_LIST:
700 value->value.list = util_list_copy_with_data(
701 va_arg(ap, GList *),
702 (util_list_data_copy_func_t*)g_strdup);
703 break;
704 }
705
706 /* invoke validator to check new value */
707 validated = value->validator_func != NULL
708 ? value->validator_func(prefs_name, value->validator_user_data)
709 : TRUE;
710
711 if(validated) {
712 /* free backup data */
713 prefs_free_value(value->type, &value_backup);
714
715 /* notify that we've changed to a new value */
716 g_hook_list_invoke(&value->change_hook, FALSE);
717 raise_event(EVENT_PREFS_CHANGED, (gpointer)prefs_name, 0);
718
719 /* preferences are not in sync with those on the disk */
720 prefs_values_saved = FALSE;
721 } else {
722 /* restore backup value
723 */
724 prefs_free_value(value->type, &value->value);
725 value->value = value_backup;
726 }
727 } else {
728 log_ferror(_(LOG_PREFS), g_strdup_printf(
729 _("Unknown preference value \"%s\""), prefs_name));
730 }
731
732 no_change:
733 va_end(ap);
734 }
735
prefs_int(const gchar * prefs_name)736 guint prefs_int(const gchar * prefs_name)
737 {
738 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
739 g_assert(value!=NULL && value->type==PREFS_TYPE_UINT);
740
741 return value->value.integer;
742 }
743
prefs_bool(const gchar * prefs_name)744 gboolean prefs_bool(const gchar * prefs_name)
745 {
746 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
747 g_assert(value!=NULL && value->type==PREFS_TYPE_BOOL);
748
749 return value->value.boolean;
750 }
751
prefs_str(const gchar * prefs_name)752 const gchar * prefs_str(const gchar * prefs_name)
753 {
754 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
755 g_assert(value!=NULL && value->type==PREFS_TYPE_STR);
756
757 return value->value.string;
758 }
759
prefs_list(const gchar * prefs_name)760 GList * prefs_list(const gchar * prefs_name)
761 {
762 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
763 g_assert(value!=NULL && value->type==PREFS_TYPE_LIST);
764
765 return value->value.list;
766 }
767
prefs_list_add(const gchar * prefs_name,const gchar * string)768 void prefs_list_add(const gchar * prefs_name, const gchar * string)
769 {
770 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
771 g_assert(value!=NULL && value->type==PREFS_TYPE_LIST);
772
773 value->value.list = g_list_prepend(value->value.list, g_strdup(string));
774
775 /* notify that we've changed to a new value */
776 g_hook_list_invoke(&value->change_hook, FALSE);
777 raise_event(EVENT_PREFS_CHANGED, (gpointer)prefs_name, 0);
778 }
779
prefs_list_add_unique(const gchar * prefs_name,const gchar * string)780 void prefs_list_add_unique(const gchar * prefs_name, const gchar * string)
781 {
782 if(!prefs_list_contains(prefs_name, string))
783 prefs_list_add(prefs_name, string);
784 }
785
prefs_list_remove(const gchar * prefs_name,const gchar * string)786 gboolean prefs_list_remove(const gchar * prefs_name, const gchar * string)
787 {
788 GList * entry;
789
790 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
791 g_assert(value!=NULL && value->type==PREFS_TYPE_LIST);
792
793 for(entry = value->value.list; entry; entry = entry->next)
794 if(!g_utf8_collate((const gchar*)entry->data, string)) {
795 g_free(entry->data);
796 value->value.list = g_list_delete_link(value->value.list, entry);
797
798 /* notify that we've changed to a new value */
799 g_hook_list_invoke(&value->change_hook, FALSE);
800 raise_event(EVENT_PREFS_CHANGED, (gpointer)prefs_name, 0);
801
802 return TRUE;
803 }
804
805 return FALSE;
806 }
807
prefs_list_clear(const gchar * prefs_name)808 void prefs_list_clear(const gchar * prefs_name)
809 {
810 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
811 g_assert(value!=NULL && value->type==PREFS_TYPE_LIST);
812
813 util_list_free_with_data(value->value.list, (GDestroyNotify)g_free);
814 value->value.list = NULL;
815
816 /* notify that we've changed to a new value */
817 g_hook_list_invoke(&value->change_hook, FALSE);
818 raise_event(EVENT_PREFS_CHANGED, (gpointer)prefs_name, 0);
819 }
820
prefs_list_contains(const gchar * prefs_name,const gchar * string)821 gboolean prefs_list_contains(const gchar * prefs_name, const gchar * string)
822 {
823 GList * entry;
824 struct prefs_value * value = g_hash_table_lookup(prefs_hash, prefs_name);
825 g_assert(value!=NULL && value->type==PREFS_TYPE_LIST);
826
827 for(entry = value->value.list; entry!=NULL; entry = entry->next)
828 if(!g_utf8_collate((const gchar *)entry->data, string))
829 return TRUE;
830 return FALSE;
831 }
832