No issues found
1 /*
2 * Copyright (C) 2009 Paul Bellamy <paul.a.bellamy@gmail.com>
3 * Copyright (C) 2010 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 #include "config.h"
30
31 #include <glib/gi18n.h>
32
33 #include "rb-sync-settings-ui.h"
34 #include "rb-debug.h"
35 #include "rb-util.h"
36 #include "rb-playlist-manager.h"
37 #include "rb-podcast-entry-types.h"
38
39 struct _RBSyncSettingsUIPrivate
40 {
41 RBMediaPlayerSource *source;
42 RBSyncSettings *sync_settings;
43
44 GtkTreeStore *sync_tree_store;
45 };
46
47 enum {
48 PROP_0,
49 PROP_SOURCE,
50 PROP_SYNC_SETTINGS
51 };
52
53 G_DEFINE_TYPE (RBSyncSettingsUI, rb_sync_settings_ui, GTK_TYPE_VBOX)
54
55
56 static void
57 set_treeview_children (RBSyncSettingsUI *ui,
58 GtkTreeIter *parent,
59 const char *category,
60 gboolean value,
61 gboolean apply_to_settings)
62 {
63 GtkTreeIter iter;
64 char *group;
65 gboolean valid;
66
67 valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (ui->priv->sync_tree_store), &iter, parent);
68
69 while (valid) {
70 gtk_tree_model_get (GTK_TREE_MODEL (ui->priv->sync_tree_store), &iter,
71 2, &group,
72 -1);
73
74 if (apply_to_settings) {
75 rb_sync_settings_set_group (ui->priv->sync_settings, category, group, value);
76 }
77 gtk_tree_store_set (ui->priv->sync_tree_store, &iter,
78 /* Active */ 0, value,
79 -1);
80
81 g_free (group);
82 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ui->priv->sync_tree_store), &iter);
83 }
84 }
85
86 static void
87 sync_entries_changed_cb (GtkCellRendererToggle *cell_renderer,
88 gchar *path,
89 RBSyncSettingsUI *ui)
90 {
91 GtkTreeIter iter;
92 char *group;
93 char *category_name;
94 gboolean is_category;
95 gboolean value;
96
97 if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ui->priv->sync_tree_store), &iter, path) == FALSE) {
98 return;
99 }
100
101 gtk_tree_model_get (GTK_TREE_MODEL (ui->priv->sync_tree_store),
102 &iter,
103 2, &group,
104 4, &is_category,
105 5, &category_name,
106 -1);
107
108 value = !gtk_cell_renderer_toggle_get_active (cell_renderer);
109
110 if (is_category) {
111 rb_debug ("state for category %s changed to %d", category_name, value);
112 rb_sync_settings_set_category (ui->priv->sync_settings, category_name, value);
113 rb_sync_settings_clear_groups (ui->priv->sync_settings, category_name);
114
115 gtk_tree_store_set (ui->priv->sync_tree_store,
116 &iter,
117 0, value,
118 1, FALSE, /* category is no longer inconsistent */
119 -1);
120 set_treeview_children (ui, &iter, category_name, value, FALSE);
121 } else {
122 gboolean parent_value;
123 gboolean parent_inconsistent;
124 GtkTreeIter parent;
125 rb_debug ("state for group %s in category %s changed to %d", group, category_name, value);
126
127 /* update parent state. if the parent was previously enabled or disabled, then we
128 * set all the other groups to that state before setting the row that was just changed,
129 * and set the parent to inconsistent state.
130 */
131 gtk_tree_model_iter_parent (GTK_TREE_MODEL (ui->priv->sync_tree_store), &parent, &iter);
132 gtk_tree_model_get (GTK_TREE_MODEL (ui->priv->sync_tree_store), &parent,
133 0, &parent_value,
134 1, &parent_inconsistent,
135 -1);
136 if (parent_inconsistent == FALSE) {
137 /* category is now inconsistent */
138 rb_debug ("setting category %s to disabled, inconsistent", category_name);
139 rb_sync_settings_set_category (ui->priv->sync_settings, category_name, FALSE);
140 gtk_tree_store_set (ui->priv->sync_tree_store,
141 &parent,
142 0, FALSE,
143 1, TRUE,
144 -1);
145
146 /* set all groups in the category to the parent's state */
147 set_treeview_children (ui, &parent, category_name, parent_value, TRUE);
148 }
149
150 rb_sync_settings_set_group (ui->priv->sync_settings, category_name, group, value);
151 gtk_tree_store_set (ui->priv->sync_tree_store, &iter,
152 0, value,
153 -1);
154
155 /* if no groups are enabled, the category is no longer inconsistent */
156 if (value == FALSE && rb_sync_settings_has_enabled_groups (ui->priv->sync_settings, category_name) == FALSE) {
157 rb_debug ("no enabled groups left in category %s", category_name);
158 gtk_tree_store_set (ui->priv->sync_tree_store, &parent,
159 1, FALSE,
160 -1);
161 } else if (value == FALSE) {
162 rb_debug ("category %s still has some groups", category_name);
163 }
164 }
165
166 g_free (category_name);
167 g_free (group);
168 }
169
170
171 GtkWidget *
172 rb_sync_settings_ui_new (RBMediaPlayerSource *source, RBSyncSettings *settings)
173 {
174 GObject *ui;
175 ui = g_object_new (RB_TYPE_SYNC_SETTINGS_UI,
176 "source", source,
177 "sync-settings", settings,
178 NULL);
179 return GTK_WIDGET (ui);
180 }
181
182 static void
183 rb_sync_settings_ui_init (RBSyncSettingsUI *ui)
184 {
185 ui->priv = G_TYPE_INSTANCE_GET_PRIVATE (ui, RB_TYPE_SYNC_SETTINGS_UI, RBSyncSettingsUIPrivate);
186 }
187
188 static void
189 impl_constructed (GObject *object)
190 {
191 RBSyncSettingsUI *ui = RB_SYNC_SETTINGS_UI (object);
192 GtkTreeIter tree_iter;
193 GtkTreeIter parent_iter;
194 GtkTreeModel *query_model;
195 GtkCellRenderer *renderer;
196 GtkTreeViewColumn *col;
197 GtkWidget *tree_view;
198 GList *l;
199 GList *playlists;
200 RBShell *shell;
201 RhythmDB *db;
202 RBPlaylistManager *playlist_manager;
203 gboolean valid;
204
205 g_object_get (ui->priv->source, "shell", &shell, NULL);
206 g_object_get (shell, "db", &db, "playlist-manager", &playlist_manager, NULL);
207
208 /* tree_store columns are: active, inconsistent, name, display-name, is-category, category name */
209 ui->priv->sync_tree_store = gtk_tree_store_new (6, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING);
210
211 /* music library parent */
212 gtk_tree_store_append (ui->priv->sync_tree_store, &parent_iter, NULL);
213 gtk_tree_store_set (ui->priv->sync_tree_store, &parent_iter,
214 0, rb_sync_settings_sync_category (ui->priv->sync_settings, SYNC_CATEGORY_MUSIC),
215 1, rb_sync_settings_has_enabled_groups (ui->priv->sync_settings, SYNC_CATEGORY_MUSIC),
216 2, _("Music"),
217 3, _("Music"),
218 4, TRUE,
219 5, SYNC_CATEGORY_MUSIC,
220 -1);
221
222 /* 'all music' entry */
223 gtk_tree_store_append (ui->priv->sync_tree_store, &tree_iter, &parent_iter);
224 gtk_tree_store_set (ui->priv->sync_tree_store, &tree_iter,
225 0, rb_sync_settings_sync_group (ui->priv->sync_settings, SYNC_CATEGORY_MUSIC, SYNC_GROUP_ALL_MUSIC),
226 1, FALSE,
227 2, SYNC_GROUP_ALL_MUSIC,
228 3, _("All Music"),
229 4, FALSE,
230 5, SYNC_CATEGORY_MUSIC,
231 -1);
232
233 /* playlist entries */
234 playlists = rb_playlist_manager_get_playlists (playlist_manager);
235 for (l = playlists; l != NULL; l = l->next) {
236 char *name;
237
238 gtk_tree_store_append (ui->priv->sync_tree_store, &tree_iter, &parent_iter);
239 /* set playlists data here */
240 g_object_get (l->data, "name", &name, NULL);
241
242 /* set this row's data */
243 gtk_tree_store_set (ui->priv->sync_tree_store, &tree_iter,
244 0, rb_sync_settings_sync_group (ui->priv->sync_settings, SYNC_CATEGORY_MUSIC, name),
245 1, FALSE,
246 2, name,
247 3, name,
248 4, FALSE,
249 5, SYNC_CATEGORY_MUSIC,
250 -1);
251
252 g_free (name);
253 }
254
255 /* Append the Podcasts parent */
256 gtk_tree_store_append (ui->priv->sync_tree_store,
257 &parent_iter,
258 NULL);
259 gtk_tree_store_set (ui->priv->sync_tree_store, &parent_iter,
260 0, rb_sync_settings_sync_category (ui->priv->sync_settings, SYNC_CATEGORY_PODCAST),
261 1, rb_sync_settings_has_enabled_groups (ui->priv->sync_settings, SYNC_CATEGORY_PODCAST),
262 2, _("Podcasts"),
263 3, _("Podcasts"),
264 4, TRUE,
265 5, SYNC_CATEGORY_PODCAST,
266 -1);
267
268 /* this really needs to use a live query model */
269 query_model = GTK_TREE_MODEL (rhythmdb_query_model_new_empty (db));
270 rhythmdb_query_model_set_sort_order (RHYTHMDB_QUERY_MODEL (query_model),
271 (GCompareDataFunc) rhythmdb_query_model_title_sort_func,
272 NULL, NULL, FALSE);
273 rhythmdb_do_full_query (db, RHYTHMDB_QUERY_RESULTS (query_model),
274 RHYTHMDB_QUERY_PROP_EQUALS,
275 RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED,
276 RHYTHMDB_QUERY_END);
277 valid = gtk_tree_model_get_iter_first (query_model, &tree_iter);
278 while (valid) {
279 RhythmDBEntry *entry;
280 GtkTreeIter tree_iter2;
281 const char *name;
282 const char *feed;
283
284 entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (query_model), &tree_iter);
285 gtk_tree_store_append (ui->priv->sync_tree_store, &tree_iter2, &parent_iter);
286
287 /* set up this row */
288 name = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
289 feed = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
290 rb_debug ("adding feed %s (name %s)", feed, name);
291 gtk_tree_store_set (ui->priv->sync_tree_store, &tree_iter2,
292 0, rb_sync_settings_sync_group (ui->priv->sync_settings, SYNC_CATEGORY_PODCAST, feed),
293 1, FALSE,
294 2, feed,
295 3, name,
296 4, FALSE,
297 5, SYNC_CATEGORY_PODCAST,
298 -1);
299
300 valid = gtk_tree_model_iter_next (query_model, &tree_iter);
301 }
302
303 /* Set up the treeview */
304 tree_view = gtk_tree_view_new ();
305 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
306 gtk_box_pack_start (GTK_BOX (ui), tree_view, TRUE, TRUE, 0);
307
308 /* First column */
309 renderer = gtk_cell_renderer_toggle_new ();
310 col = gtk_tree_view_column_new_with_attributes (NULL,
311 renderer,
312 "active", 0,
313 "inconsistent", 1,
314 NULL);
315 g_signal_connect (G_OBJECT (renderer),
316 "toggled", G_CALLBACK (sync_entries_changed_cb),
317 ui);
318 gtk_tree_view_append_column(GTK_TREE_VIEW (tree_view), col);
319
320 /* Second column */
321 renderer = gtk_cell_renderer_text_new ();
322 col = gtk_tree_view_column_new_with_attributes (NULL,
323 renderer,
324 "text", 3,
325 NULL);
326 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
327 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view),
328 GTK_TREE_MODEL (ui->priv->sync_tree_store));
329 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)),
330 GTK_SELECTION_NONE);
331
332 g_object_unref (playlist_manager);
333 g_object_unref (shell);
334 g_object_unref (db);
335
336 gtk_widget_show_all (GTK_WIDGET (ui));
337
338 RB_CHAIN_GOBJECT_METHOD(rb_sync_settings_ui_parent_class, constructed, object);
339 }
340
341
342 static void
343 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
344 {
345 RBSyncSettingsUI *ui = RB_SYNC_SETTINGS_UI (object);
346 switch (prop_id) {
347 case PROP_SOURCE:
348 ui->priv->source = g_value_get_object (value);
349 break;
350 case PROP_SYNC_SETTINGS:
351 ui->priv->sync_settings = g_value_get_object (value);
352 break;
353 default:
354 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
355 break;
356 }
357 }
358
359 static void
360 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
361 {
362 RBSyncSettingsUI *ui = RB_SYNC_SETTINGS_UI (object);
363 switch (prop_id) {
364 case PROP_SOURCE:
365 g_value_set_object (value, ui->priv->source);
366 break;
367 case PROP_SYNC_SETTINGS:
368 g_value_set_object (value, ui->priv->sync_settings);
369 break;
370 default:
371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372 break;
373 }
374 }
375
376 static void
377 impl_dispose (GObject *object)
378 {
379 RBSyncSettingsUI *ui = RB_SYNC_SETTINGS_UI (object);
380
381 if (ui->priv->sync_tree_store) {
382 g_object_unref (ui->priv->sync_tree_store);
383 ui->priv->sync_tree_store = NULL;
384 }
385
386 G_OBJECT_CLASS (rb_sync_settings_ui_parent_class)->dispose (object);
387 }
388
389 static void
390 rb_sync_settings_ui_class_init (RBSyncSettingsUIClass *klass)
391 {
392 GObjectClass *object_class = G_OBJECT_CLASS (klass);
393
394 object_class->dispose = impl_dispose;
395 object_class->constructed = impl_constructed;
396
397 object_class->set_property = impl_set_property;
398 object_class->get_property = impl_get_property;
399
400 g_object_class_install_property (object_class,
401 PROP_SOURCE,
402 g_param_spec_object ("source",
403 "source",
404 "RBMediaPlayerSource instance",
405 RB_TYPE_MEDIA_PLAYER_SOURCE,
406 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
407 g_object_class_install_property (object_class,
408 PROP_SYNC_SETTINGS,
409 g_param_spec_object ("sync-settings",
410 "sync settings",
411 "RBSyncSettings instance",
412 RB_TYPE_SYNC_SETTINGS,
413 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
414
415 g_type_class_add_private (object_class, sizeof (RBSyncSettingsUIPrivate));
416 }