1 /*
2 * Copyright (C) 2008 Marc Pavot <marc.pavot@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 */
19
20 #include "lib/ario-conf.h"
21 #include <gtk/gtk.h>
22 #include <string.h>
23 #include <config.h>
24 #include <libxml/parser.h>
25
26 #include "ario-debug.h"
27 #include "ario-util.h"
28
29 static GHashTable *hash;
30 static gboolean modified = FALSE;
31 static GSList *notifications;
32 static guint notification_counter = 1;
33
34 typedef struct
35 {
36 guint notification_id;
37 char *key;
38 ArioNotifyFunc notification_callback;
39 gpointer callback_data;
40 } ArioConfNotifyData;
41
42 #define XML_ROOT_NAME (const unsigned char *)"ario-options"
43 #define XML_VERSION (const unsigned char *)"1.0"
44
45 static void
ario_conf_free_notify_data(ArioConfNotifyData * data)46 ario_conf_free_notify_data (ArioConfNotifyData *data)
47 {
48 ARIO_LOG_FUNCTION_START;
49 if (data) {
50 g_free (data->key);
51 g_free (data);
52 }
53 }
54
55 static char *
ario_conf_get(const char * key)56 ario_conf_get (const char *key)
57 {
58 ARIO_LOG_FUNCTION_START;
59 return g_hash_table_lookup (hash, key);
60 }
61
62 static void
ario_conf_set(const char * key,char * value)63 ario_conf_set (const char *key,
64 char *value)
65 {
66 ARIO_LOG_FUNCTION_START;
67 GSList *tmp;
68 ArioConfNotifyData *data;
69
70 if (!ario_util_strcmp (ario_conf_get (key), value)) {
71 g_free (value);
72 return;
73 }
74 g_hash_table_replace (hash, g_strdup (key), value);
75 modified = TRUE;
76
77 /* Notifications */
78 for (tmp = notifications; tmp; tmp = g_slist_next (tmp)) {
79 data = tmp->data;
80 if (!strcmp (data->key, key)) {
81 data->notification_callback (data->notification_id,
82 data->callback_data);
83 }
84 }
85 }
86
87 void
ario_conf_set_boolean(const char * key,gboolean boolean_value)88 ario_conf_set_boolean (const char *key,
89 gboolean boolean_value)
90 {
91 ARIO_LOG_FUNCTION_START;
92 if (boolean_value)
93 ario_conf_set (key, g_strdup ("1"));
94 else
95 ario_conf_set (key, g_strdup ("0"));
96 }
97
98 gboolean
ario_conf_get_boolean(const char * key,const gboolean default_value)99 ario_conf_get_boolean (const char *key,
100 const gboolean default_value)
101 {
102 ARIO_LOG_FUNCTION_START;
103 gchar *value = ario_conf_get (key);
104 gboolean ret;
105
106 if (!value)
107 return default_value;
108
109 ret = !strcmp (value, "1");
110
111 return ret;
112 }
113
114 void
ario_conf_set_integer(const char * key,int int_value)115 ario_conf_set_integer (const char *key,
116 int int_value)
117 {
118 ARIO_LOG_FUNCTION_START;
119 ario_conf_set (key, g_strdup_printf ("%d", int_value));
120 }
121
122 int
ario_conf_get_integer(const char * key,const int default_value)123 ario_conf_get_integer (const char *key,
124 const int default_value)
125 {
126 ARIO_LOG_FUNCTION_START;
127 gchar *value = ario_conf_get (key);
128 int ret;
129
130 if (!value)
131 return default_value;
132
133 ret = atoi (value);
134
135 return ret;
136 }
137
138 void
ario_conf_set_float(const char * key,gfloat float_value)139 ario_conf_set_float (const char *key,
140 gfloat float_value)
141 {
142 ARIO_LOG_FUNCTION_START;
143 ario_conf_set (key, g_strdup_printf ("%f", float_value));
144 }
145
146 gfloat
ario_conf_get_float(const char * key,const gfloat default_value)147 ario_conf_get_float (const char *key,
148 const gfloat default_value)
149 {
150 ARIO_LOG_FUNCTION_START;
151 gchar *value = ario_conf_get (key);
152 gfloat ret;
153
154 if (!value)
155 return default_value;
156
157 ret = atof (value);
158 return ret;
159 }
160
161 void
ario_conf_set_string(const char * key,const char * string_value)162 ario_conf_set_string (const char *key,
163 const char *string_value)
164 {
165 ARIO_LOG_FUNCTION_START;
166 ario_conf_set (key, g_strdup (string_value));
167 }
168
169 const char *
ario_conf_get_string(const char * key,const char * default_value)170 ario_conf_get_string (const char *key,
171 const char *default_value)
172 {
173 ARIO_LOG_FUNCTION_START;
174 gchar *value = ario_conf_get (key);
175
176 if (!value)
177 return default_value;
178
179 return value;
180 }
181
182 void
ario_conf_set_string_slist(const char * key,const GSList * slist)183 ario_conf_set_string_slist (const char *key,
184 const GSList *slist)
185 {
186 ARIO_LOG_FUNCTION_START;
187 GString* value = NULL;
188 const GSList *tmp;
189 gboolean first = TRUE;
190
191 value = g_string_new("");
192
193 for (tmp = slist; tmp; tmp = g_slist_next (tmp)) {
194 if (!first) {
195 g_string_append (value, ",");
196 }
197 g_string_append (value, tmp->data);
198 first = FALSE;
199 }
200
201 ario_conf_set (key, value->str);
202
203 g_string_free (value, FALSE);
204 }
205
206 GSList *
ario_conf_get_string_slist(const char * key,const char * string_value)207 ario_conf_get_string_slist (const char *key,
208 const char *string_value)
209 {
210 ARIO_LOG_FUNCTION_START;
211 const gchar *value = ario_conf_get (key);
212 gchar **values;
213 GSList *ret = NULL;
214 int i;
215
216 if (!value)
217 value = string_value;
218
219 if (!value)
220 return NULL;
221
222 values = g_strsplit ((const gchar *) value, ",", 0);
223
224 for (i=0; values[i]!=NULL && g_utf8_collate (values[i], ""); ++i)
225 ret = g_slist_append (ret, values[i]);
226
227 g_free (values);
228
229 return ret;
230 }
231
232 static gboolean
ario_conf_save_foreach(gchar * key,gchar * value,xmlNodePtr root)233 ario_conf_save_foreach (gchar *key,
234 gchar *value,
235 xmlNodePtr root)
236 {
237 ARIO_LOG_FUNCTION_START;
238 xmlNodePtr cur;
239
240 /* We add a new "option" entry */
241 cur = xmlNewChild (root, NULL, (const xmlChar *) "option", NULL);
242 xmlSetProp (cur, (const xmlChar *) "key", (const xmlChar *) key);
243 xmlNodeAddContent (cur, (const xmlChar *) value);
244
245 return FALSE;
246 }
247
248 static gint
ario_conf_compare_keys(gconstpointer a,gconstpointer b)249 ario_conf_compare_keys (gconstpointer a,
250 gconstpointer b)
251 {
252 const xmlChar * str1 = (const xmlChar *) a;
253 const xmlChar * str2 = (const xmlChar *) b;
254 return xmlStrcmp (str1, str2);
255 }
256
257 static void
ario_conf_sorted_save_foreach(gchar * key,gchar * value,GTree * sorted_pairs)258 ario_conf_sorted_save_foreach (gchar *key,
259 gchar *value,
260 GTree *sorted_pairs)
261 {
262 g_tree_insert(sorted_pairs, key, value);
263 }
264
265 static gboolean
ario_conf_save(G_GNUC_UNUSED gpointer data)266 ario_conf_save (G_GNUC_UNUSED gpointer data)
267 {
268 ARIO_LOG_FUNCTION_START;
269 xmlNodePtr cur;
270 xmlDocPtr doc;
271 char *xml_filename;
272 GTree *sorted_pairs;
273
274 if (!modified)
275 return TRUE;
276 modified = FALSE;
277
278 doc = xmlNewDoc (XML_VERSION);
279 cur = xmlNewNode (NULL, (const xmlChar *) XML_ROOT_NAME);
280 xmlDocSetRootElement (doc, cur);
281
282 /* We sort the keys before saving to avoid changing the
283 configuration file if only the order changes */
284 sorted_pairs = g_tree_new(ario_conf_compare_keys);
285 g_hash_table_foreach (hash,
286 (GHFunc) ario_conf_sorted_save_foreach,
287 sorted_pairs);
288 g_tree_foreach (sorted_pairs,
289 (GTraverseFunc) ario_conf_save_foreach,
290 cur);
291 g_tree_destroy (sorted_pairs);
292
293 xml_filename = g_build_filename (ario_util_config_dir (), "options.xml", NULL);
294
295 /* We save the xml file */
296 xmlSaveFormatFile (xml_filename, doc, TRUE);
297
298 g_free (xml_filename);
299 xmlFreeDoc (doc);
300
301 return TRUE;
302 }
303
304 void
ario_conf_init(void)305 ario_conf_init (void)
306 {
307 ARIO_LOG_FUNCTION_START;
308 xmlNodePtr cur;
309 xmlDocPtr doc;
310 xmlChar *xml_key;
311 xmlChar *xml_value;
312 char *xml_filename;
313
314 xml_filename = g_build_filename (ario_util_config_dir (), "options.xml", NULL);
315
316 /* This option is necessary to save a well formated xml file */
317 xmlKeepBlanksDefault (0);
318
319 hash = g_hash_table_new_full (g_str_hash, g_str_equal,
320 g_free, g_free);
321
322 if (ario_util_uri_exists (xml_filename)) {
323 doc = xmlParseFile (xml_filename);
324 g_free (xml_filename);
325
326 cur = xmlDocGetRootElement(doc);
327 if (cur == NULL)
328 return;
329
330 for (cur = cur->children; cur; cur = cur->next) {
331 /* For each "option" entry */
332 if (!xmlStrcmp (cur->name, (const xmlChar *) "option")) {
333 xml_key = xmlGetProp (cur, (const unsigned char *) "key");
334 xml_value = xmlNodeGetContent (cur);
335 g_hash_table_insert (hash, xml_key, xml_value);
336 }
337 }
338
339 xmlFreeDoc (doc);
340 }
341
342 g_timeout_add (5*1000, (GSourceFunc) ario_conf_save, NULL);
343 }
344
345 void
ario_conf_shutdown(void)346 ario_conf_shutdown (void)
347 {
348 ARIO_LOG_FUNCTION_START;
349 ario_conf_save (NULL);
350 g_hash_table_remove_all (hash);
351 g_slist_foreach (notifications, (GFunc) ario_conf_free_notify_data, NULL);
352 }
353
354 guint
ario_conf_notification_add(const char * key,ArioNotifyFunc notification_callback,gpointer callback_data)355 ario_conf_notification_add (const char *key,
356 ArioNotifyFunc notification_callback,
357 gpointer callback_data)
358 {
359 ARIO_LOG_FUNCTION_START;
360 ArioConfNotifyData *data = (ArioConfNotifyData *) g_malloc0 (sizeof (ArioConfNotifyData));
361 ++notification_counter;
362
363 data->notification_id = notification_counter;
364 data->key = g_strdup (key);
365 data->notification_callback = notification_callback;
366 data->callback_data = callback_data;
367
368 notifications = g_slist_append (notifications, data);
369
370 return notification_counter;
371 }
372
373 void
ario_conf_notification_remove(guint notification_id)374 ario_conf_notification_remove (guint notification_id)
375 {
376 ARIO_LOG_FUNCTION_START;
377 GSList *tmp;
378 ArioConfNotifyData *data;
379
380 for (tmp = notifications; tmp; tmp = g_slist_next (tmp)) {
381 data = tmp->data;
382 if (data->notification_id == notification_id) {
383 notifications = g_slist_remove (notifications, data);
384 ario_conf_free_notify_data (data);
385 }
386 }
387 }
388
389