src/mn-conf.c (28805B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
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 3 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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 #include <glib/gi18n.h>
26 #include <gnome.h>
27 #include "mn-util.h"
28 #include "mn-conf.h"
29 #include "mn-shell.h"
30 #include "mn-locked-callback.h"
31 #include "mn-non-linear-range.h"
32
33 /* obsolete keys */
34 #define MN_CONF_OBSOLETE_LOCAL_NAMESPACE \
35 MN_CONF_NAMESPACE "/local"
36 #define MN_CONF_OBSOLETE_LOCAL_ENABLED \
37 MN_CONF_OBSOLETE_LOCAL_NAMESPACE "/enabled"
38 #define MN_CONF_OBSOLETE_LOCAL_DELAY_MINUTES \
39 MN_CONF_OBSOLETE_LOCAL_NAMESPACE "/delay/minutes"
40 #define MN_CONF_OBSOLETE_LOCAL_DELAY_SECONDS \
41 MN_CONF_OBSOLETE_LOCAL_NAMESPACE "/delay/seconds"
42 #define MN_CONF_OBSOLETE_REMOTE_NAMESPACE \
43 MN_CONF_NAMESPACE "/remote"
44 #define MN_CONF_OBSOLETE_REMOTE_ENABLED \
45 MN_CONF_OBSOLETE_REMOTE_NAMESPACE "/enabled"
46 #define MN_CONF_OBSOLETE_REMOTE_DELAY_MINUTES \
47 MN_CONF_OBSOLETE_REMOTE_NAMESPACE "/delay/minutes"
48 #define MN_CONF_OBSOLETE_REMOTE_DELAY_SECONDS \
49 MN_CONF_OBSOLETE_REMOTE_NAMESPACE "/delay/seconds"
50 #define MN_CONF_OBSOLETE_COMMANDS_CLICKED_NAMESPACE \
51 MN_CONF_COMMANDS_NAMESPACE "/clicked"
52 #define MN_CONF_OBSOLETE_COMMANDS_CLICKED_ENABLED \
53 MN_CONF_OBSOLETE_COMMANDS_CLICKED_NAMESPACE "/enabled"
54 #define MN_CONF_OBSOLETE_COMMANDS_CLICKED_COMMAND \
55 MN_CONF_OBSOLETE_COMMANDS_CLICKED_NAMESPACE "/command"
56 #define MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_NAMESPACE \
57 MN_CONF_COMMANDS_NAMESPACE "/double-clicked"
58 #define MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_ENABLED \
59 MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_NAMESPACE "/enabled"
60 #define MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_COMMAND \
61 MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_NAMESPACE "/command"
62 #define MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_NAMESPACE \
63 MN_CONF_COMMANDS_NAMESPACE "/mail-reader"
64 #define MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_ENABLED \
65 MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_NAMESPACE "/enabled"
66 #define MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_COMMAND \
67 MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_NAMESPACE "/command"
68 #define MN_CONF_OBSOLETE_PREFERENCES_DIALOG \
69 MN_CONF_UI_NAMESPACE "/preferences-dialog"
70 #define MN_CONF_OBSOLETE_SUMMARY_DIALOG \
71 MN_CONF_UI_NAMESPACE "/summary-dialog"
72 #define MN_CONF_OBSOLETE_DOUBLE_CLICK_ACTION \
73 MN_CONF_NAMESPACE "/double-click-action"
74 #define MN_CONF_OBSOLETE_ALREADY_RUN \
75 MN_CONF_NAMESPACE "/already-run"
76 #define MN_CONF_OBSOLETE_SUMMARY_TOOLTIP \
77 MN_CONF_NAMESPACE "/summary-tooltip"
78 #define MN_CONF_OBSOLETE_DELAY_NAMESPACE \
79 MN_CONF_NAMESPACE "/delay"
80 #define MN_CONF_OBSOLETE_DELAY_MINUTES \
81 MN_CONF_OBSOLETE_DELAY_NAMESPACE "/minutes"
82 #define MN_CONF_OBSOLETE_DELAY_SECONDS \
83 MN_CONF_OBSOLETE_DELAY_NAMESPACE "/seconds"
84 #define MN_CONF_OBSOLETE_IMMEDIATE_NOTIFICATION_ERROR_DIALOG_NAMESPACE \
85 MN_CONF_UI_NAMESPACE "/immediate-notification-error-dialog"
86 #define MN_CONF_OBSOLETE_IMMEDIATE_NOTIFICATION_ERROR_DIALOG_DO_NOT_SHOW \
87 MN_CONF_OBSOLETE_IMMEDIATE_NOTIFICATION_ERROR_DIALOG_NAMESPACE "/do-not-show"
88 #define MN_CONF_OBSOLETE_DOUBLE_CLICK_ACTION_2 \
89 MN_CONF_NAMESPACE "/double-click-action-2"
90 #define MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE \
91 MN_CONF_UI_NAMESPACE "/main-window"
92 #define MN_CONF_OBSOLETE_MAIN_WINDOW_DIMENSIONS \
93 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE "/dimensions"
94 #define MN_CONF_OBSOLETE_MAIN_WINDOW_VIEW_TOOLBARS \
95 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE "/view-toolbars"
96 #define MN_CONF_OBSOLETE_MAIN_WINDOW_VIEW_STATUSBAR \
97 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE "/view-statusbar"
98 #define MN_CONF_OBSOLETE_MAIN_WINDOW_TOOLBARS_STYLE \
99 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE "/toolbars-style"
100 #define MN_CONF_OBSOLETE_MAIN_WINDOW_EDIT_TOOLBARS_DIALOG \
101 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE "/edit-toolbars-dialog"
102 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE \
103 MN_CONF_NAMESPACE "/mail-summary-popup"
104 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_ENABLE \
105 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/enable"
106 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE \
107 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/autoclose"
108 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE_DELAY_NAMESPACE \
109 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/autoclose-delay"
110 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE_DELAY_MINUTES \
111 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE_DELAY_NAMESPACE "/minutes"
112 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE_DELAY_SECONDS \
113 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_AUTOCLOSE_DELAY_NAMESPACE "/seconds"
114 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_LAYOUT \
115 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/layout"
116 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_POSITION \
117 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/position"
118 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_OFFSET_NAMESPACE \
119 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/offset"
120 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_HORIZONTAL_OFFSET \
121 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_OFFSET_NAMESPACE "/horizontal"
122 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_VERTICAL_OFFSET \
123 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_OFFSET_NAMESPACE "/vertical"
124 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_ONLY_RECENT \
125 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/only-recent"
126 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_NAMESPACE \
127 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE "/fonts"
128 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_ASPECT_SOURCE \
129 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_NAMESPACE "/aspect-source"
130 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_TITLE_NAMESPACE \
131 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_NAMESPACE "/title"
132 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_TITLE_FONT \
133 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_TITLE_NAMESPACE "/font"
134 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_CONTENTS_NAMESPACE \
135 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_NAMESPACE "/contents"
136 #define MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_CONTENTS_FONT \
137 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_FONTS_CONTENTS_NAMESPACE "/font"
138 #define MN_CONF_OBSOLETE_CLICK_ACTION \
139 MN_CONF_NAMESPACE "/click-action"
140 #define MN_CONF_OBSOLETE_CLICK_ACTION_2 \
141 MN_CONF_NAMESPACE "/click-action-2"
142 #define MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_NAMESPACE \
143 MN_CONF_POPUPS_EXPIRATION_NAMESPACE "/delay"
144 #define MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_MINUTES \
145 MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_NAMESPACE "/minutes"
146 #define MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_SECONDS \
147 MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_NAMESPACE "/seconds"
148
149 #define BLOCK(info) \
150 g_signal_handler_block((info)->object, (info)->handler_id)
151 #define UNBLOCK(info) \
152 g_signal_handler_unblock((info)->object, (info)->handler_id)
153
154 #define LINK_INFO(ptr) ((LinkInfo *) (ptr))
155
156 typedef struct
157 {
158 gpointer object;
159 char *key;
160 unsigned long handler_id;
161 GDestroyNotify finalize;
162 } LinkInfo;
163
164 typedef struct
165 {
166 LinkInfo parent;
167 char *str;
168 } LinkRadioButtonToStringInfo;
169
170 typedef struct
171 {
172 LinkInfo parent;
173 char *width_key;
174 char *height_key;
175 } LinkWindowInfo;
176
177 typedef struct
178 {
179 LinkInfo parent;
180 GParamSpec *pspec;
181 } LinkObjectInfo;
182
183 const char *mn_conf_dot_dir = NULL;
184
185 #if WITH_GCONF_SANITY_CHECK
186 /*
187 * If the GConf schemas were not installed properly, refuse to
188 * proceed. This ensures that MN will not behave unexpectedly because
189 * of missing default values. The --disable-gconf-sanity-check
190 * configure argument disables this check and is meant to be used for
191 * development only.
192 */
193 static void
194 check_schemas (void)
195 {
196 static const char *keys[] = {
197 MN_CONF_COMMANDS_NEW_MAIL_ENABLED,
198 MN_CONF_COMMANDS_NEW_MAIL_COMMAND,
199 MN_CONF_COMMANDS_MAIL_READ_ENABLED,
200 MN_CONF_COMMANDS_MAIL_READ_COMMAND,
201 MN_CONF_COMMANDS_MAIL_CHANGED_ENABLED,
202 MN_CONF_COMMANDS_MAIL_CHANGED_COMMAND,
203 MN_CONF_SOUNDS_NEW_MAIL_ENABLED,
204 MN_CONF_SOUNDS_NEW_MAIL_FILE,
205 MN_CONF_SOUNDS_PLAY_COMMAND,
206 MN_CONF_PROPERTIES_DIALOG "/width",
207 MN_CONF_PROPERTIES_DIALOG "/height",
208 MN_CONF_BLINK_ON_ERRORS,
209 MN_CONF_TRUSTED_X509_CERTIFICATES,
210 MN_CONF_TRUSTED_SERVERS,
211 MN_CONF_DISPLAY_SEEN_MAIL,
212 MN_CONF_TOOLTIP_MAIL_SUMMARY,
213 MN_CONF_TOOLTIP_MAIL_SUMMARY_LIMIT,
214 MN_CONF_ALWAYS_DISPLAY_ICON,
215 MN_CONF_DISPLAY_MESSAGE_COUNT,
216 MN_CONF_CLICK_ACTION,
217 MN_CONF_POPUPS_ENABLED,
218 MN_CONF_POPUPS_POSITION,
219 MN_CONF_POPUPS_EXPIRATION_ENABLED,
220 MN_CONF_POPUPS_EXPIRATION_DELAY,
221 MN_CONF_POPUPS_ACTIONS,
222 MN_CONF_POPUPS_LIMIT,
223 MN_CONF_FALLBACK_CHARSETS,
224 MN_CONF_POPUPS_EXPIRATION_DELAY,
225 MN_CONF_MESSAGES_CONSIDERED_AS_READ
226 };
227 int i;
228 GConfClient *client;
229 gboolean schema_missing = FALSE;
230
231 client = mn_conf_get_client();
232 g_assert(client != NULL);
233
234 for (i = 0; i < G_N_ELEMENTS(keys); i++)
235 {
236 GConfEntry *entry;
237
238 entry = gconf_client_get_entry(client, keys[i], NULL, TRUE, NULL);
239 if (entry)
240 {
241 gboolean has_schema;
242
243 has_schema = gconf_entry_get_schema_name(entry) != NULL;
244 gconf_entry_unref(entry);
245
246 if (has_schema)
247 continue;
248 }
249
250 g_warning(_("cannot find default value of configuration key \"%s\""), keys[i]);
251 schema_missing = TRUE;
252 }
253
254 if (schema_missing)
255 mn_show_fatal_error_dialog(NULL, _("The default configuration has not been installed properly. Please check your Mail Notification installation."));
256 }
257 #endif /* WITH_GCONF_SANITY_CHECK */
258
259 static void
260 import_obsolete_key (const char *obsolete, const char *new)
261 {
262 g_return_if_fail(obsolete != NULL);
263 g_return_if_fail(new != NULL);
264
265 if (! mn_conf_is_set(new) && mn_conf_is_set(obsolete))
266 {
267 GConfValue *value;
268
269 value = mn_conf_get_value(obsolete);
270 if (value)
271 {
272 mn_conf_set_value(new, value);
273 gconf_value_free(value);
274 }
275 }
276 }
277
278 void
279 mn_conf_init (void)
280 {
281 g_assert(mn_conf_dot_dir == NULL);
282
283 mn_conf_dot_dir = g_build_filename(g_get_home_dir(),
284 GNOME_DOT_GNOME,
285 "mail-notification",
286 NULL);
287
288 if (! g_file_test(mn_conf_dot_dir, G_FILE_TEST_IS_DIR))
289 {
290 if (mkdir(mn_conf_dot_dir, 0755) < 0)
291 mn_show_error_dialog(NULL,
292 _("A directory creation error has occurred"),
293 _("Unable to create directory \"%s\": %s."),
294 mn_conf_dot_dir,
295 g_strerror(errno));
296 }
297
298 #if WITH_GCONF_SANITY_CHECK
299 check_schemas();
300 #endif
301
302 mn_conf_monitor_add(MN_CONF_NAMESPACE);
303
304 import_obsolete_key(MN_CONF_OBSOLETE_PREFERENCES_DIALOG "/height",
305 MN_CONF_PROPERTIES_DIALOG "/height");
306 import_obsolete_key(MN_CONF_OBSOLETE_PREFERENCES_DIALOG "/width",
307 MN_CONF_PROPERTIES_DIALOG "/width");
308 import_obsolete_key(MN_CONF_OBSOLETE_CLICK_ACTION,
309 MN_CONF_CLICK_ACTION);
310
311 if (! mn_conf_is_set(MN_CONF_TOOLTIP_MAIL_SUMMARY)
312 && mn_conf_is_set(MN_CONF_OBSOLETE_SUMMARY_TOOLTIP))
313 {
314 if (! mn_conf_get_bool(MN_CONF_OBSOLETE_SUMMARY_TOOLTIP))
315 mn_conf_set_string(MN_CONF_TOOLTIP_MAIL_SUMMARY,
316 mn_enum_get_value_nick(MN_TYPE_SHELL_TOOLTIP_MAIL_SUMMARY,
317 MN_SHELL_TOOLTIP_MAIL_SUMMARY_NONE));
318 }
319
320 if (! mn_conf_is_set(MN_CONF_POPUPS_EXPIRATION_DELAY)
321 && (mn_conf_is_set(MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_MINUTES)
322 || mn_conf_is_set(MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_SECONDS)))
323 {
324 int minutes;
325 int seconds;
326
327 minutes = mn_conf_get_int(MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_MINUTES);
328 seconds = mn_conf_get_int(MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_SECONDS);
329
330 mn_conf_set_int(MN_CONF_POPUPS_EXPIRATION_DELAY, minutes * 60 + seconds);
331 }
332 }
333
334 void
335 mn_conf_unset_obsolete (void)
336 {
337 static const char *obsolete[] = {
338 MN_CONF_OBSOLETE_LOCAL_NAMESPACE,
339 MN_CONF_OBSOLETE_REMOTE_NAMESPACE,
340 MN_CONF_OBSOLETE_COMMANDS_CLICKED_NAMESPACE,
341 MN_CONF_OBSOLETE_COMMANDS_DOUBLE_CLICKED_NAMESPACE,
342 MN_CONF_OBSOLETE_COMMANDS_MAIL_READER_NAMESPACE,
343 MN_CONF_OBSOLETE_PREFERENCES_DIALOG,
344 MN_CONF_OBSOLETE_SUMMARY_DIALOG,
345 MN_CONF_OBSOLETE_DOUBLE_CLICK_ACTION,
346 MN_CONF_OBSOLETE_MAILBOXES,
347 MN_CONF_OBSOLETE_ALREADY_RUN,
348 MN_CONF_OBSOLETE_SUMMARY_TOOLTIP,
349 MN_CONF_OBSOLETE_DELAY_NAMESPACE,
350 MN_CONF_OBSOLETE_IMMEDIATE_NOTIFICATION_ERROR_DIALOG_NAMESPACE,
351 MN_CONF_OBSOLETE_DOUBLE_CLICK_ACTION_2,
352 MN_CONF_OBSOLETE_MAIN_WINDOW_NAMESPACE,
353 MN_CONF_OBSOLETE_MAIL_SUMMARY_POPUP_NAMESPACE,
354 MN_CONF_OBSOLETE_CLICK_ACTION,
355 MN_CONF_OBSOLETE_CLICK_ACTION_2,
356 MN_CONF_OBSOLETE_POPUPS_EXPIRATION_DELAY_NAMESPACE
357 };
358 int i;
359
360 for (i = 0; i < G_N_ELEMENTS(obsolete); i++)
361 {
362 g_message(_("recursively unsetting %s"), obsolete[i]);
363 mn_conf_recursive_unset(obsolete[i], GCONF_UNSET_INCLUDING_SCHEMA_NAMES);
364 }
365
366 g_message(_("syncing the GConf database"));
367 mn_conf_suggest_sync();
368
369 g_message(_("completed"));
370 }
371
372 GConfClient *
373 mn_conf_get_client (void)
374 {
375 static GConfClient *client = NULL;
376
377 /*
378 * Does not need to be thread-safe since it is always called early
379 * in the main thread.
380 */
381
382 if (! client)
383 {
384 client = gconf_client_get_default();
385 g_assert(client != NULL);
386 }
387
388 return client;
389 }
390
391 static void
392 handle_error (GError **err)
393 {
394 if (*err)
395 mn_show_fatal_error_dialog(NULL, "A GConf error has occurred: %s.", (*err)->message);
396 }
397
398 GConfValue *
399 mn_conf_get_value (const char *key)
400 {
401 GConfValue *v;
402 GError *err = NULL;
403
404 g_return_val_if_fail(key != NULL, NULL);
405
406 v = gconf_client_get(mn_conf_get_client(), key, &err);
407 handle_error(&err);
408
409 return v;
410 }
411
412 void
413 mn_conf_set_value (const char *key, const GConfValue *value)
414 {
415 GError *err = NULL;
416
417 g_return_if_fail(key != NULL);
418 g_return_if_fail(value != NULL);
419
420 gconf_client_set(mn_conf_get_client(), key, value, &err);
421 handle_error(&err);
422 }
423
424 #define CONF_GETTER(name, ctype, fail_retval) \
425 ctype \
426 mn_conf_get_ ## name (const char *key) \
427 { \
428 ctype v; \
429 GError *err = NULL; \
430 \
431 g_return_val_if_fail(key != NULL, (fail_retval)); \
432 \
433 v = gconf_client_get_ ## name(mn_conf_get_client(), key, &err); \
434 handle_error(&err); \
435 \
436 return v; \
437 }
438
439 #define CONF_SETTER(name, ctype) \
440 void \
441 mn_conf_set_ ## name (const char *key, ctype value) \
442 { \
443 GError *err = NULL; \
444 \
445 g_return_if_fail(key != NULL); \
446 \
447 gconf_client_set_ ## name(mn_conf_get_client(), key, value, &err); \
448 handle_error(&err); \
449 }
450
451 #define CONF_ACCESSORS(name, get_ctype, set_ctype, fail_retval) \
452 CONF_GETTER(name, get_ctype, fail_retval) \
453 CONF_SETTER(name, set_ctype)
454
455 CONF_ACCESSORS(bool, gboolean, gboolean, FALSE)
456 CONF_ACCESSORS(int, int, int, 0)
457 CONF_ACCESSORS(string, char *, const char *, NULL)
458
459 GSList *
460 mn_conf_get_string_list (const char *key)
461 {
462 GSList *v;
463 GError *err = NULL;
464
465 g_return_val_if_fail(key != NULL, NULL);
466
467 v = gconf_client_get_list(mn_conf_get_client(), key, GCONF_VALUE_STRING, &err);
468 handle_error(&err);
469
470 return v;
471 }
472
473 void
474 mn_conf_set_string_list (const char *key, GSList *list)
475 {
476 GError *err = NULL;
477
478 g_return_if_fail(key != NULL);
479
480 gconf_client_set_list(mn_conf_get_client(), key, GCONF_VALUE_STRING, list, &err);
481 handle_error(&err);
482 }
483
484 GHashTable *
485 mn_conf_get_string_hash_set (const char *key)
486 {
487 GSList *list;
488 GSList *l;
489 GHashTable *set;
490
491 g_return_val_if_fail(key != NULL, NULL);
492
493 list = mn_conf_get_string_list(key);
494
495 set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
496
497 MN_LIST_FOREACH(l, list)
498 {
499 const char *str = l->data;
500
501 g_hash_table_replace(set, g_strdup(str), GINT_TO_POINTER(TRUE));
502 }
503
504 mn_g_slist_free_deep(list);
505
506 return set;
507 }
508
509 static void
510 set_string_hash_set_cb (const char *str,
511 gpointer value,
512 GSList **list)
513 {
514 *list = g_slist_prepend(*list, (gpointer) str);
515 }
516
517 void
518 mn_conf_set_string_hash_set (const char *key, GHashTable *set)
519 {
520 GSList *list = NULL;
521
522 g_return_if_fail(key != NULL);
523 g_return_if_fail(set != NULL);
524
525 g_hash_table_foreach(set, (GHFunc) set_string_hash_set_cb, &list);
526
527 mn_conf_set_string_list(key, list);
528
529 g_slist_free(list);
530 }
531
532 void
533 mn_conf_suggest_sync (void)
534 {
535 GError *err = NULL;
536
537 gconf_client_suggest_sync(mn_conf_get_client(), &err);
538 handle_error(&err);
539 }
540
541 void
542 mn_conf_recursive_unset (const char *key, GConfUnsetFlags flags)
543 {
544 GError *err = NULL;
545
546 g_return_if_fail(key != NULL);
547
548 gconf_client_recursive_unset(mn_conf_get_client(), key, flags, &err);
549 handle_error(&err);
550 }
551
552 gboolean
553 mn_conf_is_set (const char *key)
554 {
555 GConfValue *value;
556 GError *err = NULL;
557 gboolean set = FALSE;
558
559 g_return_val_if_fail(key != NULL, FALSE);
560
561 value = gconf_client_get_without_default(mn_conf_get_client(), key, &err);
562 if (value)
563 {
564 set = TRUE;
565 gconf_value_free(value);
566 }
567
568 if (err)
569 {
570 g_error_free(err);
571 set = FALSE;
572 }
573
574 return set;
575 }
576
577 void
578 mn_conf_monitor_add (const char *directory)
579 {
580 GError *err = NULL;
581
582 gconf_client_add_dir(mn_conf_get_client(), directory, GCONF_CLIENT_PRELOAD_NONE, &err);
583 handle_error(&err);
584 }
585
586 unsigned int
587 mn_conf_notification_add (const char *key,
588 GConfClientNotifyFunc callback,
589 gpointer user_data,
590 GFreeFunc destroy_notify)
591 {
592 unsigned int id;
593 GError *err = NULL;
594
595 g_return_val_if_fail(key != NULL, 0);
596 g_return_val_if_fail(callback != NULL, 0);
597
598 id = gconf_client_notify_add(mn_conf_get_client(), key, callback, user_data, destroy_notify, &err);
599 handle_error(&err);
600
601 return id;
602 }
603
604 void
605 mn_conf_notification_remove (unsigned int notification_id)
606 {
607 gconf_client_notify_remove(mn_conf_get_client(), notification_id);
608 }
609
610 static void
611 link_weak_notify_cb (gpointer data, GObject *former_object)
612 {
613 LinkInfo *info = data;
614
615 if (info->finalize)
616 info->finalize(info);
617 g_free(info);
618 }
619
620 static void
621 link_real (LinkInfo *info,
622 gpointer object,
623 const char *key,
624 const char *signal_name,
625 GCallback signal_handler,
626 GConfClientNotifyFunc notification_cb)
627 {
628 g_return_if_fail(G_IS_OBJECT(object));
629 g_return_if_fail(key != NULL);
630 g_return_if_fail(signal_name != NULL);
631 g_return_if_fail(signal_handler != NULL);
632 g_return_if_fail(notification_cb != NULL);
633
634 if (info)
635 {
636 g_return_if_fail(info->object == NULL);
637 g_return_if_fail(info->key == NULL);
638 g_return_if_fail(info->handler_id == 0);
639 }
640 else
641 info = g_new0(LinkInfo, 1);
642
643 info->object = object;
644 info->key = g_strdup(key);
645 info->handler_id = g_signal_connect(object, signal_name, signal_handler, info);
646 mn_g_object_gconf_notification_add_gdk_locked(object, key, notification_cb, info);
647 g_object_weak_ref(object, link_weak_notify_cb, info);
648 }
649
650 static void
651 link_object_set (gpointer object, GParamSpec *pspec, const GConfValue *value)
652 {
653 GValue gvalue = { 0, };
654
655 g_return_if_fail(G_IS_OBJECT(object));
656 g_return_if_fail(pspec != NULL);
657
658 if (! value)
659 return;
660
661 g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(pspec));
662
663 if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_BOOLEAN)
664 g_value_set_boolean(&gvalue, gconf_value_get_bool(value));
665 else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_STRING)
666 g_value_set_string(&gvalue, gconf_value_get_string(value));
667 else
668 g_return_if_reached();
669
670 g_object_set_property(object, g_param_spec_get_name(pspec), &gvalue);
671 g_value_unset(&gvalue);
672 }
673
674 static void
675 link_object_h (GObject *object, GParamSpec *pspec, gpointer user_data)
676 {
677 LinkInfo *info = user_data;
678 GValue value = { 0, };
679
680 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
681 g_object_get_property(object, g_param_spec_get_name(pspec), &value);
682
683 if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_BOOLEAN)
684 mn_conf_set_bool(info->key, g_value_get_boolean(&value));
685 else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_STRING)
686 {
687 const char *str = g_value_get_string(&value);
688 mn_conf_set_string(info->key, str ? str : "");
689 }
690 else
691 g_return_if_reached();
692
693 g_value_unset(&value);
694 }
695
696 static void
697 link_object_notify_cb (GConfClient *client,
698 unsigned int cnxn_id,
699 GConfEntry *entry,
700 gpointer user_data)
701 {
702 LinkObjectInfo *info = user_data;
703
704 BLOCK(LINK_INFO(info));
705 link_object_set(LINK_INFO(info)->object, info->pspec, gconf_entry_get_value(entry));
706 UNBLOCK(LINK_INFO(info));
707 }
708
709 void
710 mn_conf_link_object (gpointer object,
711 const char *key,
712 const char *property_name)
713 {
714 LinkObjectInfo *info;
715 GConfValue *value;
716 char *signal_name;
717
718 g_return_if_fail(G_IS_OBJECT(object));
719 g_return_if_fail(key != NULL);
720 g_return_if_fail(property_name != NULL);
721
722 info = g_new0(LinkObjectInfo, 1);
723 info->pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name);
724 g_return_if_fail(info->pspec != NULL);
725
726 value = mn_conf_get_value(key);
727 link_object_set(object, info->pspec, value);
728 if (value)
729 gconf_value_free(value);
730
731 signal_name = g_strconcat("notify::", property_name, NULL);
732
733 link_real(LINK_INFO(info), object, key,
734 signal_name,
735 G_CALLBACK(link_object_h),
736 link_object_notify_cb);
737
738 g_free(signal_name);
739 }
740
741 static gboolean
742 link_window_h (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
743 {
744 LinkWindowInfo *info = user_data;
745
746 mn_conf_set_int(info->width_key, event->width);
747 mn_conf_set_int(info->height_key, event->height);
748
749 return FALSE;
750 }
751
752 static void
753 link_window_notify_cb (GConfClient *client,
754 unsigned int cnxn_id,
755 GConfEntry *entry,
756 gpointer user_data)
757 {
758 LinkWindowInfo *info = user_data;
759
760 BLOCK(LINK_INFO(info));
761 gtk_window_resize(LINK_INFO(info)->object,
762 mn_conf_get_int(info->width_key),
763 mn_conf_get_int(info->height_key));
764 UNBLOCK(LINK_INFO(info));
765 }
766
767 static void
768 link_window_free_info (LinkWindowInfo *info)
769 {
770 g_free(info->width_key);
771 g_free(info->height_key);
772 }
773
774 void
775 mn_conf_link_window (GtkWindow *window, const char *key)
776 {
777 LinkWindowInfo *window_info;
778 LinkInfo *info;
779
780 g_return_if_fail(GTK_IS_WINDOW(window));
781 g_return_if_fail(key != NULL);
782
783 window_info = g_new0(LinkWindowInfo, 1);
784 window_info->width_key = g_strdup_printf("%s/width", key);
785 window_info->height_key = g_strdup_printf("%s/height", key);
786
787 info = LINK_INFO(window_info);
788 info->finalize = (GDestroyNotify) link_window_free_info;
789
790 gtk_window_set_default_size(window,
791 mn_conf_get_int(window_info->width_key),
792 mn_conf_get_int(window_info->height_key));
793
794 link_real(info, window, key,
795 "configure-event",
796 G_CALLBACK(link_window_h),
797 link_window_notify_cb);
798 }
799
800 static void
801 link_non_linear_range_h (GtkRange *range, gpointer user_data)
802 {
803 LinkInfo *info = user_data;
804
805 mn_conf_set_int(info->key, mn_non_linear_range_get_value(range));
806 }
807
808 static void
809 link_non_linear_range_notify_cb (GConfClient *client,
810 unsigned int cnxn_id,
811 GConfEntry *entry,
812 gpointer user_data)
813 {
814 GConfValue *value = gconf_entry_get_value(entry);
815 LinkInfo *info = user_data;
816
817 BLOCK(info);
818 mn_non_linear_range_set_value(info->object, value ? gconf_value_get_int(value) : 0);
819 UNBLOCK(info);
820 }
821
822 void
823 mn_conf_link_non_linear_range (GtkRange *range, const char *key)
824 {
825 g_return_if_fail(mn_is_non_linear_range(range));
826 g_return_if_fail(key != NULL);
827
828 mn_non_linear_range_set_value(range, mn_conf_get_int(key));
829
830 link_real(NULL, range, key,
831 "value-changed",
832 G_CALLBACK(link_non_linear_range_h),
833 link_non_linear_range_notify_cb);
834 }
835
836 static void
837 link_radio_button_to_string_h (GtkToggleButton *toggle, gpointer user_data)
838 {
839 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
840 {
841 LinkRadioButtonToStringInfo *info = user_data;
842
843 mn_conf_set_string(LINK_INFO(info)->key, info->str);
844 }
845 }
846
847 static void
848 link_radio_button_to_string_notify_cb (GConfClient *client,
849 unsigned int cnxn_id,
850 GConfEntry *entry,
851 gpointer user_data)
852 {
853 LinkRadioButtonToStringInfo *info = user_data;
854 GConfValue *value = gconf_entry_get_value(entry);
855 const char *str = value ? gconf_value_get_string(value) : NULL;
856
857 if (str && ! strcmp(str, info->str))
858 {
859 BLOCK(LINK_INFO(info));
860 gtk_toggle_button_set_active(LINK_INFO(info)->object, TRUE);
861 UNBLOCK(LINK_INFO(info));
862 }
863 }
864
865 static void
866 link_radio_button_to_string_free_info (LinkRadioButtonToStringInfo *info)
867 {
868 g_free(info->str);
869 }
870
871 static void
872 link_radio_button_to_string (GtkRadioButton *radio,
873 const char *key,
874 const char *str)
875 {
876 LinkRadioButtonToStringInfo *rinfo;
877 LinkInfo *info;
878 char *current_str;
879
880 g_return_if_fail(GTK_IS_RADIO_BUTTON(radio));
881 g_return_if_fail(key != NULL);
882 g_return_if_fail(str != NULL);
883
884 current_str = mn_conf_get_string(key);
885 if (current_str)
886 {
887 if (! strcmp(current_str, str))
888 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
889 g_free(current_str);
890 }
891
892 rinfo = g_new0(LinkRadioButtonToStringInfo, 1);
893 rinfo->str = g_strdup(str);
894
895 info = LINK_INFO(rinfo);
896 info->finalize = (GDestroyNotify) link_radio_button_to_string_free_info;
897
898 link_real(info, radio, key,
899 "toggled",
900 G_CALLBACK(link_radio_button_to_string_h),
901 link_radio_button_to_string_notify_cb);
902 }
903
904 void
905 mn_conf_link_radio_group_to_enum (GType enum_type,
906 const char *key,
907 ...)
908 {
909 GEnumClass *enum_class;
910 GtkRadioButton *radio;
911 va_list args;
912
913 g_return_if_fail(G_TYPE_IS_ENUM(enum_type));
914 g_return_if_fail(key != NULL);
915
916 enum_class = g_type_class_ref(enum_type);
917 g_return_if_fail(G_IS_ENUM_CLASS(enum_class));
918
919 va_start(args, key);
920
921 while ((radio = va_arg(args, GtkRadioButton *)))
922 {
923 int value;
924 GEnumValue *enum_value;
925
926 value = va_arg(args, int);
927
928 enum_value = g_enum_get_value(enum_class, value);
929 g_return_if_fail(enum_value != NULL);
930
931 link_radio_button_to_string(radio, key, enum_value->value_nick);
932 }
933
934 va_end(args);
935
936 g_type_class_unref(enum_class);
937 }
938
939 int
940 mn_conf_get_enum_value (GType enum_type, const char *key)
941 {
942 GEnumClass *enum_class;
943 GEnumValue *enum_value = NULL;
944 char *nick;
945
946 g_return_val_if_fail(G_TYPE_IS_ENUM(enum_type), 0);
947 g_return_val_if_fail(key != NULL, 0);
948
949 enum_class = g_type_class_ref(enum_type);
950 g_return_val_if_fail(G_IS_ENUM_CLASS(enum_class), 0);
951
952 nick = mn_conf_get_string(key);
953 if (nick)
954 {
955 enum_value = g_enum_get_value_by_nick(enum_class, nick);
956 g_free(nick);
957 }
958
959 g_type_class_unref(enum_class);
960
961 return enum_value ? enum_value->value : 0;
962 }
963
964 gboolean
965 mn_conf_has_command (const char *namespace)
966 {
967 char *enabled_key;
968 gboolean has = FALSE;
969
970 g_return_val_if_fail(namespace != NULL, FALSE);
971
972 enabled_key = g_strconcat(namespace, "/enabled", NULL);
973 if (mn_conf_get_bool(enabled_key))
974 {
975 char *command_key;
976 char *command;
977
978 command_key = g_strconcat(namespace, "/command", NULL);
979 command = mn_conf_get_string(command_key);
980 g_free(command_key);
981
982 if (command)
983 {
984 if (*command)
985 has = TRUE;
986 g_free(command);
987 }
988 }
989 g_free(enabled_key);
990
991 return has;
992 }
993
994 void
995 mn_conf_execute_command (const char *conf_key)
996 {
997 char *command;
998
999 g_return_if_fail(conf_key != NULL);
1000
1001 command = mn_conf_get_string(conf_key);
1002 g_return_if_fail(command != NULL && *command != 0);
1003
1004 mn_execute_command(command);
1005 g_free(command);
1006 }
1007
1008 void
1009 mn_conf_execute_mail_reader (void)
1010 {
1011 char *command;
1012 char *separator;
1013 char *program;
1014
1015 command = mn_conf_get_string(MN_CONF_GNOME_MAIL_READER_COMMAND);
1016 g_return_if_fail(command != NULL && *command != 0);
1017
1018 separator = strpbrk(command, " \t\n\r");
1019 program = separator
1020 ? g_strndup(command, separator - command)
1021 : g_strdup(command);
1022 g_free(command);
1023
1024 if (! strcmp(program, "mozilla"))
1025 command = g_strdup_printf("%s -mail", program);
1026 else if (! strcmp(program, "evolution"))
1027 command = g_strdup_printf("%s --component=mail", program);
1028 else
1029 command = g_strdup(program);
1030 g_free(program);
1031
1032 if (mn_conf_get_bool(MN_CONF_GNOME_MAIL_READER_NEEDS_TERMINAL))
1033 mn_execute_command_in_terminal(command);
1034 else
1035 mn_execute_command(command);
1036 g_free(command);
1037 }