hythmbox-2.98/sources/sync/rb-sync-settings.c

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 }