2006-12-14 Francisco Javier F. Serrador <serrador@openshine.com>
[rhythmbox.git] / shell / rb-playlist-manager.c
blobc463579178da6188674bfeba21ba42abe3786b96
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of Rhythmbox playlist management object
5 * Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <string.h>
26 #include <stdio.h> /* rename() */
27 #include <unistd.h> /* unlink() */
29 #include <libxml/tree.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <libgnomevfs/gnome-vfs.h>
33 #include <libgnomevfs/gnome-vfs-mime-utils.h>
35 #include "rb-playlist-manager.h"
36 #include "rb-playlist-source.h"
37 #include "rb-static-playlist-source.h"
38 #include "rb-auto-playlist-source.h"
39 #include "rb-play-queue-source.h"
40 #include "rb-sourcelist.h"
41 #include "rb-sourcelist-model.h"
42 #include "rb-query-creator.h"
43 #include "totem-pl-parser.h"
45 #include "rb-file-helpers.h"
46 #include "rb-debug.h"
47 #include "rb-dialog.h"
48 #include "rhythmdb.h"
49 #include "rb-stock-icons.h"
50 #include "eel-gconf-extensions.h"
51 #include "rb-glade-helpers.h"
52 #include "rb-util.h"
54 #define RB_PLAYLIST_MGR_VERSION (xmlChar *) "1.0"
55 #define RB_PLAYLIST_MGR_PL (xmlChar *) "rhythmdb-playlists"
57 static void rb_playlist_manager_class_init (RBPlaylistManagerClass *klass);
58 static void rb_playlist_manager_init (RBPlaylistManager *mgr);
59 static void rb_playlist_manager_dispose (GObject *object);
60 static void rb_playlist_manager_finalize (GObject *object);
61 static void rb_playlist_manager_set_property (GObject *object,
62 guint prop_id,
63 const GValue *value,
64 GParamSpec *pspec);
65 static void rb_playlist_manager_get_property (GObject *object,
66 guint prop_id,
67 GValue *value,
68 GParamSpec *pspec);
69 static void rb_playlist_manager_cmd_load_playlist (GtkAction *action,
70 RBPlaylistManager *mgr);
71 static void rb_playlist_manager_cmd_save_playlist (GtkAction *action,
72 RBPlaylistManager *mgr);
73 static void rb_playlist_manager_cmd_new_playlist (GtkAction *action,
74 RBPlaylistManager *mgr);
75 static void rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
76 RBPlaylistManager *mgr);
77 static void rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
78 RBPlaylistManager *mgr);
79 static void rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
80 RBPlaylistManager *mgr);
81 static void rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
82 RBPlaylistManager *mgr);
83 static void rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
84 RBPlaylistManager *mgr);
86 struct RBPlaylistManagerPrivate
88 RhythmDB *db;
89 RBShell *shell;
90 RBSource *selected_source;
92 char *playlists_file;
94 RBSourceList *sourcelist;
96 GtkActionGroup *actiongroup;
97 GtkUIManager *uimanager;
99 GtkWindow *window;
101 RBStaticPlaylistSource *loading_playlist;
103 gint dirty;
104 gint saving;
105 GMutex *saving_mutex;
108 enum
110 PROP_0,
111 PROP_PLAYLIST_NAME,
112 PROP_SHELL,
113 PROP_SOURCE,
114 PROP_SOURCELIST,
117 enum
119 PLAYLIST_ADDED,
120 PLAYLIST_CREATED,
121 PLAYLIST_LOAD_START,
122 PLAYLIST_LOAD_FINISH,
123 LAST_SIGNAL,
126 static guint rb_playlist_manager_signals[LAST_SIGNAL] = { 0 };
128 static GtkActionEntry rb_playlist_manager_actions [] =
130 /* Submenu of Music */
131 { "Playlist", NULL, N_("_Playlist") },
133 { "MusicPlaylistNewPlaylist", GNOME_MEDIA_PLAYLIST, N_("_New Playlist"), "<control>N",
134 N_("Create a new playlist"),
135 G_CALLBACK (rb_playlist_manager_cmd_new_playlist) },
136 { "MusicPlaylistNewAutomaticPlaylist", GNOME_MEDIA_AUTO_PLAYLIST, N_("New _Automatic Playlist..."), NULL,
137 N_("Create a new automatically updating playlist"),
138 G_CALLBACK (rb_playlist_manager_cmd_new_automatic_playlist) },
139 { "MusicPlaylistLoadPlaylist", NULL, N_("_Load from File..."), NULL,
140 N_("Choose a playlist to be loaded"),
141 G_CALLBACK (rb_playlist_manager_cmd_load_playlist) },
142 { "MusicPlaylistSavePlaylist", GTK_STOCK_SAVE_AS, N_("_Save to File..."), NULL,
143 N_("Save a playlist to a file"),
144 G_CALLBACK (rb_playlist_manager_cmd_save_playlist) },
145 { "MusicPlaylistRenamePlaylist", NULL, N_("_Rename"), NULL,
146 N_("Rename playlist"),
147 G_CALLBACK (rb_playlist_manager_cmd_rename_playlist) },
148 { "MusicPlaylistDeletePlaylist", GTK_STOCK_REMOVE, N_("_Delete"), NULL,
149 N_("Delete playlist"),
150 G_CALLBACK (rb_playlist_manager_cmd_delete_playlist) },
151 { "EditAutomaticPlaylist", GTK_STOCK_PROPERTIES, N_("_Edit..."), NULL,
152 N_("Change this automatic playlist"),
153 G_CALLBACK (rb_playlist_manager_cmd_edit_automatic_playlist) },
154 { "QueuePlaylist", NULL, N_("_Queue All Tracks"), NULL,
155 N_("Add all tracks in this playlist to the queue"),
156 G_CALLBACK (rb_playlist_manager_cmd_queue_playlist) },
158 static guint rb_playlist_manager_n_actions = G_N_ELEMENTS (rb_playlist_manager_actions);
160 G_DEFINE_TYPE (RBPlaylistManager, rb_playlist_manager, G_TYPE_OBJECT)
162 static void
163 rb_playlist_manager_class_init (RBPlaylistManagerClass *klass)
165 GObjectClass *object_class = G_OBJECT_CLASS (klass);
167 object_class->dispose = rb_playlist_manager_dispose;
168 object_class->finalize = rb_playlist_manager_finalize;
170 object_class->set_property = rb_playlist_manager_set_property;
171 object_class->get_property = rb_playlist_manager_get_property;
173 g_object_class_install_property (object_class,
174 PROP_PLAYLIST_NAME,
175 g_param_spec_string ("playlists_file",
176 "name",
177 "playlists file",
178 NULL,
179 G_PARAM_READWRITE));
181 g_object_class_install_property (object_class,
182 PROP_SOURCE,
183 g_param_spec_object ("source",
184 "RBSource",
185 "RBSource object",
186 RB_TYPE_SOURCE,
187 G_PARAM_READWRITE));
189 g_object_class_install_property (object_class,
190 PROP_SHELL,
191 g_param_spec_object ("shell",
192 "RBShell",
193 "RBShell object",
194 RB_TYPE_SHELL,
195 G_PARAM_READWRITE));
197 g_object_class_install_property (object_class,
198 PROP_SOURCELIST,
199 g_param_spec_object ("sourcelist",
200 "RBSourceList",
201 "RBSourceList",
202 RB_TYPE_SOURCELIST,
203 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
204 rb_playlist_manager_signals[PLAYLIST_ADDED] =
205 g_signal_new ("playlist_added",
206 RB_TYPE_PLAYLIST_MANAGER,
207 G_SIGNAL_RUN_LAST,
208 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_added),
209 NULL, NULL,
210 g_cclosure_marshal_VOID__OBJECT,
211 G_TYPE_NONE,
212 1, G_TYPE_OBJECT);
214 rb_playlist_manager_signals[PLAYLIST_CREATED] =
215 g_signal_new ("playlist_created",
216 RB_TYPE_PLAYLIST_MANAGER,
217 G_SIGNAL_RUN_LAST,
218 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_created),
219 NULL, NULL,
220 g_cclosure_marshal_VOID__OBJECT,
221 G_TYPE_NONE,
222 1, G_TYPE_OBJECT);
224 rb_playlist_manager_signals[PLAYLIST_LOAD_START] =
225 g_signal_new ("load_start",
226 RB_TYPE_PLAYLIST_MANAGER,
227 G_SIGNAL_RUN_LAST,
228 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_start),
229 NULL, NULL,
230 g_cclosure_marshal_VOID__VOID,
231 G_TYPE_NONE,
232 0, G_TYPE_NONE);
233 rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH] =
234 g_signal_new ("load_finish",
235 RB_TYPE_PLAYLIST_MANAGER,
236 G_SIGNAL_RUN_LAST,
237 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_finish),
238 NULL, NULL,
239 g_cclosure_marshal_VOID__VOID,
240 G_TYPE_NONE,
241 0, G_TYPE_NONE);
243 g_type_class_add_private (klass, sizeof (RBPlaylistManagerPrivate));
246 static void
247 rb_playlist_manager_init (RBPlaylistManager *mgr)
249 mgr->priv = G_TYPE_INSTANCE_GET_PRIVATE (mgr,
250 RB_TYPE_PLAYLIST_MANAGER,
251 RBPlaylistManagerPrivate);
253 mgr->priv->saving_mutex = g_mutex_new ();
254 mgr->priv->dirty = 0;
255 mgr->priv->saving = 0;
258 void
259 rb_playlist_manager_shutdown (RBPlaylistManager *mgr)
261 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (mgr));
263 g_mutex_lock (mgr->priv->saving_mutex);
264 g_mutex_unlock (mgr->priv->saving_mutex);
267 static void
268 rb_playlist_manager_dispose (GObject *object)
270 RBPlaylistManager *mgr;
272 g_return_if_fail (object != NULL);
273 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
275 rb_debug ("Disposing playlist manager");
277 mgr = RB_PLAYLIST_MANAGER (object);
279 g_return_if_fail (mgr->priv != NULL);
281 if (mgr->priv->db != NULL) {
282 g_object_unref (mgr->priv->db);
283 mgr->priv->db = NULL;
286 if (mgr->priv->uimanager != NULL) {
287 g_object_unref (mgr->priv->uimanager);
288 mgr->priv->uimanager = NULL;
291 if (mgr->priv->sourcelist != NULL) {
292 g_object_unref (mgr->priv->sourcelist);
293 mgr->priv->sourcelist = NULL;
296 if (mgr->priv->selected_source != NULL) {
297 g_object_unref (mgr->priv->selected_source);
298 mgr->priv->selected_source = NULL;
301 G_OBJECT_CLASS (rb_playlist_manager_parent_class)->dispose (object);
304 static void
305 rb_playlist_manager_finalize (GObject *object)
307 RBPlaylistManager *mgr;
309 g_return_if_fail (object != NULL);
310 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
312 rb_debug ("Finalizing playlist manager");
314 mgr = RB_PLAYLIST_MANAGER (object);
316 g_return_if_fail (mgr->priv != NULL);
318 g_mutex_free (mgr->priv->saving_mutex);
320 g_free (mgr->priv->playlists_file);
322 G_OBJECT_CLASS (rb_playlist_manager_parent_class)->finalize (object);
325 static void
326 rb_playlist_manager_set_uimanager (RBPlaylistManager *mgr,
327 GtkUIManager *uimanager)
329 if (mgr->priv->uimanager != NULL) {
330 if (mgr->priv->actiongroup != NULL) {
331 gtk_ui_manager_remove_action_group (mgr->priv->uimanager,
332 mgr->priv->actiongroup);
334 g_object_unref (mgr->priv->uimanager);
337 mgr->priv->uimanager = uimanager;
339 if (mgr->priv->actiongroup == NULL) {
340 mgr->priv->actiongroup = gtk_action_group_new ("PlaylistActions");
341 gtk_action_group_set_translation_domain (mgr->priv->actiongroup,
342 GETTEXT_PACKAGE);
343 gtk_action_group_add_actions (mgr->priv->actiongroup,
344 rb_playlist_manager_actions,
345 rb_playlist_manager_n_actions,
346 mgr);
349 gtk_ui_manager_insert_action_group (mgr->priv->uimanager,
350 mgr->priv->actiongroup,
354 static void
355 rb_playlist_manager_set_source (RBPlaylistManager *mgr,
356 RBSource *source)
358 gboolean playlist_active;
359 gboolean playlist_local = FALSE;
360 gboolean party_mode;
361 gboolean can_save;
362 gboolean can_delete;
363 gboolean can_edit;
364 gboolean can_rename;
365 GtkAction *action;
367 party_mode = rb_shell_get_party_mode (mgr->priv->shell);
369 if (mgr->priv->selected_source != NULL) {
370 g_object_unref (mgr->priv->selected_source);
372 mgr->priv->selected_source = g_object_ref (source);
374 playlist_active = RB_IS_PLAYLIST_SOURCE (mgr->priv->selected_source);
375 if (playlist_active) {
376 g_object_get (mgr->priv->selected_source, "is-local", &playlist_local, NULL);
379 can_save = playlist_local && !party_mode;
380 action = gtk_action_group_get_action (mgr->priv->actiongroup,
381 "MusicPlaylistSavePlaylist");
382 gtk_action_set_visible (action, can_save);
384 can_delete = (playlist_local && !party_mode &&
385 !RB_IS_PLAY_QUEUE_SOURCE (mgr->priv->selected_source));
386 action = gtk_action_group_get_action (mgr->priv->actiongroup,
387 "MusicPlaylistDeletePlaylist");
388 gtk_action_set_visible (action, can_delete);
390 can_edit = (playlist_local && RB_IS_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source) &&
391 !party_mode);
392 action = gtk_action_group_get_action (mgr->priv->actiongroup,
393 "EditAutomaticPlaylist");
394 gtk_action_set_visible (action, can_edit);
396 can_rename = playlist_local && rb_source_can_rename (mgr->priv->selected_source);
397 action = gtk_action_group_get_action (mgr->priv->actiongroup,
398 "MusicPlaylistRenamePlaylist");
399 gtk_action_set_visible (action, can_rename);
402 static void
403 rb_playlist_manager_set_shell_internal (RBPlaylistManager *mgr,
404 RBShell *shell)
406 GtkUIManager *uimanager = NULL;
407 RhythmDB *db = NULL;
409 if (mgr->priv->db != NULL) {
410 g_object_unref (mgr->priv->db);
413 mgr->priv->shell = shell;
415 if (mgr->priv->shell != NULL) {
416 g_object_get (mgr->priv->shell,
417 "ui-manager", &uimanager,
418 "db", &db,
419 NULL);
422 mgr->priv->db = db;
423 rb_playlist_manager_set_uimanager (mgr, uimanager);
426 static void
427 rb_playlist_manager_set_property (GObject *object,
428 guint prop_id,
429 const GValue *value,
430 GParamSpec *pspec)
432 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
434 switch (prop_id) {
435 case PROP_PLAYLIST_NAME:
436 g_free (mgr->priv->playlists_file);
437 mgr->priv->playlists_file = g_strdup (g_value_get_string (value));
438 break;
439 case PROP_SOURCE:
440 rb_playlist_manager_set_source (mgr, g_value_get_object (value));
441 break;
442 case PROP_SHELL:
443 rb_playlist_manager_set_shell_internal (mgr, g_value_get_object (value));
444 break;
445 case PROP_SOURCELIST:
446 mgr->priv->sourcelist = g_value_get_object (value);
447 g_object_ref (mgr->priv->sourcelist);
448 mgr->priv->window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (mgr->priv->sourcelist)));
449 break;
450 default:
451 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
452 break;
456 static void
457 rb_playlist_manager_get_property (GObject *object,
458 guint prop_id,
459 GValue *value,
460 GParamSpec *pspec)
462 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
464 switch (prop_id) {
465 case PROP_PLAYLIST_NAME:
466 g_value_set_string (value, mgr->priv->playlists_file);
467 break;
468 case PROP_SOURCE:
469 g_value_set_object (value, mgr->priv->selected_source);
470 break;
471 case PROP_SHELL:
472 g_value_set_object (value, mgr->priv->shell);
473 break;
474 case PROP_SOURCELIST:
475 g_value_set_object (value, mgr->priv->sourcelist);
476 break;
477 default:
478 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479 break;
483 RBPlaylistManager *
484 rb_playlist_manager_new (RBShell *shell,
485 RBSourceList *sourcelist,
486 const char *playlists_file)
488 return g_object_new (RB_TYPE_PLAYLIST_MANAGER,
489 "shell", shell,
490 "sourcelist", sourcelist,
491 "playlists_file", playlists_file,
492 NULL);
495 GQuark
496 rb_playlist_manager_error_quark (void)
498 static GQuark quark = 0;
499 if (!quark)
500 quark = g_quark_from_static_string ("rb_playlist_manager_error");
502 return quark;
505 static void
506 handle_playlist_entry_cb (TotemPlParser *playlist,
507 const char *uri_maybe,
508 const char *title,
509 const char *genre,
510 RBPlaylistManager *mgr)
512 char *uri;
514 uri = rb_canonicalise_uri (uri_maybe);
515 g_return_if_fail (uri != NULL);
517 rb_debug ("adding uri %s (title %s, genre %s) from playlist",
518 uri, title, genre);
519 if (!rb_shell_add_uri (mgr->priv->shell,
520 uri,
521 title,
522 genre,
523 NULL))
524 return;
526 if (!mgr->priv->loading_playlist) {
527 mgr->priv->loading_playlist =
528 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, NULL, FALSE));
530 if (rb_source_want_uri (RB_SOURCE (mgr->priv->loading_playlist), uri) > 0) {
531 rb_debug ("adding uri %s to playlist", uri);
532 rb_static_playlist_source_add_location (mgr->priv->loading_playlist, uri, -1);
535 g_free (uri);
538 static void
539 playlist_load_start_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
541 rb_debug ("loading new playlist %s", title);
543 mgr->priv->loading_playlist =
544 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
547 static void
548 playlist_load_end_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
550 rb_debug ("finished loading playlist %s", title);
552 if (title) {
553 g_object_set (mgr->priv->loading_playlist, "name", title, NULL);
554 mgr->priv->loading_playlist = NULL;
559 * rb_playlist_manager_parse_file:
560 * @mgr: the #RBPlaylistManager
561 * @uri: URI of the playlist to load
562 * @error: returns a GError in case of error
564 * Parses a playlist file, adding entries to the database and to a new
565 * static playlist. If the playlist file includes a title, the static
566 * playlist created will have the same title.
568 * Returns TRUE on success
570 gboolean
571 rb_playlist_manager_parse_file (RBPlaylistManager *mgr, const char *uri, GError **error)
573 rb_debug ("loading playlist from %s", uri);
575 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_START], 0);
578 TotemPlParser *parser = totem_pl_parser_new ();
580 g_signal_connect_object (parser, "entry",
581 G_CALLBACK (handle_playlist_entry_cb),
582 mgr, 0);
584 g_signal_connect_object (parser, "playlist-start",
585 G_CALLBACK (playlist_load_start_cb),
586 mgr, 0);
588 g_signal_connect_object (parser, "playlist-end",
589 G_CALLBACK (playlist_load_end_cb),
590 mgr, 0);
592 if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
593 g_object_set (parser, "recurse", FALSE, NULL);
595 if (totem_pl_parser_parse (parser, uri, TRUE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
596 g_set_error (error,
597 RB_PLAYLIST_MANAGER_ERROR,
598 RB_PLAYLIST_MANAGER_ERROR_PARSE,
599 "%s",
600 _("The playlist file may be in an unknown format or corrupted."));
601 return FALSE;
603 if (mgr->priv->loading_playlist != NULL) {
604 char *name = NULL;
606 /* totem-plparser may not have given us the playlist name */
607 g_object_get (mgr->priv->loading_playlist, "name", &name, NULL);
608 if (name == NULL || name[0] == '\0') {
609 char *path;
611 rb_debug ("setting playlist name from file name");
612 path = g_filename_from_uri (uri, NULL, NULL);
613 if (path) {
614 name = g_path_get_basename (path);
615 g_object_set (mgr->priv->loading_playlist, "name", name, NULL);
616 g_free (path);
620 g_free (name);
621 mgr->priv->loading_playlist = NULL;
624 g_object_unref (parser);
627 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH], 0);
628 return TRUE;
631 static void
632 append_new_playlist_source (RBPlaylistManager *mgr, RBPlaylistSource *source)
634 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_ADDED], 0,
635 source);
639 * rb_playlist_manager_load_playlists
640 * @mgr: the #RBPlaylistManager
642 * Loads the user's playlists, or if the playlist file does not exists,
643 * reads the default playlist file. Should be called only once on startup.
645 void
646 rb_playlist_manager_load_playlists (RBPlaylistManager *mgr)
648 char *file;
649 xmlDocPtr doc;
650 xmlNodePtr root;
651 xmlNodePtr child;
652 gboolean exists;
654 exists = FALSE;
655 file = g_strdup (mgr->priv->playlists_file);
657 /* block saves until the playlists have loaded */
658 g_mutex_lock (mgr->priv->saving_mutex);
660 exists = g_file_test (file, G_FILE_TEST_EXISTS);
661 if (! exists) {
662 rb_debug ("personal playlists not found, loading defaults");
664 /* try global playlists */
665 g_free (file);
666 file = g_strdup (rb_file ("playlists.xml"));
667 exists = g_file_test (file, G_FILE_TEST_EXISTS);
670 if (! exists) {
671 rb_debug ("default playlists file not found");
672 goto out;
675 doc = xmlParseFile (file);
676 if (doc == NULL)
677 goto out;
679 root = xmlDocGetRootElement (doc);
681 for (child = root->children; child; child = child->next) {
682 RBSource *playlist;
684 if (xmlNodeIsText (child))
685 continue;
687 playlist = rb_playlist_source_new_from_xml (mgr->priv->shell,
688 child);
689 if (playlist)
690 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
693 xmlFreeDoc (doc);
694 out:
695 g_mutex_unlock (mgr->priv->saving_mutex);
696 g_free (file);
699 static void
700 rb_playlist_manager_set_dirty (RBPlaylistManager *mgr, gboolean dirty)
702 g_atomic_int_compare_and_exchange (&mgr->priv->dirty, dirty == FALSE, dirty == TRUE);
705 /* returns TRUE if a playlist has been created, modified, or deleted since last save */
706 static gboolean
707 rb_playlist_manager_is_dirty (RBPlaylistManager *mgr)
709 gboolean dirty = FALSE;
710 GtkTreeModel *fmodel;
711 GtkTreeModel *model;
712 GtkTreeIter iter;
714 g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
715 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
716 g_object_unref (fmodel);
718 if (gtk_tree_model_get_iter_first (model, &iter)) {
719 do {
720 RBSource *source;
721 gboolean local;
723 gtk_tree_model_get (model,
724 &iter,
725 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
726 &source,
727 -1);
728 if (source == NULL) {
729 continue;
731 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
732 g_object_unref (source);
733 continue;
736 g_object_get (source,
737 "is-local", &local,
738 NULL);
739 if (local) {
740 g_object_get (source, "dirty", &dirty, NULL);
741 if (dirty)
742 break;
744 g_object_unref (source);
745 } while (gtk_tree_model_iter_next (model, &iter));
748 if (!dirty)
749 dirty = g_atomic_int_get (&mgr->priv->dirty);
751 return dirty;
754 struct RBPlaylistManagerSaveData
756 RBPlaylistManager *mgr;
757 xmlDocPtr doc;
760 static gpointer
761 rb_playlist_manager_save_data (struct RBPlaylistManagerSaveData *data)
763 char *file;
764 char *tmpname;
766 g_mutex_lock (data->mgr->priv->saving_mutex);
768 file = g_strdup (data->mgr->priv->playlists_file);
769 tmpname = g_strconcat (file, ".tmp", NULL);
771 if (xmlSaveFormatFile (tmpname, data->doc, 1) != -1) {
772 rename (tmpname, file);
773 } else {
774 rb_debug ("error in xmlSaveFormatFile(), not saving");
775 unlink (tmpname);
776 rb_playlist_manager_set_dirty (data->mgr, TRUE);
778 xmlFreeDoc (data->doc);
779 g_free (tmpname);
780 g_free (file);
782 g_atomic_int_compare_and_exchange (&data->mgr->priv->saving, 1, 0);
783 g_mutex_unlock (data->mgr->priv->saving_mutex);
785 g_object_unref (data->mgr);
787 g_free (data);
788 return NULL;
792 * rb_playlist_manager_save_playlists
793 * @mgr: the #RBPlaylistManager
794 * @force: if TRUE, save playlists synchronously and unconditionally
796 * Saves the user's playlists. If the force flag is
797 * TRUE, the playlists will always be saved. Otherwise, the playlists
798 * will only be saved if a playlist has been created, modified, or deleted
799 * since the last time the playlists were saved, and no save operation is
800 * currently taking place.
802 * Returns TRUE if a playlist save operation has been started
804 gboolean
805 rb_playlist_manager_save_playlists (RBPlaylistManager *mgr, gboolean force)
807 xmlNodePtr root;
808 struct RBPlaylistManagerSaveData *data;
809 GtkTreeIter iter;
810 GtkTreeModel *fmodel;
811 GtkTreeModel *model;
813 if (!force && !rb_playlist_manager_is_dirty (mgr)) {
814 /* playlists already in sync, so don't bother */
815 return FALSE;
818 if (!g_atomic_int_compare_and_exchange (&mgr->priv->saving, 0, 1) && !force) {
819 /* already saving, so don't bother */
820 return FALSE;
823 data = g_new0 (struct RBPlaylistManagerSaveData, 1);
824 data->mgr = mgr;
825 data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
826 g_object_ref (mgr);
828 root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
829 xmlDocSetRootElement (data->doc, root);
831 g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
832 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
833 g_object_unref (fmodel);
835 if (gtk_tree_model_get_iter_first (model, &iter)) {
836 do {
837 RBSource *source;
838 gboolean local;
840 gtk_tree_model_get (model,
841 &iter,
842 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
843 &source,
844 -1);
845 if (source == NULL) {
846 continue;
848 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
849 g_object_unref (source);
850 continue;
853 g_object_get (source, "is-local", &local, NULL);
854 if (local)
855 rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (source), root);
857 g_object_unref (source);
858 } while (gtk_tree_model_iter_next (model, &iter));
861 /* mark clean here. if the save fails, we'll mark it dirty again */
862 rb_playlist_manager_set_dirty (data->mgr, FALSE);
864 if (force)
865 rb_playlist_manager_save_data (data);
866 else
867 g_thread_create ((GThreadFunc) rb_playlist_manager_save_data, data, FALSE, NULL);
869 return TRUE;
873 * rb_playlist_manager_new_playlist
874 * @mgr: the #RBPlaylistManager
875 * @suggested_name: optional name to use for the new playlist
876 * @automatic: if TRUE, create an auto playlist
878 * Creates a new playlist and adds it to the source list.
880 * Returns the new playlist object.
882 RBSource *
883 rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
884 const char *suggested_name,
885 gboolean automatic)
887 RBSource *playlist;
888 if (automatic)
889 playlist = rb_auto_playlist_source_new (mgr->priv->shell,
890 suggested_name,
891 TRUE);
892 else
893 playlist = rb_static_playlist_source_new (mgr->priv->shell,
894 suggested_name,
895 TRUE,
896 RHYTHMDB_ENTRY_TYPE_SONG);
898 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
899 rb_sourcelist_edit_source_name (mgr->priv->sourcelist, playlist);
900 rb_playlist_manager_set_dirty (mgr, TRUE);
902 g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_CREATED], 0,
903 playlist);
905 return playlist;
908 static char *
909 create_name_from_selection_data (RBPlaylistManager *mgr,
910 GtkSelectionData *data)
912 char *name = NULL;
913 GList *list;
915 if (data->type == gdk_atom_intern ("text/uri-list", TRUE) ||
916 data->type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE)) {
917 gboolean is_id;
918 list = rb_uri_list_parse ((const char *)data->data);
919 is_id = (data->type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE));
921 if (list != NULL) {
922 GList *l;
923 char *artist;
924 char *album;
925 gboolean mixed_artists;
926 gboolean mixed_albums;
928 artist = NULL;
929 album = NULL;
930 mixed_artists = FALSE;
931 mixed_albums = FALSE;
932 for (l = list; l != NULL; l = g_list_next (l)) {
933 RhythmDBEntry *entry;
934 const char *e_artist;
935 const char *e_album;
937 entry = rhythmdb_entry_lookup_from_string (mgr->priv->db,
938 (const char *)l->data,
939 is_id);
940 if (entry == NULL) {
941 continue;
944 e_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
945 e_album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
947 /* get value of first non-NULL artist */
948 if (e_artist != NULL && artist == NULL) {
949 artist = g_strdup (e_artist);
952 /* get value of first non-NULL album */
953 if (e_album != NULL && album == NULL) {
954 album = g_strdup (e_album);
957 /* pretend that NULL fields always match */
958 if (artist != NULL && e_artist != NULL
959 && strcmp (artist, e_artist) != 0) {
960 mixed_artists = TRUE;
963 /* pretend that NULL fields always match */
964 if (album != NULL && e_album != NULL
965 && strcmp (album, e_album) != 0) {
966 mixed_albums = TRUE;
969 /* if there is a mix of both then stop */
970 if (mixed_artists && mixed_albums) {
971 break;
975 if (! mixed_artists && ! mixed_albums) {
976 name = g_strdup_printf ("%s - %s", artist, album);
977 } else if (! mixed_artists) {
978 name = g_strdup_printf ("%s", artist);
979 } else if (! mixed_albums) {
980 name = g_strdup_printf ("%s", album);
983 g_free (artist);
984 g_free (album);
985 rb_list_deep_free (list);
988 } else {
989 char **names;
991 names = g_strsplit ((char *)data->data, "\r\n", 0);
992 name = g_strjoinv (", ", names);
993 g_strfreev (names);
996 if (name == NULL) {
997 name = g_strdup (_("Untitled Playlist"));
1000 return name;
1004 * rb_playlist_manager_new_playlist_from_selection_data
1005 * @mgr: the #RBPlaylistManager
1006 * @data: the #GtkSelectionData from which to create a playlist
1008 * Creates a new playlist based on selection data from gtk.
1009 * Used to implement playlist creation through drag and drop
1010 * to the source list.
1012 * Returns the new playlist.
1014 RBSource *
1015 rb_playlist_manager_new_playlist_from_selection_data (RBPlaylistManager *mgr,
1016 GtkSelectionData *data)
1018 RBSource *playlist;
1019 gboolean automatic = TRUE;
1020 char *suggested_name;
1022 if (data->type == gdk_atom_intern ("text/uri-list", TRUE) ||
1023 data->type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE))
1024 automatic = FALSE;
1025 suggested_name = create_name_from_selection_data (mgr, data);
1027 playlist = rb_playlist_manager_new_playlist (mgr,
1028 suggested_name,
1029 automatic);
1030 g_free (suggested_name);
1032 return playlist;
1035 static void
1036 rb_playlist_manager_cmd_new_playlist (GtkAction *action,
1037 RBPlaylistManager *mgr)
1039 rb_playlist_manager_new_playlist (mgr, NULL, FALSE);
1042 static void
1043 rb_playlist_manager_set_automatic_playlist (RBPlaylistManager *mgr,
1044 RBAutoPlaylistSource *playlist,
1045 RBQueryCreator *creator)
1047 RhythmDBQueryModelLimitType limit_type;
1048 GValueArray *limit_value = NULL;
1049 const char *sort_key;
1050 gint sort_direction;
1051 GPtrArray *query;
1053 rb_query_creator_get_limit (creator, &limit_type, &limit_value);
1054 rb_query_creator_get_sort_order (creator,
1055 &sort_key,
1056 &sort_direction);
1058 query = rb_query_creator_get_query (creator);
1059 rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (playlist),
1060 query,
1061 limit_type,
1062 limit_value,
1063 sort_key,
1064 sort_direction);
1065 rhythmdb_query_free (query);
1068 static void
1069 rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
1070 RBPlaylistManager *mgr)
1072 RBQueryCreator *creator = RB_QUERY_CREATOR (rb_query_creator_new (mgr->priv->db));
1073 RBSource *playlist;
1075 switch (gtk_dialog_run (GTK_DIALOG (creator))) {
1076 case GTK_RESPONSE_NONE:
1077 case GTK_RESPONSE_CLOSE:
1078 gtk_widget_destroy (GTK_WIDGET (creator));
1079 return;
1082 playlist = rb_playlist_manager_new_playlist (mgr, NULL, TRUE);
1084 rb_playlist_manager_set_automatic_playlist (mgr, RB_AUTO_PLAYLIST_SOURCE (playlist), creator);
1086 rb_playlist_manager_set_dirty (mgr, TRUE);
1088 gtk_widget_destroy (GTK_WIDGET (creator));
1091 typedef struct {
1092 RBAutoPlaylistSource *playlist;
1093 RBPlaylistManager *mgr;
1094 RBQueryCreator *creator;
1095 gint playlist_deleted_id;
1096 gint creator_response_id;
1097 } EditAutoPlaylistData;
1099 static void
1100 cleanup_edit_data (EditAutoPlaylistData *data)
1102 g_signal_handler_disconnect (data->playlist, data->playlist_deleted_id);
1103 g_signal_handler_disconnect (data->creator, data->creator_response_id);
1104 gtk_widget_destroy (GTK_WIDGET (data->creator));
1105 g_free (data);
1108 static void
1109 edit_auto_playlist_response_cb (RBQueryCreator *dialog,
1110 gint response,
1111 EditAutoPlaylistData *data)
1113 rb_playlist_manager_set_automatic_playlist (data->mgr, data->playlist, dialog);
1114 g_object_set_data (G_OBJECT (data->playlist), "rhythmbox-playlist-editor", NULL);
1116 cleanup_edit_data (data);
1119 static void
1120 edit_auto_playlist_deleted_cb (RBAutoPlaylistSource *playlist, EditAutoPlaylistData *data)
1122 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", NULL);
1124 cleanup_edit_data (data);
1127 static void
1128 rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
1129 RBPlaylistManager *mgr)
1131 RBQueryCreator *creator;
1132 RBAutoPlaylistSource *playlist;
1134 playlist = RB_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source);
1135 creator = g_object_get_data (G_OBJECT (playlist), "rhythmbox-playlist-editor");
1136 if (creator == NULL) {
1137 RhythmDBQueryModelLimitType limit_type;
1138 GValueArray *limit_value = NULL;
1139 GPtrArray *query;
1140 char *sort_key;
1141 gint sort_direction;
1142 EditAutoPlaylistData *data;
1144 sort_key = NULL;
1145 rb_auto_playlist_source_get_query (playlist,
1146 &query,
1147 &limit_type,
1148 &limit_value,
1149 &sort_key,
1150 &sort_direction);
1152 creator = RB_QUERY_CREATOR (rb_query_creator_new_from_query (mgr->priv->db,
1153 query,
1154 limit_type,
1155 limit_value,
1156 sort_key,
1157 sort_direction));
1158 if (limit_value != NULL) {
1159 g_value_array_free (limit_value);
1161 rhythmdb_query_free (query);
1162 g_free (sort_key);
1164 data = g_new0 (EditAutoPlaylistData, 1);
1165 data->mgr = mgr;
1166 data->playlist = playlist;
1167 data->creator = creator;
1168 data->creator_response_id =
1169 g_signal_connect (creator,
1170 "response",
1171 G_CALLBACK (edit_auto_playlist_response_cb),
1172 data);
1174 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", creator);
1175 data->playlist_deleted_id =
1176 g_signal_connect (playlist,
1177 "deleted",
1178 G_CALLBACK (edit_auto_playlist_deleted_cb),
1179 data);
1181 gtk_window_present (GTK_WINDOW (creator));
1184 static gboolean
1185 _queue_track_cb (RhythmDBQueryModel *model,
1186 GtkTreePath *path,
1187 GtkTreeIter *iter,
1188 RBStaticPlaylistSource *queue_source)
1190 RhythmDBEntry *entry;
1192 entry = rhythmdb_query_model_iter_to_entry (model, iter);
1193 rb_static_playlist_source_add_entry (queue_source, entry, -1);
1194 rhythmdb_entry_unref (entry);
1196 return FALSE;
1199 static void
1200 rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
1201 RBPlaylistManager *mgr)
1203 RBSource *queue_source;
1204 RhythmDBQueryModel *model;
1206 g_object_get (mgr->priv->shell, "queue-source", &queue_source, NULL);
1207 g_object_get (mgr->priv->selected_source, "query-model", &model, NULL);
1209 gtk_tree_model_foreach (GTK_TREE_MODEL (model),
1210 (GtkTreeModelForeachFunc) _queue_track_cb,
1211 queue_source);
1213 g_object_unref (queue_source);
1214 g_object_unref (model);
1217 static void
1218 rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
1219 RBPlaylistManager *mgr)
1221 rb_debug ("Renaming playlist %p", mgr->priv->selected_source);
1223 rb_sourcelist_edit_source_name (mgr->priv->sourcelist, mgr->priv->selected_source);
1224 rb_playlist_manager_set_dirty (mgr, TRUE);
1227 static void
1228 rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
1229 RBPlaylistManager *mgr)
1231 rb_debug ("Deleting playlist %p", mgr->priv->selected_source);
1233 rb_source_delete_thyself (mgr->priv->selected_source);
1234 rb_playlist_manager_set_dirty (mgr, TRUE);
1237 static void
1238 load_playlist_response_cb (GtkDialog *dialog,
1239 int response_id,
1240 RBPlaylistManager *mgr)
1242 char *escaped_file = NULL;
1243 GError *error = NULL;
1245 if (response_id != GTK_RESPONSE_ACCEPT) {
1246 gtk_widget_destroy (GTK_WIDGET (dialog));
1247 return;
1250 escaped_file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1252 gtk_widget_destroy (GTK_WIDGET (dialog));
1254 if (escaped_file == NULL)
1255 return;
1257 if (!rb_playlist_manager_parse_file (mgr, escaped_file, &error)) {
1258 rb_error_dialog (NULL, _("Couldn't read playlist"),
1259 "%s", error->message);
1260 g_error_free (error);
1263 g_free (escaped_file);
1264 rb_playlist_manager_set_dirty (mgr, TRUE);
1267 static void
1268 rb_playlist_manager_cmd_load_playlist (GtkAction *action,
1269 RBPlaylistManager *mgr)
1271 GtkWidget *dialog;
1273 dialog = rb_file_chooser_new (_("Load Playlist"),
1274 GTK_WINDOW (mgr->priv->window),
1275 GTK_FILE_CHOOSER_ACTION_OPEN,
1276 FALSE);
1278 g_signal_connect_object (dialog, "response",
1279 G_CALLBACK (load_playlist_response_cb), mgr, 0);
1282 typedef struct {
1283 const gchar *description;
1284 /* NULL terminated array of extensions for this file format. The first
1285 * one is the prefered extension for files of this type. */
1286 const gchar **extensions;
1287 const RBPlaylistExportType type;
1288 } RBPlaylistExportFilter;
1290 static const char *m3u_extensions [] = {"m3u", NULL};
1291 static const char *pls_extensions [] = {"pls", NULL};
1293 static RBPlaylistExportFilter playlist_export_formats[] = {
1294 {N_("MPEG Version 3.0 URL"), m3u_extensions, RB_PLAYLIST_EXPORT_TYPE_M3U},
1295 {N_("Shoutcast playlist"), pls_extensions, RB_PLAYLIST_EXPORT_TYPE_PLS},
1298 static void
1299 save_playlist_response_cb (GtkDialog *dialog,
1300 int response_id,
1301 RBPlaylistManager *mgr)
1303 char *file = NULL;
1304 GtkWidget *menu;
1305 gint index;
1306 RBPlaylistExportType export_type = RB_PLAYLIST_EXPORT_TYPE_UNKNOWN;
1308 if (response_id != GTK_RESPONSE_OK) {
1309 gtk_widget_destroy (GTK_WIDGET (dialog));
1310 return;
1313 file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1314 if (file == NULL || file[0] == '\0')
1315 return;
1317 menu = g_object_get_data (G_OBJECT(dialog), "export-menu");
1318 index = gtk_combo_box_get_active (GTK_COMBO_BOX (menu));
1320 /* by extension selected */
1321 if (index <= 0) {
1322 int i;
1324 for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
1325 int j;
1327 /* determine the playlist type from the extension */
1328 for (j = 0; playlist_export_formats[i].extensions[j] != NULL; j++) {
1329 if (g_str_has_suffix (file, playlist_export_formats[i].extensions[j])) {
1330 export_type = playlist_export_formats[i].type;
1331 break;
1335 } else {
1336 export_type = playlist_export_formats[index-1].type;
1339 if (export_type == RB_PLAYLIST_EXPORT_TYPE_UNKNOWN) {
1340 rb_error_dialog (NULL, _("Couldn't save playlist"), _("Unsupported file extension given."));
1341 } else {
1342 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source),
1343 file, (export_type == RB_PLAYLIST_EXPORT_TYPE_M3U));
1344 gtk_widget_destroy (GTK_WIDGET (dialog));
1347 g_free (file);
1350 static void
1351 export_set_extension_cb (GtkWidget* widget, GtkDialog *dialog)
1353 gint index;
1354 gchar *text;
1355 gchar *last_dot;
1356 const char *extension;
1357 gchar *basename;
1358 GString *basename_str;
1360 index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1361 if (index <= 0)
1362 return;
1364 extension = playlist_export_formats[index-1].extensions[0];
1365 if (extension == NULL)
1366 return;
1368 text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1369 if (text == NULL || text[0] == '\0') {
1370 g_free (text);
1371 return;
1374 basename = g_path_get_basename (text);
1375 basename_str = g_string_new (basename);
1376 last_dot = g_utf8_strrchr (basename, -1, '.');
1377 if (last_dot)
1378 g_string_truncate (basename_str, (last_dot-basename));
1379 g_free (basename);
1380 g_free (text);
1382 g_string_append_printf (basename_str, ".%s", extension);
1383 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename_str->str);
1384 g_string_free (basename_str, TRUE);
1387 static gchar *
1388 filter_get_export_filter_label (RBPlaylistExportFilter *efilter)
1390 GString *str;
1391 gint ext;
1393 str = g_string_new (_(efilter->description));
1394 for (ext = 0; efilter->extensions[ext] != NULL; ext++) {
1395 if (ext == 0)
1396 g_string_append (str, " (*.");
1397 else
1398 g_string_append (str, ", *.");
1399 g_string_append (str, efilter->extensions[ext]);
1402 if (ext > 0)
1403 g_string_append (str, ")");
1405 return g_string_free (str, FALSE);
1408 static void
1409 setup_format_menu (GtkWidget* menu, GtkWidget *dialog)
1411 GtkTreeModel *model;
1412 int i;
1414 model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu));
1415 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu), rb_combo_box_hyphen_separator_func,
1416 NULL, NULL);
1418 for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
1419 gchar *filter_label;
1420 GtkTreeIter iter;
1422 filter_label = filter_get_export_filter_label (&playlist_export_formats[i]);
1423 gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, -1,
1424 0, filter_label, -1);
1426 g_free (filter_label);
1429 g_signal_connect_object (menu,
1430 "changed", G_CALLBACK (export_set_extension_cb),
1431 dialog, 0);
1434 static void
1435 rb_playlist_manager_cmd_save_playlist (GtkAction *action,
1436 RBPlaylistManager *mgr)
1438 GladeXML *xml;
1439 GtkWidget *dialog, *menu;
1440 char *name, *tmp;
1442 xml = rb_glade_xml_new ("playlist-save.glade",
1443 "playlist_save_dialog",
1444 mgr);
1445 dialog = glade_xml_get_widget (xml, "playlist_save_dialog");
1447 menu = glade_xml_get_widget (xml, "playlist_format_menu");
1448 setup_format_menu (menu, dialog);
1449 g_object_set_data (G_OBJECT (dialog), "export-menu", menu);
1451 g_object_get (mgr->priv->selected_source, "name", &name, NULL);
1452 tmp = g_strconcat (name, ".pls", NULL);
1453 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), tmp);
1454 g_free (tmp);
1455 g_free (name);
1457 /* FIXME: always has "by extension" as default (it should probably remember the last selection) */
1458 gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0);
1459 g_signal_connect_object (dialog, "response",
1460 G_CALLBACK (save_playlist_response_cb),
1461 mgr, 0);
1463 g_object_unref (xml);
1467 * rb_playlist_manager_get_playlists
1468 * @mgr: the #RBPlaylistManager
1470 * Returns a #GList containing all local playlist source objects.
1472 GList *
1473 rb_playlist_manager_get_playlists (RBPlaylistManager *mgr)
1475 GList *playlists = NULL;
1476 GtkTreeIter iter;
1477 GtkTreeModel *fmodel;
1478 GtkTreeModel *model;
1480 g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
1481 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1482 g_object_unref (fmodel);
1484 if (gtk_tree_model_get_iter_first (model, &iter)) {
1485 do {
1486 RBSource *source;
1487 gboolean local;
1489 gtk_tree_model_get (model,
1490 &iter,
1491 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1492 &source,
1493 -1);
1494 if (source == NULL) {
1495 continue;
1497 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE
1498 || RB_IS_PLAY_QUEUE_SOURCE (source) == TRUE) {
1499 g_object_unref (source);
1500 continue;
1503 g_object_get (source, "is-local", &local,
1504 NULL);
1505 if (local) {
1506 playlists = g_list_prepend (playlists, source);
1509 g_object_unref (source);
1510 } while (gtk_tree_model_iter_next (model, &iter));
1513 return playlists;
1517 * rb_playlist_manager_get_playlist_names
1518 * @mgr: the #RBPlaylistManager
1519 * @playlists: holds the array of playlist names on reutrn
1520 * @error: holds a #GError on return on failure
1522 * Allocates and returns an array containing the names of all local
1523 * playlists. This is part of the playlist manager dbus interface.
1525 * Returns TRUE if successful.
1527 gboolean
1528 rb_playlist_manager_get_playlist_names (RBPlaylistManager *mgr,
1529 gchar ***playlists,
1530 GError **error)
1532 GtkTreeIter iter;
1533 GtkTreeModel *fmodel;
1534 GtkTreeModel *model;
1535 int i;
1537 g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
1538 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1539 g_object_unref (fmodel);
1541 if (!gtk_tree_model_get_iter_first (model, &iter)) {
1542 *playlists = NULL;
1543 return TRUE;
1546 *playlists = g_new0 (char *, gtk_tree_model_iter_n_children (model, NULL) + 1);
1547 if (!*playlists)
1548 return FALSE;
1550 i = 0;
1551 do {
1552 RBSource *source;
1553 char *source_name;
1555 gtk_tree_model_get (model, &iter,
1556 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1557 &source,
1558 -1);
1559 if (source == NULL) {
1560 continue;
1562 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE
1563 || RB_IS_PLAY_QUEUE_SOURCE (source) == TRUE) {
1564 g_object_unref (source);
1565 continue;
1568 g_object_get (source, "name", &source_name, NULL);
1569 (*playlists)[i++] = source_name;
1571 g_object_unref (source);
1573 } while (gtk_tree_model_iter_next (model, &iter));
1575 return TRUE;
1578 static RBSource *
1579 _get_playlist_by_name (RBPlaylistManager *mgr,
1580 const char *name)
1582 GtkTreeIter iter;
1583 GtkTreeModel *fmodel;
1584 GtkTreeModel *model;
1585 RBSource *playlist = NULL;
1587 g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
1588 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1589 g_object_unref (fmodel);
1591 if (!gtk_tree_model_get_iter_first (model, &iter))
1592 return NULL;
1594 do {
1595 RBSource *source;
1596 char *source_name;
1598 gtk_tree_model_get (model, &iter,
1599 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1600 &source,
1601 -1);
1602 if (source == NULL) {
1603 continue;
1605 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
1606 g_object_unref (source);
1607 continue;
1609 g_object_get (source, "name", &source_name, NULL);
1610 if (strcmp (name, source_name) == 0)
1611 playlist = source;
1613 g_free (source_name);
1614 g_object_unref (source);
1615 } while (gtk_tree_model_iter_next (model, &iter) && playlist == NULL);
1617 return playlist;
1621 * rb_playlist_manager_create_static_playlist
1622 * @mgr: the #RBPlaylistManager
1623 * @name: name of the new playlist
1624 * @error: holds a #GError on return on failure
1626 * Creates a new static playlist source with the given name.
1627 * Will fail if a playlist with that name already exists.
1628 * This is part of the playlist manager dbus interface.
1630 * Returns TRUE if successful.
1632 gboolean
1633 rb_playlist_manager_create_static_playlist (RBPlaylistManager *mgr,
1634 const gchar *name,
1635 GError **error)
1637 if (_get_playlist_by_name (mgr, name)) {
1638 g_set_error (error,
1639 RB_PLAYLIST_MANAGER_ERROR,
1640 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_EXISTS,
1641 _("Playlist %s already exists"),
1642 name);
1643 return FALSE;
1646 rb_playlist_manager_new_playlist (mgr, name, FALSE);
1647 return TRUE;
1651 * rb_playlist_manager_delete_playlist
1652 * @mgr: the #RBPlaylistManager
1653 * @name: name of the playlist to delete
1654 * @error: holds a #GError on return on failure
1656 * Deletes the specified playlist. Will fail if no playlist with
1657 * that name exists. This is part of the playlist manager dbus interface.
1659 * Returns TRUE if successful.
1661 gboolean
1662 rb_playlist_manager_delete_playlist (RBPlaylistManager *mgr,
1663 const gchar *name,
1664 GError **error)
1666 RBSource *playlist = _get_playlist_by_name (mgr, name);
1667 if (!playlist) {
1668 g_set_error (error,
1669 RB_PLAYLIST_MANAGER_ERROR,
1670 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1671 _("Unknown playlist: %s"),
1672 name);
1673 return FALSE;
1675 rb_source_delete_thyself (playlist);
1676 rb_playlist_manager_set_dirty (mgr, TRUE);
1677 return TRUE;
1681 * rb_playlist_manager_add_to_playlist
1682 * @mgr: the #RBPlaylistManager
1683 * @name: name of the playlist to add to
1684 * @uri: URI of the entry to add to the playlist
1685 * @error: holds a #GError on return on failure
1687 * Adds an entry to the specified playlist.
1688 * Fails if no playlist with that name exists.
1689 * This is part of the playlist manager dbus interface.
1691 * Returns TRUE if successful.
1693 gboolean
1694 rb_playlist_manager_add_to_playlist (RBPlaylistManager *mgr,
1695 const gchar *playlist,
1696 const gchar *uri,
1697 GError **error)
1699 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1700 if (!source) {
1701 g_set_error (error,
1702 RB_PLAYLIST_MANAGER_ERROR,
1703 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1704 _("Unknown playlist: %s"),
1705 playlist);
1706 return FALSE;
1708 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1709 g_set_error (error,
1710 RB_PLAYLIST_MANAGER_ERROR,
1711 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1712 _("Playlist %s is an automatic playlist"),
1713 playlist);
1714 return FALSE;
1716 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (source), uri, -1);
1717 return TRUE;
1721 * rb_playlist_manager_remove_from_playlist
1722 * @mgr: the #RBPlaylistManager
1723 * @name: name of the playlist to remove from
1724 * @uri: URI of the entry to remove from the playlist
1725 * @error: holds a #GError on return on failure
1727 * Removes an entry from the specified playlist.
1728 * Fails if no playlist with that name exists.
1729 * This is part of the playlist manager dbus interface.
1731 * Returns TRUE if successful.
1733 gboolean
1734 rb_playlist_manager_remove_from_playlist (RBPlaylistManager *mgr,
1735 const gchar *playlist,
1736 const gchar *uri,
1737 GError **error)
1739 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1740 if (!source) {
1741 g_set_error (error,
1742 RB_PLAYLIST_MANAGER_ERROR,
1743 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1744 _("Unknown playlist: %s"),
1745 playlist);
1746 return FALSE;
1748 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1749 g_set_error (error,
1750 RB_PLAYLIST_MANAGER_ERROR,
1751 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1752 _("Playlist %s is an automatic playlist"),
1753 playlist);
1754 return FALSE;
1757 if (rb_playlist_source_location_in_map (RB_PLAYLIST_SOURCE (source), uri))
1758 rb_static_playlist_source_remove_location (RB_STATIC_PLAYLIST_SOURCE (source), uri);
1759 return TRUE;
1763 * rb_playlist_manager_export_playlist
1764 * @mgr: the #RBPlaylistManager
1765 * @name: name of the playlist to export
1766 * @uri: playlist save location
1767 * @m3u_format: if TRUE, save in M3U format, otherwise save in PLS format
1768 * @error: holds a #GError on return on failure
1770 * Saves the specified playlist to a file in either M3U or PLS format.
1771 * This is part of the playlist manager dbus interface.
1773 * Returns TRUE if successful.
1775 gboolean
1776 rb_playlist_manager_export_playlist (RBPlaylistManager *mgr,
1777 const gchar *playlist,
1778 const gchar *uri,
1779 gboolean m3u_format,
1780 GError **error)
1782 RBSource *source = _get_playlist_by_name (mgr, playlist);
1783 if (!source) {
1784 g_set_error (error,
1785 RB_PLAYLIST_MANAGER_ERROR,
1786 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1787 _("Unknown playlist: %s"),
1788 playlist);
1789 return FALSE;
1792 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (source),
1793 uri,
1794 m3u_format);
1795 return TRUE;