No issues found
1 /*
2 * Copyright (C) 2009 Paul Bellamy <paul.a.bellamy@gmail.com>
3 * Copyright (C) 2009 Jonathan Matthew <jonathan@d14n.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 /*
30 * sync settings consist of categories and groups within those categories.
31 * categories are things like music and podcasts, groups are things like
32 * playlists and individual podcast feeds. if sync for a category is enabled,
33 * then all groups within the category will be synced.
34 *
35 * at some point we probably need to have more than just enabled/disabled settings
36 * at all levels.. things like the number of episodes for a podcast feed to keep
37 * on the device.
38 *
39 * categories are stored as groups in the keyfile, where the keyfile group name
40 * matches the category name. if the category as a whole is enabled, the
41 * 'enabled' key will be set in the keyfile group. the list of groups enabled
42 * within the category is stored in the 'groups' key in the keyfile group.
43 *
44 * if any settings exist for a sync group, they will be stored in a keyfile group
45 * named <category>:<sync-group>. there is no way to set any such settings at
46 * present.
47 */
48
49 #include "config.h"
50
51 #include <gio/gio.h>
52 #include <string.h>
53
54 #include "rb-sync-settings.h"
55 #include "rb-debug.h"
56 #include "rb-util.h"
57
58 #define CATEGORY_ENABLED_KEY "enabled"
59 #define CATEGORY_ALL_GROUPS_KEY "all-groups"
60 #define CATEGORY_GROUPS_KEY "groups"
61
62 typedef struct {
63 GKeyFile *key_file;
64 char *key_file_path;
65
66 guint save_key_file_id;
67 } RBSyncSettingsPrivate;
68
69 enum {
70 PROP_0,
71 PROP_KEYFILE_PATH
72 };
73
74 enum {
75 UPDATED,
76 LAST_SIGNAL
77 };
78
79 static guint signals[LAST_SIGNAL] = { 0 };
80
81 G_DEFINE_TYPE (RBSyncSettings, rb_sync_settings, G_TYPE_OBJECT)
82
83 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SYNC_SETTINGS, RBSyncSettingsPrivate))
84
85 RBSyncSettings *
86 rb_sync_settings_new (const char *keyfile)
87 {
88 GObject *settings;
89 settings = g_object_new (RB_TYPE_SYNC_SETTINGS,
90 "keyfile-path", keyfile,
91 NULL);
92 return RB_SYNC_SETTINGS (settings);
93 }
94
95 gboolean
96 rb_sync_settings_save (RBSyncSettings *settings)
97 {
98 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
99 char *data;
100 gsize length;
101 GError *error = NULL;
102 GFile *file;
103
104 data = g_key_file_to_data (priv->key_file, &length, &error);
105 if (error != NULL) {
106 rb_debug ("unable to save sync settings: %s", error->message);
107 g_error_free (error);
108 return FALSE;
109 }
110
111 file = g_file_new_for_path (priv->key_file_path);
112 g_file_replace_contents (file, data, length, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
113 if (error != NULL) {
114 rb_debug ("unable to save sync settings: %s", error->message);
115 g_error_free (error);
116 }
117 g_object_unref (file);
118 g_free (data);
119 return (error == NULL);
120 }
121
122 static gboolean
123 _save_idle_cb (RBSyncSettings *settings)
124 {
125 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
126 priv->save_key_file_id = 0;
127 rb_sync_settings_save (settings);
128
129 g_signal_emit (settings, signals[UPDATED], 0);
130 return FALSE;
131 }
132
133 static void
134 _save_idle (RBSyncSettings *settings)
135 {
136 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
137 if (priv->save_key_file_id == 0) {
138 priv->save_key_file_id = g_idle_add ((GSourceFunc) _save_idle_cb, settings);
139 }
140 }
141
142 static gboolean
143 _get_boolean_with_default (GKeyFile *keyfile, const char *group, const char *key, gboolean default_value)
144 {
145 GError *error = NULL;
146 gboolean v;
147 v = g_key_file_get_boolean (keyfile, group, key, &error);
148 if (error != NULL) {
149 g_error_free (error);
150 return default_value;
151 }
152 return v;
153 }
154
155 void
156 rb_sync_settings_set_category (RBSyncSettings *settings,
157 const char *category,
158 gboolean enabled)
159 {
160 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
161 g_key_file_set_boolean (priv->key_file, category, CATEGORY_ENABLED_KEY, enabled);
162 _save_idle (settings);
163 }
164
165 gboolean
166 rb_sync_settings_sync_category (RBSyncSettings *settings,
167 const char *category)
168 {
169 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
170 return _get_boolean_with_default (priv->key_file, category, CATEGORY_ENABLED_KEY, FALSE);
171 }
172
173 GList *
174 rb_sync_settings_get_enabled_categories (RBSyncSettings *settings)
175 {
176 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
177 char **groups;
178 GList *categories;
179 int i;
180
181 categories = NULL;
182 groups = g_key_file_get_groups (priv->key_file, NULL);
183 for (i = 0; groups[i] != NULL; i++) {
184 /* filter out group entries */
185 if (g_utf8_strchr (groups[i], -1, ':') != NULL) {
186 continue;
187 }
188
189 categories = g_list_prepend (categories, g_strdup (groups[i]));
190 }
191 g_strfreev (groups);
192 return g_list_reverse (categories);
193 }
194
195 void
196 rb_sync_settings_set_group (RBSyncSettings *settings,
197 const char *category,
198 const char *group,
199 gboolean enabled)
200 {
201 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
202 char **groups;
203 int ngroups;
204
205 ngroups = 0;
206 groups = g_key_file_get_string_list (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL, NULL);
207
208 if (groups != NULL) {
209 int i;
210 ngroups = g_strv_length (groups);
211
212 for (i = 0; i < ngroups; i++) {
213 if (strcmp (groups[i], group) == 0) {
214 if (enabled) {
215 return;
216 } else {
217 char *t;
218 t = groups[i];
219 groups[i] = groups[ngroups-1];
220 groups[ngroups-1] = t;
221 ngroups--;
222 }
223 }
224 }
225 }
226
227 if (enabled) {
228 groups = g_realloc (groups, (ngroups+2) * sizeof(char *));
229 if (ngroups > 0 && groups[ngroups] != NULL) {
230 g_free (groups[ngroups]);
231 }
232 groups[ngroups] = g_strdup (group);
233 groups[ngroups+1] = NULL;
234 ngroups++;
235 }
236
237 if (ngroups == 0) {
238 g_key_file_remove_key (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL);
239 } else {
240 g_key_file_set_string_list (priv->key_file, category, CATEGORY_GROUPS_KEY, (const char * const *)groups, ngroups);
241 }
242 g_strfreev (groups);
243
244 _save_idle (settings);
245 }
246
247 gboolean
248 rb_sync_settings_group_enabled (RBSyncSettings *settings,
249 const char *category,
250 const char *group)
251 {
252 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
253 char **groups;
254 int i;
255 gboolean found = FALSE;
256
257 groups = g_key_file_get_string_list (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL, NULL);
258 if (groups == NULL) {
259 return FALSE;
260 }
261
262 for (i = 0; groups[i] != NULL; i++) {
263 if (strcmp (groups[i], group) == 0) {
264 found = TRUE;
265 break;
266 }
267 }
268
269 g_strfreev (groups);
270 return found;
271 }
272
273 gboolean
274 rb_sync_settings_sync_group (RBSyncSettings *settings,
275 const char *category,
276 const char *group)
277 {
278 if (rb_sync_settings_sync_category (settings, category) == TRUE) {
279 return TRUE;
280 }
281 return rb_sync_settings_group_enabled (settings, category, group);
282 }
283
284 gboolean
285 rb_sync_settings_has_enabled_groups (RBSyncSettings *settings,
286 const char *category)
287 {
288 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
289 char **groups;
290
291 groups = g_key_file_get_string_list (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL, NULL);
292 if (groups == NULL) {
293 return FALSE;
294 }
295
296 g_strfreev (groups);
297 return TRUE;
298 }
299
300 GList *
301 rb_sync_settings_get_enabled_groups (RBSyncSettings *settings,
302 const char *category)
303 {
304 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
305 char **groups;
306 GList *glist = NULL;
307 int i;
308
309 groups = g_key_file_get_string_list (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL, NULL);
310 if (groups == NULL) {
311 return NULL;
312 }
313
314 for (i = 0; groups[i] != NULL; i++) {
315 glist = g_list_prepend (glist, g_strdup (groups[i]));
316 }
317
318 g_strfreev (groups);
319 return g_list_reverse (glist);
320 }
321
322 void
323 rb_sync_settings_clear_groups (RBSyncSettings *settings, const char *category)
324 {
325 RBSyncSettingsPrivate *priv = GET_PRIVATE (settings);
326 g_key_file_remove_key (priv->key_file, category, CATEGORY_GROUPS_KEY, NULL);
327 _save_idle (settings);
328 }
329
330
331
332 static void
333 rb_sync_settings_init (RBSyncSettings *settings)
334 {
335 /* nothing */
336 }
337
338 static void
339 impl_constructed (GObject *object)
340 {
341 RBSyncSettingsPrivate *priv = GET_PRIVATE (object);
342 GError *error = NULL;
343
344 priv->key_file = g_key_file_new ();
345 if (g_key_file_load_from_file (priv->key_file,
346 priv->key_file_path,
347 G_KEY_FILE_KEEP_COMMENTS,
348 &error) == FALSE) {
349 rb_debug ("unable to load sync settings from %s: %s", priv->key_file_path, error->message);
350 g_error_free (error);
351
352 /* probably need a way to set defaults.. syncing nothing by default
353 * is kind of boring. used to default to syncing all music and all
354 * podcasts.
355 */
356 }
357
358 RB_CHAIN_GOBJECT_METHOD(rb_sync_settings_parent_class, constructed, object);
359 }
360
361 static void
362 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
363 {
364 RBSyncSettingsPrivate *priv = GET_PRIVATE (object);
365 switch (prop_id) {
366 case PROP_KEYFILE_PATH:
367 priv->key_file_path = g_value_dup_string (value);
368 break;
369 default:
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 break;
372 }
373 }
374
375 static void
376 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
377 {
378 RBSyncSettingsPrivate *priv = GET_PRIVATE (object);
379 switch (prop_id) {
380 case PROP_KEYFILE_PATH:
381 g_value_set_string (value, priv->key_file_path);
382 break;
383 default:
384 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
385 break;
386 }
387 }
388
389 static void
390 impl_dispose (GObject *object)
391 {
392 RBSyncSettingsPrivate *priv = GET_PRIVATE (object);
393
394 /* if a save is pending, do it now */
395 if (priv->save_key_file_id != 0) {
396 g_source_remove (priv->save_key_file_id);
397 priv->save_key_file_id = 0;
398 rb_sync_settings_save (RB_SYNC_SETTINGS (object));
399 }
400
401 G_OBJECT_CLASS (rb_sync_settings_parent_class)->dispose (object);
402 }
403
404 static void
405 impl_finalize (GObject *object)
406 {
407 RBSyncSettingsPrivate *priv = GET_PRIVATE (object);
408
409 g_key_file_free (priv->key_file);
410 g_free (priv->key_file_path);
411
412 G_OBJECT_CLASS (rb_sync_settings_parent_class)->finalize (object);
413 }
414
415 static void
416 rb_sync_settings_class_init (RBSyncSettingsClass *klass)
417 {
418 GObjectClass *object_class = G_OBJECT_CLASS (klass);
419
420 object_class->finalize = impl_finalize;
421 object_class->dispose = impl_dispose;
422 object_class->constructed = impl_constructed;
423
424 object_class->set_property = impl_set_property;
425 object_class->get_property = impl_get_property;
426
427 g_object_class_install_property (object_class,
428 PROP_KEYFILE_PATH,
429 g_param_spec_string ("keyfile-path",
430 "keyfile path",
431 "path to the key file storing the sync settings",
432 NULL,
433 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
434 signals[UPDATED] = g_signal_new ("updated",
435 RB_TYPE_SYNC_SETTINGS,
436 G_SIGNAL_RUN_LAST,
437 G_STRUCT_OFFSET (RBSyncSettingsClass, updated),
438 NULL, NULL,
439 g_cclosure_marshal_VOID__VOID,
440 G_TYPE_NONE,
441 0);
442 g_type_class_add_private (object_class, sizeof (RBSyncSettingsPrivate));
443 }