2006-05-08 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / rb-playlist-manager.c
blobe6b455ecf879d46148b68ab89c0cb05967a9a22f
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-recorder.h"
41 #include "rb-sourcelist.h"
42 #include "rb-sourcelist-model.h"
43 #include "rb-query-creator.h"
44 #include "totem-pl-parser.h"
46 #include "rb-file-helpers.h"
47 #include "rb-debug.h"
48 #include "rb-dialog.h"
49 #include "rhythmdb.h"
50 #include "rb-stock-icons.h"
51 #include "eel-gconf-extensions.h"
52 #include "rb-glade-helpers.h"
53 #include "rb-util.h"
55 #define RB_PLAYLIST_MGR_VERSION (xmlChar *) "1.0"
56 #define RB_PLAYLIST_MGR_PL (xmlChar *) "rhythmdb-playlists"
58 static void rb_playlist_manager_class_init (RBPlaylistManagerClass *klass);
59 static void rb_playlist_manager_init (RBPlaylistManager *mgr);
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_burn_playlist (GtkAction *action,
74 RBPlaylistManager *mgr);
75 static void rb_playlist_manager_cmd_new_playlist (GtkAction *action,
76 RBPlaylistManager *mgr);
77 static void rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
78 RBPlaylistManager *mgr);
79 static void rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
80 RBPlaylistManager *mgr);
81 static void rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
82 RBPlaylistManager *mgr);
83 static void rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
84 RBPlaylistManager *mgr);
85 static void rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
86 RBPlaylistManager *mgr);
87 static void rb_playlist_manager_playlist_entries_changed (GtkTreeModel *entry_view,
88 RhythmDBEntry *entry,
89 RBPlaylistManager *mgr);
91 struct RBPlaylistManagerPrivate
93 RhythmDB *db;
94 RBShell *shell;
95 RBSource *selected_source;
97 RBSourceList *sourcelist;
99 GtkActionGroup *actiongroup;
100 GtkUIManager *uimanager;
102 GtkWindow *window;
104 RBStaticPlaylistSource *loading_playlist;
106 gint dirty;
107 gint saving;
108 GMutex *saving_mutex;
111 enum
113 PROP_0,
114 PROP_SHELL,
115 PROP_SOURCE,
116 PROP_SOURCELIST,
119 enum
121 PLAYLIST_ADDED,
122 PLAYLIST_CREATED,
123 PLAYLIST_LOAD_START,
124 PLAYLIST_LOAD_FINISH,
125 LAST_SIGNAL,
128 static guint rb_playlist_manager_signals[LAST_SIGNAL] = { 0 };
130 static GtkActionEntry rb_playlist_manager_actions [] =
132 /* Submenu of Music */
133 { "Playlist", NULL, N_("_Playlist") },
135 { "MusicPlaylistNewPlaylist", GNOME_MEDIA_PLAYLIST, N_("_New Playlist"), "<control>N",
136 N_("Create a new playlist"),
137 G_CALLBACK (rb_playlist_manager_cmd_new_playlist) },
138 { "MusicPlaylistNewAutomaticPlaylist", GNOME_MEDIA_AUTO_PLAYLIST, N_("New _Automatic Playlist..."), NULL,
139 N_("Create a new automatically updating playlist"),
140 G_CALLBACK (rb_playlist_manager_cmd_new_automatic_playlist) },
141 { "MusicPlaylistLoadPlaylist", NULL, N_("_Load from File..."), NULL,
142 N_("Choose a playlist to be loaded"),
143 G_CALLBACK (rb_playlist_manager_cmd_load_playlist) },
144 { "MusicPlaylistSavePlaylist", GTK_STOCK_SAVE_AS, N_("_Save to File..."), NULL,
145 N_("Save a playlist to a file"),
146 G_CALLBACK (rb_playlist_manager_cmd_save_playlist) },
147 { "MusicPlaylistBurnPlaylist", GTK_STOCK_CDROM, N_("_Create Audio CD..."), NULL,
148 N_("Create an audio CD from playlist"),
149 G_CALLBACK (rb_playlist_manager_cmd_burn_playlist) },
150 { "MusicPlaylistRenamePlaylist", NULL, N_("_Rename"), NULL,
151 N_("Rename playlist"),
152 G_CALLBACK (rb_playlist_manager_cmd_rename_playlist) },
153 { "MusicPlaylistDeletePlaylist", GTK_STOCK_REMOVE, N_("_Delete"), NULL,
154 N_("Delete playlist"),
155 G_CALLBACK (rb_playlist_manager_cmd_delete_playlist) },
156 { "EditAutomaticPlaylist", GTK_STOCK_PROPERTIES, N_("_Edit..."), NULL,
157 N_("Change this automatic playlist"),
158 G_CALLBACK (rb_playlist_manager_cmd_edit_automatic_playlist) },
159 { "QueuePlaylist", NULL, N_("_Queue All Tracks"), NULL,
160 N_("Add all tracks in this playlist to the queue"),
161 G_CALLBACK (rb_playlist_manager_cmd_queue_playlist) },
163 static guint rb_playlist_manager_n_actions = G_N_ELEMENTS (rb_playlist_manager_actions);
165 G_DEFINE_TYPE (RBPlaylistManager, rb_playlist_manager, G_TYPE_OBJECT)
167 static void
168 rb_playlist_manager_class_init (RBPlaylistManagerClass *klass)
170 GObjectClass *object_class = G_OBJECT_CLASS (klass);
172 object_class->finalize = rb_playlist_manager_finalize;
174 object_class->set_property = rb_playlist_manager_set_property;
175 object_class->get_property = rb_playlist_manager_get_property;
177 g_object_class_install_property (object_class,
178 PROP_SOURCE,
179 g_param_spec_object ("source",
180 "RBSource",
181 "RBSource object",
182 RB_TYPE_SOURCE,
183 G_PARAM_READWRITE));
185 g_object_class_install_property (object_class,
186 PROP_SHELL,
187 g_param_spec_object ("shell",
188 "RBShell",
189 "RBShell object",
190 RB_TYPE_SHELL,
191 G_PARAM_READWRITE));
194 g_object_class_install_property (object_class,
195 PROP_SOURCELIST,
196 g_param_spec_object ("sourcelist",
197 "RBSourceList",
198 "RBSourceList",
199 RB_TYPE_SOURCELIST,
200 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
201 rb_playlist_manager_signals[PLAYLIST_ADDED] =
202 g_signal_new ("playlist_added",
203 RB_TYPE_PLAYLIST_MANAGER,
204 G_SIGNAL_RUN_LAST,
205 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_added),
206 NULL, NULL,
207 g_cclosure_marshal_VOID__OBJECT,
208 G_TYPE_NONE,
209 1, G_TYPE_OBJECT);
211 rb_playlist_manager_signals[PLAYLIST_CREATED] =
212 g_signal_new ("playlist_created",
213 RB_TYPE_PLAYLIST_MANAGER,
214 G_SIGNAL_RUN_LAST,
215 G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_created),
216 NULL, NULL,
217 g_cclosure_marshal_VOID__OBJECT,
218 G_TYPE_NONE,
219 1, G_TYPE_OBJECT);
221 rb_playlist_manager_signals[PLAYLIST_LOAD_START] =
222 g_signal_new ("load_start",
223 RB_TYPE_PLAYLIST_MANAGER,
224 G_SIGNAL_RUN_LAST,
225 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_start),
226 NULL, NULL,
227 g_cclosure_marshal_VOID__VOID,
228 G_TYPE_NONE,
229 0, G_TYPE_NONE);
230 rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH] =
231 g_signal_new ("load_finish",
232 RB_TYPE_PLAYLIST_MANAGER,
233 G_SIGNAL_RUN_LAST,
234 G_STRUCT_OFFSET (RBPlaylistManagerClass, load_finish),
235 NULL, NULL,
236 g_cclosure_marshal_VOID__VOID,
237 G_TYPE_NONE,
238 0, G_TYPE_NONE);
240 g_type_class_add_private (klass, sizeof (RBPlaylistManagerPrivate));
243 static void
244 rb_playlist_manager_init (RBPlaylistManager *mgr)
246 mgr->priv = G_TYPE_INSTANCE_GET_PRIVATE (mgr,
247 RB_TYPE_PLAYLIST_MANAGER,
248 RBPlaylistManagerPrivate);
250 mgr->priv->saving_mutex = g_mutex_new ();
251 mgr->priv->dirty = 0;
252 mgr->priv->saving = 0;
255 void
256 rb_playlist_manager_shutdown (RBPlaylistManager *mgr)
258 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (mgr));
260 g_mutex_lock (mgr->priv->saving_mutex);
261 g_mutex_unlock (mgr->priv->saving_mutex);
264 static void
265 rb_playlist_manager_finalize (GObject *object)
267 RBPlaylistManager *mgr;
269 g_return_if_fail (object != NULL);
270 g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
272 rb_debug ("Finalizing playlist manager");
274 mgr = RB_PLAYLIST_MANAGER (object);
276 g_return_if_fail (mgr->priv != NULL);
278 g_mutex_free (mgr->priv->saving_mutex);
280 G_OBJECT_CLASS (rb_playlist_manager_parent_class)->finalize (object);
283 static void
284 rb_playlist_manager_set_uimanager (RBPlaylistManager *mgr,
285 GtkUIManager *uimanager)
287 if (mgr->priv->uimanager != NULL) {
288 if (mgr->priv->actiongroup != NULL) {
289 gtk_ui_manager_remove_action_group (mgr->priv->uimanager,
290 mgr->priv->actiongroup);
292 g_object_unref (G_OBJECT (mgr->priv->uimanager));
293 mgr->priv->uimanager = NULL;
296 mgr->priv->uimanager = uimanager;
298 if (mgr->priv->actiongroup == NULL) {
299 mgr->priv->actiongroup = gtk_action_group_new ("PlaylistActions");
300 gtk_action_group_set_translation_domain (mgr->priv->actiongroup,
301 GETTEXT_PACKAGE);
302 gtk_action_group_add_actions (mgr->priv->actiongroup,
303 rb_playlist_manager_actions,
304 rb_playlist_manager_n_actions,
305 mgr);
308 gtk_ui_manager_insert_action_group (mgr->priv->uimanager,
309 mgr->priv->actiongroup,
313 static void
314 rb_playlist_manager_playlist_entries_changed (GtkTreeModel *model, RhythmDBEntry *entry, RBPlaylistManager *mgr)
316 int num_tracks;
317 GtkAction *action;
319 num_tracks = gtk_tree_model_iter_n_children (model, NULL);
321 action = gtk_action_group_get_action (mgr->priv->actiongroup, "MusicPlaylistBurnPlaylist");
322 gtk_action_set_visible (action, (num_tracks > 0));
325 static void
326 rb_playlist_manager_playlist_row_inserted_cb (GtkTreeModel *model,
327 GtkTreePath *path,
328 GtkTreeIter *iter,
329 RBPlaylistManager *mgr)
331 RhythmDBEntry *entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (model), iter);
333 rb_playlist_manager_playlist_entries_changed (model, entry, mgr);
337 static void
338 rb_playlist_manager_set_source (RBPlaylistManager *mgr,
339 RBSource *source)
341 gboolean playlist_active;
342 gboolean playlist_local = FALSE;
343 gboolean party_mode;
344 gboolean can_save;
345 gboolean can_delete;
346 gboolean can_edit;
347 gboolean can_rename;
348 GtkAction *action;
350 if (mgr->priv->selected_source != NULL) {
351 RhythmDBQueryModel *model;
353 g_object_get (G_OBJECT (mgr->priv->selected_source), "query-model", &model, NULL);
355 g_signal_handlers_disconnect_by_func (G_OBJECT (model),
356 G_CALLBACK (rb_playlist_manager_playlist_entries_changed),
357 mgr);
358 g_signal_handlers_disconnect_by_func (G_OBJECT (model),
359 G_CALLBACK (rb_playlist_manager_playlist_row_inserted_cb),
360 mgr);
361 g_object_unref (G_OBJECT (model));
364 party_mode = rb_shell_get_party_mode (mgr->priv->shell);
366 mgr->priv->selected_source = source;
367 playlist_active = RB_IS_PLAYLIST_SOURCE (mgr->priv->selected_source);
368 if (playlist_active) {
369 g_object_get (G_OBJECT (mgr->priv->selected_source), "is-local", &playlist_local, NULL);
372 can_save = playlist_local && !party_mode;
373 action = gtk_action_group_get_action (mgr->priv->actiongroup,
374 "MusicPlaylistSavePlaylist");
375 gtk_action_set_visible (action, can_save);
377 can_delete = (playlist_local && !party_mode &&
378 !RB_IS_PLAY_QUEUE_SOURCE (mgr->priv->selected_source));
379 action = gtk_action_group_get_action (mgr->priv->actiongroup,
380 "MusicPlaylistDeletePlaylist");
381 gtk_action_set_visible (action, can_delete);
383 can_edit = (playlist_local && RB_IS_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source) &&
384 !party_mode);
385 action = gtk_action_group_get_action (mgr->priv->actiongroup,
386 "EditAutomaticPlaylist");
387 gtk_action_set_visible (action, can_edit);
389 can_rename = playlist_local && rb_source_can_rename (mgr->priv->selected_source);
390 action = gtk_action_group_get_action (mgr->priv->actiongroup,
391 "MusicPlaylistRenamePlaylist");
392 gtk_action_set_visible (action, can_rename);
394 if (playlist_active && rb_recorder_enabled ()) {
395 RhythmDBQueryModel *model;
397 g_object_get (G_OBJECT (mgr->priv->selected_source), "query-model", &model, NULL);
398 /* monitor for changes, to enable/disable the burn menu item */
399 g_signal_connect_object (G_OBJECT (model),
400 "row_inserted",
401 G_CALLBACK (rb_playlist_manager_playlist_row_inserted_cb),
402 mgr, 0);
403 g_signal_connect_object (G_OBJECT (model),
404 "entry-removed",
405 G_CALLBACK (rb_playlist_manager_playlist_entries_changed),
406 mgr, 0);
408 rb_playlist_manager_playlist_entries_changed (GTK_TREE_MODEL (model), NULL, mgr);
409 g_object_unref (model);
410 } else {
411 action = gtk_action_group_get_action (mgr->priv->actiongroup,
412 "MusicPlaylistBurnPlaylist");
413 gtk_action_set_visible (action, FALSE);
417 static void
418 rb_playlist_manager_set_property (GObject *object,
419 guint prop_id,
420 const GValue *value,
421 GParamSpec *pspec)
423 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
425 switch (prop_id) {
426 case PROP_SOURCE:
427 rb_playlist_manager_set_source (mgr, g_value_get_object (value));
428 break;
429 case PROP_SHELL:
431 GtkUIManager *uimanager;
432 mgr->priv->shell = g_value_get_object (value);
433 g_object_get (G_OBJECT (mgr->priv->shell),
434 "ui-manager", &uimanager,
435 "db", &mgr->priv->db,
436 NULL);
437 rb_playlist_manager_set_uimanager (mgr, uimanager);
438 break;
440 case PROP_SOURCELIST:
441 mgr->priv->sourcelist = g_value_get_object (value);
442 mgr->priv->window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (mgr->priv->sourcelist)));
443 break;
444 default:
445 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
446 break;
450 static void
451 rb_playlist_manager_get_property (GObject *object,
452 guint prop_id,
453 GValue *value,
454 GParamSpec *pspec)
456 RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
458 switch (prop_id) {
459 case PROP_SOURCE:
460 g_value_set_object (value, mgr->priv->selected_source);
461 break;
462 case PROP_SHELL:
463 g_value_set_object (value, mgr->priv->shell);
464 break;
465 case PROP_SOURCELIST:
466 g_value_set_object (value, mgr->priv->sourcelist);
467 break;
468 default:
469 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
470 break;
474 RBPlaylistManager *
475 rb_playlist_manager_new (RBShell *shell,
476 RBSourceList *sourcelist)
478 return g_object_new (RB_TYPE_PLAYLIST_MANAGER,
479 "shell", shell,
480 "sourcelist", sourcelist,
481 NULL);
484 GQuark
485 rb_playlist_manager_error_quark (void)
487 static GQuark quark = 0;
488 if (!quark)
489 quark = g_quark_from_static_string ("rb_playlist_manager_error");
491 return quark;
494 static void
495 handle_playlist_entry_cb (TotemPlParser *playlist, const char *uri_maybe,
496 const char *title,
497 const char *genre, RBPlaylistManager *mgr)
499 char *uri = rb_canonicalise_uri (uri_maybe);
500 RhythmDBEntryType entry_type;
502 g_return_if_fail (uri != NULL);
504 entry_type = rb_shell_guess_type_for_uri (mgr->priv->shell, uri);
505 if (entry_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
506 g_free (uri);
507 return;
510 rb_shell_add_uri (mgr->priv->shell,
511 entry_type,
512 uri,
513 title,
514 genre,
515 NULL);
517 if (entry_type == RHYTHMDB_ENTRY_TYPE_SONG) {
518 if (!mgr->priv->loading_playlist) {
519 mgr->priv->loading_playlist =
520 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, NULL, FALSE));
522 rb_static_playlist_source_add_location (mgr->priv->loading_playlist, uri, -1);
525 g_free (uri);
528 static void
529 playlist_load_start_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
531 rb_debug ("loading new playlist %s", title);
533 if (!mgr->priv->loading_playlist) {
534 mgr->priv->loading_playlist =
535 RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
539 static void
540 playlist_load_end_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
542 rb_debug ("finished loading playlist %s", title);
543 mgr->priv->loading_playlist = NULL;
547 * rb_playlist_manager_parse_file:
548 * @mgr: the #RBPlaylistManager
549 * @uri: URI of the playlist to load
550 * @error: returns a GError in case of error
552 * Parses a playlist file, adding entries to the database and to a new
553 * static playlist. If the playlist file includes a title, the static
554 * playlist created will have the same title.
556 * Returns TRUE on success
558 gboolean
559 rb_playlist_manager_parse_file (RBPlaylistManager *mgr, const char *uri, GError **error)
561 rb_debug ("loading playlist from %s", uri);
563 g_signal_emit (G_OBJECT (mgr), rb_playlist_manager_signals[PLAYLIST_LOAD_START], 0);
566 TotemPlParser *parser = totem_pl_parser_new ();
568 g_signal_connect_object (G_OBJECT (parser), "entry",
569 G_CALLBACK (handle_playlist_entry_cb),
570 mgr, 0);
572 g_signal_connect_object (G_OBJECT (parser), "playlist-start",
573 G_CALLBACK (playlist_load_start_cb),
574 mgr, 0);
576 g_signal_connect_object (G_OBJECT (parser), "playlist-end",
577 G_CALLBACK (playlist_load_end_cb),
578 mgr, 0);
580 if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
581 g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
583 if (totem_pl_parser_parse (parser, uri, TRUE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
584 g_set_error (error,
585 RB_PLAYLIST_MANAGER_ERROR,
586 RB_PLAYLIST_MANAGER_ERROR_PARSE,
587 "%s",
588 _("The playlist file may be in an unknown format or corrupted."));
589 return FALSE;
591 mgr->priv->loading_playlist = NULL;
593 g_object_unref (G_OBJECT (parser));
596 g_signal_emit (G_OBJECT (mgr), rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH], 0);
597 return TRUE;
600 static void
601 append_new_playlist_source (RBPlaylistManager *mgr, RBPlaylistSource *source)
603 g_signal_emit (G_OBJECT (mgr), rb_playlist_manager_signals[PLAYLIST_ADDED], 0,
604 source);
608 * rb_playlist_manager_load_playlists
609 * @mgr: the #RBPlaylistManager
611 * Loads the user's playlists, or if the playlist file does not exists,
612 * reads the default playlist file. Should be called only once on startup.
614 void
615 rb_playlist_manager_load_playlists (RBPlaylistManager *mgr)
617 char *file;
618 xmlDocPtr doc;
619 xmlNodePtr root;
620 xmlNodePtr child;
621 gboolean exists;
623 exists = FALSE;
624 file = g_build_filename (rb_dot_dir (), "playlists.xml", NULL);
626 /* block saves until the playlists have loaded */
627 g_mutex_lock (mgr->priv->saving_mutex);
629 exists = g_file_test (file, G_FILE_TEST_EXISTS);
630 if (! exists) {
631 rb_debug ("personal playlists not found, loading defaults");
633 /* try global playlists */
634 g_free (file);
635 file = g_strdup (rb_file ("playlists.xml"));
636 exists = g_file_test (file, G_FILE_TEST_EXISTS);
639 if (! exists) {
640 rb_debug ("default playlists file not found");
641 goto out;
644 doc = xmlParseFile (file);
645 if (doc == NULL)
646 goto out;
648 root = xmlDocGetRootElement (doc);
650 for (child = root->children; child; child = child->next) {
651 RBSource *playlist;
653 if (xmlNodeIsText (child))
654 continue;
656 playlist = rb_playlist_source_new_from_xml (mgr->priv->shell,
657 child);
658 if (playlist)
659 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
662 xmlFreeDoc (doc);
663 out:
664 g_mutex_unlock (mgr->priv->saving_mutex);
665 g_free (file);
668 static void
669 rb_playlist_manager_set_dirty (RBPlaylistManager *mgr, gboolean dirty)
671 g_atomic_int_compare_and_exchange (&mgr->priv->dirty, dirty == FALSE, dirty == TRUE);
674 /* returns TRUE if a playlist has been created, modified, or deleted since last save */
675 static gboolean
676 rb_playlist_manager_is_dirty (RBPlaylistManager *mgr)
678 gboolean dirty = FALSE;
679 GtkTreeModel *fmodel;
680 GtkTreeModel *model;
681 GtkTreeIter iter;
683 g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
684 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
685 g_object_unref (fmodel);
687 if (gtk_tree_model_get_iter_first (model, &iter)) {
688 do {
689 RBSource *source;
690 gboolean local;
691 GValue v = {0,};
692 gtk_tree_model_get_value (model,
693 &iter,
694 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
695 &v);
696 source = g_value_get_pointer (&v);
697 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
698 continue;
700 g_object_get (G_OBJECT (source),
701 "is-local", &local,
702 NULL);
703 if (local) {
704 g_object_get (G_OBJECT (source),
705 "dirty", &dirty,
706 NULL);
707 if (dirty)
708 break;
710 } while (gtk_tree_model_iter_next (model, &iter));
713 if (!dirty)
714 dirty = g_atomic_int_get (&mgr->priv->dirty);
716 return dirty;
719 struct RBPlaylistManagerSaveData
721 RBPlaylistManager *mgr;
722 xmlDocPtr doc;
725 static gpointer
726 rb_playlist_manager_save_data (struct RBPlaylistManagerSaveData *data)
728 char *file;
729 char *tmpname;
731 g_mutex_lock (data->mgr->priv->saving_mutex);
733 file = g_build_filename (rb_dot_dir (), "playlists.xml", NULL);
734 tmpname = g_strconcat (file, ".tmp", NULL);
736 if (xmlSaveFormatFile (tmpname, data->doc, 1) != -1) {
737 rename (tmpname, file);
738 } else {
739 rb_debug ("error in xmlSaveFormatFile(), not saving");
740 unlink (tmpname);
741 rb_playlist_manager_set_dirty (data->mgr, TRUE);
743 xmlFreeDoc (data->doc);
744 g_free (tmpname);
745 g_free (file);
747 g_atomic_int_compare_and_exchange (&data->mgr->priv->saving, 1, 0);
748 g_mutex_unlock (data->mgr->priv->saving_mutex);
750 g_object_unref (G_OBJECT (data->mgr));
752 g_free (data);
753 return NULL;
757 * rb_playlist_manager_save_playlists
758 * @mgr: the #RBPlaylistManager
759 * @force: if TRUE, save playlists synchronously and unconditionally
761 * Saves the user's playlists. If the force flag is
762 * TRUE, the playlists will always be saved. Otherwise, the playlists
763 * will only be saved if a playlist has been created, modified, or deleted
764 * since the last time the playlists were saved, and no save operation is
765 * currently taking place.
767 * Returns TRUE if a playlist save operation has been started
769 gboolean
770 rb_playlist_manager_save_playlists (RBPlaylistManager *mgr, gboolean force)
772 xmlNodePtr root;
773 struct RBPlaylistManagerSaveData *data;
774 GtkTreeIter iter;
775 GtkTreeModel *fmodel;
776 GtkTreeModel *model;
778 if (!force && !rb_playlist_manager_is_dirty (mgr)) {
779 /* playlists already in sync, so don't bother */
780 return FALSE;
783 if (!g_atomic_int_compare_and_exchange (&mgr->priv->saving, 0, 1) && !force) {
784 /* already saving, so don't bother */
785 return FALSE;
788 data = g_new0 (struct RBPlaylistManagerSaveData, 1);
789 data->mgr = mgr;
790 data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
791 g_object_ref (G_OBJECT (mgr));
793 root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
794 xmlDocSetRootElement (data->doc, root);
796 g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
797 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
798 g_object_unref (fmodel);
800 if (gtk_tree_model_get_iter_first (model, &iter)) {
801 do {
802 RBSource *source;
803 GValue v = {0,};
804 gboolean local;
806 gtk_tree_model_get_value (model,
807 &iter,
808 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
809 &v);
810 source = g_value_get_pointer (&v);
811 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
812 continue;
814 g_object_get (G_OBJECT (source), "is-local", &local, NULL);
815 if (local)
816 rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (source), root);
817 } while (gtk_tree_model_iter_next (model, &iter));
820 /* mark clean here. if the save fails, we'll mark it dirty again */
821 rb_playlist_manager_set_dirty (data->mgr, FALSE);
823 if (force)
824 rb_playlist_manager_save_data (data);
825 else
826 g_thread_create ((GThreadFunc) rb_playlist_manager_save_data, data, FALSE, NULL);
828 return TRUE;
832 * rb_playlist_manager_new_playlist
833 * @mgr: the #RBPlaylistManager
834 * @suggested_name: optional name to use for the new playlist
835 * @automatic: if TRUE, create an auto playlist
837 * Creates a new playlist and adds it to the source list.
839 * Returns the new playlist object.
841 RBSource *
842 rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
843 const char *suggested_name,
844 gboolean automatic)
846 RBSource *playlist;
847 if (automatic)
848 playlist = rb_auto_playlist_source_new (mgr->priv->shell,
849 suggested_name,
850 TRUE);
851 else
852 playlist = rb_static_playlist_source_new (mgr->priv->shell,
853 suggested_name,
854 TRUE,
855 RHYTHMDB_ENTRY_TYPE_SONG);
857 append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
858 rb_sourcelist_edit_source_name (mgr->priv->sourcelist, playlist);
859 rb_playlist_manager_set_dirty (mgr, TRUE);
861 g_signal_emit (G_OBJECT (mgr), rb_playlist_manager_signals[PLAYLIST_CREATED], 0,
862 playlist);
864 return playlist;
867 static char *
868 create_name_from_selection_data (RBPlaylistManager *mgr,
869 GtkSelectionData *data)
871 char *name = NULL;
872 GList *list;
874 if (data->type == gdk_atom_intern ("text/uri-list", TRUE)) {
875 list = gnome_vfs_uri_list_parse ((char *) data->data);
877 if (list != NULL) {
878 GList *l;
879 char *artist;
880 char *album;
881 gboolean mixed_artists;
882 gboolean mixed_albums;
884 artist = NULL;
885 album = NULL;
886 mixed_artists = FALSE;
887 mixed_albums = FALSE;
888 for (l = list; l != NULL; l = g_list_next (l)) {
889 RhythmDBEntry *entry;
890 char *location;
891 const char *e_artist;
892 const char *e_album;
894 location = gnome_vfs_uri_to_string ((const GnomeVFSURI *) l->data, 0);
895 if (location == NULL) {
896 continue;
899 entry = rhythmdb_entry_lookup_by_location (mgr->priv->db, location);
900 if (entry == NULL) {
901 continue;
904 e_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
905 e_album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
907 /* get value of first non-NULL artist */
908 if (e_artist != NULL && artist == NULL) {
909 artist = g_strdup (e_artist);
912 /* get value of first non-NULL album */
913 if (e_album != NULL && album == NULL) {
914 album = g_strdup (e_album);
917 /* pretend that NULL fields always match */
918 if (artist != NULL && e_artist != NULL
919 && strcmp (artist, e_artist) != 0) {
920 mixed_artists = TRUE;
923 /* pretend that NULL fields always match */
924 if (album != NULL && e_album != NULL
925 && strcmp (album, e_album) != 0) {
926 mixed_albums = TRUE;
929 /* if there is a mix of both then stop */
930 if (mixed_artists && mixed_albums) {
931 break;
935 if (! mixed_artists && ! mixed_albums) {
936 name = g_strdup_printf ("%s - %s", artist, album);
937 } else if (! mixed_artists) {
938 name = g_strdup_printf ("%s", artist);
939 } else if (! mixed_albums) {
940 name = g_strdup_printf ("%s", album);
943 g_free (artist);
944 g_free (album);
945 gnome_vfs_uri_list_free (list);
948 } else {
949 char **names;
951 names = g_strsplit ((char *)data->data, "\r\n", 0);
952 name = g_strjoinv (", ", names);
953 g_strfreev (names);
956 if (name == NULL) {
957 name = g_strdup (_("Untitled Playlist"));
960 return name;
964 * rb_playlist_manager_new_playlist_from_selection_data
965 * @mgr: the #RBPlaylistManager
966 * @data: the #GtkSelectionData from which to create a playlist
968 * Creates a new playlist based on selection data from gtk.
969 * Used to implement playlist creation through drag and drop
970 * to the source list.
972 * Returns the new playlist.
974 RBSource *
975 rb_playlist_manager_new_playlist_from_selection_data (RBPlaylistManager *mgr,
976 GtkSelectionData *data)
978 RBSource *playlist;
979 gboolean automatic;
980 char *suggested_name;
982 automatic = (data->type != gdk_atom_intern ("text/uri-list", TRUE));
983 suggested_name = create_name_from_selection_data (mgr, data);
985 playlist = rb_playlist_manager_new_playlist (mgr,
986 suggested_name,
987 automatic);
988 g_free (suggested_name);
990 return playlist;
993 static void
994 rb_playlist_manager_cmd_new_playlist (GtkAction *action,
995 RBPlaylistManager *mgr)
997 rb_playlist_manager_new_playlist (mgr, NULL, FALSE);
1000 static void
1001 rb_playlist_manager_set_automatic_playlist (RBPlaylistManager *mgr,
1002 RBAutoPlaylistSource *playlist,
1003 RBQueryCreator *creator)
1005 RBQueryCreatorLimitType type;
1006 guint limit, limit_count = 0, limit_size = 0, limit_time = 0;
1007 const char *sort_key;
1008 gint sort_direction;
1010 rb_query_creator_get_limit (creator, &type, &limit);
1011 if (type == RB_QUERY_CREATOR_LIMIT_COUNT)
1012 limit_count = limit;
1013 else if (type == RB_QUERY_CREATOR_LIMIT_MB)
1014 limit_size = limit;
1015 else if (type == RB_QUERY_CREATOR_LIMIT_SECONDS)
1016 limit_time = limit;
1017 else
1018 g_assert_not_reached ();
1020 rb_query_creator_get_sort_order (creator, &sort_key, &sort_direction);
1022 rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (playlist),
1023 rb_query_creator_get_query (creator),
1024 limit_count, limit_size, limit_time,
1025 sort_key, sort_direction);
1028 static void
1029 rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
1030 RBPlaylistManager *mgr)
1032 RBQueryCreator *creator = RB_QUERY_CREATOR (rb_query_creator_new (mgr->priv->db));
1033 RBSource *playlist;
1035 switch (gtk_dialog_run (GTK_DIALOG (creator))) {
1036 case GTK_RESPONSE_NONE:
1037 case GTK_RESPONSE_CLOSE:
1038 gtk_widget_destroy (GTK_WIDGET (creator));
1039 return;
1042 playlist = rb_playlist_manager_new_playlist (mgr, NULL, TRUE);
1044 rb_playlist_manager_set_automatic_playlist (mgr, RB_AUTO_PLAYLIST_SOURCE (playlist), creator);
1046 rb_playlist_manager_set_dirty (mgr, TRUE);
1048 gtk_widget_destroy (GTK_WIDGET (creator));
1051 typedef struct {
1052 RBAutoPlaylistSource *playlist;
1053 RBPlaylistManager *mgr;
1054 RBQueryCreator *creator;
1055 gint playlist_deleted_id;
1056 gint creator_response_id;
1057 } EditAutoPlaylistData;
1059 static void
1060 cleanup_edit_data (EditAutoPlaylistData *data)
1062 g_signal_handler_disconnect (data->playlist, data->playlist_deleted_id);
1063 g_signal_handler_disconnect (data->creator, data->creator_response_id);
1064 gtk_widget_destroy (GTK_WIDGET (data->creator));
1065 g_free (data);
1068 static void
1069 edit_auto_playlist_response_cb (RBQueryCreator *dialog,
1070 gint response,
1071 EditAutoPlaylistData *data)
1073 rb_playlist_manager_set_automatic_playlist (data->mgr, data->playlist, dialog);
1074 g_object_set_data (G_OBJECT (data->playlist), "rhythmbox-playlist-editor", NULL);
1076 cleanup_edit_data (data);
1079 static void
1080 edit_auto_playlist_deleted_cb (RBAutoPlaylistSource *playlist, EditAutoPlaylistData *data)
1082 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", NULL);
1084 cleanup_edit_data (data);
1087 static void
1088 rb_playlist_manager_cmd_edit_automatic_playlist (GtkAction *action,
1089 RBPlaylistManager *mgr)
1091 RBQueryCreator *creator;
1092 RBAutoPlaylistSource *playlist;
1093 GPtrArray *query;
1094 guint limit_count = 0, limit_size = 0, limit_time = 0;
1095 const char *sort_key;
1096 gint sort_direction;
1097 EditAutoPlaylistData *data;
1099 playlist = RB_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source);
1100 creator = g_object_get_data (G_OBJECT (playlist), "rhythmbox-playlist-editor");
1101 if (creator == NULL) {
1102 rb_auto_playlist_source_get_query (playlist, &query, &limit_count, &limit_size, &limit_time, &sort_key, &sort_direction);
1104 creator = RB_QUERY_CREATOR (rb_query_creator_new_from_query (mgr->priv->db,
1105 query,
1106 limit_count, limit_size, limit_time,
1107 sort_key, sort_direction));
1108 rhythmdb_query_free (query);
1110 data = g_new0 (EditAutoPlaylistData, 1);
1111 data->mgr = mgr;
1112 data->playlist = playlist;
1113 data->creator = creator;
1114 data->creator_response_id =
1115 g_signal_connect (G_OBJECT (creator),
1116 "response",
1117 G_CALLBACK (edit_auto_playlist_response_cb),
1118 data);
1120 g_object_set_data (G_OBJECT (playlist), "rhythmbox-playlist-editor", creator);
1121 data->playlist_deleted_id =
1122 g_signal_connect (G_OBJECT (playlist),
1123 "deleted",
1124 G_CALLBACK (edit_auto_playlist_deleted_cb),
1125 data);
1127 gtk_window_present (GTK_WINDOW (creator));
1130 static gboolean
1131 _queue_track_cb (RhythmDBQueryModel *model,
1132 GtkTreePath *path,
1133 GtkTreeIter *iter,
1134 RBStaticPlaylistSource *queue_source)
1136 RhythmDBEntry *entry;
1138 entry = rhythmdb_query_model_iter_to_entry (model, iter);
1139 rb_static_playlist_source_add_entry (queue_source, entry, -1);
1141 return FALSE;
1144 static void
1145 rb_playlist_manager_cmd_queue_playlist (GtkAction *action,
1146 RBPlaylistManager *mgr)
1148 RBSource *queue_source;
1149 RhythmDBQueryModel *model;
1151 g_object_get (G_OBJECT (mgr->priv->shell), "queue-source", &queue_source, NULL);
1152 g_object_get (G_OBJECT (mgr->priv->selected_source), "query-model", &model, NULL);
1154 gtk_tree_model_foreach (GTK_TREE_MODEL (model),
1155 (GtkTreeModelForeachFunc) _queue_track_cb,
1156 queue_source);
1158 g_object_unref (G_OBJECT (queue_source));
1159 g_object_unref (G_OBJECT (model));
1162 static void
1163 rb_playlist_manager_cmd_rename_playlist (GtkAction *action,
1164 RBPlaylistManager *mgr)
1166 rb_debug ("Renaming playlist %p", mgr->priv->selected_source);
1168 rb_sourcelist_edit_source_name (mgr->priv->sourcelist, mgr->priv->selected_source);
1169 rb_playlist_manager_set_dirty (mgr, TRUE);
1172 static void
1173 rb_playlist_manager_cmd_delete_playlist (GtkAction *action,
1174 RBPlaylistManager *mgr)
1176 rb_debug ("Deleting playlist %p", mgr->priv->selected_source);
1178 rb_source_delete_thyself (mgr->priv->selected_source);
1179 rb_playlist_manager_set_dirty (mgr, TRUE);
1182 static void
1183 load_playlist_response_cb (GtkDialog *dialog,
1184 int response_id,
1185 RBPlaylistManager *mgr)
1187 char *escaped_file = NULL;
1188 GError *error = NULL;
1190 if (response_id != GTK_RESPONSE_ACCEPT) {
1191 gtk_widget_destroy (GTK_WIDGET (dialog));
1192 return;
1195 escaped_file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1197 gtk_widget_destroy (GTK_WIDGET (dialog));
1199 if (escaped_file == NULL)
1200 return;
1202 if (!rb_playlist_manager_parse_file (mgr, escaped_file, &error)) {
1203 rb_error_dialog (NULL, _("Couldn't read playlist"),
1204 error->message);
1205 g_error_free (error);
1208 g_free (escaped_file);
1209 rb_playlist_manager_set_dirty (mgr, TRUE);
1212 static void
1213 rb_playlist_manager_cmd_load_playlist (GtkAction *action,
1214 RBPlaylistManager *mgr)
1216 GtkWidget *dialog;
1218 dialog = rb_file_chooser_new (_("Load Playlist"),
1219 GTK_WINDOW (mgr->priv->window),
1220 GTK_FILE_CHOOSER_ACTION_OPEN,
1221 FALSE);
1223 g_signal_connect_object (G_OBJECT (dialog), "response",
1224 G_CALLBACK (load_playlist_response_cb), mgr, 0);
1229 typedef struct {
1230 const gchar *description;
1231 /* NULL terminated array of extensions for this file format. The first
1232 * one is the prefered extension for files of this type. */
1233 const gchar **extensions;
1234 const RBPlaylistExportType type;
1235 } RBPlaylistExportFilter;
1237 static const char *m3u_extensions [] = {"m3u", NULL};
1238 static const char *pls_extensions [] = {"pls", NULL};
1240 static RBPlaylistExportFilter playlist_export_formats[] = {
1241 {N_("MPEG Version 3.0 URL"), m3u_extensions, RB_PLAYLIST_EXPORT_TYPE_M3U},
1242 {N_("Shoutcast playlist"), pls_extensions, RB_PLAYLIST_EXPORT_TYPE_PLS},
1245 static void
1246 save_playlist_response_cb (GtkDialog *dialog,
1247 int response_id,
1248 RBPlaylistManager *mgr)
1250 char *file = NULL;
1251 GtkWidget *menu;
1252 gint index;
1253 RBPlaylistExportType export_type = RB_PLAYLIST_EXPORT_TYPE_UNKNOWN;
1255 if (response_id != GTK_RESPONSE_OK) {
1256 gtk_widget_destroy (GTK_WIDGET (dialog));
1257 return;
1260 file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
1261 if (file == NULL || file[0] == '\0')
1262 return;
1264 menu = g_object_get_data (G_OBJECT(dialog), "export-menu");
1265 index = gtk_combo_box_get_active (GTK_COMBO_BOX (menu));
1267 /* by extension selected */
1268 if (index <= 0) {
1269 int i;
1271 for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
1272 int j;
1274 /* determine the playlist type from the extension */
1275 for (j = 0; playlist_export_formats[i].extensions[j] != NULL; j++) {
1276 if (g_str_has_suffix (file, playlist_export_formats[i].extensions[j])) {
1277 export_type = playlist_export_formats[i].type;
1278 break;
1282 } else {
1283 export_type = playlist_export_formats[index-1].type;
1286 if (export_type == RB_PLAYLIST_EXPORT_TYPE_UNKNOWN) {
1287 rb_error_dialog (NULL, _("Couldn't save playlist"), _("Unsupported file extension given."));
1288 } else {
1289 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source),
1290 file, (export_type == RB_PLAYLIST_EXPORT_TYPE_M3U));
1291 gtk_widget_destroy (GTK_WIDGET (dialog));
1294 g_free (file);
1297 static void
1298 export_set_extension_cb (GtkWidget* widget, GtkDialog *dialog)
1300 gint index;
1301 gchar *text;
1302 gchar *last_dot;
1303 const char *extension;
1304 gchar *basename;
1305 GString *basename_str;
1307 index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1308 if (index <= 0)
1309 return;
1311 extension = playlist_export_formats[index-1].extensions[0];
1312 if (extension == NULL)
1313 return;
1315 text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1316 if (text == NULL || text[0] == '\0') {
1317 g_free (text);
1318 return;
1321 basename = g_path_get_basename (text);
1322 basename_str = g_string_new (basename);
1323 last_dot = g_utf8_strrchr (basename, -1, '.');
1324 if (last_dot)
1325 g_string_truncate (basename_str, (last_dot-basename));
1326 g_free (basename);
1327 g_free (text);
1329 g_string_append_printf (basename_str, ".%s", extension);
1330 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename_str->str);
1331 g_string_free (basename_str, TRUE);
1334 static gchar *
1335 filter_get_export_filter_label (RBPlaylistExportFilter *efilter)
1337 GString *str;
1338 gint ext;
1340 str = g_string_new (_(efilter->description));
1341 for (ext = 0; efilter->extensions[ext] != NULL; ext++) {
1342 if (ext == 0)
1343 g_string_append (str, " (*.");
1344 else
1345 g_string_append (str, ", *.");
1346 g_string_append (str, efilter->extensions[ext]);
1349 if (ext > 0)
1350 g_string_append (str, ")");
1352 return g_string_free (str, FALSE);
1355 static void
1356 setup_format_menu (GtkWidget* menu, GtkWidget *dialog)
1358 GtkTreeModel *model;
1359 int i;
1361 model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu));
1362 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu), rb_combo_box_hyphen_separator_func,
1363 NULL, NULL);
1365 for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
1366 gchar *filter_label;
1367 GtkTreeIter iter;
1369 filter_label = filter_get_export_filter_label (&playlist_export_formats[i]);
1370 gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, -1,
1371 0, filter_label, -1);
1373 g_free (filter_label);
1376 g_signal_connect_object (GTK_OBJECT (menu),
1377 "changed", G_CALLBACK (export_set_extension_cb),
1378 dialog, 0);
1381 static void
1382 rb_playlist_manager_cmd_save_playlist (GtkAction *action,
1383 RBPlaylistManager *mgr)
1385 GladeXML *xml;
1386 GtkWidget *dialog, *menu;
1388 xml = rb_glade_xml_new ("playlist-save.glade",
1389 "playlist_save_dialog",
1390 mgr);
1391 dialog = glade_xml_get_widget (xml, "playlist_save_dialog");
1393 menu = glade_xml_get_widget (xml, "playlist_format_menu");
1394 setup_format_menu (menu, dialog);
1395 g_object_set_data (G_OBJECT (dialog), "export-menu", menu);
1397 /* FIXME: always has "by extension" as default (it should probably remember the last selection) */
1398 gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0);
1399 g_signal_connect_object (G_OBJECT (dialog), "response",
1400 G_CALLBACK (save_playlist_response_cb),
1401 mgr, 0);
1403 g_object_unref (G_OBJECT (xml));
1406 static void
1407 rb_playlist_manager_cmd_burn_playlist (GtkAction *action,
1408 RBPlaylistManager *mgr)
1410 rb_playlist_source_burn_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source));
1414 * rb_playlist_manager_get_playlists
1415 * @mgr: the #RBPlaylistManager
1417 * Returns a #GList containing all local playlist source objects.
1419 GList *
1420 rb_playlist_manager_get_playlists (RBPlaylistManager *mgr)
1422 GList *playlists = NULL;
1423 GtkTreeIter iter;
1424 GtkTreeModel *fmodel;
1425 GtkTreeModel *model;
1427 g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
1428 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1429 g_object_unref (fmodel);
1431 if (gtk_tree_model_get_iter_first (model, &iter)) {
1432 do {
1433 RBSource *source;
1434 GValue v = {0,};
1435 gboolean local;
1437 gtk_tree_model_get_value (model,
1438 &iter,
1439 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1440 &v);
1441 source = g_value_get_pointer (&v);
1442 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
1443 continue;
1444 if (RB_IS_PLAY_QUEUE_SOURCE (source) == TRUE)
1445 continue;
1446 g_object_get (G_OBJECT (source), "is-local", &local,
1447 NULL);
1448 if (local) {
1449 playlists = g_list_prepend (playlists, source);
1452 } while (gtk_tree_model_iter_next (model, &iter));
1455 return playlists;
1459 * rb_playlist_manager_get_playlist_names
1460 * @mgr: the #RBPlaylistManager
1461 * @playlists: holds the array of playlist names on reutrn
1462 * @error: holds a #GError on return on failure
1464 * Allocates and returns an array containing the names of all local
1465 * playlists. This is part of the playlist manager dbus interface.
1467 * Returns TRUE if successful.
1469 gboolean
1470 rb_playlist_manager_get_playlist_names (RBPlaylistManager *mgr,
1471 gchar ***playlists,
1472 GError **error)
1474 GtkTreeIter iter;
1475 GtkTreeModel *fmodel;
1476 GtkTreeModel *model;
1477 int i;
1479 g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
1480 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1481 g_object_unref (fmodel);
1483 if (!gtk_tree_model_get_iter_first (model, &iter)) {
1484 *playlists = NULL;
1485 return TRUE;
1488 *playlists = g_new0 (char *, gtk_tree_model_iter_n_children (model, NULL) + 1);
1489 if (!*playlists)
1490 return FALSE;
1492 i = 0;
1493 do {
1494 RBSource *source;
1495 GValue v = {0,};
1496 char *source_name;
1498 gtk_tree_model_get_value (model, &iter,
1499 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1500 &v);
1501 source = g_value_get_pointer (&v);
1502 if (!RB_IS_PLAYLIST_SOURCE (source))
1503 continue;
1504 if (RB_IS_PLAY_QUEUE_SOURCE (source))
1505 continue;
1507 g_object_get (G_OBJECT (source), "name", &source_name, NULL);
1508 (*playlists)[i++] = source_name;
1509 } while (gtk_tree_model_iter_next (model, &iter));
1511 return TRUE;
1514 static RBSource *
1515 _get_playlist_by_name (RBPlaylistManager *mgr,
1516 const char *name)
1518 GtkTreeIter iter;
1519 GtkTreeModel *fmodel;
1520 GtkTreeModel *model;
1521 RBSource *playlist = NULL;
1523 g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
1524 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
1525 g_object_unref (G_OBJECT (fmodel));
1527 if (!gtk_tree_model_get_iter_first (model, &iter))
1528 return NULL;
1530 do {
1531 RBSource *source;
1532 GValue v = {0,};
1533 char *source_name;
1535 gtk_tree_model_get_value (model, &iter,
1536 RB_SOURCELIST_MODEL_COLUMN_SOURCE,
1537 &v);
1538 source = g_value_get_pointer (&v);
1539 if (!RB_IS_PLAYLIST_SOURCE (source))
1540 continue;
1541 g_object_get (G_OBJECT (source), "name", &source_name, NULL);
1542 if (strcmp (name, source_name) == 0)
1543 playlist = source;
1545 g_free (source_name);
1547 } while (gtk_tree_model_iter_next (model, &iter) && playlist == NULL);
1549 return playlist;
1553 * rb_playlist_manager_create_static_playlist
1554 * @mgr: the #RBPlaylistManager
1555 * @name: name of the new playlist
1556 * @error: holds a #GError on return on failure
1558 * Creates a new static playlist source with the given name.
1559 * Will fail if a playlist with that name already exists.
1560 * This is part of the playlist manager dbus interface.
1562 * Returns TRUE if successful.
1564 gboolean
1565 rb_playlist_manager_create_static_playlist (RBPlaylistManager *mgr,
1566 const gchar *name,
1567 GError **error)
1569 if (_get_playlist_by_name (mgr, name)) {
1570 g_set_error (error,
1571 RB_PLAYLIST_MANAGER_ERROR,
1572 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_EXISTS,
1573 _("Playlist %s already exists"),
1574 name);
1575 return FALSE;
1578 rb_playlist_manager_new_playlist (mgr, name, FALSE);
1579 return TRUE;
1583 * rb_playlist_manager_delete_playlist
1584 * @mgr: the #RBPlaylistManager
1585 * @name: name of the playlist to delete
1586 * @error: holds a #GError on return on failure
1588 * Deletes the specified playlist. Will fail if no playlist with
1589 * that name exists. This is part of the playlist manager dbus interface.
1591 * Returns TRUE if successful.
1593 gboolean
1594 rb_playlist_manager_delete_playlist (RBPlaylistManager *mgr,
1595 const gchar *name,
1596 GError **error)
1598 RBSource *playlist = _get_playlist_by_name (mgr, name);
1599 if (!playlist) {
1600 g_set_error (error,
1601 RB_PLAYLIST_MANAGER_ERROR,
1602 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1603 _("Unknown playlist: %s"),
1604 name);
1605 return FALSE;
1607 rb_source_delete_thyself (playlist);
1608 rb_playlist_manager_set_dirty (mgr, TRUE);
1609 return TRUE;
1613 * rb_playlist_manager_add_to_playlist
1614 * @mgr: the #RBPlaylistManager
1615 * @name: name of the playlist to add to
1616 * @uri: URI of the entry to add to the playlist
1617 * @error: holds a #GError on return on failure
1619 * Adds an entry to the specified playlist.
1620 * Fails if no playlist with that name exists.
1621 * This is part of the playlist manager dbus interface.
1623 * Returns TRUE if successful.
1625 gboolean
1626 rb_playlist_manager_add_to_playlist (RBPlaylistManager *mgr,
1627 const gchar *playlist,
1628 const gchar *uri,
1629 GError **error)
1631 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1632 if (!source) {
1633 g_set_error (error,
1634 RB_PLAYLIST_MANAGER_ERROR,
1635 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1636 _("Unknown playlist: %s"),
1637 playlist);
1638 return FALSE;
1640 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1641 g_set_error (error,
1642 RB_PLAYLIST_MANAGER_ERROR,
1643 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1644 _("Playlist %s is an automatic playlist"),
1645 playlist);
1646 return FALSE;
1648 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (source), uri, -1);
1649 return TRUE;
1653 * rb_playlist_manager_remove_from_playlist
1654 * @mgr: the #RBPlaylistManager
1655 * @name: name of the playlist to remove from
1656 * @uri: URI of the entry to remove from the playlist
1657 * @error: holds a #GError on return on failure
1659 * Removes an entry from the specified playlist.
1660 * Fails if no playlist with that name exists.
1661 * This is part of the playlist manager dbus interface.
1663 * Returns TRUE if successful.
1665 gboolean
1666 rb_playlist_manager_remove_from_playlist (RBPlaylistManager *mgr,
1667 const gchar *playlist,
1668 const gchar *uri,
1669 GError **error)
1671 RBSource *source = _get_playlist_by_name (mgr, playlist);;
1672 if (!source) {
1673 g_set_error (error,
1674 RB_PLAYLIST_MANAGER_ERROR,
1675 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1676 _("Unknown playlist: %s"),
1677 playlist);
1678 return FALSE;
1680 if (RB_IS_AUTO_PLAYLIST_SOURCE (source)) {
1681 g_set_error (error,
1682 RB_PLAYLIST_MANAGER_ERROR,
1683 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1684 _("Playlist %s is an automatic playlist"),
1685 playlist);
1686 return FALSE;
1689 if (rb_playlist_source_location_in_map (RB_PLAYLIST_SOURCE (source), uri))
1690 rb_static_playlist_source_remove_location (RB_STATIC_PLAYLIST_SOURCE (source), uri);
1691 return TRUE;
1695 * rb_playlist_manager_export_playlist
1696 * @mgr: the #RBPlaylistManager
1697 * @name: name of the playlist to export
1698 * @uri: playlist save location
1699 * @m3u_format: if TRUE, save in M3U format, otherwise save in PLS format
1700 * @error: holds a #GError on return on failure
1702 * Saves the specified playlist to a file in either M3U or PLS format.
1703 * This is part of the playlist manager dbus interface.
1705 * Returns TRUE if successful.
1707 gboolean
1708 rb_playlist_manager_export_playlist (RBPlaylistManager *mgr,
1709 const gchar *playlist,
1710 const gchar *uri,
1711 gboolean m3u_format,
1712 GError **error)
1714 RBSource *source = _get_playlist_by_name (mgr, playlist);
1715 if (!source) {
1716 g_set_error (error,
1717 RB_PLAYLIST_MANAGER_ERROR,
1718 RB_PLAYLIST_MANAGER_ERROR_PLAYLIST_NOT_FOUND,
1719 _("Unknown playlist: %s"),
1720 playlist);
1721 return FALSE;
1724 rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (source),
1725 uri,
1726 m3u_format);
1727 return TRUE;