No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | rb-playlist-manager.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2003,2004 Colin Walters <walters@gnome.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 * SECTION:rb-playlist-manager
31 * @short_description: Playlist management object
32 *
33 * The playlist manager loads and saves the on-disk playlist file, provides
34 * UI actions and a DBus interface for dealing with playlists, and internal
35 * interfaces for creating playlists.
36 */
37
38 #include "config.h"
39
40 #include <string.h>
41 #include <stdio.h> /* rename() */
42 #include <unistd.h> /* unlink() */
43
44 #include <libxml/tree.h>
45 #include <glib/gi18n.h>
46 #include <gtk/gtk.h>
47
48 #include "rb-playlist-manager.h"
49 #include "rb-playlist-source.h"
50 #include "rb-static-playlist-source.h"
51 #include "rb-auto-playlist-source.h"
52 #include "rb-play-queue-source.h"
53 #include "rb-query-creator.h"
54 #include "totem-pl-parser.h"
55
56 #include "rb-file-helpers.h"
57 #include "rb-debug.h"
58 #include "rb-dialog.h"
59 #include "rhythmdb.h"
60 #include "rb-stock-icons.h"
61 #include "rb-builder-helpers.h"
62 #include "rb-util.h"
63
64 #define RB_PLAYLIST_MGR_VERSION (xmlChar *) "1.0"
65 #define RB_PLAYLIST_MGR_PL (xmlChar *) "rhythmdb-playlists"
66
67 #define RB_PLAYLIST_MANAGER_IFACE_NAME "org.gnome.Rhythmbox3.PlaylistManager"
68 #define RB_PLAYLIST_MANAGER_DBUS_PATH "/org/gnome/Rhythmbox3/PlaylistManager"
69
70 static const char *rb_playlist_manager_dbus_spec =
71 "<node>"
72 " <interface name='org.gnome.Rhythmbox3.PlaylistManager'>"
73 " <method name='GetPlaylists'>"
74 " <arg type='as' direction='out'/>"
75 " </method>"
76 " <method name='CreatePlaylist'>"
77 " <arg type='s' name='name'/>"
78 " </method>"
79 " <method name='DeletePlaylist'>"
80 " <arg type='s' name='name'/>"
81 " </method>"
82 " <method name='AddToPlaylist'>"
83 " <arg type='s' name='playlist'/>"
84 " <arg type='s' name='uri'/>"
85 " </method>"
86 " <method name='RemoveFromPlaylist'>"
87 " <arg type='s' name='playlist'/>"
88 " <arg type='s' name='uri'/>"
89 " </method>"
90 " <method name='ExportPlaylist'>"
91 " <arg type='s' name='playlist'/>"
92 " <arg type='s' name='uri'/>"
93 " <arg type='b' name='mp3_format'/>"
94 " </method>"
95 " <method name='ImportPlaylist'>"
96 " <arg type='s' name='uri'/>"
97 " </method>"
98 " </interface>"
99 "</node>";
100
101 static void rb_playlist_manager_class_init (RBPlaylistManagerClass *klass);
102 static void rb_playlist_manager_init (RBPlaylistManager *mgr);
103 static void rb_playlist_manager_cmd_load_playlist (GtkAction *action,
104 RBPlaylistManager *mgr);
105 static void rb_playlist_manager_cmd_save_playlist (GtkAction *action,
106 RBPlaylistManager *mgr);
107 static void rb_playlist_manager_cmd_save_queue (GtkAction *action,
108 RBPlaylistManager *mgr);
109 static void rb_playlist_manager_cmd_new_playlist (GtkAction *action,
110 RBPlaylistManager *mgr);
111 static void rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
112 RBPlaylistManager *mgr);
113 static void rb_playlist_manager_cmd_shuffle_playlist (GtkAction *action,
114 RBPlaylistManager *mgr);
115 static void rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
116 RBPlaylistManager *mgr);
117 static void rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
118 RBPlaylistManager *mgr);
119 static void rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
120 RBPlaylistManager *mgr);
121 static void rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
122 RBPlaylistManager *mgr);
123
124 struct RBPlaylistManagerPrivate
125 {
126 RhythmDB *db;
127 RBShell *shell;
128 RBSource *selected_source;
129
130 char *playlists_file;
131
132 RBDisplayPageModel *page_model;
133 RBDisplayPageTree *display_page_tree;
134
135 GtkActionGroup *actiongroup;
136 GtkUIManager *uimanager;
137
138 RBStaticPlaylistSource *loading_playlist;
139
140 gint dirty;
141 gint saving;
142 GMutex saving_mutex;
143 };
144
145 enum
146 {
147 PROP_0,
148 PROP_PLAYLIST_NAME,
149 PROP_SHELL,
150 PROP_SOURCE,
151 PROP_DISPLAY_PAGE_MODEL,
152 PROP_DISPLAY_PAGE_TREE,
153 };
154
155 enum
156 {
157 PLAYLIST_ADDED,
158 PLAYLIST_CREATED,
159 PLAYLIST_LOAD_START,
160 PLAYLIST_LOAD_FINISH,
161 LAST_SIGNAL,
162 };
163
164 static guint rb_playlist_manager_signals[LAST_SIGNAL] = { 0 };
165
166 typedef struct {
167 const gchar *description;
168 /* NULL terminated array of extensions for this file format. The first
169 * one is the prefered extension for files of this type. */
170 const gchar **extensions;
171 const char *mimetype;
172 const RBPlaylistExportType type;
173 } RBPlaylistExportFilter;
174
175 static const char *m3u_extensions [] = {"m3u", NULL};
176 static const char *pls_extensions [] = {"pls", NULL};
177 static const char *xspf_extensions[] = {"xspf", NULL};
178
179 static RBPlaylistExportFilter playlist_formats[] = {
180 {N_("MPEG Version 3.0 URL"), m3u_extensions, "audio/x-mpegurl", RB_PLAYLIST_EXPORT_TYPE_M3U},
181 {N_("Shoutcast playlist"), pls_extensions, "audio/x-scpls", RB_PLAYLIST_EXPORT_TYPE_PLS},
182 {N_("XML Shareable Playlist Format"), xspf_extensions, "application/xspf+xml", RB_PLAYLIST_EXPORT_TYPE_XSPF},
183 };
184
185
186 static GtkActionEntry rb_playlist_manager_actions [] =
187 {
188 /* Submenu of Music */
189 { "Playlist", NULL, N_("_Playlist") },
190
191 { "MusicPlaylistNewPlaylist", RB_STOCK_PLAYLIST_NEW, N_("_New Playlist..."), "<control>N",
192 N_("Create a new playlist"),
193 G_CALLBACK (rb_playlist_manager_cmd_new_playlist) },
194 { "MusicPlaylistNewAutomaticPlaylist", RB_STOCK_AUTO_PLAYLIST_NEW, N_("New _Automatic Playlist..."), NULL,
195 N_("Create a new automatically updating playlist"),
196 G_CALLBACK (rb_playlist_manager_cmd_new_automatic_playlist) },
197 { "MusicPlaylistLoadPlaylist", NULL, N_("_Load from File..."), NULL,
198 N_("Choose a playlist to be loaded"),
199 G_CALLBACK (rb_playlist_manager_cmd_load_playlist) },
200 { "MusicPlaylistSavePlaylist", GTK_STOCK_SAVE_AS, N_("_Save to File..."), NULL,
201 N_("Save a playlist to a file"),
202 G_CALLBACK (rb_playlist_manager_cmd_save_playlist) },
203 { "MusicPlaylistRenamePlaylist", NULL, N_("_Rename"), NULL,
204 N_("Rename playlist"),
205 G_CALLBACK (rb_playlist_manager_cmd_rename_playlist) },
206 { "MusicPlaylistDeletePlaylist", GTK_STOCK_REMOVE, N_("_Delete"), NULL,
207 N_("Delete playlist"),
208 G_CALLBACK (rb_playlist_manager_cmd_delete_playlist) },
209 { "EditAutomaticPlaylist", GTK_STOCK_PROPERTIES, N_("_Edit..."), NULL,
210 N_("Change this automatic playlist"),
211 G_CALLBACK (rb_playlist_manager_cmd_edit_automatic_playlist) },
212 { "QueuePlaylist", NULL, N_("_Queue All Tracks"), NULL,
213 N_("Add all tracks in this playlist to the queue"),
214 G_CALLBACK (rb_playlist_manager_cmd_queue_playlist) },
215 { "ShufflePlaylist", NULL, N_("_Shuffle Playlist"), NULL,
216 N_("Shuffle the tracks in this playlist"),
217 G_CALLBACK (rb_playlist_manager_cmd_shuffle_playlist) },
218 { "MusicPlaylistSaveQueue", GTK_STOCK_SAVE_AS, N_("_Save to File..."), NULL,
219 N_("Save the play queue to a file"),
220 G_CALLBACK (rb_playlist_manager_cmd_save_queue) },
221 };
222 static guint rb_playlist_manager_n_actions = G_N_ELEMENTS (rb_playlist_manager_actions);
223
224 G_DEFINE_TYPE (RBPlaylistManager, rb_playlist_manager, G_TYPE_OBJECT)
225
226
227 /**
228 * rb_playlist_manager_shutdown:
229 * @mgr: the #RBPlaylistManager
230 *
231 * Shuts down the playlist manager, making sure any outstanding playlist save
232 * operation finishes.
233 */
234 void
235 rb_playlist_manager_shutdown (RBPlaylistManager *mgr)
236 {
237 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (mgr));
238
239 g_mutex_lock (&mgr->priv->saving_mutex);
240 g_mutex_unlock (&mgr->priv->saving_mutex);
241 }
242
243 /**
244 * rb_playlist_manager_new:
245 * @shell: the #RBShell
246 * @page_model: the #RBDisplayPageModel
247 * @page_tree: the #RBDisplayPageTree
248 * @playlists_file: the full path to the playlist file to load
249 *
250 * Creates the #RBPlaylistManager instance
251 *
252 * Return value: the #RBPlaylistManager
253 */
254 RBPlaylistManager *
255 rb_playlist_manager_new (RBShell *shell,
256 RBDisplayPageModel *page_model,
257 RBDisplayPageTree *page_tree,
258 const char *playlists_file)
259 {
260 return g_object_new (RB_TYPE_PLAYLIST_MANAGER,
261 "shell", shell,
262 "display-page-model", page_model,
263 "display-page-tree", page_tree,
264 "playlists_file", playlists_file,
265 NULL);
266 }
267
268 GQuark
269 rb_playlist_manager_error_quark (void)
270 {
271 static GQuark quark = 0;
272 if (!quark)
273 quark = g_quark_from_static_string ("rb_playlist_manager_error");
274
275 return quark;
276 }
277
278 static void
279 handle_playlist_entry_cb (TotemPlParser *playlist,
280 const char *uri_maybe,
281 GHashTable *metadata,
282 RBPlaylistManager *mgr)
283 {
284 char *uri;
285 const char *title, *genre;
286
287 title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
288 genre = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_GENRE);
289
290 uri = rb_canonicalise_uri (uri_maybe);
291 g_return_if_fail (uri != NULL);
292
293 rb_debug ("adding uri %s (title %s, genre %s) from playlist",
294 uri, title, genre);
295 if (!rb_shell_add_uri (mgr->priv->shell,
296 uri,
297 title,
298 genre,
299 NULL))
300 return;
301
302 if (!mgr->priv->loading_playlist) {
303 mgr->priv->loading_playlist =
304 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, NULL, FALSE));
305 }
306 if (rb_source_want_uri (RB_SOURCE (mgr->priv->loading_playlist), uri) > 0) {
307 rb_debug ("adding uri %s to playlist", uri);
308 rb_static_playlist_source_add_location (mgr->priv->loading_playlist, uri, -1);
309 }
310
311 g_free (uri);
312 }
313
314 static void
315 playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr)
316 {
317 const char *title;
318
319 rb_debug ("loading new playlist %s", uri);
320
321 title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
322 if (title == NULL)
323 title = _("Unnamed playlist");
324
325 mgr->priv->loading_playlist =
326 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
327 }
328
329 /**
330 * rb_playlist_manager_parse_file:
331 * @mgr: the #RBPlaylistManager
332 * @uri: URI of the playlist to load
333 * @error: returns a GError in case of error
334 *
335 * Parses a playlist file, adding entries to the database and to a new
336 * static playlist. If the playlist file includes a title, the static
337 * playlist created will have the same title.
338 *
339 * Return value: TRUE on success
340 **/
341 gboolean
342 rb_playlist_manager_parse_file (RBPlaylistManager *mgr, const char *uri, GError **error)
343 {
344 rb_debug ("loading playlist from %s", uri);
345
346 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_START], 0);
347
348 {
349 TotemPlParser *parser = totem_pl_parser_new ();
350
351 g_signal_connect_object (parser, "entry-parsed",
352 G_CALLBACK (handle_playlist_entry_cb),
353 mgr, 0);
354
355 g_signal_connect_object (parser, "playlist-started",
356 G_CALLBACK (playlist_load_started_cb),
357 mgr, 0);
358
359 g_object_set (parser, "recurse", FALSE, NULL);
360
361 if (totem_pl_parser_parse (parser, uri, TRUE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
362 g_set_error (error,
363 RB_PLAYLIST_MANAGER_ERROR,
364 RB_PLAYLIST_MANAGER_ERROR_PARSE,
365 "%s",
366 _("The playlist file may be in an unknown format or corrupted."));
367 return FALSE;
368 }
369
370 if (mgr->priv->loading_playlist != NULL) {
371 char *name = NULL;
372
373 /* totem-plparser may not have given us the playlist name */
374 g_object_get (mgr->priv->loading_playlist, "name", &name, NULL);
375 if (name == NULL || name[0] == '\0') {
376 char *path;
377
378 rb_debug ("setting playlist name from file name");
379 path = g_filename_from_uri (uri, NULL, NULL);
380 if (path) {
381 name = g_path_get_basename (path);
382 g_object_set (mgr->priv->loading_playlist, "name", name, NULL);
383 g_free (path);
384 }
385 }
386
387 g_free (name);
388 mgr->priv->loading_playlist = NULL;
389 }
390
391 g_object_unref (parser);
392 }
393
394 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH], 0);
395 return TRUE;
396 }
397
398 static void
399 append_new_playlist_source (RBPlaylistManager *mgr, RBPlaylistSource *source)
400 {
401 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_ADDED], 0,
402 source);
403 }
404
405 /**
406 * rb_playlist_manager_load_playlists:
407 * @mgr: the #RBPlaylistManager
408 *
409 * Loads the user's playlists, or if the playlist file does not exists,
410 * reads the default playlist file. Should be called only once on startup.
411 **/
412 void
413 rb_playlist_manager_load_playlists (RBPlaylistManager *mgr)
414 {
415 char *file;
416 xmlDocPtr doc;
417 xmlNodePtr root;
418 xmlNodePtr child;
419 gboolean exists;
420
421 exists = FALSE;
422 file = g_strdup (mgr->priv->playlists_file);
423
424 /* block saves until the playlists have loaded */
425 g_mutex_lock (&mgr->priv->saving_mutex);
426
427 exists = g_file_test (file, G_FILE_TEST_EXISTS);
428 if (! exists) {
429 rb_debug ("personal playlists not found, loading defaults");
430
431 /* try global playlists */
432 g_free (file);
433 file = g_strdup (rb_file ("playlists.xml"));
434 exists = g_file_test (file, G_FILE_TEST_EXISTS);
435 }
436
437 if (! exists) {
438 rb_debug ("default playlists file not found");
439 goto out;
440 }
441
442 doc = xmlParseFile (file);
443 if (doc == NULL)
444 goto out;
445
446 root = xmlDocGetRootElement (doc);
447
448 for (child = root->children; child; child = child->next) {
449 RBSource *playlist;
450
451 if (xmlNodeIsText (child))
452 continue;
453
454 playlist = rb_playlist_source_new_from_xml (mgr->priv->shell,
455 child);
456 if (playlist)
457 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
458 }
459
460 xmlFreeDoc (doc);
461 out:
462 g_mutex_unlock (&mgr->priv->saving_mutex);
463 g_free (file);
464 }
465
466 static void
467 rb_playlist_manager_set_dirty (RBPlaylistManager *mgr, gboolean dirty)
468 {
469 g_atomic_int_compare_and_exchange (&mgr->priv->dirty, dirty == FALSE, dirty == TRUE);
470 }
471
472 static gboolean
473 _is_dirty_playlist (GtkTreeModel *model,
474 GtkTreePath *path,
475 GtkTreeIter *iter,
476 gboolean *dirty)
477 {
478 RBDisplayPage *page;
479 gboolean local;
480 gboolean ret;
481
482 gtk_tree_model_get (model,
483 iter,
484 RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE,
485 &page,
486 -1);
487 if (page == NULL) {
488 return FALSE;
489 }
490 if (RB_IS_PLAYLIST_SOURCE (page) == FALSE) {
491 g_object_unref (page);
492 return FALSE;
493 }
494
495 ret = FALSE;
496 g_object_get (page, "is-local", &local, NULL);
497 if (local) {
498 gboolean pdirty;
499
500 g_object_get (page, "dirty", &pdirty, NULL);
501 if (pdirty) {
502 *dirty = TRUE;
503 ret = TRUE;
504 }
505 }
506 g_object_unref (page);
507
508 return ret;
509 }
510
511 /* returns TRUE if a playlist has been created, modified, or deleted since last save */
512 static gboolean
513 rb_playlist_manager_is_dirty (RBPlaylistManager *mgr)
514 {
515 gboolean dirty = FALSE;
516
517 gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
518 (GtkTreeModelForeachFunc) _is_dirty_playlist,
519 &dirty);
520
521 /* explicitly check the play queue */
522 if (dirty == FALSE) {
523 RBSource *queue_source;
524
525 g_object_get (mgr->priv->shell, "queue-source", &queue_source, NULL);
526 g_object_get (queue_source, "dirty", &dirty, NULL);
527 g_object_unref (queue_source);
528 }
529
530 if (!dirty)
531 dirty = g_atomic_int_get (&mgr->priv->dirty);
532
533 return dirty;
534 }
535
536 struct RBPlaylistManagerSaveData
537 {
538 RBPlaylistManager *mgr;
539 xmlDocPtr doc;
540 };
541
542 static gpointer
543 rb_playlist_manager_save_data (struct RBPlaylistManagerSaveData *data)
544 {
545 char *file;
546 char *tmpname;
547
548 g_mutex_lock (&data->mgr->priv->saving_mutex);
549
550 file = g_strdup (data->mgr->priv->playlists_file);
551 tmpname = g_strconcat (file, ".tmp", NULL);
552
553 if (xmlSaveFormatFile (tmpname, data->doc, 1) != -1) {
554 rename (tmpname, file);
555 } else {
556 rb_debug ("error in xmlSaveFormatFile(), not saving");
557 unlink (tmpname);
558 rb_playlist_manager_set_dirty (data->mgr, TRUE);
559 }
560 xmlFreeDoc (data->doc);
561 g_free (tmpname);
562 g_free (file);
563
564 g_atomic_int_compare_and_exchange (&data->mgr->priv->saving, 1, 0);
565 g_mutex_unlock (&data->mgr->priv->saving_mutex);
566
567 g_object_unref (data->mgr);
568
569 g_free (data);
570 return NULL;
571 }
572
573 static gboolean
574 save_playlist_cb (GtkTreeModel *model,
575 GtkTreePath *path,
576 GtkTreeIter *iter,
577 xmlNodePtr root)
578 {
579 RBDisplayPage *page;
580 gboolean local;
581
582 gtk_tree_model_get (model,
583 iter,
584 RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
585 -1);
586 if (page == NULL) {
587 goto out;
588 }
589 if (RB_IS_PLAYLIST_SOURCE (page) == FALSE || RB_IS_PLAY_QUEUE_SOURCE (page)) {
590 goto out;
591 }
592
593 g_object_get (page, "is-local", &local, NULL);
594 if (local) {
595 rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (page), root);
596 }
597 out:
598 if (page != NULL) {
599 g_object_unref (page);
600 }
601
602 return FALSE;
603 }
604
605 /**
606 * rb_playlist_manager_save_playlists:
607 * @mgr: the #RBPlaylistManager
608 * @force: if TRUE, save playlists synchronously and unconditionally
609 *
610 * Saves the user's playlists. If the force flag is
611 * TRUE, the playlists will always be saved. Otherwise, the playlists
612 * will only be saved if a playlist has been created, modified, or deleted
613 * since the last time the playlists were saved, and no save operation is
614 * currently taking place.
615 *
616 * Return value: TRUE if a playlist save operation has been started
617 **/
618 gboolean
619 rb_playlist_manager_save_playlists (RBPlaylistManager *mgr, gboolean force)
620 {
621 xmlNodePtr root;
622 struct RBPlaylistManagerSaveData *data;
623 RBSource *queue_source;
624
625 if (!force && !rb_playlist_manager_is_dirty (mgr)) {
626 /* playlists already in sync, so don't bother */
627 return FALSE;
628 }
629
630 if (!g_atomic_int_compare_and_exchange (&mgr->priv->saving, 0, 1) && !force) {
631 /* already saving, so don't bother */
632 return FALSE;
633 }
634
635 data = g_new0 (struct RBPlaylistManagerSaveData, 1);
636 data->mgr = mgr;
637 data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
638 g_object_ref (mgr);
639
640 root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
641 xmlDocSetRootElement (data->doc, root);
642
643 gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
644 (GtkTreeModelForeachFunc)save_playlist_cb,
645 root);
646
647 /* also save the play queue */
648 g_object_get (mgr->priv->shell, "queue-source", &queue_source, NULL);
649 rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (queue_source), root);
650 g_object_unref (queue_source);
651
652 /* mark clean here. if the save fails, we'll mark it dirty again */
653 rb_playlist_manager_set_dirty (data->mgr, FALSE);
654
655 if (force)
656 rb_playlist_manager_save_data (data);
657 else
658 g_thread_new ("playlist-save", (GThreadFunc) rb_playlist_manager_save_data, data);
659
660 return TRUE;
661 }
662
663 /**
664 * rb_playlist_manager_new_playlist:
665 * @mgr: the #RBPlaylistManager
666 * @suggested_name: optional name to use for the new playlist
667 * @automatic: if TRUE, create an auto playlist
668 *
669 * Creates a new playlist and adds it to the source list.
670 *
671 * Return value: (transfer none): the new playlist object.
672 */
673 RBSource *
674 rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
675 const char *suggested_name,
676 gboolean automatic)
677 {
678 RBSource *playlist;
679 if (automatic)
680 playlist = rb_auto_playlist_source_new (mgr->priv->shell,
681 suggested_name,
682 TRUE);
683 else
684 playlist = rb_static_playlist_source_new (mgr->priv->shell,
685 suggested_name,
686 NULL,
687 TRUE,
688 RHYTHMDB_ENTRY_TYPE_SONG);
689
690 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
691 rb_display_page_tree_edit_source_name (mgr->priv->display_page_tree, playlist);
692 rb_playlist_manager_set_dirty (mgr, TRUE);
693
694 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_CREATED], 0,
695 playlist);
696
697 return playlist;
698 }
699
700 static char *
701 create_name_from_selection_data (RBPlaylistManager *mgr,
702 GtkSelectionData *data)
703 {
704 GdkAtom type;
705 char *name = NULL;
706 const guchar *selection_data_data;
707 GList *list;
708
709 type = gtk_selection_data_get_data_type (data);
710 selection_data_data = gtk_selection_data_get_data (data);
711
712 if (type == gdk_atom_intern ("text/uri-list", TRUE) ||
713 type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE)) {
714 gboolean is_id;
715 list = rb_uri_list_parse ((const char *) selection_data_data);
716 is_id = (type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE));
717
718 if (list != NULL) {
719 GList *l;
720 char *artist;
721 char *album;
722 gboolean mixed_artists;
723 gboolean mixed_albums;
724
725 artist = NULL;
726 album = NULL;
727 mixed_artists = FALSE;
728 mixed_albums = FALSE;
729 for (l = list; l != NULL; l = g_list_next (l)) {
730 RhythmDBEntry *entry;
731 const char *e_artist;
732 const char *e_album;
733
734 entry = rhythmdb_entry_lookup_from_string (mgr->priv->db,
735 (const char *)l->data,
736 is_id);
737 if (entry == NULL) {
738 continue;
739 }
740
741 e_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
742 e_album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
743
744 /* get value of first non-NULL artist */
745 if (e_artist != NULL && artist == NULL) {
746 artist = g_strdup (e_artist);
747 }
748
749 /* get value of first non-NULL album */
750 if (e_album != NULL && album == NULL) {
751 album = g_strdup (e_album);
752 }
753
754 /* pretend that NULL fields always match */
755 if (artist != NULL && e_artist != NULL
756 && strcmp (artist, e_artist) != 0) {
757 mixed_artists = TRUE;
758 }
759
760 /* pretend that NULL fields always match */
761 if (album != NULL && e_album != NULL
762 && strcmp (album, e_album) != 0) {
763 mixed_albums = TRUE;
764 }
765
766 /* if there is a mix of both then stop */
767 if (mixed_artists && mixed_albums) {
768 break;
769 }
770 }
771
772 if (! mixed_artists && ! mixed_albums) {
773 name = g_strdup_printf ("%s - %s", artist, album);
774 } else if (! mixed_artists) {
775 name = g_strdup_printf ("%s", artist);
776 } else if (! mixed_albums) {
777 name = g_strdup_printf ("%s", album);
778 }
779
780 g_free (artist);
781 g_free (album);
782 rb_list_deep_free (list);
783 }
784
785 } else {
786 char **names;
787
788 names = g_strsplit ((char *) selection_data_data, "\r\n", 0);
789 name = g_strjoinv (", ", names);
790 g_strfreev (names);
791 }
792
793 if (name == NULL) {
794 name = g_strdup (_("Untitled Playlist"));
795 }
796
797 return name;
798 }
799
800 /**
801 * rb_playlist_manager_new_playlist_from_selection_data:
802 * @mgr: the #RBPlaylistManager
803 * @data: the #GtkSelectionData from which to create a playlist
804 *
805 * Creates a new playlist based on selection data from gtk.
806 * Used to implement playlist creation through drag and drop
807 * to the source list.
808 *
809 * Return value: (transfer none): the new playlist.
810 **/
811 RBSource *
812 rb_playlist_manager_new_playlist_from_selection_data (RBPlaylistManager *mgr,
813 GtkSelectionData *data)
814 {
815 RBSource *playlist;
816 GdkAtom type;
817 gboolean automatic = TRUE;
818 char *suggested_name;
819
820 type = gtk_selection_data_get_data_type (data);
821
822 if (type == gdk_atom_intern ("text/uri-list", TRUE) ||
823 type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE))
824 automatic = FALSE;
825 suggested_name = create_name_from_selection_data (mgr, data);
826
827 playlist = rb_playlist_manager_new_playlist (mgr,
828 suggested_name,
829 automatic);
830 g_free (suggested_name);
831
832 return playlist;
833 }
834
835 static void
836 rb_playlist_manager_cmd_new_playlist (GtkAction *action,
837 RBPlaylistManager *mgr)
838 {
839 rb_playlist_manager_new_playlist (mgr, _("New Playlist"), FALSE);
840 }
841
842 static void
843 rb_playlist_manager_set_automatic_playlist (RBPlaylistManager *mgr,
844 RBAutoPlaylistSource *playlist,
845 RBQueryCreator *creator)
846 {
847 RhythmDBQueryModelLimitType limit_type;
848 GArray *limit_value = NULL;
849 const char *sort_key;
850 gint sort_direction;
851 GPtrArray *query;
852
853 rb_query_creator_get_limit (creator, &limit_type, &limit_value);
854 rb_query_creator_get_sort_order (creator,
855 &sort_key,
856 &sort_direction);
857
858 query = rb_query_creator_get_query (creator);
859 rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (playlist),
860 query,
861 limit_type,
862 limit_value,
863 sort_key,
864 sort_direction);
865 rhythmdb_query_free (query);
866 if (limit_value != NULL) {
867 g_array_unref (limit_value);
868 }
869 }
870
871 static void
872 new_automatic_playlist_response_cb (GtkDialog *dialog, int response, RBPlaylistManager *mgr)
873 {
874 RBSource *playlist;
875
876 switch (response) {
877 case GTK_RESPONSE_NONE:
878 case GTK_RESPONSE_CLOSE:
879 break;
880
881 default:
882 playlist = rb_playlist_manager_new_playlist (mgr, _("New Playlist"), TRUE);
883
884 rb_playlist_manager_set_automatic_playlist (mgr,
885 RB_AUTO_PLAYLIST_SOURCE (playlist),
886 RB_QUERY_CREATOR (dialog));
887 rb_playlist_manager_set_dirty (mgr, TRUE);
888 break;
889 }
890
891 gtk_widget_destroy (GTK_WIDGET (dialog));
892 }
893
894 static void
895 rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
896 RBPlaylistManager *mgr)
897 {
898 GtkWidget *creator = rb_query_creator_new (mgr->priv->db);
899 gtk_widget_show_all (creator);
900
901 g_signal_connect (creator,
902 "response",
903 G_CALLBACK (new_automatic_playlist_response_cb),
904 mgr);
905 }
906
907 typedef struct {
908 RBAutoPlaylistSource *playlist;
909 RBPlaylistManager *mgr;
910 RBQueryCreator *creator;
911 gint playlist_deleted_id;
912 gint creator_response_id;
913 } EditAutoPlaylistData;
914
915 static void
916 cleanup_edit_data (EditAutoPlaylistData *data)
917 {
918 g_signal_handler_disconnect (data->playlist, data->playlist_deleted_id);
919 g_signal_handler_disconnect (data->creator, data->creator_response_id);
920 gtk_widget_destroy (GTK_WIDGET (data->creator));
921 g_free (data);
922 }
923
924 static void
925 edit_auto_playlist_response_cb (RBQueryCreator *dialog,
926 gint response,
927 EditAutoPlaylistData *data)
928 {
929 rb_playlist_manager_set_automatic_playlist (data->mgr, data->playlist, dialog);
930 g_object_set_data (G_OBJECT (data->playlist), "rhythmbox-playlist-editor", NULL);
931
932 cleanup_edit_data (data);
933 }
934
935 static void
936 edit_auto_playlist_deleted_cb (RBAutoPlaylistSource *playlist, EditAutoPlaylistData *data)
937 {
938 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", NULL);
939
940 cleanup_edit_data (data);
941 }
942
943 static void
944 rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
945 RBPlaylistManager *mgr)
946 {
947 RBQueryCreator *creator;
948 RBAutoPlaylistSource *playlist;
949
950 playlist = RB_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source);
951 creator = g_object_get_data (G_OBJECT (playlist), "rhythmbox-playlist-editor");
952 if (creator == NULL) {
953 RhythmDBQueryModelLimitType limit_type;
954 GArray *limit_value = NULL;
955 GPtrArray *query;
956 char *sort_key;
957 gint sort_direction;
958 EditAutoPlaylistData *data;
959
960 sort_key = NULL;
961 rb_auto_playlist_source_get_query (playlist,
962 &query,
963 &limit_type,
964 &limit_value,
965 &sort_key,
966 &sort_direction);
967
968 creator = RB_QUERY_CREATOR (rb_query_creator_new_from_query (mgr->priv->db,
969 query,
970 limit_type,
971 limit_value,
972 sort_key,
973 sort_direction));
974 if (limit_value != NULL) {
975 g_array_unref (limit_value);
976 }
977 rhythmdb_query_free (query);
978 g_free (sort_key);
979
980 data = g_new0 (EditAutoPlaylistData, 1);
981 data->mgr = mgr;
982 data->playlist = playlist;
983 data->creator = creator;
984 data->creator_response_id =
985 g_signal_connect (creator,
986 "response",
987 G_CALLBACK (edit_auto_playlist_response_cb),
988 data);
989
990 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", creator);
991 data->playlist_deleted_id =
992 g_signal_connect (playlist,
993 "deleted",
994 G_CALLBACK (edit_auto_playlist_deleted_cb),
995 data);
996 }
997 gtk_window_present (GTK_WINDOW (creator));
998 }
999
1000 static gboolean
1001 _queue_track_cb (RhythmDBQueryModel *model,
1002 GtkTreePath *path,
1003 GtkTreeIter *iter,
1004 RBStaticPlaylistSource *queue_source)
1005 {
1006 RhythmDBEntry *entry;
1007
1008 entry = rhythmdb_query_model_iter_to_entry (model, iter);
1009 rb_static_playlist_source_add_entry (queue_source, entry, -1);
1010 rhythmdb_entry_unref (entry);
1011
1012 return FALSE;
1013 }
1014
1015 static void
1016 rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
1017 RBPlaylistManager *mgr)
1018 {
1019 RBSource *queue_source;
1020 RhythmDBQueryModel *model;
1021
1022 g_object_get (mgr->priv->shell, "queue-source", &queue_source, NULL);
1023 g_object_get (mgr->priv->selected_source, "query-model", &model, NULL);
1024
1025 gtk_tree_model_foreach (GTK_TREE_MODEL (model),
1026 (GtkTreeModelForeachFunc) _queue_track_cb,
1027 queue_source);
1028
1029 g_object_unref (queue_source);
1030 g_object_unref (model);
1031 }
1032
1033 static void
1034 rb_playlist_manager_cmd_shuffle_playlist (GtkAction *action,
1035 RBPlaylistManager *mgr)
1036 {
1037 RhythmDBQueryModel *base_model;
1038
1039 g_object_get (mgr->priv->selected_source, "base-query-model", &base_model, NULL);
1040 rhythmdb_query_model_shuffle_entries (base_model);
1041 g_object_unref (base_model);
1042 }
1043
1044 static void
1045 rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
1046 RBPlaylistManager *mgr)
1047 {
1048 rb_debug ("Renaming playlist %p", mgr->priv->selected_source);
1049
1050 rb_display_page_tree_edit_source_name (mgr->priv->display_page_tree,
1051 mgr->priv->selected_source);
1052 rb_playlist_manager_set_dirty (mgr, TRUE);
1053 }
1054
1055 static void
1056 rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
1057 RBPlaylistManager *mgr)
1058 {
1059 rb_debug ("Deleting playlist %p", mgr->priv->selected_source);
1060
1061 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (mgr->priv->selected_source));
1062 rb_playlist_manager_set_dirty (mgr, TRUE);
1063 }
1064
1065 static void
1066 load_playlist_response_cb (GtkDialog *dialog,
1067 int response_id,
1068 RBPlaylistManager *mgr)
1069 {
1070 char *escaped_file = NULL;
1071 GError *error = NULL;
1072
1073 if (response_id != GTK_RESPONSE_ACCEPT) {
1074 gtk_widget_destroy (GTK_WIDGET (dialog));
1075 return;
1076 }
1077
1078 escaped_file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1079
1080 gtk_widget_destroy (GTK_WIDGET (dialog));
1081
1082 if (escaped_file == NULL)
1083 return;
1084
1085 if (!rb_playlist_manager_parse_file (mgr, escaped_file, &error)) {
1086 rb_error_dialog (NULL, _("Couldn't read playlist"),
1087 "%s", error->message);
1088 g_error_free (error);
1089 }
1090
1091 g_free (escaped_file);
1092 rb_playlist_manager_set_dirty (mgr, TRUE);
1093 }
1094
1095 static void
1096 rb_playlist_manager_cmd_load_playlist (GtkAction *action,
1097 RBPlaylistManager *mgr)
1098 {
1099 GtkWindow *window;
1100 GtkWidget *dialog;
1101 GtkFileFilter *filter;
1102 GtkFileFilter *filter_all;
1103 int i;
1104
1105 filter = gtk_file_filter_new ();
1106 gtk_file_filter_set_name (filter, _("Playlists"));
1107 for (i = 0; i < G_N_ELEMENTS (playlist_formats); i++) {
1108 gtk_file_filter_add_mime_type (filter, playlist_formats[i].mimetype);
1109 }
1110
1111 filter_all = gtk_file_filter_new ();
1112 gtk_file_filter_set_name (filter_all, _("All Files"));
1113 gtk_file_filter_add_pattern (filter_all, "*");
1114
1115 g_object_get (mgr->priv->shell, "window", &window, NULL);
1116
1117 dialog = rb_file_chooser_new (_("Load Playlist"),
1118 window,
1119 GTK_FILE_CHOOSER_ACTION_OPEN,
1120 FALSE);
1121 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1122 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter_all);
1123 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
1124
1125 g_signal_connect_object (dialog, "response",
1126 G_CALLBACK (load_playlist_response_cb), mgr, 0);
1127
1128 g_object_unref (window);
1129 }
1130
1131 static void
1132 save_playlist_response_cb (GtkDialog *dialog,
1133 int response_id,
1134 RBSource *source)
1135 {
1136 char *file = NULL;
1137 GtkWidget *menu;
1138 gint index;
1139 RBPlaylistExportType export_type = RB_PLAYLIST_EXPORT_TYPE_UNKNOWN;
1140
1141 if (response_id != GTK_RESPONSE_OK) {
1142 gtk_widget_destroy (GTK_WIDGET (dialog));
1143 return;
1144 }
1145
1146 file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1147 if (file == NULL || file[0] == '\0')
1148 return;
1149
1150 menu = g_object_get_data (G_OBJECT(dialog), "export-menu");
1151 index = gtk_combo_box_get_active (GTK_COMBO_BOX (menu));
1152
1153 /* by extension selected */
1154 if (index <= 0) {
1155 int i;
1156
1157 for (i = 0; i < G_N_ELEMENTS (playlist_formats); i++) {
1158 int j;
1159
1160 /* determine the playlist type from the extension */
1161 for (j = 0; playlist_formats[i].extensions[j] != NULL; j++) {
1162 if (g_str_has_suffix (file, playlist_formats[i].extensions[j])) {
1163 export_type = playlist_formats[i].type;
1164 break;
1165 }
1166 }
1167 }
1168 } else {
1169 export_type = playlist_formats[index-1].type;
1170 }
1171
1172 if (export_type == RB_PLAYLIST_EXPORT_TYPE_UNKNOWN) {
1173 rb_error_dialog (NULL, _("Couldn't save playlist"), _("Unsupported file extension given."));
1174 } else {
1175 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (source),
1176 file, export_type);
1177 gtk_widget_destroy (GTK_WIDGET (dialog));
1178 }
1179
1180 g_free (file);
1181 }
1182
1183 static void
1184 export_set_extension_cb (GtkWidget* widget, GtkDialog *dialog)
1185 {
1186 gint index;
1187 gchar *text;
1188 gchar *last_dot;
1189 const char *extension;
1190 gchar *basename;
1191 GString *basename_str;
1192
1193 index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1194 if (index <= 0)
1195 return;
1196
1197 extension = playlist_formats[index-1].extensions[0];
1198 if (extension == NULL)
1199 return;
1200
1201 text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1202 if (text == NULL || text[0] == '\0') {
1203 g_free (text);
1204 return;
1205 }
1206
1207 basename = g_path_get_basename (text);
1208 basename_str = g_string_new (basename);
1209 last_dot = g_utf8_strrchr (basename, -1, '.');
1210 if (last_dot)
1211 g_string_truncate (basename_str, (last_dot-basename));
1212 g_free (basename);
1213 g_free (text);
1214
1215 g_string_append_printf (basename_str, ".%s", extension);
1216 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename_str->str);
1217 g_string_free (basename_str, TRUE);
1218 }
1219
1220 static gchar *
1221 filter_get_export_filter_label (RBPlaylistExportFilter *efilter)
1222 {
1223 GString *str;
1224 gint ext;
1225
1226 str = g_string_new (_(efilter->description));
1227 for (ext = 0; efilter->extensions[ext] != NULL; ext++) {
1228 if (ext == 0)
1229 g_string_append (str, " (*.");
1230 else
1231 g_string_append (str, ", *.");
1232 g_string_append (str, efilter->extensions[ext]);
1233 }
1234
1235 if (ext > 0)
1236 g_string_append (str, ")");
1237
1238 return g_string_free (str, FALSE);
1239 }
1240
1241 static void
1242 setup_format_menu (GtkWidget* menu, GtkWidget *dialog)
1243 {
1244 GtkTreeModel *model;
1245 int i;
1246
1247 model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu));
1248 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu), rb_combo_box_hyphen_separator_func,
1249 NULL, NULL);
1250
1251 for (i = 0; i < G_N_ELEMENTS (playlist_formats); i++) {
1252 gchar *filter_label;
1253 GtkTreeIter iter;
1254
1255 filter_label = filter_get_export_filter_label (&playlist_formats[i]);
1256 gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, -1,
1257 0, filter_label, -1);
1258
1259 g_free (filter_label);
1260 }
1261
1262 g_signal_connect_object (menu,
1263 "changed", G_CALLBACK (export_set_extension_cb),
1264 dialog, 0);
1265 }
1266
1267 static void
1268 save_playlist (RBPlaylistManager *mgr, RBSource *source)
1269 {
1270 GtkBuilder *builder;
1271 GtkWidget *dialog;
1272 GtkWidget *menu;
1273 char *name;
1274 char *tmp;
1275
1276 builder = rb_builder_load ("playlist-save.ui", mgr);
1277 dialog = GTK_WIDGET (gtk_builder_get_object (builder, "playlist_save_dialog"));
1278
1279 menu = GTK_WIDGET (gtk_builder_get_object (builder, "playlist_format_menu"));
1280 setup_format_menu (menu, dialog);
1281 g_object_set_data (G_OBJECT (dialog), "export-menu", menu);
1282
1283 g_object_get (source, "name", &name, NULL);
1284 tmp = g_strconcat (name, ".pls", NULL);
1285 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), tmp);
1286 g_free (tmp);
1287 g_free (name);
1288
1289 /* FIXME: always has "by extension" as default (it should probably remember the last selection) */
1290 gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0);
1291 g_signal_connect_object (dialog, "response",
1292 G_CALLBACK (save_playlist_response_cb),
1293 source, 0);
1294
1295 g_object_unref (builder);
1296 }
1297
1298 static void
1299 rb_playlist_manager_cmd_save_playlist (GtkAction *action,
1300 RBPlaylistManager *mgr)
1301 {
1302 save_playlist (mgr, mgr->priv->selected_source);
1303 }
1304
1305 static void
1306 rb_playlist_manager_cmd_save_queue (GtkAction *action,
1307 RBPlaylistManager *mgr)
1308 {
1309 RBSource *queue;
1310 g_object_get (mgr->priv->shell, "queue-source", &queue, NULL);
1311 save_playlist (mgr, queue);
1312 g_object_unref (queue);
1313 }
1314
1315 static gboolean
1316 list_playlists_cb (GtkTreeModel *model,
1317 GtkTreePath *path,
1318 GtkTreeIter *iter,
1319 GList **playlists)
1320 {
1321 RBDisplayPage *page;
1322 gboolean local;
1323
1324 gtk_tree_model_get (model,
1325 iter,
1326 RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
1327 -1);
1328 if (page != NULL) {
1329 if (RB_IS_PLAYLIST_SOURCE (page) && !RB_IS_PLAY_QUEUE_SOURCE (page)) {
1330 g_object_get (page, "is-local", &local, NULL);
1331 if (local) {
1332 *playlists = g_list_prepend (*playlists, RB_SOURCE (page));
1333 }
1334 }
1335
1336 g_object_unref (page);
1337 }
1338
1339 return FALSE;
1340 }
1341
1342
1343 /**
1344 * rb_playlist_manager_get_playlists:
1345 * @mgr: the #RBPlaylistManager
1346 *
1347 * Returns a #GList containing all local playlist source objects.
1348 *
1349 * Return value: (element-type RB.Source) (transfer container): list of playlists
1350 **/
1351 GList *
1352 rb_playlist_manager_get_playlists (RBPlaylistManager *mgr)
1353 {
1354 GList *playlists = NULL;
1355
1356 gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
1357 (GtkTreeModelForeachFunc)list_playlists_cb,
1358 &playlists);
1359 return g_list_reverse (playlists);
1360 }
1361
1362 /**
1363 * rb_playlist_manager_get_playlist_names:
1364 * @mgr: the #RBPlaylistManager
1365 * @playlists: (out callee-allocates) (transfer full): holds the array of playlist names on reutrn
1366 * @error: holds a #GError on return on failure
1367 *
1368 * Allocates and returns an array containing the names of all local
1369 * playlists. This is part of the playlist manager dbus interface.
1370 *
1371 * Return value: TRUE if successful.
1372 **/
1373 gboolean
1374 rb_playlist_manager_get_playlist_names (RBPlaylistManager *mgr,
1375 gchar ***playlists,
1376 GError **error)
1377 {
1378 GList *pl;
1379 GList *t;
1380 int i;
1381
1382 pl = rb_playlist_manager_get_playlists (mgr);
1383 *playlists = g_new0 (char *, g_list_length (pl) + 1);
1384 if (!*playlists)
1385 return FALSE;
1386
1387 i = 0;
1388 for (t = pl; t != NULL; t = t->next, i++) {
1389 const char *name;
1390 RBSource *source = (RBSource *)t->data;
1391
1392 g_object_get (source, "name", &name, NULL);
1393 (*playlists)[i] = g_strdup (name);
1394 }
1395
1396 return TRUE;
1397 }
1398
1399 typedef struct {
1400 const char *name;
1401 RBSource *source;
1402 } FindPlaylistData;
1403
1404 static gboolean
1405 find_playlist_by_name_cb (GtkTreeModel *model,
1406 GtkTreePath *path,
1407 GtkTreeIter *iter,
1408 FindPlaylistData *data)
1409 {
1410 RBDisplayPage *page;
1411
1412 gtk_tree_model_get (model,
1413 iter,
1414 RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
1415 -1);
1416 if (page != NULL) {
1417 if (RB_IS_PLAYLIST_SOURCE (page) && !RB_IS_PLAY_QUEUE_SOURCE (page)) {
1418 char *name;
1419
1420 g_object_get (page, "name", &name, NULL);
1421 if (strcmp (name, data->name) == 0) {
1422 data->source = RB_SOURCE (page);
1423 }
1424 g_free (name);
1425 }
1426
1427 g_object_unref (page);
1428 }
1429
1430 return (data->source != NULL);
1431 }
1432
1433 static RBSource *
1434 _get_playlist_by_name (RBPlaylistManager *mgr,
1435 const char *name)
1436 {
1437 FindPlaylistData d;
1438
1439 d.name = name;
1440 d.source = NULL;
1441
1442 gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
1443 (GtkTreeModelForeachFunc)find_playlist_by_name_cb,
1444 &d);
1445 return d.source;
1446 }
1447
1448 /**
1449 * rb_playlist_manager_create_static_playlist:
1450 * @mgr: the #RBPlaylistManager
1451 * @name: name of the new playlist
1452 * @error: holds a #GError on return on failure
1453 *
1454 * Creates a new static playlist source with the given name.
1455 * Will fail if a playlist with that name already exists.
1456 * This is part of the playlist manager dbus interface.
1457 *
1458 * Return value: TRUE if successful.
1459 **/
1460 gboolean
1461 rb_playlist_manager_create_static_playlist (RBPlaylistManager *mgr,
1462 const gchar *name,
1463 GError **error)
1464 {
1465 if (_get_playlist_by_name (mgr, name)) {
1466 g_set_error (error,
1467 RB_PLAYLIST_MANAGER_ERROR,
1468 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_EXISTS,
1469 _("Playlist %s already exists"),
1470 name);
1471 return FALSE;
1472 }
1473
1474 rb_playlist_manager_new_playlist (mgr, name, FALSE);
1475 return TRUE;
1476 }
1477
1478 /**
1479 * rb_playlist_manager_delete_playlist:
1480 * @mgr: the #RBPlaylistManager
1481 * @name: name of the playlist to delete
1482 * @error: holds a #GError on return on failure
1483 *
1484 * Deletes the specified playlist. Will fail if no playlist with
1485 * that name exists. This is part of the playlist manager dbus interface.
1486 *
1487 * Return value: TRUE if successful.
1488 */
1489 gboolean
1490 rb_playlist_manager_delete_playlist (RBPlaylistManager *mgr,
1491 const gchar *name,
1492 GError **error)
1493 {
1494 RBSource *playlist = _get_playlist_by_name (mgr, name);
1495 if (!playlist) {
1496 g_set_error (error,
1497 RB_PLAYLIST_MANAGER_ERROR,
1498 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1499 _("Unknown playlist: %s"),
1500 name);
1501 return FALSE;
1502 }
1503 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (playlist));
1504 rb_playlist_manager_set_dirty (mgr, TRUE);
1505 return TRUE;
1506 }
1507
1508 /**
1509 * rb_playlist_manager_add_to_playlist:
1510 * @mgr: the #RBPlaylistManager
1511 * @name: name of the playlist to add to
1512 * @uri: URI of the entry to add to the playlist
1513 * @error: holds a #GError on return on failure
1514 *
1515 * Adds an entry to the specified playlist.
1516 * Fails if no playlist with that name exists.
1517 * This is part of the playlist manager dbus interface.
1518 *
1519 * Return value: TRUE if successful.
1520 **/
1521 gboolean
1522 rb_playlist_manager_add_to_playlist (RBPlaylistManager *mgr,
1523 const gchar *playlist,
1524 const gchar *uri,
1525 GError **error)
1526 {
1527 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1528 if (!source) {
1529 g_set_error (error,
1530 RB_PLAYLIST_MANAGER_ERROR,
1531 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1532 _("Unknown playlist: %s"),
1533 playlist);
1534 return FALSE;
1535 }
1536 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1537 g_set_error (error,
1538 RB_PLAYLIST_MANAGER_ERROR,
1539 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1540 _("Playlist %s is an automatic playlist"),
1541 playlist);
1542 return FALSE;
1543 }
1544 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (source), uri, -1);
1545 return TRUE;
1546 }
1547
1548 /**
1549 * rb_playlist_manager_remove_from_playlist:
1550 * @mgr: the #RBPlaylistManager
1551 * @name: name of the playlist to remove from
1552 * @uri: URI of the entry to remove from the playlist
1553 * @error: holds a #GError on return on failure
1554 *
1555 * Removes an entry from the specified playlist.
1556 * Fails if no playlist with that name exists.
1557 * This is part of the playlist manager dbus interface.
1558 *
1559 * Return value: TRUE if successful.
1560 **/
1561 gboolean
1562 rb_playlist_manager_remove_from_playlist (RBPlaylistManager *mgr,
1563 const gchar *playlist,
1564 const gchar *uri,
1565 GError **error)
1566 {
1567 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1568 if (!source) {
1569 g_set_error (error,
1570 RB_PLAYLIST_MANAGER_ERROR,
1571 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1572 _("Unknown playlist: %s"),
1573 playlist);
1574 return FALSE;
1575 }
1576 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1577 g_set_error (error,
1578 RB_PLAYLIST_MANAGER_ERROR,
1579 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1580 _("Playlist %s is an automatic playlist"),
1581 playlist);
1582 return FALSE;
1583 }
1584
1585 if (rb_playlist_source_location_in_map (RB_PLAYLIST_SOURCE (source), uri))
1586 rb_static_playlist_source_remove_location (RB_STATIC_PLAYLIST_SOURCE (source), uri);
1587 return TRUE;
1588 }
1589
1590 /**
1591 * rb_playlist_manager_export_playlist:
1592 * @mgr: the #RBPlaylistManager
1593 * @name: name of the playlist to export
1594 * @uri: playlist save location
1595 * @m3u_format: if TRUE, save in M3U format, otherwise save in PLS format
1596 * @error: holds a #GError on return on failure
1597 *
1598 * Saves the specified playlist to a file in either M3U or PLS format.
1599 * This is part of the playlist manager dbus interface.
1600 *
1601 * Return value: TRUE if successful.
1602 **/
1603 gboolean
1604 rb_playlist_manager_export_playlist (RBPlaylistManager *mgr,
1605 const gchar *playlist,
1606 const gchar *uri,
1607 gboolean m3u_format,
1608 GError **error)
1609 {
1610 RBSource *source = _get_playlist_by_name (mgr, playlist);
1611 if (!source) {
1612 g_set_error (error,
1613 RB_PLAYLIST_MANAGER_ERROR,
1614 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1615 _("Unknown playlist: %s"),
1616 playlist);
1617 return FALSE;
1618 }
1619
1620 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (source),
1621 uri,
1622 m3u_format);
1623 return TRUE;
1624 }
1625
1626 static void
1627 playlist_manager_method_call (GDBusConnection *connection,
1628 const char *sender,
1629 const char *object_path,
1630 const char *interface_name,
1631 const char *method_name,
1632 GVariant *parameters,
1633 GDBusMethodInvocation *invocation,
1634 RBPlaylistManager *mgr)
1635 {
1636 GError *error = NULL;
1637 const char *name;
1638 const char *uri;
1639
1640 if (g_strcmp0 (interface_name, RB_PLAYLIST_MANAGER_IFACE_NAME) != 0) {
1641 rb_debug ("method call on unexpected interface %s", interface_name);
1642 g_dbus_method_invocation_return_error (invocation,
1643 G_DBUS_ERROR,
1644 G_DBUS_ERROR_NOT_SUPPORTED,
1645 "Method %s.%s not supported",
1646 interface_name,
1647 method_name);
1648 return;
1649 }
1650
1651 if (g_strcmp0 (method_name, "GetPlaylists") == 0) {
1652 char **names;
1653
1654 rb_playlist_manager_get_playlist_names (mgr, &names, NULL);
1655 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(^as)", names));
1656 g_strfreev (names);
1657 } else if (g_strcmp0 (method_name, "CreatePlaylist") == 0) {
1658 g_variant_get (parameters, "(&s)", &name);
1659 if (rb_playlist_manager_create_static_playlist (mgr, name, &error)) {
1660 g_dbus_method_invocation_return_value (invocation, NULL);
1661 } else {
1662 g_dbus_method_invocation_return_gerror (invocation, error);
1663 g_clear_error (&error);
1664 }
1665 } else if (g_strcmp0 (method_name, "DeletePlaylist") == 0) {
1666 g_variant_get (parameters, "(&s)", &name);
1667 if (rb_playlist_manager_delete_playlist (mgr, name, &error)) {
1668 g_dbus_method_invocation_return_value (invocation, NULL);
1669 } else {
1670 g_dbus_method_invocation_return_gerror (invocation, error);
1671 g_clear_error (&error);
1672 }
1673 } else if (g_strcmp0 (method_name, "AddToPlaylist") == 0) {
1674 g_variant_get (parameters, "(ss)", &name, &uri);
1675 if (rb_playlist_manager_add_to_playlist (mgr, name, uri, &error)) {
1676 g_dbus_method_invocation_return_value (invocation, NULL);
1677 } else {
1678 g_dbus_method_invocation_return_gerror (invocation, error);
1679 g_clear_error (&error);
1680 }
1681 } else if (g_strcmp0 (method_name, "RemoveFromPlaylist") == 0) {
1682 g_variant_get (parameters, "(ss)", &name, &uri);
1683 if (rb_playlist_manager_remove_from_playlist (mgr, name, uri, &error)) {
1684 g_dbus_method_invocation_return_value (invocation, NULL);
1685 } else {
1686 g_dbus_method_invocation_return_gerror (invocation, error);
1687 g_clear_error (&error);
1688 }
1689 } else if (g_strcmp0 (method_name, "ExportPlaylist") == 0) {
1690 gboolean m3u_format;
1691 g_variant_get (parameters, "(ssb)", &name, &uri, &m3u_format);
1692 if (rb_playlist_manager_export_playlist (mgr, name, uri, m3u_format, &error)) {
1693 g_dbus_method_invocation_return_value (invocation, NULL);
1694 } else {
1695 g_dbus_method_invocation_return_gerror (invocation, error);
1696 g_clear_error (&error);
1697 }
1698 } else if (g_strcmp0 (method_name, "ImportPlaylist") == 0) {
1699 g_variant_get (parameters, "(s)", &uri);
1700 if (rb_playlist_manager_parse_file (mgr, uri, &error)) {
1701 g_dbus_method_invocation_return_value (invocation, NULL);
1702 } else {
1703 g_dbus_method_invocation_return_gerror (invocation, error);
1704 g_clear_error (&error);
1705 }
1706 } else {
1707 g_dbus_method_invocation_return_error (invocation,
1708 G_DBUS_ERROR,
1709 G_DBUS_ERROR_NOT_SUPPORTED,
1710 "Method %s.%s not supported",
1711 interface_name,
1712 method_name);
1713 }
1714 }
1715
1716 static const GDBusInterfaceVTable playlist_manager_vtable = {
1717 (GDBusInterfaceMethodCallFunc) playlist_manager_method_call,
1718 NULL,
1719 NULL
1720 };
1721
1722 static void
1723 rb_playlist_manager_set_uimanager (RBPlaylistManager *mgr,
1724 GtkUIManager *uimanager)
1725 {
1726 if (mgr->priv->uimanager != NULL) {
1727 if (mgr->priv->actiongroup != NULL) {
1728 gtk_ui_manager_remove_action_group (mgr->priv->uimanager,
1729 mgr->priv->actiongroup);
1730 }
1731 g_object_unref (mgr->priv->uimanager);
1732 }
1733
1734 mgr->priv->uimanager = uimanager;
1735
1736 if (mgr->priv->actiongroup == NULL) {
1737 mgr->priv->actiongroup = gtk_action_group_new ("PlaylistManagerActions");
1738 gtk_action_group_set_translation_domain (mgr->priv->actiongroup,
1739 GETTEXT_PACKAGE);
1740 gtk_action_group_add_actions (mgr->priv->actiongroup,
1741 rb_playlist_manager_actions,
1742 rb_playlist_manager_n_actions,
1743 mgr);
1744 }
1745
1746 gtk_ui_manager_insert_action_group (mgr->priv->uimanager,
1747 mgr->priv->actiongroup,
1748 0);
1749 }
1750
1751 static void
1752 rb_playlist_manager_set_source (RBPlaylistManager *mgr,
1753 RBSource *source)
1754 {
1755 gboolean playlist_active;
1756 gboolean playlist_local = FALSE;
1757 gboolean party_mode;
1758 gboolean can_save;
1759 gboolean can_delete;
1760 gboolean can_edit;
1761 gboolean can_rename;
1762 gboolean can_shuffle;
1763 GtkAction *action;
1764
1765 party_mode = rb_shell_get_party_mode (mgr->priv->shell);
1766
1767 if (mgr->priv->selected_source != NULL) {
1768 g_object_unref (mgr->priv->selected_source);
1769 }
1770 mgr->priv->selected_source = g_object_ref (source);
1771
1772 playlist_active = RB_IS_PLAYLIST_SOURCE (mgr->priv->selected_source);
1773 if (playlist_active) {
1774 g_object_get (mgr->priv->selected_source, "is-local", &playlist_local, NULL);
1775 }
1776
1777 can_save = playlist_local && !party_mode;
1778 action = gtk_action_group_get_action (mgr->priv->actiongroup,
1779 "MusicPlaylistSavePlaylist");
1780 gtk_action_set_visible (action, can_save);
1781
1782 can_delete = (playlist_local && !party_mode &&
1783 !RB_IS_PLAY_QUEUE_SOURCE (mgr->priv->selected_source));
1784 action = gtk_action_group_get_action (mgr->priv->actiongroup,
1785 "MusicPlaylistDeletePlaylist");
1786 gtk_action_set_visible (action, can_delete);
1787
1788 can_edit = (playlist_local && RB_IS_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source) &&
1789 !party_mode);
1790 action = gtk_action_group_get_action (mgr->priv->actiongroup,
1791 "EditAutomaticPlaylist");
1792 gtk_action_set_visible (action, can_edit);
1793
1794 can_rename = playlist_local && rb_source_can_rename (mgr->priv->selected_source);
1795 action = gtk_action_group_get_action (mgr->priv->actiongroup,
1796 "MusicPlaylistRenamePlaylist");
1797 gtk_action_set_visible (action, can_rename);
1798
1799 can_shuffle = RB_IS_STATIC_PLAYLIST_SOURCE (mgr->priv->selected_source);
1800 action = gtk_action_group_get_action (mgr->priv->actiongroup,
1801 "ShufflePlaylist");
1802 gtk_action_set_sensitive (action, can_shuffle);
1803 }
1804
1805 static void
1806 rb_playlist_manager_set_shell_internal (RBPlaylistManager *mgr,
1807 RBShell *shell)
1808 {
1809 GtkUIManager *uimanager = NULL;
1810 RhythmDB *db = NULL;
1811
1812 if (mgr->priv->db != NULL) {
1813 g_object_unref (mgr->priv->db);
1814 }
1815
1816 mgr->priv->shell = shell;
1817
1818 if (mgr->priv->shell != NULL) {
1819 g_object_get (mgr->priv->shell,
1820 "ui-manager", &uimanager,
1821 "db", &db,
1822 NULL);
1823 }
1824
1825 mgr->priv->db = db;
1826 rb_playlist_manager_set_uimanager (mgr, uimanager);
1827 }
1828
1829 static void
1830 rb_playlist_manager_set_property (GObject *object,
1831 guint prop_id,
1832 const GValue *value,
1833 GParamSpec *pspec)
1834 {
1835 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
1836
1837 switch (prop_id) {
1838 case PROP_PLAYLIST_NAME:
1839 g_free (mgr->priv->playlists_file);
1840 mgr->priv->playlists_file = g_strdup (g_value_get_string (value));
1841 break;
1842 case PROP_SOURCE:
1843 rb_playlist_manager_set_source (mgr, g_value_get_object (value));
1844 break;
1845 case PROP_SHELL:
1846 rb_playlist_manager_set_shell_internal (mgr, g_value_get_object (value));
1847 break;
1848 case PROP_DISPLAY_PAGE_MODEL:
1849 mgr->priv->page_model = g_value_dup_object (value);
1850 break;
1851 case PROP_DISPLAY_PAGE_TREE:
1852 mgr->priv->display_page_tree = g_value_dup_object (value);
1853 break;
1854 default:
1855 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1856 break;
1857 }
1858 }
1859
1860 static void
1861 rb_playlist_manager_get_property (GObject *object,
1862 guint prop_id,
1863 GValue *value,
1864 GParamSpec *pspec)
1865 {
1866 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
1867
1868 switch (prop_id) {
1869 case PROP_PLAYLIST_NAME:
1870 g_value_set_string (value, mgr->priv->playlists_file);
1871 break;
1872 case PROP_SOURCE:
1873 g_value_set_object (value, mgr->priv->selected_source);
1874 break;
1875 case PROP_SHELL:
1876 g_value_set_object (value, mgr->priv->shell);
1877 break;
1878 case PROP_DISPLAY_PAGE_MODEL:
1879 g_value_set_object (value, mgr->priv->page_model);
1880 break;
1881 case PROP_DISPLAY_PAGE_TREE:
1882 g_value_set_object (value, mgr->priv->display_page_tree);
1883 break;
1884 default:
1885 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1886 break;
1887 }
1888 }
1889
1890 static void
1891 rb_playlist_manager_constructed (GObject *object)
1892 {
1893 GDBusConnection *bus;
1894 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
1895
1896 RB_CHAIN_GOBJECT_METHOD(rb_playlist_manager_parent_class, constructed, G_OBJECT (mgr));
1897
1898 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1899 if (bus) {
1900 GDBusNodeInfo *node_info;
1901 GError *error = NULL;
1902
1903 node_info = g_dbus_node_info_new_for_xml (rb_playlist_manager_dbus_spec, &error);
1904 if (error != NULL) {
1905 g_warning ("Unable to parse playlist manager dbus spec: %s", error->message);
1906 g_clear_error (&error);
1907 return;
1908 }
1909
1910 g_dbus_connection_register_object (bus,
1911 RB_PLAYLIST_MANAGER_DBUS_PATH,
1912 g_dbus_node_info_lookup_interface (node_info, RB_PLAYLIST_MANAGER_IFACE_NAME),
1913 &playlist_manager_vtable,
1914 g_object_ref (mgr),
1915 g_object_unref,
1916 &error);
1917 if (error != NULL) {
1918 g_warning ("Unable to register playlist manager dbus object: %s", error->message);
1919 g_clear_error (&error);
1920 }
1921 }
1922 }
1923
1924 static void
1925 rb_playlist_manager_init (RBPlaylistManager *mgr)
1926 {
1927 mgr->priv = G_TYPE_INSTANCE_GET_PRIVATE (mgr,
1928 RB_TYPE_PLAYLIST_MANAGER,
1929 RBPlaylistManagerPrivate);
1930
1931 mgr->priv->dirty = 0;
1932 mgr->priv->saving = 0;
1933 }
1934
1935 static void
1936 rb_playlist_manager_dispose (GObject *object)
1937 {
1938 RBPlaylistManager *mgr;
1939
1940 g_return_if_fail (object != NULL);
1941 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
1942
1943 rb_debug ("Disposing playlist manager");
1944
1945 mgr = RB_PLAYLIST_MANAGER (object);
1946
1947 g_return_if_fail (mgr->priv != NULL);
1948
1949 if (mgr->priv->db != NULL) {
1950 g_object_unref (mgr->priv->db);
1951 mgr->priv->db = NULL;
1952 }
1953
1954 if (mgr->priv->uimanager != NULL) {
1955 g_object_unref (mgr->priv->uimanager);
1956 mgr->priv->uimanager = NULL;
1957 }
1958
1959 if (mgr->priv->page_model != NULL) {
1960 g_object_unref (mgr->priv->page_model);
1961 mgr->priv->page_model = NULL;
1962 }
1963
1964 if (mgr->priv->display_page_tree != NULL) {
1965 g_object_unref (mgr->priv->display_page_tree);
1966 mgr->priv->display_page_tree = NULL;
1967 }
1968
1969 if (mgr->priv->selected_source != NULL) {
1970 g_object_unref (mgr->priv->selected_source);
1971 mgr->priv->selected_source = NULL;
1972 }
1973
1974 G_OBJECT_CLASS (rb_playlist_manager_parent_class)->dispose (object);
1975 }
1976
1977 static void
1978 rb_playlist_manager_finalize (GObject *object)
1979 {
1980 RBPlaylistManager *mgr;
1981
1982 g_return_if_fail (object != NULL);
1983 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
1984
1985 rb_debug ("Finalizing playlist manager");
1986
1987 mgr = RB_PLAYLIST_MANAGER (object);
1988
1989 g_return_if_fail (mgr->priv != NULL);
1990
1991 g_free (mgr->priv->playlists_file);
1992
1993 G_OBJECT_CLASS (rb_playlist_manager_parent_class)->finalize (object);
1994 }
1995
1996
1997 static void
1998 rb_playlist_manager_class_init (RBPlaylistManagerClass *klass)
1999 {
2000 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2001
2002 object_class->constructed = rb_playlist_manager_constructed;
2003 object_class->dispose = rb_playlist_manager_dispose;
2004 object_class->finalize = rb_playlist_manager_finalize;
2005
2006 object_class->set_property = rb_playlist_manager_set_property;
2007 object_class->get_property = rb_playlist_manager_get_property;
2008
2009 g_object_class_install_property (object_class,
2010 PROP_PLAYLIST_NAME,
2011 g_param_spec_string ("playlists_file",
2012 "name",
2013 "playlists file",
2014 NULL,
2015 G_PARAM_READWRITE));
2016
2017 g_object_class_install_property (object_class,
2018 PROP_SOURCE,
2019 g_param_spec_object ("source",
2020 "RBSource",
2021 "RBSource object",
2022 RB_TYPE_SOURCE,
2023 G_PARAM_READWRITE));
2024
2025 g_object_class_install_property (object_class,
2026 PROP_SHELL,
2027 g_param_spec_object ("shell",
2028 "RBShell",
2029 "RBShell object",
2030 RB_TYPE_SHELL,
2031 G_PARAM_READWRITE));
2032
2033 g_object_class_install_property (object_class,
2034 PROP_DISPLAY_PAGE_MODEL,
2035 g_param_spec_object ("display-page-model",
2036 "RBDisplayPageModel",
2037 "RBDisplayPageModel",
2038 RB_TYPE_DISPLAY_PAGE_MODEL,
2039 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2040 g_object_class_install_property (object_class,
2041 PROP_DISPLAY_PAGE_TREE,
2042 g_param_spec_object ("display-page-tree",
2043 "RBDisplayPageTree",
2044 "RBDisplayPageTree",
2045 RB_TYPE_DISPLAY_PAGE_TREE,
2046 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2047 /**
2048 * RBPlaylistManager::playlist-added:
2049 * @manager: the #RBPlaylistManager
2050 * @source: the new #RBSource
2051 *
2052 * Emitted when a playlist is added, including when being loaded
2053 * from the user's playlist file.
2054 */
2055 rb_playlist_manager_signals[PLAYLIST_ADDED] =
2056 g_signal_new ("playlist_added",
2057 RB_TYPE_PLAYLIST_MANAGER,
2058 G_SIGNAL_RUN_LAST,
2059 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_added),
2060 NULL, NULL,
2061 g_cclosure_marshal_VOID__OBJECT,
2062 G_TYPE_NONE,
2063 1, G_TYPE_OBJECT);
2064
2065 /**
2066 * RBPlaylistManager::playlist-created:
2067 * @manager: the #RBPlaylistManager
2068 * @source: the newly created playlist #RBSource
2069 *
2070 * Emitted when a new playlist is created.
2071 */
2072 rb_playlist_manager_signals[PLAYLIST_CREATED] =
2073 g_signal_new ("playlist_created",
2074 RB_TYPE_PLAYLIST_MANAGER,
2075 G_SIGNAL_RUN_LAST,
2076 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_created),
2077 NULL, NULL,
2078 g_cclosure_marshal_VOID__OBJECT,
2079 G_TYPE_NONE,
2080 1, G_TYPE_OBJECT);
2081
2082 /**
2083 * RBPlaylistManager::load-start:
2084 * @manager: the #RBPlaylistManager
2085 *
2086 * Emitted when the playlist manager starts loading the user's
2087 * playlist file.
2088 */
2089 rb_playlist_manager_signals[PLAYLIST_LOAD_START] =
2090 g_signal_new ("load_start",
2091 RB_TYPE_PLAYLIST_MANAGER,
2092 G_SIGNAL_RUN_LAST,
2093 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_start),
2094 NULL, NULL,
2095 g_cclosure_marshal_VOID__VOID,
2096 G_TYPE_NONE,
2097 0, G_TYPE_NONE);
2098 /**
2099 * RBPlaylistManager::load-finish:
2100 * @manager: the #RBPlaylistManager
2101 *
2102 * Emitted when the playlist manager finishes loading the user's
2103 * playlist file.
2104 */
2105 rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH] =
2106 g_signal_new ("load_finish",
2107 RB_TYPE_PLAYLIST_MANAGER,
2108 G_SIGNAL_RUN_LAST,
2109 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_finish),
2110 NULL, NULL,
2111 g_cclosure_marshal_VOID__VOID,
2112 G_TYPE_NONE,
2113 0, G_TYPE_NONE);
2114
2115 g_type_class_add_private (klass, sizeof (RBPlaylistManagerPrivate));
2116 }