WIP fix gtk3 setup issues
[platform/upstream/ibus.git] / dconf / config.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* ibus - The Input Bus
4  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
6  * Copyright (C) 2008-2011 Red Hat, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <string.h>
25 #include <ibus.h>
26 #include "config.h"
27
28 #define DCONF_PREFIX "/desktop/ibus"
29 #define DCONF_PRESERVE_NAME_PREFIXES_KEY \
30     DCONF_PREFIX"/general/dconf-preserve-name-prefixes"
31
32 struct _IBusConfigDConf {
33     IBusConfigService parent;
34     DConfClient *client;
35
36     /* if a dconf path matches one of preserve_name_prefixes, don't convert
37        key names from/to GSettings key names (see _to_gsettings_name
38        and _from_gsettings_name) */
39     GSList *preserve_name_prefixes;
40 };
41
42 struct _IBusConfigDConfClass {
43     IBusConfigServiceClass parent;
44 };
45
46 /* functions prototype */
47 static void      ibus_config_dconf_class_init  (IBusConfigDConfClass *class);
48 static void      ibus_config_dconf_init        (IBusConfigDConf      *config);
49 static void      ibus_config_dconf_destroy     (IBusConfigDConf      *config);
50 static gboolean  ibus_config_dconf_set_value   (IBusConfigService    *config,
51                                                 const gchar          *section,
52                                                 const gchar          *name,
53                                                 GVariant             *value,
54                                                 GError              **error);
55 static GVariant *ibus_config_dconf_get_value   (IBusConfigService    *config,
56                                                 const gchar          *section,
57                                                 const gchar          *name,
58                                                 GError              **error);
59 static GVariant *ibus_config_dconf_get_values  (IBusConfigService    *config,
60                                                 const gchar          *section,
61                                                 GError              **error);
62 static gboolean  ibus_config_dconf_unset_value (IBusConfigService    *config,
63                                                 const gchar          *section,
64                                                 const gchar          *name,
65                                                 GError              **error);
66
67 G_DEFINE_TYPE (IBusConfigDConf, ibus_config_dconf, IBUS_TYPE_CONFIG_SERVICE)
68
69 static void
70 ibus_config_dconf_class_init (IBusConfigDConfClass *class)
71 {
72     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
73     IBusConfigServiceClass *config_class = IBUS_CONFIG_SERVICE_CLASS (class);
74
75     object_class->destroy = (IBusObjectDestroyFunc) ibus_config_dconf_destroy;
76     config_class->set_value = ibus_config_dconf_set_value;
77     config_class->get_value = ibus_config_dconf_get_value;
78     config_class->get_values = ibus_config_dconf_get_values;
79     config_class->unset_value = ibus_config_dconf_unset_value;
80 }
81
82 static gboolean
83 _has_prefixes (const gchar *path, GSList *prefixes)
84 {
85     GSList *head = prefixes;
86     for (; head; head = head->next) {
87         if (g_str_has_prefix (path, head->data)) {
88             return TRUE;
89         }
90     }
91     return FALSE;
92 }
93
94 /* Convert key names from/to GSettings names.  While GSettings only
95  * accepts lowercase letters / numbers / and dash ('-'), IBus uses
96  * underscore ('_') and some engines even use uppercase letters.
97  *
98  * To minimize the gap, we do the following conversion:
99  *
100  * - when converting from IBus names to GSettings names, first convert
101  *   all letters to lowercase and then replace underscores with dashes.
102  * - when converting from GSettings names to IBus names, simply
103  *   replace dashes with underscores.
104  *
105  * Note that though the conversion does not always roundtrip, it does
106  * in most cases.
107  */
108 static gchar *
109 _to_gsettings_name (const gchar *name)
110 {
111     return g_strcanon (g_ascii_strdown (name, -1),
112                        "abcdefghijklmnopqrstuvwxyz0123456789-",
113                        '-');
114 }
115
116 static gchar *
117 _from_gsettings_name (const gchar *name)
118 {
119     gchar *retval = g_strdup (name), *p;
120     for (p = retval; *p != '\0'; p++)
121         if (*p == '-')
122             *p = '_';
123     return retval;
124 }
125
126 typedef gchar *(* NameConvFunc) (const gchar *);
127
128 static gchar *
129 _conv_path (const gchar *path, NameConvFunc conv_func)
130 {
131     gchar **strv = g_strsplit (path, "/", -1), **p;
132     gchar *retval;
133
134     for (p = strv; *p; p++) {
135         gchar *canon;
136         canon = (*conv_func) (*p);
137         g_free (*p);
138         *p = canon;
139     }
140
141     retval = g_strjoinv ("/", strv);
142     g_strfreev (strv);
143     return retval;
144 }
145
146 static gchar *
147 _to_gsettings_path (const gchar *path)
148 {
149     return _conv_path (path, _to_gsettings_name);
150 }
151
152 static gchar *
153 _from_gsettings_path (const gchar *gpath)
154 {
155     return _conv_path (gpath, _from_gsettings_name);
156 }
157
158 static void
159 _watch_func (DConfClient         *client,
160              const gchar         *gpath,
161              const gchar * const *items,
162              gint                 n_items,
163              const gchar         *tag,
164              IBusConfigDConf     *config)
165 {
166     gchar **gkeys = NULL;
167     gint i;
168
169     g_return_if_fail (gpath != NULL);
170     g_return_if_fail (n_items >= 0);
171
172     if (dconf_is_key (gpath, NULL)) {
173         /* If path is a key, the notification should be taken to mean
174            that one key may have changed. */
175         n_items = 1;
176         gkeys = g_malloc0_n (n_items + 1, sizeof (gchar *));
177         gkeys[0] = g_strdup (gpath);
178     } else {
179         if (n_items == 0) {
180             /* If path is a dir and items is empty then it is an
181                indication that any key under path may have
182                changed. */
183             gkeys = dconf_client_list (config->client, gpath, &n_items);
184         } else {
185             gkeys = g_boxed_copy (G_TYPE_STRV, items);
186         }
187         for (i = 0; i < n_items; i++) {
188             gchar *gname = gkeys[i];
189             gkeys[i] = g_strdup_printf ("%s/%s", gpath, gname);
190             g_free (gname);
191         }
192     }
193
194     for (i = 0; i < n_items; i++) {
195         gchar *gname, *path, *name;
196         GVariant *variant = dconf_client_read (config->client, gkeys[i]);
197
198         if (variant == NULL) {
199             /* Use a empty tuple for a unset value */
200             variant = g_variant_new_tuple (NULL, 0);
201         }
202
203         gname = strrchr (gkeys[i], '/');
204         g_assert (gname);
205         *gname++ = '\0';
206
207         if (_has_prefixes (gkeys[i], config->preserve_name_prefixes)) {
208             path = gkeys[i];
209             name = gname;
210         } else {
211             path = _from_gsettings_path (gkeys[i]);
212             name = _from_gsettings_name (gname);
213         }
214
215         ibus_config_service_value_changed ((IBusConfigService *) config,
216                                            path + sizeof (DCONF_PREFIX),
217                                            name,
218                                            variant);
219         if (path != gkeys[i]) {
220             g_free (path);
221         }
222         if (name != gname) {
223             g_free (name);
224         }
225         g_variant_unref (variant);
226     }
227     g_strfreev (gkeys);
228 }
229
230 static void
231 ibus_config_dconf_init (IBusConfigDConf *config)
232 {
233     GVariant *variant;
234     GError *error;
235
236     config->client = dconf_client_new ("ibus",
237                                        (DConfWatchFunc)_watch_func,
238                                        config,
239                                        NULL);
240
241     error = NULL;
242     if (!dconf_client_watch (config->client, DCONF_PREFIX"/", NULL, &error))
243         g_warning ("Can not watch dconf path %s", DCONF_PREFIX"/");
244
245     config->preserve_name_prefixes = NULL;
246     variant = dconf_client_read (config->client,
247                                  DCONF_PRESERVE_NAME_PREFIXES_KEY);
248     if (variant != NULL) {
249         GVariantIter iter;
250         GVariant *child;
251
252         g_variant_iter_init (&iter, variant);
253         while ((child = g_variant_iter_next_value (&iter))) {
254             char *prefix = g_variant_dup_string (child, NULL);
255             config->preserve_name_prefixes =
256                 g_slist_prepend (config->preserve_name_prefixes,
257                                  prefix);
258             g_variant_unref (child);
259         }
260         g_variant_unref (variant);
261     }
262 }
263
264 static void
265 ibus_config_dconf_destroy (IBusConfigDConf *config)
266 {
267     if (config->client) {
268         GError *error = NULL;
269         if (!dconf_client_unwatch (config->client, DCONF_PREFIX"/", NULL, &error))
270             g_warning ("Can not unwatch dconf path %s", DCONF_PREFIX"/");
271
272         g_object_unref (config->client);
273         config->client = NULL;
274     }
275
276     g_slist_free_full (config->preserve_name_prefixes, (GDestroyNotify) g_free);
277     config->preserve_name_prefixes = NULL;
278
279     IBUS_OBJECT_CLASS (ibus_config_dconf_parent_class)->
280         destroy ((IBusObject *)config);
281 }
282
283 static gboolean
284 ibus_config_dconf_set_value (IBusConfigService *config,
285                              const gchar       *section,
286                              const gchar       *name,
287                              GVariant          *value,
288                              GError           **error)
289 {
290     IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
291     DConfClient *client = dconf->client;
292     gchar *path, *gpath, *gname, *gkey;
293     gboolean retval;
294
295     path = g_strdup_printf (DCONF_PREFIX"/%s", section);
296     gpath = _to_gsettings_path (path);
297     g_free (path);
298
299     if (_has_prefixes (gpath, dconf->preserve_name_prefixes)) {
300         gname = (char *) name;
301     } else {
302         gname = _to_gsettings_name (name);
303     }
304     gkey = g_strconcat (gpath, "/", gname, NULL);
305     g_free (gpath);
306     if (gname != name) {
307         g_free (gname);
308     }
309
310     retval = dconf_client_write (client,
311                                  gkey,
312                                  value,
313                                  NULL,   /* tag */
314                                  NULL,   /* cancellable */
315                                  error);
316     g_free (gkey);
317
318     /* notify the caller that the value has changed, as dconf does not
319        call watch_func when the caller is the process itself */
320     if (retval) {
321         if (value == NULL) {
322             /* Use a empty tuple for a unset value */
323             value = g_variant_new_tuple (NULL, 0);
324         }
325         ibus_config_service_value_changed (config, section, name, value);
326     }
327     return retval;
328 }
329
330 static GVariant *
331 ibus_config_dconf_get_value (IBusConfigService *config,
332                              const gchar       *section,
333                              const gchar       *name,
334                              GError           **error)
335 {
336     IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
337     DConfClient *client = dconf->client;
338     gchar *path, *gpath, *gname, *gkey;
339     GVariant *variant;
340
341     path = g_strdup_printf (DCONF_PREFIX"/%s", section);
342     gpath = _to_gsettings_path (path);
343     g_free (path);
344
345     if (_has_prefixes (gpath, dconf->preserve_name_prefixes)) {
346         gname = (char *) name;
347     } else {
348         gname = _to_gsettings_name (name);
349     }
350     gkey = g_strconcat (gpath, "/", gname, NULL);
351     g_free (gpath);
352     if (gname != name) {
353         g_free (gname);
354     }
355
356     variant = dconf_client_read (client, gkey);
357     g_free (gkey);
358
359     if (variant == NULL) {
360         *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
361                         "Config value [%s:%s] does not exist.", section, name);
362         return NULL;
363     }
364
365     return variant;
366 }
367
368 static GVariant *
369 ibus_config_dconf_get_values (IBusConfigService *config,
370                               const gchar       *section,
371                               GError           **error)
372 {
373     IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
374     DConfClient *client = dconf->client;
375     gchar *dir, *gdir;
376     gint len;
377     gchar **entries, **p;
378     GVariantBuilder *builder;
379     gboolean preserve_name;
380
381     dir = g_strdup_printf (DCONF_PREFIX"/%s/", section);
382     gdir = _to_gsettings_path (dir);
383     g_free (dir);
384
385     preserve_name = _has_prefixes (gdir, dconf->preserve_name_prefixes);
386
387     entries = dconf_client_list (client, gdir, &len);
388     builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
389     for (p = entries; *p != NULL; p++) {
390         gchar *gkey = g_strconcat (gdir, *p, NULL);
391         GVariant *value = dconf_client_read (client, gkey);
392         g_free (gkey);
393         if (value) {
394             gchar *name = *p;
395             if (!preserve_name) {
396                 name = _from_gsettings_name (*p);
397             }
398             g_variant_builder_add (builder, "{sv}", name, value);
399             if (name != *p) {
400                 g_free (name);
401             }
402             g_variant_unref (value);
403         }
404     }
405     g_strfreev (entries);
406     g_free (gdir);
407
408     return g_variant_builder_end (builder);
409 }
410
411 static gboolean
412 ibus_config_dconf_unset_value (IBusConfigService *config,
413                                const gchar       *section,
414                                const gchar       *name,
415                                GError           **error)
416 {
417     return ibus_config_dconf_set_value (config, section, name, NULL, error);
418 }
419
420 IBusConfigDConf *
421 ibus_config_dconf_new (GDBusConnection *connection)
422 {
423     IBusConfigDConf *config;
424     config = (IBusConfigDConf *) g_object_new (IBUS_TYPE_CONFIG_DCONF,
425                                                "object-path", IBUS_PATH_CONFIG,
426                                                "connection", connection,
427                                                NULL);
428     return config;
429 }