* Added a new signal to the account mgr that notifies changes in the default account...
[modest] / src / modest-conf.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <gconf/gconf-client.h>
31 #include <config.h>
32 #include <string.h>
33 #include <glib/gi18n.h>
34 #include "modest-conf.h"
35 #include "modest-marshal.h"
36 #include <stdio.h>
37
38 static void   modest_conf_class_init     (ModestConfClass *klass);
39
40 static void   modest_conf_init           (ModestConf *obj);
41
42 static void   modest_conf_finalize       (GObject *obj);
43
44 static void   modest_conf_on_change      (GConfClient *client, guint conn_id,
45                                           GConfEntry *entry, gpointer data);
46
47 static GConfValueType modest_conf_type_to_gconf_type (ModestConfValueType value_type, 
48                                                       GError **err);
49
50 /* list my signals */
51 enum {
52         KEY_CHANGED_SIGNAL,
53         LAST_SIGNAL
54 };
55 static guint signals[LAST_SIGNAL] = {0}; 
56
57
58 typedef struct _ModestConfPrivate ModestConfPrivate;
59 struct _ModestConfPrivate {
60         GConfClient *gconf_client;
61 };
62 #define MODEST_CONF_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
63                                                                      MODEST_TYPE_CONF, \
64                                                                      ModestConfPrivate))
65 /* globals */
66 static GObjectClass *parent_class = NULL;
67
68 GType
69 modest_conf_get_type (void)
70 {
71         static GType my_type = 0;
72         if (!my_type) {
73                 static const GTypeInfo my_info = {
74                         sizeof(ModestConfClass),
75                         NULL,           /* base init */
76                         NULL,           /* base finalize */
77                         (GClassInitFunc) modest_conf_class_init,
78                         NULL,           /* class finalize */
79                         NULL,           /* class data */
80                         sizeof(ModestConf),
81                         1,              /* n_preallocs */
82                         (GInstanceInitFunc) modest_conf_init,
83                         NULL
84                 };
85                 my_type = g_type_register_static (G_TYPE_OBJECT,
86                                                   "ModestConf",
87                                                   &my_info, 0);
88         }
89         return my_type;
90 }
91
92 static void
93 modest_conf_class_init (ModestConfClass *klass)
94 {
95         GObjectClass *gobject_class;
96         gobject_class = (GObjectClass*) klass;
97
98         parent_class            = g_type_class_peek_parent (klass);
99         gobject_class->finalize = modest_conf_finalize;
100
101         g_type_class_add_private (gobject_class, sizeof(ModestConfPrivate));
102         
103         signals[KEY_CHANGED_SIGNAL] =
104                 g_signal_new ("key_changed",
105                               G_TYPE_FROM_CLASS (gobject_class),
106                               G_SIGNAL_RUN_FIRST,
107                               G_STRUCT_OFFSET (ModestConfClass,key_changed),
108                               NULL, NULL,
109                               modest_marshal_VOID__STRING_INT_INT,
110                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); 
111 }
112
113 static void
114 modest_conf_init (ModestConf *obj)
115 {
116         GConfClient *conf = NULL;
117         ModestConfPrivate *priv = MODEST_CONF_GET_PRIVATE(obj);
118         GError *err      = NULL;
119
120         priv->gconf_client = NULL;
121         
122         conf = gconf_client_get_default ();
123         if (!conf) {
124                 g_printerr ("modest: could not get gconf client\n");
125                 return;
126         }
127
128         priv->gconf_client = conf;      /* all went well! */
129 }
130
131 static void
132 modest_conf_finalize (GObject *obj)
133 {
134         ModestConfPrivate *priv = MODEST_CONF_GET_PRIVATE(obj);
135         if (priv->gconf_client) {
136
137                 gconf_client_suggest_sync (priv->gconf_client, NULL);
138
139                 g_object_unref (priv->gconf_client);
140                 priv->gconf_client = NULL;
141         }       
142
143         G_OBJECT_CLASS(parent_class)->finalize (obj);
144 }
145
146 ModestConf*
147 modest_conf_new (void)
148 {
149         ModestConf *conf;
150         ModestConfPrivate *priv;
151         
152         conf = MODEST_CONF(g_object_new(MODEST_TYPE_CONF, NULL));
153         if (!conf) {
154                 g_printerr ("modest: failed to init ModestConf (GConf)\n");
155                 return NULL;
156         }
157
158         priv = MODEST_CONF_GET_PRIVATE(conf);
159         if (!priv->gconf_client) {
160                 g_printerr ("modest: failed to init gconf\n");
161                 g_object_unref (conf);
162                 return NULL;
163         }
164         
165         return conf;
166 }
167
168
169 gchar*
170 modest_conf_get_string (ModestConf* self, const gchar* key, GError **err)
171 {
172         ModestConfPrivate *priv;
173         
174         g_return_val_if_fail (self, NULL);
175         g_return_val_if_fail (key,  NULL);
176
177         priv = MODEST_CONF_GET_PRIVATE(self);
178         return gconf_client_get_string (priv->gconf_client, key, err);
179 }
180
181
182 gint
183 modest_conf_get_int (ModestConf* self, const gchar* key, GError **err)
184 {
185         ModestConfPrivate *priv;
186
187         g_return_val_if_fail (self, -1);
188         g_return_val_if_fail (key, -1);
189
190         priv = MODEST_CONF_GET_PRIVATE(self);
191         
192         return gconf_client_get_int (priv->gconf_client, key, err);
193 }
194
195
196 gboolean
197 modest_conf_get_bool (ModestConf* self, const gchar* key, GError **err)
198 {
199         ModestConfPrivate *priv;
200
201         g_return_val_if_fail (self, FALSE);
202         g_return_val_if_fail (key, FALSE);
203
204         priv = MODEST_CONF_GET_PRIVATE(self);
205         
206         return gconf_client_get_bool (priv->gconf_client, key, err);
207 }
208
209
210 GSList * 
211 modest_conf_get_list (ModestConf* self, const gchar* key, ModestConfValueType list_type,
212                       GError **err)
213 {
214         ModestConfPrivate *priv;
215         GConfValueType gconf_type;
216        
217         g_return_val_if_fail (self, NULL);
218         g_return_val_if_fail (key,  NULL);
219
220         priv = MODEST_CONF_GET_PRIVATE(self);
221
222         gconf_type = modest_conf_type_to_gconf_type (list_type, err);
223
224         return gconf_client_get_list (priv->gconf_client, key, gconf_type, err);
225 }
226
227
228
229
230 gboolean
231 modest_conf_set_string (ModestConf* self, const gchar* key, const gchar* val,
232                         GError **err)
233 {
234         ModestConfPrivate *priv;
235                 
236         g_return_val_if_fail (self,FALSE);
237         g_return_val_if_fail (key, FALSE);
238         g_return_val_if_fail (val, FALSE);
239         
240         priv = MODEST_CONF_GET_PRIVATE(self);
241
242         if (!gconf_client_key_is_writable(priv->gconf_client,key,err)) {
243                 g_printerr ("modest: '%s' is not writable\n", key);
244                 return FALSE;
245         }
246                         
247         return gconf_client_set_string (priv->gconf_client, key, val, err);
248 }
249
250 gboolean
251 modest_conf_set_int  (ModestConf* self, const gchar* key, gint val,
252                       GError **err)
253 {
254         ModestConfPrivate *priv;
255                 
256         g_return_val_if_fail (self,FALSE);
257         g_return_val_if_fail (key, FALSE);
258         
259         priv = MODEST_CONF_GET_PRIVATE(self);
260
261         if (!gconf_client_key_is_writable(priv->gconf_client,key,err)) {
262                 g_printerr ("modest: '%s' is not writable\n", key);
263                 return FALSE;
264         }
265                         
266         return gconf_client_set_int (priv->gconf_client, key, val, err);
267 }
268
269
270 gboolean
271 modest_conf_set_bool (ModestConf* self, const gchar* key, gboolean val,
272                       GError **err)
273 {
274         ModestConfPrivate *priv;
275                 
276         g_return_val_if_fail (self,FALSE);
277         g_return_val_if_fail (key, FALSE);
278         
279         priv = MODEST_CONF_GET_PRIVATE(self);
280
281         if (!gconf_client_key_is_writable(priv->gconf_client,key, err)) {
282                 g_warning ("modest: '%s' is not writable\n", key);
283                 return FALSE;
284         }
285
286         return gconf_client_set_bool (priv->gconf_client, key, val, err);
287 }
288
289
290 gboolean
291 modest_conf_set_list (ModestConf* self, const gchar* key, 
292                       GSList *val, ModestConfValueType list_type, 
293                       GError **err)
294 {
295         ModestConfPrivate *priv;
296         GConfValueType gconf_type;
297         gboolean result;
298        
299         g_return_val_if_fail (self, FALSE);
300         g_return_val_if_fail (key, FALSE);
301
302         priv = MODEST_CONF_GET_PRIVATE(self);
303
304         gconf_type = modest_conf_type_to_gconf_type (list_type, err);
305         if (*err)
306                 return FALSE;
307
308         result = gconf_client_set_list (priv->gconf_client, key, gconf_type, val, err);
309         if(*err) {
310                 g_warning("gconf_client_set_list() failed with key=%s. error=%s", key,
311                           (*err)->message);
312                 result = FALSE;
313         }
314        
315         /* TODO: Remove this, when we fix the problem: */
316         /* This shows that sometimes set_list fails, while saying that it succeeded: */
317         if (result) {
318                 const gint debug_list_length_start = g_slist_length(val);
319                 GSList* debug_list = gconf_client_get_list(priv->gconf_client, key, gconf_type, err);
320                 const gint debug_list_length_after = g_slist_length(debug_list);
321                
322                 if(debug_list_length_start != debug_list_length_after)
323                         g_warning("modest_conf_set_list(): The list length after setting is "
324                                   "not the same as the specified list. key=%s. "
325                                   "We think that we fixed this, so tell us if you see this.", key);
326                 g_slist_free(debug_list);
327         }
328         
329         return result;
330 }
331
332
333 GSList*
334 modest_conf_list_subkeys (ModestConf* self, const gchar* key, GError **err)
335 {
336         ModestConfPrivate *priv;
337                 
338         g_return_val_if_fail (self,FALSE);
339         g_return_val_if_fail (key, FALSE);
340         
341         priv = MODEST_CONF_GET_PRIVATE(self);
342                         
343         return gconf_client_all_dirs (priv->gconf_client,key,err);
344 }
345
346
347 gboolean
348 modest_conf_remove_key (ModestConf* self, const gchar* key, GError **err)
349 {
350         ModestConfPrivate *priv;
351         gboolean retval;
352         
353         g_return_val_if_fail (self,FALSE);
354         g_return_val_if_fail (key, FALSE);
355         
356         priv = MODEST_CONF_GET_PRIVATE(self);
357                         
358         retval = gconf_client_recursive_unset (priv->gconf_client,key,0,err);
359         gconf_client_suggest_sync (priv->gconf_client, NULL);
360
361         return retval;
362 }
363
364
365 gboolean
366 modest_conf_key_exists (ModestConf* self, const gchar* key, GError **err)
367 {
368         ModestConfPrivate *priv;
369         GConfValue *val;
370
371         g_return_val_if_fail (self,FALSE);
372         g_return_val_if_fail (key, FALSE);
373         
374         priv = MODEST_CONF_GET_PRIVATE(self);
375
376         /* the fast way... */
377         if (gconf_client_dir_exists (priv->gconf_client,key,err))
378                 return TRUE;
379         
380         val = gconf_client_get (priv->gconf_client, key, NULL);
381         if (!val)
382                 return FALSE;
383         else {
384                 gconf_value_free (val);
385                 return TRUE;
386         }       
387 }
388
389
390 gchar*
391 modest_conf_key_escape (const gchar* key)
392 {
393         g_return_val_if_fail (key, NULL);
394         g_return_val_if_fail (strlen (key) > 0, g_strdup (key));
395         
396         return gconf_escape_key (key, strlen(key));
397 }
398
399
400 gchar*
401 modest_conf_key_unescape (const gchar* key)
402 {
403         g_return_val_if_fail (key, NULL);
404
405         return gconf_unescape_key (key, strlen(key));
406 }
407
408 gboolean
409 modest_conf_key_is_valid (const gchar* key)
410 {
411         return gconf_valid_key (key, NULL);
412 }
413
414 static void
415 modest_conf_on_change (GConfClient *client,
416                        guint conn_id,
417                        GConfEntry *entry,
418                        gpointer data)
419 {
420         ModestConfEvent event;
421         const gchar* key;
422
423         event = (entry->value) ? MODEST_CONF_EVENT_KEY_CHANGED : MODEST_CONF_EVENT_KEY_UNSET;
424         key    = gconf_entry_get_key (entry);
425
426         g_signal_emit (G_OBJECT(data),
427                        signals[KEY_CHANGED_SIGNAL], 0,
428                        key, event, conn_id);
429 }
430
431
432 static GConfValueType
433 modest_conf_type_to_gconf_type (ModestConfValueType value_type, GError **err)
434 {
435         GConfValueType gconf_type;
436
437         switch (value_type) {
438         case MODEST_CONF_VALUE_INT:
439                 gconf_type = GCONF_VALUE_INT;
440                 break;
441         case MODEST_CONF_VALUE_BOOL:
442                 gconf_type = GCONF_VALUE_BOOL;
443                 break;
444         case MODEST_CONF_VALUE_FLOAT:
445                 gconf_type = GCONF_VALUE_FLOAT;
446                 break;
447         case MODEST_CONF_VALUE_STRING:
448                 gconf_type = GCONF_VALUE_STRING;
449                 break;
450         default:
451                 /* FIXME: use MODEST_ERROR, and error code */
452                 gconf_type = GCONF_VALUE_INVALID;
453                 g_printerr ("modest: invalid list value type %d\n", value_type);
454                 *err = g_error_new_literal (0, 0, "invalid list value type");
455         }       
456         return gconf_type;
457 }
458
459 ModestConfNotificationId
460 modest_conf_listen_to_namespace (ModestConf *self,
461                                  const gchar *namespace)
462 {
463         ModestConfPrivate *priv;
464         GError *error = NULL;
465         ModestConfNotificationId notification_id;
466
467         g_return_val_if_fail (MODEST_IS_CONF (self), 0);
468         g_return_val_if_fail (namespace, 0);
469         
470         priv = MODEST_CONF_GET_PRIVATE(self);
471
472         /* Add the namespace to the list of the namespaces that will
473            be observed */
474         gconf_client_add_dir (priv->gconf_client, namespace,
475                               GCONF_CLIENT_PRELOAD_NONE,
476                               &error);
477
478         if (error)
479                 return 0;
480
481         /* Notify every change under namespace */
482         notification_id = gconf_client_notify_add (priv->gconf_client,
483                                                    namespace,
484                                                    modest_conf_on_change,
485                                                    self,
486                                                    NULL,
487                                                    &error);
488         if (error)
489                 return 0;
490         else
491                 return notification_id;
492 }
493
494 void 
495 modest_conf_forget_namespace (ModestConf *self,
496                               const gchar *namespace,
497                               ModestConfNotificationId id)
498 {
499         ModestConfPrivate *priv;
500
501         g_return_if_fail (MODEST_IS_CONF (self));
502         g_return_if_fail (namespace);
503         
504         priv = MODEST_CONF_GET_PRIVATE(self);
505
506         /* Remove the namespace to the list of the namespaces that will
507            be observed */
508         gconf_client_remove_dir (priv->gconf_client, namespace, NULL);
509
510         /* Notify every change under namespace */
511         gconf_client_notify_remove (priv->gconf_client, id);
512 }