1 /* OGMRip - A library for DVD ripping and encoding
2 * Copyright (C) 2004-2012 Olivier Rolland <billl@users.sourceforge.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /**
20 * SECTION:ogmrip-settings
21 * @title: Settings
22 * @short_description: Common interface for settings managers
23 * @include: ogmrip-settings.h
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "ogmrip-settings.h"
31
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35
36 #include <glib/gi18n.h>
37
38 #include <libxml/parser.h>
39
40 #define OGMRIP_SETTINGS_PRIV "__ogmrip_settings_binding_priv__"
41
42 typedef struct
43 {
44 GSList *bindings;
45 GParamSpecPool *pool;
46 } OGMRipSettingsPriv;
47
48 typedef struct
49 {
50 OGMRipSettingsPriv *priv;
51
52 OGMRipSettings *settings;
53 GObject *object;
54
55 OGMRipSetFunc set_func;
56 OGMRipGetFunc get_func;
57 gpointer data;
58
59 gchar *property;
60 gchar *section;
61 gchar *key;
62
63 GType type;
64
65 gulong signal_handler;
66 gulong notify_handler;
67 gboolean blocked;
68 } OGMRipBinding;
69
70 static OGMRipSettings *default_settings;
71
72 /*
73 * Binding
74 */
75
76 static void
ogmrip_binding_disconnect_cb(OGMRipBinding * binding)77 ogmrip_binding_disconnect_cb (OGMRipBinding *binding)
78 {
79 binding->signal_handler = 0;
80 }
81
82 static void
ogmrip_binding_property_notify_cb(OGMRipBinding * binding)83 ogmrip_binding_property_notify_cb (OGMRipBinding *binding)
84 {
85 if (!binding->blocked)
86 {
87 GValue value = {0};
88
89 g_value_init (&value, binding->type);
90 (* binding->get_func) (binding->object, binding->property, &value, binding->data);
91 ogmrip_settings_set_value (binding->settings, binding->section, binding->key, &value);
92 g_value_unset (&value);
93 }
94 }
95
96 static void
ogmrip_binding_key_notify_cb(OGMRipSettings * settings,const gchar * section,const gchar * key,const GValue * value,OGMRipBinding * binding)97 ogmrip_binding_key_notify_cb (OGMRipSettings *settings, const gchar *section, const gchar *key,
98 const GValue *value, OGMRipBinding *binding)
99 {
100 if (!binding->blocked)
101 {
102 g_signal_handlers_block_by_func (binding->object, ogmrip_binding_property_notify_cb, binding);
103
104 if (value && G_IS_VALUE (value))
105 (* binding->set_func) (binding->object, binding->property, value, binding->data);
106 else
107 {
108 GValue val = {0};
109
110 ogmrip_settings_get_value (binding->settings, binding->section, binding->key, &val);
111 if (G_IS_VALUE (&val))
112 {
113 (* binding->set_func) (binding->object, binding->property, &val, binding->data);
114 g_value_unset (&val);
115 }
116 }
117
118 g_signal_handlers_unblock_by_func (binding->object, ogmrip_binding_property_notify_cb, binding);
119 }
120 }
121
122 static void
ogmrip_binding_remove(OGMRipBinding * binding)123 ogmrip_binding_remove (OGMRipBinding *binding)
124 {
125 binding->priv->bindings = g_slist_remove (binding->priv->bindings, binding);
126
127 if (binding->signal_handler)
128 g_signal_handler_disconnect (binding->object, binding->signal_handler);
129
130 g_free (binding->property);
131 g_free (binding->section);
132 g_free (binding->key);
133
134 g_free (binding);
135 }
136
137 /*
138 * Settings priv
139 */
140
141 static void
ogmrip_binding_priv_free(OGMRipSettingsPriv * priv)142 ogmrip_binding_priv_free (OGMRipSettingsPriv *priv)
143 {
144 while (priv->bindings)
145 ogmrip_binding_remove (priv->bindings->data);
146
147 g_free (priv);
148 }
149
150 static OGMRipSettingsPriv *
ogmrip_settings_get_priv(OGMRipSettings * settings)151 ogmrip_settings_get_priv (OGMRipSettings *settings)
152 {
153 OGMRipSettingsPriv *priv;
154
155 priv = g_object_get_data (G_OBJECT (settings), OGMRIP_SETTINGS_PRIV);
156 if (!priv)
157 {
158 priv = g_new0 (OGMRipSettingsPriv, 1);
159
160 g_object_set_data_full (G_OBJECT (settings), OGMRIP_SETTINGS_PRIV,
161 priv, (GDestroyNotify) ogmrip_binding_priv_free);
162 }
163
164 return priv;
165 }
166
167 static GParamSpecPool *
ogmrip_settings_get_pool(OGMRipSettings * settings)168 ogmrip_settings_get_pool (OGMRipSettings *settings)
169 {
170 OGMRipSettingsPriv *priv;
171
172 priv = ogmrip_settings_get_priv (settings);
173 if (!priv->pool)
174 priv->pool = g_param_spec_pool_new (FALSE);
175
176 return priv->pool;
177 }
178
179 /*
180 * Settings
181 */
182
183 static void ogmrip_settings_class_init (gpointer g_iface);
184
185 GType
ogmrip_settings_get_type(void)186 ogmrip_settings_get_type (void)
187 {
188 static GType settings_type = 0;
189
190 if (!settings_type)
191 {
192 settings_type = g_type_register_static_simple (G_TYPE_INTERFACE,
193 "OGMRipSettings",
194 sizeof (OGMRipSettingsIface),
195 (GClassInitFunc) ogmrip_settings_class_init,
196 0, NULL, 0);
197
198 g_type_interface_add_prerequisite (settings_type, G_TYPE_OBJECT);
199 }
200
201 return settings_type;
202 }
203
204 static void
g_value_transform_string_int(const GValue * src_value,GValue * dest_value)205 g_value_transform_string_int (const GValue *src_value, GValue *dest_value)
206 {
207 const gchar *str;
208
209 str = g_value_get_string (src_value);
210 g_value_set_int (dest_value, atoi (str));
211 }
212
213 static void
g_value_transform_int_string(const GValue * src_value,GValue * dest_value)214 g_value_transform_int_string (const GValue *src_value, GValue *dest_value)
215 {
216 gchar *str;
217
218 str = g_strdup_printf ("%d", g_value_get_int (src_value));
219 g_value_take_string (dest_value, str);
220 }
221
222 static void
g_value_transform_string_uint(const GValue * src_value,GValue * dest_value)223 g_value_transform_string_uint (const GValue *src_value, GValue *dest_value)
224 {
225 const gchar *str;
226
227 str = g_value_get_string (src_value);
228 g_value_set_uint (dest_value, atoi (str));
229 }
230
231 static void
g_value_transform_uint_string(const GValue * src_value,GValue * dest_value)232 g_value_transform_uint_string (const GValue *src_value, GValue *dest_value)
233 {
234 gchar *str;
235
236 str = g_strdup_printf ("%u", g_value_get_uint (src_value));
237 g_value_take_string (dest_value, str);
238 }
239
240 static void
g_value_transform_string_double(const GValue * src_value,GValue * dest_value)241 g_value_transform_string_double (const GValue *src_value, GValue *dest_value)
242 {
243 const gchar *str;
244
245 str = g_value_get_string (src_value);
246 g_value_set_double (dest_value, strtod (str, NULL));
247 }
248
249 static void
g_value_transform_double_string(const GValue * src_value,GValue * dest_value)250 g_value_transform_double_string (const GValue *src_value, GValue *dest_value)
251 {
252 gchar *str;
253
254 str = g_strdup_printf ("%lf", g_value_get_double (src_value));
255 g_value_take_string (dest_value, str);
256 }
257
258 static void
g_value_transform_string_boolean(const GValue * src_value,GValue * dest_value)259 g_value_transform_string_boolean (const GValue *src_value, GValue *dest_value)
260 {
261 const gchar *str;
262
263 str = g_value_get_string (src_value);
264 if (g_ascii_strcasecmp (str, "true") == 0)
265 g_value_set_boolean (dest_value, TRUE);
266 else if (g_ascii_strcasecmp (str, "false") == 0)
267 g_value_set_boolean (dest_value, FALSE);
268 }
269
270 static void
g_value_transform_boolean_string(const GValue * src_value,GValue * dest_value)271 g_value_transform_boolean_string (const GValue *src_value, GValue *dest_value)
272 {
273 g_value_set_static_string (dest_value,
274 g_value_get_boolean (src_value) ? "true" : "false");
275 }
276
277 static void
ogmrip_settings_install_key_internal(OGMRipSettings * settings,GParamSpec * pspec)278 ogmrip_settings_install_key_internal (OGMRipSettings *settings, GParamSpec *pspec)
279 {
280 GParamSpecPool *pool;
281
282 pool = ogmrip_settings_get_pool (settings);
283
284 g_param_spec_pool_insert (pool, pspec, OGMRIP_TYPE_SETTINGS);
285 }
286
287 static GType
ogmrip_settings_get_key_type_internal(OGMRipSettings * settings,const gchar * section,const gchar * key)288 ogmrip_settings_get_key_type_internal (OGMRipSettings *settings, const gchar *section, const gchar *key)
289 {
290 GParamSpecPool *pool;
291 GParamSpec *pspec;
292
293 pool = ogmrip_settings_get_pool (settings);
294 pspec = g_param_spec_pool_lookup (pool, key, OGMRIP_TYPE_SETTINGS, FALSE);
295 if (!pspec)
296 return G_TYPE_NONE;
297
298 return pspec->value_type;
299 }
300
301 static void
ogmrip_settings_class_init(gpointer g_iface)302 ogmrip_settings_class_init (gpointer g_iface)
303 {
304 OGMRipSettingsIface *iface = g_iface;
305
306 iface->get_type = ogmrip_settings_get_key_type_internal;
307 iface->install_key = ogmrip_settings_install_key_internal;
308
309 g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, g_value_transform_string_int);
310 g_value_register_transform_func (G_TYPE_INT, G_TYPE_STRING, g_value_transform_int_string);
311
312 g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, g_value_transform_string_uint);
313 g_value_register_transform_func (G_TYPE_UINT, G_TYPE_STRING, g_value_transform_uint_string);
314
315 g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, g_value_transform_string_double);
316 g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_STRING, g_value_transform_double_string);
317
318 g_value_register_transform_func (G_TYPE_STRING, G_TYPE_BOOLEAN, g_value_transform_string_boolean);
319 g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_STRING, g_value_transform_boolean_string);
320 }
321
322 /**
323 * ogmrip_settings_get_default:
324 *
325 * Gets the default setting manager if it exists.
326 *
327 * Returns: the default #OGMRipSettings, or NULL
328 */
329 OGMRipSettings *
ogmrip_settings_get_default(void)330 ogmrip_settings_get_default (void)
331 {
332 return default_settings;
333 }
334
335 /**
336 * ogmrip_settings_set_default:
337 * @settings: an #OGMRipSettings, or NULL
338 *
339 * Sets the default setting manager. If @settings is NULL, the current default
340 * setting manager is removed.
341 */
342 void
ogmrip_settings_set_default(OGMRipSettings * settings)343 ogmrip_settings_set_default (OGMRipSettings *settings)
344 {
345 g_return_if_fail (settings == NULL || OGMRIP_IS_SETTINGS (settings));
346
347 if (default_settings)
348 g_object_unref (default_settings);
349
350 if (settings)
351 g_object_ref (settings);
352
353 default_settings = settings;
354 }
355
356 /**
357 * ogmrip_settings_install_key:
358 * @settings: an #OGMRipSettings
359 * @pspec: a #GParamSpec
360 *
361 * Installs a new key.
362 */
363 void
ogmrip_settings_install_key(OGMRipSettings * settings,GParamSpec * pspec)364 ogmrip_settings_install_key (OGMRipSettings *settings, GParamSpec *pspec)
365 {
366 OGMRipSettingsIface *iface;
367
368 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
369 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
370
371 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
372
373 if (iface->install_key)
374 (* iface->install_key) (settings, pspec);
375 }
376
377 /**
378 * ogmrip_settings_find_key:
379 * @settings: an #OGMRipSettings
380 * @key: the name of the key to look up
381 *
382 * Looks up the GParamSpec for a key.
383 *
384 * Returns: the GParamSpec for the key, or NULL
385 */
386 GParamSpec *
ogmrip_settings_find_key(OGMRipSettings * settings,const gchar * key)387 ogmrip_settings_find_key (OGMRipSettings *settings, const gchar *key)
388 {
389 GParamSpecPool *pool;
390
391 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), NULL);
392 g_return_val_if_fail (key != NULL, NULL);
393
394 pool = ogmrip_settings_get_pool (settings);
395
396 return g_param_spec_pool_lookup (pool, key, OGMRIP_TYPE_SETTINGS, FALSE);
397 }
398
399 static GParamSpec *
g_param_spec_copy(const gchar * name,GParamSpec * pspec)400 g_param_spec_copy (const gchar *name, GParamSpec *pspec)
401 {
402 GParamSpec *pspec_new = NULL;
403 GType type;
404
405 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
406
407 type = G_PARAM_SPEC_TYPE (pspec);
408 if (type == G_TYPE_PARAM_BOOLEAN)
409 pspec_new = g_param_spec_boolean (name,
410 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
411 G_PARAM_SPEC_BOOLEAN (pspec)->default_value, pspec->flags);
412 else if (type == G_TYPE_PARAM_CHAR)
413 pspec_new = g_param_spec_char (name,
414 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
415 G_PARAM_SPEC_CHAR (pspec)->minimum, G_PARAM_SPEC_CHAR (pspec)->maximum,
416 G_PARAM_SPEC_CHAR (pspec)->default_value, pspec->flags);
417 else if (type == G_TYPE_PARAM_UCHAR)
418 pspec_new = g_param_spec_uchar (name,
419 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
420 G_PARAM_SPEC_UCHAR (pspec)->minimum, G_PARAM_SPEC_UCHAR (pspec)->maximum,
421 G_PARAM_SPEC_UCHAR (pspec)->default_value, pspec->flags);
422 else if (type == G_TYPE_PARAM_INT)
423 pspec_new = g_param_spec_int (name,
424 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
425 G_PARAM_SPEC_INT (pspec)->minimum, G_PARAM_SPEC_INT (pspec)->maximum,
426 G_PARAM_SPEC_INT (pspec)->default_value, pspec->flags);
427 else if (type == G_TYPE_PARAM_UINT)
428 pspec_new = g_param_spec_uint (name,
429 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
430 G_PARAM_SPEC_UINT (pspec)->minimum, G_PARAM_SPEC_UINT (pspec)->maximum,
431 G_PARAM_SPEC_UINT (pspec)->default_value, pspec->flags);
432 else if (type == G_TYPE_PARAM_LONG)
433 pspec_new = g_param_spec_long (name,
434 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
435 G_PARAM_SPEC_LONG (pspec)->minimum, G_PARAM_SPEC_LONG (pspec)->maximum,
436 G_PARAM_SPEC_LONG (pspec)->default_value, pspec->flags);
437 else if (type == G_TYPE_PARAM_ULONG)
438 pspec_new = g_param_spec_ulong (name,
439 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
440 G_PARAM_SPEC_ULONG (pspec)->minimum, G_PARAM_SPEC_ULONG (pspec)->maximum,
441 G_PARAM_SPEC_ULONG (pspec)->default_value, pspec->flags);
442 else if (type == G_TYPE_PARAM_INT64)
443 pspec_new = g_param_spec_int64 (name,
444 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
445 G_PARAM_SPEC_INT64 (pspec)->minimum, G_PARAM_SPEC_INT64 (pspec)->maximum,
446 G_PARAM_SPEC_INT64 (pspec)->default_value, pspec->flags);
447 else if (type == G_TYPE_PARAM_UINT64)
448 pspec_new = g_param_spec_uint64 (name,
449 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
450 G_PARAM_SPEC_UINT64 (pspec)->minimum, G_PARAM_SPEC_UINT64 (pspec)->maximum,
451 G_PARAM_SPEC_UINT64 (pspec)->default_value, pspec->flags);
452 else if (type == G_TYPE_PARAM_FLOAT)
453 pspec_new = g_param_spec_float (name,
454 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
455 G_PARAM_SPEC_FLOAT (pspec)->minimum, G_PARAM_SPEC_FLOAT (pspec)->maximum,
456 G_PARAM_SPEC_FLOAT (pspec)->default_value, pspec->flags);
457 else if (type == G_TYPE_PARAM_DOUBLE)
458 pspec_new = g_param_spec_double (name,
459 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
460 G_PARAM_SPEC_DOUBLE (pspec)->minimum, G_PARAM_SPEC_DOUBLE (pspec)->maximum,
461 G_PARAM_SPEC_DOUBLE (pspec)->default_value, pspec->flags);
462 else if (type == G_TYPE_PARAM_STRING)
463 pspec_new = g_param_spec_string (name,
464 g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
465 G_PARAM_SPEC_STRING (pspec)->default_value, pspec->flags);
466 else
467 {
468 g_message ("name: %s, type: %s", name, G_PARAM_SPEC_TYPE_NAME (pspec));
469 g_assert_not_reached ();
470 }
471
472 return pspec_new;
473 }
474
475 /**
476 * ogmrip_settings_get_key_type:
477 * @settings: an #OGMRipSettings
478 * @section: the section of the key
479 * @key: the name of the key to fetch
480 *
481 * Gets the type of the setting named by @key in @section.
482 *
483 * Returns: the type of @key, or %G_TYPE_NONE
484 */
485 GType
ogmrip_settings_get_key_type(OGMRipSettings * settings,const gchar * section,const gchar * key)486 ogmrip_settings_get_key_type (OGMRipSettings *settings, const gchar *section, const gchar *key)
487 {
488 OGMRipSettingsIface *iface;
489
490 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), G_TYPE_NONE);
491 g_return_val_if_fail (section != NULL, G_TYPE_NONE);
492 g_return_val_if_fail (key != NULL, G_TYPE_NONE);
493
494 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
495
496 if (!iface->get_type)
497 return G_TYPE_NONE;
498
499 return (* iface->get_type) (settings, section, key);
500 }
501
502 /**
503 * ogmrip_settings_get_value:
504 * @settings: an #OGMRipSettings
505 * @section: the section of the key
506 * @key: the name of the key to fetch
507 * @value: a #GValue of the correct type
508 *
509 * Gets the value associated with the setting named by @key in @section.
510 */
511 void
ogmrip_settings_get_value(OGMRipSettings * settings,const gchar * section,const gchar * key,GValue * value)512 ogmrip_settings_get_value (OGMRipSettings *settings, const gchar *section, const gchar *key, GValue *value)
513 {
514 OGMRipSettingsIface *iface;
515
516 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
517 g_return_if_fail (key != NULL);
518 g_return_if_fail (section != NULL);
519 g_return_if_fail (value != NULL);
520
521 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
522
523 if (iface->get_value)
524 {
525 (* iface->get_value) (settings, section, key, value);
526
527 if (!G_IS_VALUE (value))
528 {
529 OGMRipSettingsPriv *priv;
530
531 priv = ogmrip_settings_get_priv (settings);
532 if (priv->pool)
533 {
534 GParamSpec *pspec;
535
536 pspec = g_param_spec_pool_lookup (priv->pool, key, OGMRIP_TYPE_SETTINGS, FALSE);
537 if (pspec)
538 {
539 g_value_init (value, pspec->value_type);
540 g_param_value_set_default (pspec, value);
541 }
542 }
543 }
544
545 if (!G_IS_VALUE (value))
546 g_warning ("Cannot set key '%s': no value", key);
547 }
548 }
549
550 /**
551 * ogmrip_settings_set_value:
552 * @settings: an #OGMRipSettings
553 * @section: the section of the key
554 * @key: the name of the key to fetch
555 * @value: a #GValue of the correct type
556 *
557 * Sets the setting named by @key in @section to @value.
558 */
559 void
ogmrip_settings_set_value(OGMRipSettings * settings,const gchar * section,const gchar * key,const GValue * value)560 ogmrip_settings_set_value (OGMRipSettings *settings, const gchar *section, const gchar *key, const GValue *value)
561 {
562 OGMRipSettingsIface *iface;
563
564 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
565 g_return_if_fail (section != NULL);
566 g_return_if_fail (key != NULL);
567 g_return_if_fail (value != NULL);
568
569 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
570
571 if (iface->set_value)
572 {
573 GValue dest_value = {0};
574 GType type;
575
576 type = ogmrip_settings_get_key_type (settings, section, key);
577 if (G_TYPE_IS_VALUE (type))
578 {
579 g_value_init (&dest_value, type);
580
581 if (type == G_VALUE_TYPE (value) || g_value_type_compatible (G_VALUE_TYPE (value), type))
582 g_value_copy (value, &dest_value);
583 else if (g_value_type_transformable (G_VALUE_TYPE (value), type))
584 g_value_transform (value, &dest_value);
585 else
586 g_warning ("Cannot set key '%s': incompatible type", key);
587 }
588
589 if (G_IS_VALUE (&dest_value))
590 {
591 OGMRipSettingsPriv *priv;
592
593 priv = ogmrip_settings_get_priv (settings);
594 if (priv->pool)
595 {
596 GParamSpec *pspec;
597
598 pspec = g_param_spec_pool_lookup (priv->pool, key, OGMRIP_TYPE_SETTINGS, FALSE);
599 if (pspec)
600 g_param_value_validate (pspec, &dest_value);
601 }
602
603 (* iface->set_value) (settings, section, key, &dest_value);
604 }
605 }
606 }
607
608 static void
ogmrip_settings_get_valist(OGMRipSettings * settings,const gchar * section,const gchar * key,va_list var_args)609 ogmrip_settings_get_valist (OGMRipSettings *settings, const gchar *section, const gchar *key, va_list var_args)
610 {
611 gpointer data;
612
613 while (key)
614 {
615 GValue value = {0};
616
617 ogmrip_settings_get_value (settings, section, key, &value);
618 data = va_arg (var_args, gpointer);
619
620 if (G_IS_VALUE (&value))
621 {
622 switch (G_VALUE_TYPE (&value))
623 {
624 case G_TYPE_INT:
625 {
626 gint *i = data;
627 *i = g_value_get_int (&value);
628 }
629 break;
630 case G_TYPE_UINT:
631 {
632 guint *i = data;
633 *i = g_value_get_uint (&value);
634 }
635 break;
636 case G_TYPE_BOOLEAN:
637 {
638 gboolean *b = data;
639 *b = g_value_get_boolean (&value);
640 }
641 break;
642 case G_TYPE_DOUBLE:
643 {
644 gdouble *d = data;
645 *d = g_value_get_double (&value);
646 }
647 break;
648 case G_TYPE_STRING:
649 {
650 gchar **str = data;
651 *str = g_value_dup_string (&value);
652 }
653 break;
654 default:
655 break;
656 }
657
658 g_value_unset (&value);
659 }
660
661 key = va_arg (var_args, const char *);
662 }
663 }
664
665 static void
ogmrip_settings_set_valist(OGMRipSettings * settings,const gchar * section,const gchar * key,va_list var_args)666 ogmrip_settings_set_valist (OGMRipSettings *settings, const gchar *section, const gchar *key, va_list var_args)
667 {
668 GType type;
669
670 while (key)
671 {
672 GValue value = {0};
673
674 type = ogmrip_settings_get_key_type (settings, section, key);
675 g_value_init (&value, type);
676
677 switch (type)
678 {
679 case G_TYPE_INT:
680 g_value_set_int (&value, va_arg (var_args, gint));
681 break;
682 case G_TYPE_BOOLEAN:
683 g_value_set_boolean (&value, va_arg (var_args, gboolean));
684 break;
685 case G_TYPE_DOUBLE:
686 g_value_set_double (&value, va_arg (var_args, gdouble));
687 break;
688 case G_TYPE_STRING:
689 {
690 gchar *str;
691
692 str = va_arg (var_args, gchar *);
693 g_value_set_string (&value, str);
694 }
695 break;
696 default:
697 type = G_TYPE_NONE;
698 break;
699 }
700
701 if (type != G_TYPE_NONE)
702 ogmrip_settings_set_value (settings, section, key, &value);
703
704 g_value_unset (&value);
705
706 key = va_arg (var_args, const char *);
707 }
708 }
709
710 /**
711 * ogmrip_settings_get:
712 * @settings: an #OGMRipSettings
713 * @section: the section of the keys
714 * @key: the name of the first key to fetch
715 * @...: pointers to the locations to store the value of the first key, followed
716 * by more name/pointer groupings, followed by %NULL.
717 *
718 * Gets the values associated with any number of settings in the same section.
719 */
720 void
ogmrip_settings_get(OGMRipSettings * settings,const gchar * section,const gchar * key,...)721 ogmrip_settings_get (OGMRipSettings *settings, const gchar *section, const gchar *key, ...)
722 {
723 va_list var_args;
724
725 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
726 g_return_if_fail (section != NULL);
727
728 va_start (var_args, key);
729 ogmrip_settings_get_valist (settings, section, key, var_args);
730 va_end (var_args);
731 }
732
733 /**
734 * ogmrip_settings_set:
735 * @settings: an #OGMRipSettings
736 * @section: the section of the keys
737 * @key: the name of the first key to set
738 * @...: pointers to the value of the first key, followed by more name/pointer
739 * groupings, followed by %NULL.
740 *
741 * Sets the values associated with any number of settings in the same section.
742 */
743 void
ogmrip_settings_set(OGMRipSettings * settings,const gchar * section,const gchar * key,...)744 ogmrip_settings_set (OGMRipSettings *settings, const gchar *section, const gchar *key, ...)
745 {
746 va_list var_args;
747
748 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
749 g_return_if_fail (section != NULL);
750
751 va_start (var_args, key);
752 ogmrip_settings_set_valist (settings, section, key, var_args);
753 va_end (var_args);
754 }
755
756 /**
757 * ogmrip_settings_sync:
758 * @settings: an #OGMRipSettings
759 *
760 * Blah
761 */
762 void
ogmrip_settings_sync(OGMRipSettings * settings)763 ogmrip_settings_sync (OGMRipSettings *settings)
764 {
765 OGMRipSettingsIface *iface;
766
767 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
768
769 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
770
771 if (iface->sync)
772 (* iface->sync) (settings);
773 }
774
775 /**
776 * ogmrip_settings_build_section:
777 * @settings: an #OGMRipSettings
778 * @element: the first section element
779 * @...: more section elements
780 *
781 * Builds a section from many section elements.
782 *
783 * Returns: the new section
784 */
785 gchar *
ogmrip_settings_build_section(OGMRipSettings * settings,const gchar * element,...)786 ogmrip_settings_build_section (OGMRipSettings *settings, const gchar *element, ...)
787 {
788 OGMRipSettingsIface *iface;
789 va_list var_args;
790 gchar *section;
791
792 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), NULL);
793
794 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
795
796 if (!iface->build_section)
797 return NULL;
798
799 va_start (var_args, element);
800 section = (* iface->build_section) (settings, element, var_args);
801 va_end (var_args);
802
803 return section;
804 }
805
806 /**
807 * ogmrip_settings_get_section_name:
808 * @settings: an #OGMRipSettings
809 * @section: a section
810 *
811 * Gets the name of the section.
812 *
813 * Returns: the name of the section
814 */
815 const gchar *
ogmrip_settings_get_section_name(OGMRipSettings * settings,const gchar * section)816 ogmrip_settings_get_section_name (OGMRipSettings *settings, const gchar *section)
817 {
818 OGMRipSettingsIface *iface;
819
820 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), NULL);
821
822 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
823
824 if (!iface->get_section_name)
825 return NULL;
826
827 return (* iface->get_section_name) (settings, section);
828 }
829
830 /**
831 * ogmrip_settings_get_subsections:
832 * @settings: an #OGMRipSettings
833 * @section: the section from which to get the subsections
834 *
835 * Lists the subsections in @section. The returned list contains allocated
836 * strings. You should g_free() each string in the list, then g_slist_free() the
837 * list itself.
838 *
839 * Returns: List of allocated subsection names
840 */
841 GSList *
ogmrip_settings_get_subsections(OGMRipSettings * settings,const gchar * section)842 ogmrip_settings_get_subsections (OGMRipSettings *settings, const gchar *section)
843 {
844 OGMRipSettingsIface *iface;
845
846 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), NULL);
847
848 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
849
850 if (!iface->get_subsections)
851 return NULL;
852
853 return (* iface->get_subsections) (settings, section);;
854 }
855
856 /**
857 * ogmrip_settings_get_keys:
858 * @settings: an #OGMRipSettings
859 * @section: the section from which to get the keys
860 * @recursive: perform a recursive search
861 *
862 * Lists the keys in @section. The returned list contains allocated
863 * strings. You should g_free() each string in the list, then g_slist_free() the
864 * list itself.
865 *
866 * Returns: List of allocated key names
867 */
868 GSList *
ogmrip_settings_get_keys(OGMRipSettings * settings,const gchar * section,gboolean recursive)869 ogmrip_settings_get_keys (OGMRipSettings *settings, const gchar *section, gboolean recursive)
870 {
871 GSList *list;
872 OGMRipSettingsIface *iface;
873
874 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), NULL);
875
876 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
877
878 if (!iface->get_keys)
879 return NULL;
880
881 list = (* iface->get_keys) (settings, section, recursive);;
882 list = g_slist_sort (list, (GCompareFunc) strcmp);
883
884 return list;
885 }
886
887 /**
888 * ogmrip_settings_remove_key:
889 * @settings: an #OGMRipSettings
890 * @section: a section
891 * @key: the key to remove
892 *
893 * Removeѕ @key from @section.
894 */
895 void
ogmrip_settings_remove_key(OGMRipSettings * settings,const gchar * section,const gchar * key)896 ogmrip_settings_remove_key (OGMRipSettings *settings, const gchar *section, const gchar *key)
897 {
898 OGMRipSettingsIface *iface;
899
900 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
901 g_return_if_fail (section != NULL);
902 g_return_if_fail (key != NULL);
903
904 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
905
906 if (iface->remove_key)
907 (* iface->remove_key) (settings, section, key);
908 }
909
910 /**
911 * ogmrip_settings_remove_section:
912 * @settings: an #OGMRipSettings
913 * @section: the section to remove
914 *
915 * Removeѕ @section and all its keys and subsections.
916 */
917 void
ogmrip_settings_remove_section(OGMRipSettings * settings,const gchar * section)918 ogmrip_settings_remove_section (OGMRipSettings *settings, const gchar *section)
919 {
920 OGMRipSettingsIface *iface;
921
922 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
923 g_return_if_fail (section != NULL);
924
925 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
926
927 if (iface->remove_section)
928 (* iface->remove_section) (settings, section);
929 }
930
931 /**
932 * ogmrip_settings_has_key:
933 * @settings: an #OGMRipSettings
934 * @section: the section
935 * @key: the key
936 *
937 * Returns whether a key exists or not.
938 *
939 * Returns: %TRUE if @key exists, %FALSE otherwise
940 */
941 gboolean
ogmrip_settings_has_key(OGMRipSettings * settings,const gchar * section,const gchar * key)942 ogmrip_settings_has_key (OGMRipSettings *settings, const gchar *section, const gchar *key)
943 {
944 OGMRipSettingsIface *iface;
945
946 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), FALSE);
947 g_return_val_if_fail (section != NULL, FALSE);
948 g_return_val_if_fail (key != NULL, FALSE);
949
950 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
951
952 if (!iface->has_key)
953 return FALSE;
954
955 return (* iface->has_key) (settings, section, key);
956 }
957
958 /**
959 * ogmrip_settings_has_section:
960 * @settings: an #OGMRipSettings
961 * @section: the section
962 *
963 * Returns whether a section exists or not.
964 *
965 * Returns: %TRUE if @section exists, %FALSE otherwise
966 */
967 gboolean
ogmrip_settings_has_section(OGMRipSettings * settings,const gchar * section)968 ogmrip_settings_has_section (OGMRipSettings *settings, const gchar *section)
969 {
970 OGMRipSettingsIface *iface;
971
972 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), FALSE);
973 g_return_val_if_fail (section != NULL, FALSE);
974
975 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
976
977 if (!iface->has_section)
978 return FALSE;
979
980 return (* iface->has_section) (settings, section);
981 }
982
983 /**
984 * ogmrip_settings_add_notify:
985 * @settings: an #OGMRipSettings
986 * @section: the section
987 * @key: the key
988 * @func: function to call when changes occur
989 * @data: user data to pass to @func
990 *
991 * Request notification of changes of @key in @section.
992 *
993 * Returns: a connection ID for removing the notification
994 */
995 gulong
ogmrip_settings_add_notify(OGMRipSettings * settings,const gchar * section,const gchar * key,OGMRipNotifyFunc func,gpointer data)996 ogmrip_settings_add_notify (OGMRipSettings *settings, const gchar *section, const gchar *key, OGMRipNotifyFunc func, gpointer data)
997 {
998 OGMRipSettingsIface *iface;
999
1000 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), 0);
1001 g_return_val_if_fail (section != NULL, 0);
1002 g_return_val_if_fail (key != NULL, 0);
1003 g_return_val_if_fail (func != NULL, 0);
1004
1005 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
1006
1007 if (!iface->add_notify)
1008 return 0;
1009
1010 return (* iface->add_notify) (settings, section, key, func, data);
1011 }
1012
1013 typedef struct
1014 {
1015 OGMRipSettings *settings;
1016 guint handler_id;
1017 } OGMRipDisconnector;
1018
1019 static void
ogmrip_settings_notify_disconnector(gpointer data,GObject * gobject)1020 ogmrip_settings_notify_disconnector (gpointer data, GObject *gobject)
1021 {
1022 OGMRipDisconnector *disconnector = data;
1023
1024 ogmrip_settings_remove_notify (disconnector->settings, disconnector->handler_id);
1025
1026 g_free (disconnector);
1027 }
1028
1029 /**
1030 * ogmrip_settings_add_notify_while_alive:
1031 * @settings: an #OGMRipSettings
1032 * @section: the section
1033 * @key: the key
1034 * @func: function to call when changes occur
1035 * @data: user data to pass to @func
1036 * @object: a #GObject
1037 *
1038 * Request notification of changes of @key in @section. When @object is destroyed,
1039 * the notification is automatically removed.
1040 *
1041 * Returns: a connection ID for removing the notification
1042 */
1043 gulong
ogmrip_settings_add_notify_while_alive(OGMRipSettings * settings,const gchar * section,const gchar * key,OGMRipNotifyFunc func,gpointer data,GObject * object)1044 ogmrip_settings_add_notify_while_alive (OGMRipSettings *settings, const gchar *section, const gchar *key,
1045 OGMRipNotifyFunc func, gpointer data, GObject *object)
1046 {
1047 OGMRipDisconnector *disconnector;
1048
1049 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), 0);
1050 g_return_val_if_fail (G_IS_OBJECT (object), 0);
1051 g_return_val_if_fail (func != NULL, 0);
1052 g_return_val_if_fail (section != NULL, 0);
1053 g_return_val_if_fail (key != NULL, 0);
1054
1055 disconnector = g_new0 (OGMRipDisconnector, 1);
1056
1057 disconnector->settings = settings;
1058 disconnector->handler_id = ogmrip_settings_add_notify (settings, section, key, func, data);
1059
1060 g_object_weak_ref (object, ogmrip_settings_notify_disconnector, disconnector);
1061
1062 return disconnector->handler_id;
1063 }
1064
1065 /**
1066 * ogmrip_settings_remove_notify:
1067 * @settings: an #OGMRipSettings
1068 * @handler_id: a connection ID
1069 *
1070 * Remove a notification using the ID returned from ogmrip_settings_add_notify()
1071 * or ogmrip_settings_add_notify_while_alive().
1072 */
1073 void
ogmrip_settings_remove_notify(OGMRipSettings * settings,gulong handler_id)1074 ogmrip_settings_remove_notify (OGMRipSettings *settings, gulong handler_id)
1075 {
1076 OGMRipSettingsIface *iface;
1077
1078 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1079 g_return_if_fail (handler_id != 0);
1080
1081 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
1082
1083 if (iface->remove_notify)
1084 (* iface->remove_notify) (settings, handler_id);
1085 }
1086
1087 /**
1088 * ogmrip_settings_bind_custom:
1089 * @settings: an #OGMRipSettings
1090 * @section: the section
1091 * @key: the key
1092 * @object: a #GObject
1093 * @property: a property of @object
1094 * @get_func: function called whenever @property changes setting a custom value to @key
1095 * @set_func: function called whenever @key changes settings a custom value to @object
1096 * @data: user data to pass to @get_func and @set_func
1097 *
1098 * Binds @key in @section with @property of @object. Whenever @property changes,
1099 * @key is updated. Whenever @key changeѕ, @property is updated.
1100 */
1101 void
ogmrip_settings_bind_custom(OGMRipSettings * settings,const gchar * section,const gchar * key,GObject * object,const gchar * property,OGMRipGetFunc get_func,OGMRipSetFunc set_func,gpointer data)1102 ogmrip_settings_bind_custom (OGMRipSettings *settings, const gchar *section, const gchar *key,
1103 GObject *object, const gchar *property, OGMRipGetFunc get_func, OGMRipSetFunc set_func, gpointer data)
1104 {
1105 OGMRipBinding *binding;
1106 gchar *signame;
1107
1108 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1109 g_return_if_fail (G_IS_OBJECT (object));
1110
1111 g_return_if_fail (section != NULL);
1112 g_return_if_fail (key != NULL);
1113 g_return_if_fail (property != NULL);
1114 g_return_if_fail (get_func != NULL);
1115 g_return_if_fail (set_func != NULL);
1116
1117 binding = g_new0 (OGMRipBinding, 1);
1118
1119 binding->key = g_strdup (key);
1120 binding->section = g_strdup (section);
1121 binding->property = g_strdup (property);
1122 binding->settings = settings;
1123 binding->object = object;
1124
1125 binding->get_func = get_func;
1126 binding->set_func = set_func;
1127 binding->data = data;
1128
1129 binding->type = ogmrip_settings_get_key_type (settings, section, key);
1130
1131 g_object_weak_ref (object, (GWeakNotify) ogmrip_binding_remove, binding);
1132
1133 binding->priv = ogmrip_settings_get_priv (settings);
1134 binding->priv->bindings = g_slist_prepend (binding->priv->bindings, binding);
1135
1136 binding->notify_handler = ogmrip_settings_add_notify_while_alive (settings, section, key,
1137 (OGMRipNotifyFunc) ogmrip_binding_key_notify_cb, binding, object);
1138
1139 signame = g_strdup_printf ("notify::%s", property);
1140 binding->signal_handler = g_signal_connect_data (object, signame,
1141 G_CALLBACK (ogmrip_binding_property_notify_cb), binding,
1142 (GClosureNotify) ogmrip_binding_disconnect_cb, G_CONNECT_SWAPPED);
1143 g_free (signame);
1144
1145 ogmrip_binding_key_notify_cb (settings, section, key, NULL, binding);
1146 }
1147
1148 /**
1149 * ogmrip_settings_bind:
1150 * @settings: an #OGMRipSettings
1151 * @section: the section
1152 * @key: the key
1153 * @object: a #GObject
1154 * @property: a property of @object
1155 *
1156 * Binds @key in @section with @property of @object. Whenever @property changes,
1157 * @key is updated. Whenever @key changeѕ, @property is updated.
1158 */
1159 void
ogmrip_settings_bind(OGMRipSettings * settings,const gchar * section,const gchar * key,GObject * object,const gchar * property)1160 ogmrip_settings_bind (OGMRipSettings *settings, const gchar *section, const gchar *key, GObject *object, const gchar *property)
1161 {
1162 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1163 g_return_if_fail (G_IS_OBJECT (object));
1164
1165 g_return_if_fail (key != NULL);
1166 g_return_if_fail (property != NULL);
1167
1168 ogmrip_settings_bind_custom (settings, section, key, object, property,
1169 (OGMRipGetFunc) g_object_get_property,
1170 (OGMRipSetFunc) g_object_set_property,
1171 NULL);
1172 }
1173
1174 /**
1175 * ogmrip_settings_unbind:
1176 * @settings: an #OGMRipSettings
1177 * @object: a #GObject
1178 *
1179 * Removes the bindings associated to @object.
1180 */
1181 void
ogmrip_settings_unbind(OGMRipSettings * settings,GObject * object)1182 ogmrip_settings_unbind (OGMRipSettings *settings, GObject *object)
1183 {
1184 OGMRipSettingsIface *iface;
1185 OGMRipSettingsPriv *priv;
1186 OGMRipBinding *binding;
1187 GSList *link;
1188
1189 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1190 g_return_if_fail (G_IS_OBJECT (object));
1191
1192 iface = OGMRIP_SETTINGS_GET_IFACE (settings);
1193
1194 priv = ogmrip_settings_get_priv (settings);
1195
1196 link = priv->bindings;
1197 while (link)
1198 {
1199 binding = link->data;
1200 link = link->next;
1201
1202 if (binding->object == object)
1203 {
1204 if (iface->remove_notify)
1205 (* iface->remove_notify) (settings, binding->notify_handler);
1206
1207 g_object_weak_unref (binding->object,
1208 (GWeakNotify) ogmrip_binding_remove, binding);
1209 ogmrip_binding_remove (binding);
1210 }
1211 }
1212 }
1213
1214 /**
1215 * ogmrip_settings_block:
1216 * @settings: an #OGMRipSettings
1217 * @section: the section
1218 * @key: the key
1219 *
1220 * Blocks all notifications related to @key in @section. If @section is NULL, notifications
1221 * related to @key from all sections are blocked.
1222 */
1223 void
ogmrip_settings_block(OGMRipSettings * settings,const gchar * section,const gchar * key)1224 ogmrip_settings_block (OGMRipSettings *settings, const gchar *section, const gchar *key)
1225 {
1226 OGMRipSettingsPriv *priv;
1227 OGMRipBinding *binding;
1228 GSList *link;
1229
1230 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1231 g_return_if_fail (key != NULL);
1232
1233 priv = ogmrip_settings_get_priv (settings);
1234
1235 link = priv->bindings;
1236 while (link)
1237 {
1238 binding = link->data;
1239 link = link->next;
1240
1241 if ((!section || g_str_equal (section, binding->section)) && g_str_equal (key, binding->key))
1242 {
1243 binding->blocked = TRUE;
1244 break;
1245 }
1246 }
1247 }
1248
1249 /**
1250 * ogmrip_settings_unblock:
1251 * @settings: an #OGMRipSettings
1252 * @section: the section
1253 * @key: the key
1254 *
1255 * Unblocks all notifications related to @key in @section. If @section is NULL, notifications
1256 * related to @key from all sections are unblocked.
1257 */
1258 void
ogmrip_settings_unblock(OGMRipSettings * settings,const gchar * section,const gchar * key)1259 ogmrip_settings_unblock (OGMRipSettings *settings, const gchar *section, const gchar *key)
1260 {
1261 OGMRipSettingsPriv *priv;
1262 OGMRipBinding *binding;
1263 GSList *link;
1264
1265 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1266 g_return_if_fail (key != NULL);
1267
1268 priv = ogmrip_settings_get_priv (settings);
1269
1270 link = priv->bindings;
1271 while (link)
1272 {
1273 binding = link->data;
1274 link = link->next;
1275
1276 if ((!section || g_str_equal (section, binding->section)) && g_str_equal (key, binding->key))
1277 {
1278 binding->blocked = FALSE;
1279 break;
1280 }
1281 }
1282 }
1283
1284 /**
1285 * ogmrip_settings_install_key_from_property:
1286 * @settings: An #OGMRipSettings
1287 * @klass: A #GObjectClass
1288 * @section: A section
1289 * @key: A key
1290 * @property: A property
1291 *
1292 * Installs a new key using the GParamSpec of @property.
1293 */
1294 void
ogmrip_settings_install_key_from_property(OGMRipSettings * settings,GObjectClass * klass,const gchar * section,const gchar * key,const gchar * property)1295 ogmrip_settings_install_key_from_property (OGMRipSettings *settings, GObjectClass *klass, const gchar *section, const gchar *key, const gchar *property)
1296 {
1297 GParamSpec *pspec;
1298
1299 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1300 g_return_if_fail (G_IS_OBJECT_CLASS (klass));
1301 g_return_if_fail (key != NULL);
1302 g_return_if_fail (property != NULL);
1303
1304 pspec = g_object_class_find_property (klass, property);
1305 if (pspec)
1306 {
1307 gchar *full_key;
1308
1309 if (section)
1310 full_key = ogmrip_settings_build_section (settings, section, key, NULL);
1311 else
1312 full_key = g_strdup (key);
1313
1314 ogmrip_settings_install_key (settings, g_param_spec_copy (full_key, pspec));
1315
1316 g_free (full_key);
1317 }
1318 }
1319
1320 /**
1321 * ogmrip_settings_set_property_from_key:
1322 * @settings: An #OGMRipSettings
1323 * @object: A #GObject
1324 * @property: Name of the property to set
1325 * @section: Section of a key
1326 * @key: Name of a key
1327 *
1328 * Sets a property of an object using the value of a settings key.
1329 */
1330 void
ogmrip_settings_set_property_from_key(OGMRipSettings * settings,GObject * object,const gchar * property,const gchar * section,const gchar * key)1331 ogmrip_settings_set_property_from_key (OGMRipSettings *settings, GObject *object, const gchar *property, const gchar *section, const gchar *key)
1332 {
1333 GValue value = {0};
1334
1335 g_return_if_fail (OGMRIP_IS_SETTINGS (settings));
1336 g_return_if_fail (G_IS_OBJECT (object));
1337 g_return_if_fail (section != NULL);
1338 g_return_if_fail (property != NULL);
1339 g_return_if_fail (key != NULL);
1340
1341 ogmrip_settings_get_value (settings, section, key, &value);
1342
1343 g_object_set_property (object, property, &value);
1344 g_value_unset (&value);
1345 }
1346
1347 static gchar *
g_value_to_string(const GValue * value)1348 g_value_to_string (const GValue *value)
1349 {
1350 gchar *str = NULL;
1351
1352 if (g_value_type_compatible (value->g_type, G_TYPE_STRING))
1353 str = g_value_dup_string (value);
1354 else if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
1355 {
1356 GValue dst_value = {0};
1357
1358 g_value_init (&dst_value, G_TYPE_STRING);
1359 g_value_transform (value, &dst_value);
1360 str = g_value_dup_string (&dst_value);
1361 g_value_unset (&dst_value);
1362 }
1363
1364 return str;
1365 }
1366
1367 typedef struct
1368 {
1369 OGMRipSettings *settings;
1370 const gchar *section;
1371 FILE *f;
1372 } DumpData;
1373
1374 static void
ogmrip_settings_dump_entry(gchar * key,DumpData * data)1375 ogmrip_settings_dump_entry (gchar *key, DumpData *data)
1376 {
1377 if (!g_str_equal (key, "version"))
1378 {
1379 GValue value = {0};
1380 gchar *str;
1381
1382 ogmrip_settings_get_value (data->settings, data->section, key, &value);
1383
1384 if (G_IS_VALUE (&value))
1385 {
1386 fprintf (data->f, " <entry>\n");
1387 fprintf (data->f, " <key>%s</key>\n", key);
1388 fprintf (data->f, " <value>\n");
1389
1390 switch (value.g_type)
1391 {
1392 case G_TYPE_INT:
1393 case G_TYPE_UINT:
1394 case G_TYPE_LONG:
1395 case G_TYPE_ULONG:
1396 case G_TYPE_INT64:
1397 case G_TYPE_UINT64:
1398 str = g_value_to_string (&value);
1399 fprintf (data->f, " <int>%s</int>\n", str);
1400 g_free (str);
1401 break;
1402 case G_TYPE_FLOAT:
1403 case G_TYPE_DOUBLE:
1404 str = g_value_to_string (&value);
1405 fprintf (data->f, " <double>%s</double>\n", str);
1406 g_free (str);
1407 break;
1408 case G_TYPE_STRING:
1409 str = g_markup_escape_text (g_value_get_string (&value), -1);
1410 fprintf (data->f, " <string>%s</string>\n", (str[0] == ' ' && str[1] == '\0') ? "" : str);
1411 g_free (str);
1412 break;
1413 case G_TYPE_BOOLEAN:
1414 str = g_value_to_string (&value);
1415 fprintf (data->f, " <bool>%s</bool>\n", str);
1416 g_free (str);
1417 break;
1418 default:
1419 g_message ("%s", g_type_name (value.g_type));
1420 g_assert_not_reached();
1421 break;
1422 }
1423
1424 fprintf (data->f, " </value>\n");
1425 fprintf (data->f, " </entry>\n");
1426 }
1427 }
1428
1429 g_free (key);
1430 }
1431
1432 /**
1433 * ogmrip_settings_export:
1434 * @settings: An #OGMRipSettings
1435 * @filename: A filename to export into
1436 * @section: The section to export
1437 * @error: Return location for error
1438 *
1439 * Exports settings from @section in @filename.
1440 *
1441 * Returns: %TRUE if @section has been exported, %FALSE otherwise
1442 */
1443 gboolean
ogmrip_settings_export(OGMRipSettings * settings,const gchar * section,const gchar * filename,GError ** error)1444 ogmrip_settings_export (OGMRipSettings *settings, const gchar *section, const gchar *filename, GError **error)
1445 {
1446 DumpData data;
1447 GSList *keys;
1448
1449 gchar *version;
1450
1451 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), FALSE);
1452 g_return_val_if_fail (filename != NULL, FALSE);
1453 g_return_val_if_fail (section != NULL, FALSE);
1454 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1455
1456 data.section = section;
1457 data.settings = settings;
1458 data.f = fopen (filename, "w");
1459 if (!data.f)
1460 return FALSE;
1461
1462 fprintf (data.f, "<ogmrip>\n");
1463
1464 ogmrip_settings_get (settings, section, "version", &version, NULL);
1465 if (version)
1466 {
1467 fprintf (data.f, " <profile base=\"%s\" version=\"%s\">\n", section, version);
1468 g_free (version);
1469 }
1470 else
1471 fprintf (data.f, " <profile base=\"%s\">\n", section);
1472
1473 keys = ogmrip_settings_get_keys (settings, section, TRUE);
1474 g_slist_foreach (keys, (GFunc) ogmrip_settings_dump_entry, &data);
1475 g_slist_free (keys);
1476
1477 fprintf (data.f, " </profile>\n");
1478 fprintf (data.f, "</ogmrip>\n");
1479
1480 fclose (data.f);
1481
1482 return TRUE;
1483 }
1484
1485 gboolean
xmlNodeCheckLang(xmlNode * node)1486 xmlNodeCheckLang (xmlNode *node)
1487 {
1488 const gchar * const * languages;
1489
1490 xmlChar *lang;
1491 guint i;
1492
1493 lang = xmlNodeGetLang (node);
1494 if (!lang)
1495 return FALSE;
1496
1497 languages = g_get_language_names ();
1498
1499 for (i = 0; languages[i]; i++)
1500 if (xmlStrEqual ((xmlChar *) languages[i], lang))
1501 break;
1502
1503 xmlFree (lang);
1504
1505 return languages[i] != NULL;
1506 }
1507
1508 static void
xmlNodeContentToValue(xmlNode * node,GValue * value)1509 xmlNodeContentToValue (xmlNode *node, GValue *value)
1510 {
1511 xmlNode *iter;
1512 gchar *content = NULL;
1513
1514 for (iter = node->children; iter; iter = iter->next)
1515 if (iter->type == XML_ELEMENT_NODE)
1516 break;
1517
1518 if (!iter)
1519 return;
1520
1521 if (g_str_equal (iter->name, "string"))
1522 {
1523 xmlNode *iter2;
1524
1525 for (iter2 = iter; iter2; iter2 = iter2->next)
1526 if (xmlNodeCheckLang (iter2))
1527 break;
1528
1529 if (iter2)
1530 iter = iter2;
1531 }
1532
1533 content = (gchar *) xmlNodeGetContent (iter);
1534 if (content)
1535 {
1536 g_value_init (value, G_TYPE_STRING);
1537 g_value_take_string (value, content);
1538 }
1539 }
1540
1541 static void
ogmrip_settings_parse_entry(OGMRipSettings * settings,xmlNode * node,const xmlChar * section)1542 ogmrip_settings_parse_entry (OGMRipSettings *settings, xmlNode *node, const xmlChar *section)
1543 {
1544 xmlNode *iter;
1545 GValue value = {0};
1546 gchar *key = NULL;
1547
1548 for (iter = node->children; iter; iter = iter->next)
1549 {
1550 if (g_str_equal ((char *) iter->name, "key"))
1551 key = (char *) xmlNodeGetContent (iter);
1552 else if (g_str_equal ((char *) iter->name, "value"))
1553 xmlNodeContentToValue (iter, &value);
1554 }
1555
1556 if (key && G_IS_VALUE (&value))
1557 ogmrip_settings_set_value (settings, (const gchar *) section, key, &value);
1558
1559 if (G_IS_VALUE (&value))
1560 g_value_unset (&value);
1561
1562 if (key)
1563 xmlFree (key);
1564 }
1565
1566 static void
ogmrip_settings_parse_section(OGMRipSettings * settings,xmlNode * root,const xmlChar * section)1567 ogmrip_settings_parse_section (OGMRipSettings *settings, xmlNode *root, const xmlChar *section)
1568 {
1569 xmlNode *iter;
1570
1571 for (iter = root->children; iter; iter = iter->next)
1572 if (iter->type == XML_ELEMENT_NODE && g_str_equal ((char *) iter->name, "entry"))
1573 ogmrip_settings_parse_entry (settings, iter, section);
1574
1575 ogmrip_settings_sync (settings);
1576 }
1577
1578 /**
1579 * ogmrip_settings_compare_versions:
1580 * @version1: A profile's version
1581 * @version2: Another profile's version
1582 *
1583 * Compares the versions of two profiles.
1584 *
1585 * Returns: Negative value if @version1 < @version2; zero if @version1 = @version2;
1586 * positive value if @version1 > @version2
1587 */
1588 gint
ogmrip_settings_compare_versions(const gchar * version1,const gchar * version2)1589 ogmrip_settings_compare_versions (const gchar *version1, const gchar *version2)
1590 {
1591 gint major1 = 0, minor1 = 0, major2 = 0, minor2 = 0;
1592 gchar *end;
1593
1594 errno = 0;
1595
1596 if (version1)
1597 {
1598 major1 = strtoul (version1, &end, 10);
1599 if (!errno && *end == '.')
1600 minor1 = strtoul (end + 1, NULL, 10);
1601 }
1602
1603 if (version2)
1604 {
1605 major2 = strtoul (version2, &end, 10);
1606 if (!errno && *end == '.')
1607 minor2 = strtoul (end + 1, NULL, 10);
1608 }
1609
1610 if (major1 == major2)
1611 return minor1 - minor2;
1612
1613 return major1 - major2;
1614 }
1615
1616 /**
1617 * ogmrip_settings_import:
1618 * @settings: An #OGMRipSettings
1619 * @filename: A filename to import from
1620 * @section: The section in which to import
1621 * @error: Return location for error
1622 *
1623 * Imports settings from @filename in @section.
1624 *
1625 * Returns: %TRUE if @filename has been imported, %FALSE otherwise
1626 */
1627 gboolean
ogmrip_settings_import(OGMRipSettings * settings,const gchar * filename,gchar ** section,GError ** error)1628 ogmrip_settings_import (OGMRipSettings *settings, const gchar *filename, gchar **section, GError **error)
1629 {
1630 gchar *old_version = NULL;
1631 xmlChar *base, *new_version;
1632 xmlDocPtr doc;
1633 xmlNode *iter;
1634
1635 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), FALSE);
1636 g_return_val_if_fail (filename != NULL, FALSE);
1637 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1638
1639 doc = xmlParseFile (filename);
1640 if (!doc)
1641 {
1642 g_set_error (error, 0, 0, _("Failed to open '%s'"), filename);
1643 return FALSE;
1644 }
1645
1646 iter = xmlDocGetRootElement (doc);
1647
1648 if (!iter)
1649 {
1650 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1651 xmlFreeDoc (doc);
1652 return FALSE;
1653 }
1654
1655 while (iter != NULL)
1656 {
1657 if (iter->type == XML_ELEMENT_NODE)
1658 {
1659 if (!g_str_equal((char *)iter->name, "ogmrip"))
1660 {
1661 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1662 xmlFreeDoc (doc);
1663 return FALSE;
1664 }
1665 else
1666 break;
1667 }
1668
1669 iter = iter->next;
1670 }
1671
1672 if (!iter)
1673 {
1674 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1675 xmlFreeDoc (doc);
1676 return FALSE;
1677 }
1678
1679 if (!ogmrip_settings_find_key (settings, "version"))
1680 ogmrip_settings_install_key (settings,
1681 g_param_spec_string ("version", NULL, NULL, NULL, G_PARAM_READWRITE));
1682
1683 iter = iter->children;
1684
1685 while (iter)
1686 {
1687 if (iter->type == XML_ELEMENT_NODE)
1688 {
1689 if (!g_str_equal ((char *) iter->name, "profile"))
1690 {
1691 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1692 xmlFreeDoc (doc);
1693 return FALSE;
1694 }
1695
1696 base = xmlGetProp (iter, (xmlChar *) "base");
1697 if (!base)
1698 {
1699 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1700 xmlFreeDoc (doc);
1701 return FALSE;
1702 }
1703
1704 if (section)
1705 *section = g_strdup ((gchar *) base);
1706
1707 ogmrip_settings_get (settings, (gchar *) base, "version", &old_version, NULL);
1708
1709 new_version = xmlGetProp (iter, (xmlChar *) "version");
1710
1711 if (!ogmrip_settings_has_section (settings, (gchar *) base) ||
1712 ogmrip_settings_compare_versions ((gchar *) new_version, old_version) > 0)
1713 {
1714 if (new_version)
1715 ogmrip_settings_set (settings, (gchar *) base, "version", new_version, NULL);
1716
1717 ogmrip_settings_parse_section (settings, iter, base);
1718 }
1719
1720 if (new_version)
1721 xmlFree (new_version);
1722 new_version = NULL;
1723
1724 if (old_version)
1725 g_free (old_version);
1726 old_version = NULL;
1727
1728 xmlFree (base);
1729 }
1730 iter = iter->next;
1731 }
1732
1733 xmlFreeDoc (doc);
1734
1735 return TRUE;
1736 }
1737
1738 static void
ogmrip_settings_foreach(xmlNode * root,OGMRipParseFunc func,gpointer user_data)1739 ogmrip_settings_foreach (xmlNode *root, OGMRipParseFunc func, gpointer user_data)
1740 {
1741 xmlNode *iter;
1742
1743 for (iter = root->children; iter; iter = iter->next)
1744 {
1745 if (!(* func) (iter, user_data))
1746 break;
1747
1748 if (iter->children)
1749 ogmrip_settings_foreach (iter, func, user_data);
1750 }
1751 }
1752
1753 /**
1754 * ogmrip_settings_parse:
1755 * @settings: An #OGMRipSettings
1756 * @filename: A filename to parse
1757 * @func: The function to call for each entries
1758 * @user_data: User data passed to the function
1759 * @error: Return location for error
1760 *
1761 * Parses the settings in @filename, calling @func for each entries.
1762 *
1763 * Returns: %TRUE on success, %FALSE if an error was set
1764 *
1765 */
1766 gboolean
ogmrip_settings_parse(OGMRipSettings * settings,const gchar * filename,OGMRipParseFunc func,gpointer user_data,GError ** error)1767 ogmrip_settings_parse (OGMRipSettings *settings, const gchar *filename, OGMRipParseFunc func, gpointer user_data, GError **error)
1768 {
1769 xmlChar *base;
1770 xmlDocPtr doc;
1771 xmlNode *iter;
1772
1773 g_return_val_if_fail (OGMRIP_IS_SETTINGS (settings), FALSE);
1774 g_return_val_if_fail (filename != NULL, FALSE);
1775 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1776
1777 doc = xmlParseFile (filename);
1778 if (!doc)
1779 {
1780 g_set_error (error, 0, 0, _("Failed to open '%s'"), filename);
1781 return FALSE;
1782 }
1783
1784 iter = xmlDocGetRootElement (doc);
1785
1786 if (!iter)
1787 {
1788 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1789 xmlFreeDoc (doc);
1790 return FALSE;
1791 }
1792
1793 while (iter != NULL)
1794 {
1795 if (iter->type == XML_ELEMENT_NODE)
1796 {
1797 if (!g_str_equal((char *)iter->name, "ogmrip"))
1798 {
1799 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1800 xmlFreeDoc (doc);
1801 return FALSE;
1802 }
1803 else
1804 break;
1805 }
1806
1807 iter = iter->next;
1808 }
1809
1810 if (!iter)
1811 {
1812 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1813 xmlFreeDoc (doc);
1814 return FALSE;
1815 }
1816
1817 if (!ogmrip_settings_find_key (settings, "version"))
1818 ogmrip_settings_install_key (settings,
1819 g_param_spec_string ("version", NULL, NULL, NULL, G_PARAM_READWRITE));
1820
1821 iter = iter->children;
1822
1823 while (iter)
1824 {
1825 if (iter->type == XML_ELEMENT_NODE)
1826 {
1827 if (!g_str_equal ((char *) iter->name, "profile"))
1828 {
1829 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1830 xmlFreeDoc (doc);
1831 return FALSE;
1832 }
1833
1834 base = xmlGetProp (iter, (xmlChar *) "base");
1835 if (!base)
1836 {
1837 g_set_error (error, 0, 0, _("'%s' does not contain a valid profile"), filename);
1838 xmlFreeDoc (doc);
1839 return FALSE;
1840 }
1841 xmlFree (base);
1842
1843 (* func) (iter, user_data);
1844
1845 if (iter->children)
1846 ogmrip_settings_foreach (iter, func, user_data);
1847 }
1848 iter = iter->next;
1849 }
1850
1851 xmlFreeDoc (doc);
1852
1853 return TRUE;
1854 }
1855
1856