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