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.1 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 Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24 #include <string.h>
25 #include <ibus.h>
26 #include "config-private.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
G_DEFINE_TYPE(IBusConfigDConf,ibus_config_dconf,IBUS_TYPE_CONFIG_SERVICE)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
_has_prefixes(const gchar * path,GSList * prefixes)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 *
_to_gsettings_name(const gchar * name)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 *
_from_gsettings_name(const gchar * name)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 *
_conv_path(const gchar * path,NameConvFunc conv_func)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 *
_to_gsettings_path(const gchar * path)147 _to_gsettings_path (const gchar *path)
148 {
149 return _conv_path (path, _to_gsettings_name);
150 }
151
152 static gchar *
_from_gsettings_path(const gchar * gpath)153 _from_gsettings_path (const gchar *gpath)
154 {
155 return _conv_path (gpath, _from_gsettings_name);
156 }
157
158 static void
_watch_func(DConfClient * client,const gchar * gpath,const gchar * const * items,gint n_items,const gchar * tag,IBusConfigDConf * config)159 _watch_func (DConfClient *client,
160 const gchar *gpath,
161 const gchar * const *items,
162 #ifndef DCONF_0_13_4
163 gint n_items,
164 #endif
165 const gchar *tag,
166 IBusConfigDConf *config)
167 {
168 gchar **gkeys = NULL;
169 gint i;
170 #ifdef DCONF_0_13_4
171 gint n_items;
172
173 n_items = g_strv_length ((gchar **)items);
174 #endif
175
176 g_return_if_fail (gpath != NULL);
177 g_return_if_fail (n_items >= 0);
178
179 if (dconf_is_key (gpath, NULL)) {
180 /* If path is a key, the notification should be taken to mean
181 that one key may have changed. */
182 n_items = 1;
183 gkeys = g_malloc0_n (n_items + 1, sizeof (gchar *));
184 gkeys[0] = g_strdup (gpath);
185 } else {
186 if (n_items == 0) {
187 /* If path is a dir and items is empty then it is an
188 indication that any key under path may have
189 changed. */
190 gkeys = dconf_client_list (config->client, gpath, &n_items);
191 } else {
192 gkeys = g_boxed_copy (G_TYPE_STRV, items);
193 }
194 for (i = 0; i < n_items; i++) {
195 gchar *gname = gkeys[i];
196 gkeys[i] = g_strdup_printf ("%s/%s", gpath, gname);
197 g_free (gname);
198 }
199 }
200
201 for (i = 0; i < n_items; i++) {
202 gchar *gname, *path, *name;
203 GVariant *variant = dconf_client_read (config->client, gkeys[i]);
204
205 if (variant == NULL) {
206 /* Use a empty tuple for a unset value */
207 variant = g_variant_new_tuple (NULL, 0);
208 g_variant_ref_sink (variant);
209 }
210
211 gname = strrchr (gkeys[i], '/');
212 g_assert (gname);
213 *gname++ = '\0';
214
215 if (_has_prefixes (gkeys[i], config->preserve_name_prefixes)) {
216 path = gkeys[i];
217 name = gname;
218 } else {
219 path = _from_gsettings_path (gkeys[i]);
220 name = _from_gsettings_name (gname);
221 }
222
223 ibus_config_service_value_changed ((IBusConfigService *) config,
224 path + sizeof (DCONF_PREFIX),
225 name,
226 variant);
227 if (path != gkeys[i]) {
228 g_free (path);
229 }
230 if (name != gname) {
231 g_free (name);
232 }
233 g_variant_unref (variant);
234 }
235 g_strfreev (gkeys);
236 }
237
238 static void
ibus_config_dconf_init(IBusConfigDConf * config)239 ibus_config_dconf_init (IBusConfigDConf *config)
240 {
241 GVariant *variant;
242 #ifdef DCONF_0_13_4
243 config->client = dconf_client_new ();
244
245 g_signal_connect (config->client, "changed",
246 G_CALLBACK (_watch_func), config);
247
248 dconf_client_watch_fast (config->client, DCONF_PREFIX"/");
249 #else
250 GError *error;
251
252 config->client = dconf_client_new ("ibus",
253 (DConfWatchFunc)_watch_func,
254 config,
255 NULL);
256
257 error = NULL;
258 if (!dconf_client_watch (config->client, DCONF_PREFIX"/", NULL, &error)) {
259 g_warning ("Can not watch dconf path %s: %s",
260 DCONF_PREFIX"/", error->message);
261 g_error_free (error);
262 }
263 #endif
264
265 config->preserve_name_prefixes = NULL;
266 variant = dconf_client_read (config->client,
267 DCONF_PRESERVE_NAME_PREFIXES_KEY);
268 if (variant != NULL) {
269 GVariantIter iter;
270 GVariant *child;
271
272 g_variant_iter_init (&iter, variant);
273 while ((child = g_variant_iter_next_value (&iter))) {
274 char *prefix = g_variant_dup_string (child, NULL);
275 config->preserve_name_prefixes =
276 g_slist_prepend (config->preserve_name_prefixes,
277 prefix);
278 g_variant_unref (child);
279 }
280 g_variant_unref (variant);
281 }
282 }
283
284 static void
ibus_config_dconf_destroy(IBusConfigDConf * config)285 ibus_config_dconf_destroy (IBusConfigDConf *config)
286 {
287 if (config->client) {
288 #ifdef DCONF_0_13_4
289 dconf_client_unwatch_fast (config->client, DCONF_PREFIX"/");
290 #else
291 GError *error = NULL;
292 if (!dconf_client_unwatch (config->client, DCONF_PREFIX"/", NULL, &error)) {
293 g_warning ("Can not unwatch dconf path %s: %s",
294 DCONF_PREFIX"/", error->message);
295 g_error_free (error);
296 }
297 #endif
298
299 g_object_unref (config->client);
300 config->client = NULL;
301 }
302
303 g_slist_free_full (config->preserve_name_prefixes, (GDestroyNotify) g_free);
304 config->preserve_name_prefixes = NULL;
305
306 IBUS_OBJECT_CLASS (ibus_config_dconf_parent_class)->
307 destroy ((IBusObject *)config);
308 }
309
310 static gboolean
ibus_config_dconf_set_value(IBusConfigService * config,const gchar * section,const gchar * name,GVariant * value,GError ** error)311 ibus_config_dconf_set_value (IBusConfigService *config,
312 const gchar *section,
313 const gchar *name,
314 GVariant *value,
315 GError **error)
316 {
317 IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
318 DConfClient *client = dconf->client;
319 gchar *path, *gpath, *gname, *gkey;
320 gboolean retval;
321
322 path = g_strdup_printf (DCONF_PREFIX"/%s", section);
323 gpath = _to_gsettings_path (path);
324 g_free (path);
325
326 if (_has_prefixes (gpath, dconf->preserve_name_prefixes)) {
327 gname = (char *) name;
328 } else {
329 gname = _to_gsettings_name (name);
330 }
331 gkey = g_strconcat (gpath, "/", gname, NULL);
332 g_free (gpath);
333 if (gname != name) {
334 g_free (gname);
335 }
336
337 #ifdef DCONF_0_13_4
338 /* Use dconf_client_write_sync() instead of dconf_client_write_fast()
339 * because dconf_client_write_fast() does not sync the data when
340 * ibus_config_get_values() is called immediately after
341 * ibus_config_set_value() is called.
342 * We won't add a new API for the sync only since IBusConfig is
343 * now deprecated and GSettings is recommended.
344 */
345 retval = dconf_client_write_sync (client,
346 gkey,
347 value,
348 NULL,
349 NULL,
350 error);
351 #else
352 retval = dconf_client_write (client,
353 gkey,
354 value,
355 NULL, /* tag */
356 NULL, /* cancellable */
357 error);
358 #endif
359 g_free (gkey);
360
361 return retval;
362 }
363
364 static GVariant *
ibus_config_dconf_get_value(IBusConfigService * config,const gchar * section,const gchar * name,GError ** error)365 ibus_config_dconf_get_value (IBusConfigService *config,
366 const gchar *section,
367 const gchar *name,
368 GError **error)
369 {
370 IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
371 DConfClient *client = dconf->client;
372 gchar *path, *gpath, *gname, *gkey;
373 GVariant *variant;
374
375 path = g_strdup_printf (DCONF_PREFIX"/%s", section);
376 gpath = _to_gsettings_path (path);
377 g_free (path);
378
379 if (_has_prefixes (gpath, dconf->preserve_name_prefixes)) {
380 gname = (char *) name;
381 } else {
382 gname = _to_gsettings_name (name);
383 }
384 gkey = g_strconcat (gpath, "/", gname, NULL);
385 g_free (gpath);
386 if (gname != name) {
387 g_free (gname);
388 }
389
390 variant = dconf_client_read (client, gkey);
391 g_free (gkey);
392
393 if (variant == NULL) {
394 g_set_error (error,
395 G_DBUS_ERROR,
396 G_DBUS_ERROR_FAILED,
397 "Config value [%s:%s] does not exist.",
398 section, name);
399 return NULL;
400 }
401
402 return variant;
403 }
404
405 static GVariant *
ibus_config_dconf_get_values(IBusConfigService * config,const gchar * section,GError ** error)406 ibus_config_dconf_get_values (IBusConfigService *config,
407 const gchar *section,
408 GError **error)
409 {
410 IBusConfigDConf *dconf = IBUS_CONFIG_DCONF (config);
411 DConfClient *client = dconf->client;
412 gchar *dir, *gdir;
413 gint len;
414 gchar **entries, **p;
415 GVariantBuilder builder;
416 gboolean preserve_name;
417
418 dir = g_strdup_printf (DCONF_PREFIX"/%s/", section);
419 gdir = _to_gsettings_path (dir);
420 g_free (dir);
421
422 preserve_name = _has_prefixes (gdir, dconf->preserve_name_prefixes);
423
424 entries = dconf_client_list (client, gdir, &len);
425 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
426 for (p = entries; *p != NULL; p++) {
427 gchar *gkey = g_strconcat (gdir, *p, NULL);
428 GVariant *value = dconf_client_read (client, gkey);
429 g_free (gkey);
430 if (value) {
431 gchar *name = *p;
432 if (!preserve_name) {
433 name = _from_gsettings_name (*p);
434 }
435 g_variant_builder_add (&builder, "{sv}", name, value);
436 if (name != *p) {
437 g_free (name);
438 }
439 g_variant_unref (value);
440 }
441 }
442 g_strfreev (entries);
443 g_free (gdir);
444
445 return g_variant_builder_end (&builder);
446 }
447
448 static gboolean
ibus_config_dconf_unset_value(IBusConfigService * config,const gchar * section,const gchar * name,GError ** error)449 ibus_config_dconf_unset_value (IBusConfigService *config,
450 const gchar *section,
451 const gchar *name,
452 GError **error)
453 {
454 return ibus_config_dconf_set_value (config, section, name, NULL, error);
455 }
456
457 IBusConfigDConf *
ibus_config_dconf_new(GDBusConnection * connection)458 ibus_config_dconf_new (GDBusConnection *connection)
459 {
460 IBusConfigDConf *config;
461 config = (IBusConfigDConf *) g_object_new (IBUS_TYPE_CONFIG_DCONF,
462 "object-path", IBUS_PATH_CONFIG,
463 "connection", connection,
464 NULL);
465 return config;
466 }
467