mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

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 }