No issues found
1 /*
2 * Copyright (C) 2007 James Livingston <doclivingston@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 of the License, or
7 * (at your option) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26 #include <gpod/itdb.h>
27
28 #include "rb-debug.h"
29 #include "rb-util.h"
30 #include "rhythmdb.h"
31
32 #include "rb-ipod-static-playlist-source.h"
33 #include "rb-media-player-source.h"
34 #include "rb-ipod-source.h"
35
36 static void rb_ipod_static_playlist_source_constructed (GObject *object);
37 static void rb_ipod_static_playlist_source_dispose (GObject *object);
38 static void rb_ipod_static_playlist_source_set_property (GObject *object,
39 guint prop_id,
40 const GValue *value,
41 GParamSpec *pspec);
42 static void rb_ipod_static_playlist_source_get_property (GObject *object,
43 guint prop_id,
44 GValue *value,
45 GParamSpec *pspec);
46
47 static gboolean impl_show_popup (RBDisplayPage *page);
48
49 typedef struct
50 {
51 RbIpodDb *ipod_db;
52 Itdb_Playlist *itdb_playlist;
53 RBiPodSource *ipod_source;
54 gboolean was_reordered;
55 } RBIpodStaticPlaylistSourcePrivate;
56
57 G_DEFINE_DYNAMIC_TYPE(RBIpodStaticPlaylistSource, rb_ipod_static_playlist_source, RB_TYPE_STATIC_PLAYLIST_SOURCE)
58
59 #define IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_STATIC_PLAYLIST_SOURCE, RBIpodStaticPlaylistSourcePrivate))
60
61 enum {
62 PROP_0,
63 PROP_IPOD_SOURCE,
64 PROP_IPOD_DB,
65 PROP_ITDB_PLAYLIST
66 };
67
68 static void
69 playlist_track_removed (RhythmDBQueryModel *m,
70 RhythmDBEntry *entry,
71 gpointer data)
72 {
73 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (data);
74 Itdb_Track *track;
75
76 track = rb_ipod_source_lookup_track (priv->ipod_source, entry);
77 g_return_if_fail (track != NULL);
78 rb_ipod_db_remove_from_playlist (priv->ipod_db, priv->itdb_playlist, track);
79 }
80
81 static void
82 playlist_track_added (GtkTreeModel *model,
83 GtkTreePath *path,
84 GtkTreeIter *iter,
85 gpointer data)
86 {
87 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (data);
88 Itdb_Track *track;
89 RhythmDBEntry *entry;
90
91 gtk_tree_model_get (model, iter, 0, &entry, -1);
92 track = rb_ipod_source_lookup_track (priv->ipod_source, entry);
93 g_return_if_fail (track != NULL);
94
95 rb_ipod_db_add_to_playlist (priv->ipod_db, priv->itdb_playlist, track);
96 }
97
98 static void
99 playlist_before_save (RbIpodDb *ipod_db, gpointer data)
100 {
101 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (data);
102 RhythmDBQueryModel *model;
103 GtkTreeIter iter;
104
105 if (priv->was_reordered == FALSE)
106 return;
107 priv->was_reordered = FALSE;
108
109 /* Sanity check that all tracks are in entry_map */
110
111 g_object_get (G_OBJECT (data), "base-query-model", &model, NULL);
112 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) {
113 do {
114 RhythmDBEntry *entry;
115 Itdb_Track *track;
116
117 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &entry, -1);
118 track = rb_ipod_source_lookup_track (priv->ipod_source, entry);
119
120 g_return_if_fail (track != NULL);
121 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter));
122 }
123
124 /* Remove all tracks then re-add in correct order */
125
126 while (priv->itdb_playlist->members != NULL) {
127 Itdb_Track *track;
128
129 track = (Itdb_Track *)priv->itdb_playlist->members->data;
130
131 rb_debug ("removing \"%s\" from \"%s\"", track->title, priv->itdb_playlist->name);
132
133 /* Call directly to itdb to avoid scheduling another save */
134 itdb_playlist_remove_track (priv->itdb_playlist, track);
135 }
136
137 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) {
138 do {
139 RhythmDBEntry *entry;
140 Itdb_Track *track;
141
142 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &entry, -1);
143 track = rb_ipod_source_lookup_track (priv->ipod_source, entry);
144
145 rb_debug ("adding \"%s\" to \"%s\"", track->title, priv->itdb_playlist->name);
146
147 /* Call directly to itdb to avoid scheduling another save */
148 itdb_playlist_add_track (priv->itdb_playlist, track, -1);
149 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter));
150 }
151
152 g_object_unref (model);
153 }
154
155 static void
156 playlist_rows_reordered (GtkTreeModel *model,
157 GtkTreePath *path,
158 GtkTreeIter *iter,
159 gint *order,
160 gpointer data)
161 {
162 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (data);
163 priv->was_reordered = TRUE;
164
165 rb_ipod_db_save_async (priv->ipod_db);
166 }
167
168 static void
169 playlist_source_model_disconnect_signals (RBIpodStaticPlaylistSource *source)
170 {
171 RhythmDBQueryModel *model;
172
173 g_return_if_fail (RB_IS_IPOD_STATIC_PLAYLIST_SOURCE (source));
174
175 g_object_get (source, "base-query-model", &model, NULL);
176
177 g_signal_handlers_disconnect_by_func (model,
178 G_CALLBACK (playlist_track_added),
179 source);
180 g_signal_handlers_disconnect_by_func (model,
181 G_CALLBACK (playlist_track_removed),
182 source);
183 g_signal_handlers_disconnect_by_func (model,
184 G_CALLBACK (playlist_rows_reordered),
185 source);
186
187 g_object_unref (model);
188 }
189
190 static void
191 playlist_source_model_connect_signals (RBIpodStaticPlaylistSource *playlist_source)
192 {
193 RhythmDBQueryModel *model;
194
195 g_return_if_fail (RB_IS_IPOD_STATIC_PLAYLIST_SOURCE (playlist_source));
196
197 g_object_get (playlist_source, "base-query-model", &model, NULL);
198 g_signal_connect (model, "row-inserted",
199 G_CALLBACK (playlist_track_added),
200 playlist_source);
201 g_signal_connect (model, "entry-removed",
202 G_CALLBACK (playlist_track_removed),
203 playlist_source);
204 g_signal_connect (model, "rows-reordered",
205 G_CALLBACK (playlist_rows_reordered),
206 playlist_source);
207 g_object_unref (model);
208 }
209
210 static void
211 source_name_changed_cb (RBIpodStaticPlaylistSource *source,
212 GParamSpec *spec,
213 gpointer data)
214 {
215 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
216 char *name;
217
218 g_object_get (source, "name", &name, NULL);
219
220 if ((priv->itdb_playlist != NULL) && (strcmp (name, priv->itdb_playlist->name) != 0)) {
221 rb_debug ("changing playlist name to %s", name);
222 rb_ipod_db_rename_playlist (priv->ipod_db, priv->itdb_playlist, name);
223 }
224 g_free (name);
225 }
226
227
228 static void
229 rb_ipod_static_playlist_source_class_init (RBIpodStaticPlaylistSourceClass *klass)
230 {
231 GObjectClass *object_class = G_OBJECT_CLASS (klass);
232 RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
233 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
234
235 object_class->constructed = rb_ipod_static_playlist_source_constructed;
236 object_class->dispose = rb_ipod_static_playlist_source_dispose;
237 object_class->get_property = rb_ipod_static_playlist_source_get_property;
238 object_class->set_property = rb_ipod_static_playlist_source_set_property;
239
240 page_class->show_popup = impl_show_popup;
241
242 source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
243 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
244
245 /* Don't override impl_delete here -- it's provided by RBStaticPlaylistSource */
246
247 g_object_class_install_property (object_class,
248 PROP_IPOD_SOURCE,
249 g_param_spec_object ("ipod-source",
250 "ipod-source",
251 "ipod-source",
252 RB_TYPE_IPOD_SOURCE,
253 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
254
255 g_object_class_install_property (object_class,
256 PROP_IPOD_DB,
257 g_param_spec_object ("ipod-db",
258 "ipod-db",
259 "ipod-db",
260 RB_TYPE_IPOD_DB,
261 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
262
263 g_object_class_install_property (object_class,
264 PROP_ITDB_PLAYLIST,
265 g_param_spec_pointer ("itdb-playlist",
266 "itdb-playlist",
267 "itdb-playlist",
268 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
269
270 g_type_class_add_private (klass, sizeof (RBIpodStaticPlaylistSourcePrivate));
271 }
272
273 static void
274 rb_ipod_static_playlist_source_class_finalize (RBIpodStaticPlaylistSourceClass *klass)
275 {
276 }
277
278 static void
279 rb_ipod_static_playlist_source_init (RBIpodStaticPlaylistSource *source)
280 {
281
282 }
283
284 static void
285 rb_ipod_static_playlist_source_constructed (GObject *object)
286 {
287 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
288 RhythmDBQueryModel *model;
289
290 RB_CHAIN_GOBJECT_METHOD (rb_ipod_static_playlist_source_parent_class, constructed, object);
291
292 g_signal_connect (object, "notify::name", (GCallback)source_name_changed_cb, NULL);
293
294 g_object_get (object, "base-query-model", &model, NULL);
295 g_signal_connect (priv->ipod_db, "before-save",
296 G_CALLBACK (playlist_before_save),
297 object);
298 g_object_unref (model);
299 playlist_source_model_connect_signals (RB_IPOD_STATIC_PLAYLIST_SOURCE (object));
300
301 }
302
303 static void
304 rb_ipod_static_playlist_source_dispose (GObject *object)
305 {
306 RBIpodStaticPlaylistSource *source = RB_IPOD_STATIC_PLAYLIST_SOURCE (object);
307 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
308
309 if (priv->ipod_source) {
310 g_object_unref (priv->ipod_source);
311 priv->ipod_source = NULL;
312 }
313 if (priv->ipod_db) {
314 g_signal_handlers_disconnect_by_func (priv->ipod_db,
315 G_CALLBACK (playlist_before_save),
316 source);
317
318 g_object_unref (priv->ipod_db);
319 priv->ipod_db = NULL;
320 }
321
322 playlist_source_model_disconnect_signals (source);
323
324 G_OBJECT_CLASS (rb_ipod_static_playlist_source_parent_class)->dispose (object);
325 }
326
327 RBIpodStaticPlaylistSource *
328 rb_ipod_static_playlist_source_new (RBShell *shell,
329 RBiPodSource *ipod_source,
330 RbIpodDb *ipod_db,
331 Itdb_Playlist *playlist,
332 RhythmDBEntryType *entry_type)
333 {
334 RBIpodStaticPlaylistSource *source;
335
336 g_assert (RB_IS_IPOD_SOURCE (ipod_source));
337
338 source = RB_IPOD_STATIC_PLAYLIST_SOURCE (g_object_new (RB_TYPE_IPOD_STATIC_PLAYLIST_SOURCE,
339 "entry-type", entry_type,
340 "shell", shell,
341 "is-local", FALSE,
342 "name", playlist->name,
343 "ipod-source", ipod_source,
344 "ipod-db", ipod_db,
345 "itdb-playlist", playlist,
346 NULL));
347
348 return source;
349 }
350
351
352 static void
353 rb_ipod_static_playlist_source_set_property (GObject *object,
354 guint prop_id,
355 const GValue *value,
356 GParamSpec *pspec)
357 {
358 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
359
360 switch (prop_id) {
361 case PROP_IPOD_SOURCE:
362 priv->ipod_source = g_value_dup_object (value);
363 break;
364 case PROP_IPOD_DB:
365 priv->ipod_db = g_value_dup_object (value);
366 break;
367 case PROP_ITDB_PLAYLIST:
368 priv->itdb_playlist = g_value_get_pointer (value);
369 break;
370 default:
371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372 break;
373 }
374 }
375
376 static void
377 rb_ipod_static_playlist_source_get_property (GObject *object,
378 guint prop_id,
379 GValue *value,
380 GParamSpec *pspec)
381 {
382 RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
383
384 switch (prop_id) {
385 case PROP_IPOD_SOURCE:
386 g_value_set_object (value, priv->ipod_source);
387 break;
388 case PROP_IPOD_DB:
389 g_value_set_object (value, priv->ipod_db);
390 break;
391 case PROP_ITDB_PLAYLIST:
392 g_value_set_pointer (value, priv->itdb_playlist);
393 break;
394 default:
395 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
396 break;
397 }
398 }
399
400 static gboolean
401 impl_show_popup (RBDisplayPage *page)
402 {
403 _rb_display_page_show_popup (page, "/iPodPlaylistSourcePopup");
404 return TRUE;
405 }
406
407 void
408 _rb_ipod_static_playlist_source_register_type (GTypeModule *module)
409 {
410 rb_ipod_static_playlist_source_register_type (module);
411 }