1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of the
5 * License, or (at your option) version 3.
6 *
7 * This library is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU Library General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
15 * USA.
16 */
17
18 #include <string.h>
19
20 struct _GOConfNode {
21 gchar *path;
22 gchar *id;
23 gchar *key;
24 GSettings *settings;
25 unsigned ref_count;
26 };
27
28 static GHashTable *installed_schemas, *closures;
29 void
_go_conf_init(void)30 _go_conf_init (void)
31 {
32 char const * const *schemas = g_settings_list_schemas ();
33 char const * const *cur = schemas;
34 installed_schemas = g_hash_table_new (g_str_hash, g_str_equal);
35 while (*cur) {
36 g_hash_table_insert (installed_schemas, (gpointer) *cur, GUINT_TO_POINTER (TRUE));
37 cur++;
38 }
39 closures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) go_conf_closure_free);
40 }
41
42 void
_go_conf_shutdown(void)43 _go_conf_shutdown (void)
44 {
45 g_hash_table_destroy (installed_schemas);
46 g_hash_table_destroy (closures);
47 }
48
49 static gchar *
go_conf_format_id(gchar const * key)50 go_conf_format_id (gchar const *key)
51 {
52 char *cur, *res;
53 if (!key)
54 return NULL;
55 res = g_strdup (key);
56 /* replace all slashes by dots */
57 cur = res;
58 while ((cur = strchr (cur, '/')) && *cur)
59 *cur = '.';
60 /* replace all underscores by hyphens */
61 cur = res;
62 while ((cur = strchr (cur, '_')) && *cur)
63 *cur = '-';
64 cur = res;
65 while (*cur) {
66 *cur = g_ascii_tolower (*cur);
67 cur++;
68 }
69 return res;
70 }
71
72 GOConfNode *
go_conf_get_node(GOConfNode * parent,gchar const * key)73 go_conf_get_node (GOConfNode *parent, gchar const *key)
74 {
75 GOConfNode *node;
76 char *formatted;
77
78 g_return_val_if_fail (parent || key, NULL);
79
80 formatted = go_conf_format_id (key);
81 node = g_new0 (GOConfNode, 1);
82 node->ref_count = 1;
83 if (parent) {
84 if (key && !parent->key) {
85 node->path = g_strconcat (parent->path, "/", key, NULL);
86 node->id = g_strconcat (parent->id, ".", formatted, NULL);
87 } else {
88 node->path = g_strdup (parent->path);
89 node->id = g_strdup (parent->id);
90 node->key = g_strdup (key? key: parent->key);
91 }
92 } else {
93 if (key[0] == '/') {
94 node->path = g_strdup (key);
95 node->id = g_strconcat ("org.gnome", formatted, NULL);
96 } else {
97 node->path = g_strconcat ("/apps/", key, NULL);
98 node->id = g_strconcat ("org.gnome.", formatted, NULL);
99 }
100 }
101 node->settings = g_hash_table_lookup (installed_schemas, node->id)? g_settings_new (node->id): NULL;
102 g_free (formatted);
103 if (!node->settings) {
104 char *last_dot = strrchr (node->id, '.');
105 *last_dot = 0;
106 node->settings = g_hash_table_lookup (installed_schemas, node->id)? g_settings_new (node->id): NULL;
107 if (node->settings) {
108 g_free (node->key);
109 node->key = g_strdup (last_dot + 1);
110 } else {
111 go_conf_free_node (node);
112 node = NULL;
113 }
114 }
115 return node;
116 }
117
118 void
go_conf_free_node(GOConfNode * node)119 go_conf_free_node (GOConfNode *node)
120 {
121 if (!node)
122 return;
123
124 g_return_if_fail (node->ref_count > 0);
125
126 node->ref_count--;
127 if (node->ref_count > 0)
128 return;
129
130 if (node->settings)
131 g_object_unref (node->settings);
132 g_free (node->path);
133 g_free (node->id);
134 g_free (node->key);
135 g_free (node);
136 }
137
138 void
go_conf_sync(GOConfNode * node)139 go_conf_sync (GOConfNode *node)
140 {
141 g_settings_sync ();
142 }
143
144 void
go_conf_set_bool(GOConfNode * node,gchar const * key,gboolean val)145 go_conf_set_bool (GOConfNode *node, gchar const *key, gboolean val)
146 {
147 GOConfNode *real_node = go_conf_get_node (node, key);
148 if (!real_node) {
149 d (g_warning ("Unable to set key '%s'", key));
150 return;
151 }
152 g_settings_set_boolean (real_node->settings, real_node->key, val);
153 go_conf_free_node (real_node);
154 }
155
156 void
go_conf_set_int(GOConfNode * node,gchar const * key,gint val)157 go_conf_set_int (GOConfNode *node, gchar const *key, gint val)
158 {
159 GOConfNode *real_node = go_conf_get_node (node, key);
160 if (!real_node) {
161 d (g_warning ("Unable to set key '%s'", key));
162 return;
163 }
164 g_settings_set_int (real_node->settings, real_node->key, val);
165 go_conf_free_node (real_node);
166 }
167
168 void
go_conf_set_double(GOConfNode * node,gchar const * key,gdouble val)169 go_conf_set_double (GOConfNode *node, gchar const *key, gdouble val)
170 {
171 GOConfNode *real_node = go_conf_get_node (node, key);
172 if (!real_node) {
173 d (g_warning ("Unable to set key '%s'", key));
174 return;
175 }
176 g_settings_set_double (real_node->settings, real_node->key, val);
177 go_conf_free_node (real_node);
178 }
179
180 void
go_conf_set_string(GOConfNode * node,gchar const * key,gchar const * str)181 go_conf_set_string (GOConfNode *node, gchar const *key, gchar const *str)
182 {
183 GOConfNode *real_node = go_conf_get_node (node, key);
184 if (!real_node) {
185 d (g_warning ("Unable to set key '%s'", key));
186 return;
187 }
188 g_settings_set_string (real_node->settings, real_node->key, str);
189 go_conf_free_node (real_node);
190 }
191
192 void
go_conf_set_str_list(GOConfNode * node,gchar const * key,GSList * list)193 go_conf_set_str_list (GOConfNode *node, gchar const *key, GSList *list)
194 {
195 GOConfNode *real_node = go_conf_get_node (node, key);
196 gssize n = g_slist_length (list);
197 char const ** strs;
198 GSList *ptr = list;
199 unsigned i = 0;
200
201 if (!real_node) {
202 d (g_warning ("Unable to set key '%s'", key));
203 return;
204 }
205 strs = g_new (char const*, n + 1);
206 for (; ptr; ptr = ptr->next)
207 strs[i++] = (char const *) ptr->data;
208 strs[n] = NULL;
209 g_settings_set_strv (real_node->settings, real_node->key, strs);
210 g_free (strs);
211 go_conf_free_node (real_node);
212 }
213
214 static GVariant *
go_conf_get(GOConfNode * node,gchar const * key,GVariantType const * t)215 go_conf_get (GOConfNode *node, gchar const *key, GVariantType const *t)
216 {
217 GVariant *res = g_settings_get_value (node->settings, key);
218 if (res == NULL) {
219 d (g_warning ("Unable to load key '%s'", key));
220 return NULL;
221 }
222
223 if (!g_variant_is_of_type (res, t)) {
224 char *tstr = g_variant_type_dup_string (t);
225 g_warning ("Expected `%s' got `%s' for key %s",
226 tstr,
227 g_variant_get_type_string (res),
228 key);
229 g_free (tstr);
230 g_variant_unref (res);
231 return NULL;
232 }
233
234 return res;
235 }
236
237 gboolean
go_conf_load_bool(GOConfNode * node,gchar const * key,gboolean default_val)238 go_conf_load_bool (GOConfNode *node, gchar const *key, gboolean default_val)
239 {
240 gboolean res;
241 GVariant *val = NULL;
242 if (node) {
243 if (key && !strchr (key, '/') && !strchr (key, '.'))
244 val = go_conf_get (node, key, G_VARIANT_TYPE_BOOLEAN);
245 else if (node->key)
246 val = go_conf_get (node, node->key, G_VARIANT_TYPE_BOOLEAN);
247 }
248 if (val == NULL) {
249 GOConfNode *real_node = go_conf_get_node (node, key);
250 val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_BOOLEAN): NULL;
251 go_conf_free_node (real_node);
252 }
253
254 if (val != NULL) {
255 res = g_variant_get_boolean (val);
256 g_variant_unref (val);
257 } else {
258 d (g_warning ("Using default value '%s'", default_val ? "true" : "false"));
259 return default_val;
260 }
261 return res;
262 }
263
264 gint
go_conf_load_int(GOConfNode * node,gchar const * key,gint minima,gint maxima,gint default_val)265 go_conf_load_int (GOConfNode *node, gchar const *key, gint minima, gint maxima, gint default_val)
266 {
267 gint res = -1;
268 GVariant *val = NULL;
269 if (node) {
270 if (key && !strchr (key, '/') && !strchr (key, '.'))
271 val = go_conf_get (node, key, G_VARIANT_TYPE_INT32);
272 else if (node->key)
273 val = go_conf_get (node, node->key, G_VARIANT_TYPE_INT32);
274 }
275 if (val == NULL) {
276 GOConfNode *real_node = go_conf_get_node (node, key);
277 val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_INT32): NULL;
278 go_conf_free_node (real_node);
279 }
280 if (val != NULL) {
281 res = g_variant_get_int32 (val);
282 g_variant_unref (val);
283 if (res < minima || maxima < res) {
284 g_warning ("Invalid value '%d' for %s. If should be >= %d and <= %d",
285 res, key, minima, maxima);
286 val = NULL;
287 }
288 } else {
289 d (g_warning ("Using default value '%d'", default_val));
290 return default_val;
291 }
292 return res;
293 }
294
295 gdouble
go_conf_load_double(GOConfNode * node,gchar const * key,gdouble minima,gdouble maxima,gdouble default_val)296 go_conf_load_double (GOConfNode *node, gchar const *key,
297 gdouble minima, gdouble maxima, gdouble default_val)
298 {
299 gdouble res = -1;
300 GVariant *val = NULL;
301 if (node) {
302 if (key && !strchr (key, '/') && !strchr (key, '.'))
303 val = go_conf_get (node, key, G_VARIANT_TYPE_DOUBLE);
304 else if (node->key)
305 val = go_conf_get (node, node->key, G_VARIANT_TYPE_DOUBLE);
306 }
307 if (val == NULL) {
308 GOConfNode *real_node = go_conf_get_node (node, key);
309 val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_DOUBLE): NULL;
310 go_conf_free_node (real_node);
311 }
312 if (val != NULL) {
313 res = g_variant_get_double (val);
314 g_variant_unref (val);
315 if (res < minima || maxima < res) {
316 g_warning ("Invalid value '%g' for %s. If should be >= %g and <= %g",
317 res, key, minima, maxima);
318 val = NULL;
319 }
320 } else {
321 d (g_warning ("Using default value '%g'", default_val));
322 return default_val;
323 }
324 return res;
325 }
326
327 gchar *
go_conf_load_string(GOConfNode * node,gchar const * key)328 go_conf_load_string (GOConfNode *node, gchar const *key)
329 {
330 char *res = NULL;
331 if (node) {
332 if (key && !strchr (key, '/') && !strchr (key, '.'))
333 res = g_settings_get_string (node->settings, key);
334 else if (node->key)
335 res = g_settings_get_string (node->settings, node->key);
336 }
337 if (res == NULL) {
338 GOConfNode *real_node = go_conf_get_node (node, key);
339 res = (real_node)? g_settings_get_string (real_node->settings, real_node->key): NULL;
340 go_conf_free_node (real_node);
341 }
342 return res;
343 }
344
345 GSList *
go_conf_load_str_list(GOConfNode * node,gchar const * key)346 go_conf_load_str_list (GOConfNode *node, gchar const *key)
347 {
348 char **strs = NULL, **ptr;
349 GSList *list = NULL;
350
351 if (node) {
352 if (key && !strchr (key, '/') && !strchr (key, '.'))
353 strs = g_settings_get_strv (node->settings, key);
354 else if (node->key)
355 strs = g_settings_get_strv (node->settings, node->key);
356 }
357 if (strs == NULL) {
358 GOConfNode *real_node = go_conf_get_node (node, key);
359 strs = real_node? g_settings_get_strv (node->settings, real_node->key): NULL;
360 go_conf_free_node (real_node);
361 }
362 if (strs) {
363 for (ptr = strs; *ptr; ptr++)
364 list = g_slist_prepend (list, g_strdup (*ptr));
365 g_strfreev (strs);
366 list = g_slist_reverse (list);
367 }
368
369 return list;
370 }
371
372 gboolean
go_conf_get_bool(GOConfNode * node,gchar const * key)373 go_conf_get_bool (GOConfNode *node, gchar const *key)
374 {
375 gboolean res, failed = node == NULL;
376 if (!failed) {
377 if (key && !strchr (key, '/') && !strchr (key, '.'))
378 res = g_settings_get_boolean (node->settings, key);
379 else if (node->key)
380 res = g_settings_get_boolean (node->settings, node->key);
381 else
382 failed = TRUE;
383 }
384 if (failed) {
385 GOConfNode *real_node = go_conf_get_node (node, key);
386 res = (real_node)? g_settings_get_boolean (real_node->settings, real_node->key): FALSE;
387 go_conf_free_node (real_node);
388 }
389 return res;
390 }
391
392 gint
go_conf_get_int(GOConfNode * node,gchar const * key)393 go_conf_get_int (GOConfNode *node, gchar const *key)
394 {
395 GOConfNode *real_node = go_conf_get_node (node, key);
396 gint res = (real_node)? g_settings_get_int (real_node->settings, real_node->key): 0;
397 go_conf_free_node (real_node);
398 return res;
399 }
400
401 gdouble
go_conf_get_double(GOConfNode * node,gchar const * key)402 go_conf_get_double (GOConfNode *node, gchar const *key)
403 {
404 GOConfNode *real_node = go_conf_get_node (node, key);
405 gdouble res = (real_node)? g_settings_get_double (real_node->settings, real_node->key): 0.;
406 go_conf_free_node (real_node);
407 return res;
408 }
409
410 gchar *
go_conf_get_string(GOConfNode * node,gchar const * key)411 go_conf_get_string (GOConfNode *node, gchar const *key)
412 {
413 GOConfNode *real_node = go_conf_get_node (node, key);
414 gchar *res = (real_node)? g_settings_get_string (real_node->settings, real_node->key): NULL;
415 go_conf_free_node (real_node);
416 return res;
417 }
418
419 GSList *
go_conf_get_str_list(GOConfNode * node,gchar const * key)420 go_conf_get_str_list (GOConfNode *node, gchar const *key)
421 {
422 return go_conf_load_str_list (node, key);
423 }
424
425 void
go_conf_remove_monitor(guint monitor_id)426 go_conf_remove_monitor (guint monitor_id)
427 {
428 GOConfClosure *cls = g_hash_table_lookup (closures, GUINT_TO_POINTER (monitor_id));
429 if (cls) {
430 g_signal_handler_disconnect (cls->node->settings, monitor_id);
431 g_hash_table_remove (closures, GUINT_TO_POINTER (monitor_id));
432 } else
433 g_warning ("unknown GOConfMonitor id.");
434 }
435
436 static void
cb_key_changed(GSettings * settings,char * key,GOConfClosure * cls)437 cb_key_changed (GSettings *settings,
438 char *key,
439 GOConfClosure *cls)
440 {
441 char *real_key;
442 if (cls->key) {
443 if (strcmp (cls->key, key))
444 return; /* not the watched key */
445 real_key = g_strdup (cls->real_key);
446 } else
447 real_key = g_strconcat (cls->real_key, "/", key, NULL);
448 cls->monitor (cls->node, real_key , cls->data);
449 g_free (real_key);
450 }
451
452 guint
go_conf_add_monitor(GOConfNode * node,G_GNUC_UNUSED gchar const * key,GOConfMonitorFunc monitor,gpointer data)453 go_conf_add_monitor (GOConfNode *node, G_GNUC_UNUSED gchar const *key,
454 GOConfMonitorFunc monitor, gpointer data)
455 {
456 guint ret;
457 GOConfClosure *cls;
458
459 g_return_val_if_fail (node || key, 0);
460 g_return_val_if_fail (monitor != NULL, 0);
461
462 cls = g_new (GOConfClosure, 1);
463 cls->monitor = monitor;
464 cls->node = node;
465 cls->data = data;
466 cls->key = g_strdup (key? key: node->key);
467 cls->real_key = (key)? g_strconcat (node->path, '/', key, NULL): g_strdup (node->path);
468 ret = g_signal_connect
469 (node->settings,
470 "changed", G_CALLBACK (cb_key_changed),
471 cls);
472
473 g_hash_table_insert (closures, GUINT_TO_POINTER (ret), cls);
474 return ret;
475 }
476