[rhythmbox] convert playlist manager dbus interface to use gdbus



commit b5d1bf1e3de9447307305a55136f23eb81066216
Author: Jonathan Matthew <jonathan d14n org>
Date:   Mon Mar 7 08:16:11 2011 +1000

    convert playlist manager dbus interface to use gdbus

 shell/Makefile.am             |    6 +-
 shell/main.c                  |    6 -
 shell/rb-playlist-manager.c   | 1548 +++++++++++++++++++++++------------------
 shell/rb-playlist-manager.xml |   42 --
 4 files changed, 855 insertions(+), 747 deletions(-)
---
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 192f2d9..8b1959d 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -148,15 +148,13 @@ rb-shell-glue.h: rb-shell.xml Makefile
 	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-server --output=$@ $<
 rb-shell-player-glue.h: rb-shell-player.xml Makefile
 	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell_player --mode=glib-server --output=$@ $<
-rb-playlist-manager-glue.h: rb-playlist-manager.xml Makefile
-	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_playlist_manager --mode=glib-server --output=$@ $<
 rb-shell-binding.h: rb-shell.xml Makefile
 	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-client --output=$@ $<
 rb-shell-player-binding.h: rb-shell-player.xml Makefile
 	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-client --output=$@ $<
 
-BUILT_SOURCES += rb-shell-glue.h rb-shell-binding.h rb-shell-player-glue.h rb-shell-player-binding.h rb-playlist-manager-glue.h
-EXTRA_DIST += rb-shell.xml rb-shell-player.xml rb-playlist-manager.xml
+BUILT_SOURCES += rb-shell-glue.h rb-shell-binding.h rb-shell-player-glue.h rb-shell-player-binding.h
+EXTRA_DIST += rb-shell.xml rb-shell-player.xml
 
 rhythmbox_LDADD = 					\
 	librhythmbox-core.la				\
diff --git a/shell/main.c b/shell/main.c
index 97d217f..36dabcc 100644
--- a/shell/main.c
+++ b/shell/main.c
@@ -295,12 +295,6 @@ main (int argc, char **argv)
 			path = rb_shell_get_player_path (rb_shell);
 			dbus_g_connection_register_g_object (session_bus, path, obj);
 
-			/* register playlist manager object */
-			dbus_g_object_type_install_info (RB_TYPE_PLAYLIST_MANAGER, &dbus_glib_rb_playlist_manager_object_info);
-			obj = rb_shell_get_playlist_manager (rb_shell);
-			path = rb_shell_get_playlist_manager_path (rb_shell);
-			dbus_g_connection_register_g_object (session_bus, path, obj);
-
 			g_signal_connect (G_OBJECT (rb_shell),
 					  "database-load-complete",
 					  G_CALLBACK (database_load_complete),
diff --git a/shell/rb-playlist-manager.c b/shell/rb-playlist-manager.c
index 6b52a3e..acb3daa 100644
--- a/shell/rb-playlist-manager.c
+++ b/shell/rb-playlist-manager.c
@@ -65,18 +65,42 @@
 #define RB_PLAYLIST_MGR_VERSION (xmlChar *) "1.0"
 #define RB_PLAYLIST_MGR_PL (xmlChar *) "rhythmdb-playlists"
 
+#define RB_PLAYLIST_MANAGER_IFACE_NAME "org.gnome.Rhythmbox.PlaylistManager"
+#define RB_PLAYLIST_MANAGER_DBUS_PATH "/org/gnome/Rhythmbox/PlaylistManager"
+
+static const char *rb_playlist_manager_dbus_spec =
+"<node>"
+"  <interface name='org.gnome.Rhythmbox.PlaylistManager'>"
+"    <method name='getPlaylists'>"
+"      <arg type='as' direction='out'/>"
+"    </method>"
+"    <method name='createPlaylist'>"
+"      <arg type='s' name='name'/>"
+"    </method>"
+"    <method name='deletePlaylist'>"
+"      <arg type='s' name='name'/>"
+"    </method>"
+"    <method name='addToPlaylist'>"
+"      <arg type='s' name='playlist'/>"
+"      <arg type='s' name='uri'/>"
+"    </method>"
+"    <method name='removeFromPlaylist'>"
+"      <arg type='s' name='playlist'/>"
+"      <arg type='s' name='uri'/>"
+"    </method>"
+"    <method name='exportPlaylist'>"
+"      <arg type='s' name='playlist'/>"
+"      <arg type='s' name='uri'/>"
+"      <arg type='b' name='mp3_format'/>"
+"    </method>"
+"    <method name='importPlaylist'>"
+"      <arg type='s' name='uri'/>"
+"    </method>"
+"  </interface>"
+"</node>";
+
 static void rb_playlist_manager_class_init (RBPlaylistManagerClass *klass);
 static void rb_playlist_manager_init (RBPlaylistManager *mgr);
-static void rb_playlist_manager_dispose (GObject *object);
-static void rb_playlist_manager_finalize (GObject *object);
-static void rb_playlist_manager_set_property (GObject *object,
-					      guint prop_id,
-					      const GValue *value,
-					      GParamSpec *pspec);
-static void rb_playlist_manager_get_property (GObject *object,
-					      guint prop_id,
-					      GValue *value,
-					      GParamSpec *pspec);
 static void rb_playlist_manager_cmd_load_playlist (GtkAction *action,
 						   RBPlaylistManager *mgr);
 static void rb_playlist_manager_cmd_save_playlist (GtkAction *action,
@@ -200,137 +224,6 @@ static guint rb_playlist_manager_n_actions = G_N_ELEMENTS (rb_playlist_manager_a
 
 G_DEFINE_TYPE (RBPlaylistManager, rb_playlist_manager, G_TYPE_OBJECT)
 
-static void
-rb_playlist_manager_class_init (RBPlaylistManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = rb_playlist_manager_dispose;
-	object_class->finalize = rb_playlist_manager_finalize;
-
-	object_class->set_property = rb_playlist_manager_set_property;
-	object_class->get_property = rb_playlist_manager_get_property;
-
-	g_object_class_install_property (object_class,
-					 PROP_PLAYLIST_NAME,
-                                         g_param_spec_string ("playlists_file",
-                                                              "name",
-                                                              "playlists file",
-                                                              NULL,
-                                                              G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_SOURCE,
-					 g_param_spec_object ("source",
-							      "RBSource",
-							      "RBSource object",
-							      RB_TYPE_SOURCE,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_SHELL,
-					 g_param_spec_object ("shell",
-							      "RBShell",
-							      "RBShell object",
-							      RB_TYPE_SHELL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_DISPLAY_PAGE_MODEL,
-					 g_param_spec_object ("display-page-model",
-							      "RBDisplayPageModel",
-							      "RBDisplayPageModel",
-							      RB_TYPE_DISPLAY_PAGE_MODEL,
-							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-	g_object_class_install_property (object_class,
-					 PROP_DISPLAY_PAGE_TREE,
-					 g_param_spec_object ("display-page-tree",
-							      "RBDisplayPageTree",
-							      "RBDisplayPageTree",
-							      RB_TYPE_DISPLAY_PAGE_TREE,
-							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-	/**
-	 * RBPlaylistManager::playlist-added:
-	 * @manager: the #RBPlaylistManager
-	 * @source: the new #RBSource
-	 *
-	 * Emitted when a playlist is added, including when being loaded
-	 * from the user's playlist file.
-	 */
-	rb_playlist_manager_signals[PLAYLIST_ADDED] =
-		g_signal_new ("playlist_added",
-			      RB_TYPE_PLAYLIST_MANAGER,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_added),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__OBJECT,
-			      G_TYPE_NONE,
-			      1, G_TYPE_OBJECT);
-
-	/**
-	 * RBPlaylistManager::playlist-created:
-	 * @manager: the #RBPlaylistManager
-	 * @source: the newly created playlist #RBSource
-	 *
-	 * Emitted when a new playlist is created.
-	 */
-	rb_playlist_manager_signals[PLAYLIST_CREATED] =
-		g_signal_new ("playlist_created",
-			      RB_TYPE_PLAYLIST_MANAGER,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_created),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__OBJECT,
-			      G_TYPE_NONE,
-			      1, G_TYPE_OBJECT);
-
-	/**
-	 * RBPlaylistManager::load-start:
-	 * @manager: the #RBPlaylistManager
-	 *
-	 * Emitted when the playlist manager starts loading the user's
-	 * playlist file.
-	 */
-	rb_playlist_manager_signals[PLAYLIST_LOAD_START] =
-		g_signal_new ("load_start",
-			      RB_TYPE_PLAYLIST_MANAGER,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RBPlaylistManagerClass, load_start),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID,
-			      G_TYPE_NONE,
-			      0, G_TYPE_NONE);
-	/**
-	 * RBPlaylistManager::load-finish
-	 * @manager: the #RBPlaylistManager
-	 *
-	 * Emitted when the playlist manager finishes loading the user's
-	 * playlist file.
-	 */
-	rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH] =
-		g_signal_new ("load_finish",
-			      RB_TYPE_PLAYLIST_MANAGER,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RBPlaylistManagerClass, load_finish),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID,
-			      G_TYPE_NONE,
-			      0, G_TYPE_NONE);
-
-	g_type_class_add_private (klass, sizeof (RBPlaylistManagerPrivate));
-}
-
-static void
-rb_playlist_manager_init (RBPlaylistManager *mgr)
-{
-	mgr->priv = G_TYPE_INSTANCE_GET_PRIVATE (mgr,
-						 RB_TYPE_PLAYLIST_MANAGER,
-						 RBPlaylistManagerPrivate);
-
-	mgr->priv->saving_mutex = g_mutex_new ();
-	mgr->priv->dirty = 0;
-	mgr->priv->saving = 0;
-}
 
 /**
  * rb_playlist_manager_shutdown:
@@ -348,656 +241,425 @@ rb_playlist_manager_shutdown (RBPlaylistManager *mgr)
 	g_mutex_unlock (mgr->priv->saving_mutex);
 }
 
-static void
-rb_playlist_manager_dispose (GObject *object)
+/**
+ * rb_playlist_manager_new:
+ * @shell: the #RBShell
+ * @page_model: the #RBDisplayPageModel
+ * @page_tree: the #RBDisplayPageTree
+ * @playlists_file: the full path to the playlist file to load
+ *
+ * Creates the #RBPlaylistManager instance
+ *
+ * Return value: the #RBPlaylistManager
+ */
+RBPlaylistManager *
+rb_playlist_manager_new (RBShell *shell,
+			 RBDisplayPageModel *page_model,
+			 RBDisplayPageTree *page_tree,
+			 const char *playlists_file)
 {
-	RBPlaylistManager *mgr;
-
-	g_return_if_fail (object != NULL);
-	g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
+	return g_object_new (RB_TYPE_PLAYLIST_MANAGER,
+			     "shell", shell,
+			     "display-page-model", page_model,
+			     "display-page-tree", page_tree,
+			     "playlists_file", playlists_file,
+			     NULL);
+}
 
-	rb_debug ("Disposing playlist manager");
+GQuark
+rb_playlist_manager_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("rb_playlist_manager_error");
 
-	mgr = RB_PLAYLIST_MANAGER (object);
+	return quark;
+}
 
-	g_return_if_fail (mgr->priv != NULL);
+static void
+handle_playlist_entry_cb (TotemPlParser *playlist,
+			  const char *uri_maybe,
+			  GHashTable *metadata,
+			  RBPlaylistManager *mgr)
+{
+	char *uri;
+	const char *title, *genre;
 
-	if (mgr->priv->db != NULL) {
-		g_object_unref (mgr->priv->db);
-		mgr->priv->db = NULL;
-	}
+	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
+	genre = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_GENRE);
 
-	if (mgr->priv->uimanager != NULL) {
-		g_object_unref (mgr->priv->uimanager);
-		mgr->priv->uimanager = NULL;
-	}
+	uri = rb_canonicalise_uri (uri_maybe);
+	g_return_if_fail (uri != NULL);
 
-	if (mgr->priv->page_model != NULL) {
-		g_object_unref (mgr->priv->page_model);
-		mgr->priv->page_model = NULL;
-	}
+	rb_debug ("adding uri %s (title %s, genre %s) from playlist",
+		  uri, title, genre);
+	if (!rb_shell_add_uri (mgr->priv->shell,
+			       uri,
+			       title,
+			       genre,
+			       NULL))
+		return;
 
-	if (mgr->priv->display_page_tree != NULL) {
-		g_object_unref (mgr->priv->display_page_tree);
-		mgr->priv->display_page_tree = NULL;
+	if (!mgr->priv->loading_playlist) {
+		mgr->priv->loading_playlist =
+			RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, NULL, FALSE));
 	}
-
-	if (mgr->priv->selected_source != NULL) {
-		g_object_unref (mgr->priv->selected_source);
-		mgr->priv->selected_source = NULL;
+	if (rb_source_want_uri (RB_SOURCE (mgr->priv->loading_playlist), uri) > 0) {
+		rb_debug ("adding uri %s to playlist", uri);
+		rb_static_playlist_source_add_location (mgr->priv->loading_playlist, uri, -1);
 	}
 
-	G_OBJECT_CLASS (rb_playlist_manager_parent_class)->dispose (object);
+	g_free (uri);
 }
 
 static void
-rb_playlist_manager_finalize (GObject *object)
+playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr)
 {
-	RBPlaylistManager *mgr;
+	const char *title;
 
-	g_return_if_fail (object != NULL);
-	g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
+	rb_debug ("loading new playlist %s", uri);
 
-	rb_debug ("Finalizing playlist manager");
+	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
+	if (title == NULL)
+		title = _("Unnamed playlist");
 
-	mgr = RB_PLAYLIST_MANAGER (object);
+	mgr->priv->loading_playlist =
+			RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
+}
 
-	g_return_if_fail (mgr->priv != NULL);
+/**
+ * rb_playlist_manager_parse_file:
+ * @mgr: the #RBPlaylistManager
+ * @uri: URI of the playlist to load
+ * @error: returns a GError in case of error
+ *
+ * Parses a playlist file, adding entries to the database and to a new
+ * static playlist.  If the playlist file includes a title, the static
+ * playlist created will have the same title.
+ *
+ * Return value: TRUE on success
+ **/
+gboolean
+rb_playlist_manager_parse_file (RBPlaylistManager *mgr, const char *uri, GError **error)
+{
+	rb_debug ("loading playlist from %s", uri);
 
-	g_mutex_free (mgr->priv->saving_mutex);
+	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_START], 0);
 
-	g_free (mgr->priv->playlists_file);
+	{
+		TotemPlParser *parser = totem_pl_parser_new ();
 
-	G_OBJECT_CLASS (rb_playlist_manager_parent_class)->finalize (object);
-}
+		g_signal_connect_object (parser, "entry-parsed",
+					 G_CALLBACK (handle_playlist_entry_cb),
+					 mgr, 0);
 
-static void
-rb_playlist_manager_set_uimanager (RBPlaylistManager *mgr,
-				   GtkUIManager *uimanager)
-{
-	if (mgr->priv->uimanager != NULL) {
-		if (mgr->priv->actiongroup != NULL) {
-			gtk_ui_manager_remove_action_group (mgr->priv->uimanager,
-							    mgr->priv->actiongroup);
+		g_signal_connect_object (parser, "playlist-started",
+					 G_CALLBACK (playlist_load_started_cb),
+					 mgr, 0);
+
+		g_object_set (parser, "recurse", FALSE, NULL);
+
+		if (totem_pl_parser_parse (parser, uri, TRUE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
+			g_set_error (error,
+				     RB_PLAYLIST_MANAGER_ERROR,
+				     RB_PLAYLIST_MANAGER_ERROR_PARSE,
+				     "%s",
+				     _("The playlist file may be in an unknown format or corrupted."));
+			return FALSE;
 		}
-		g_object_unref (mgr->priv->uimanager);
-	}
 
-	mgr->priv->uimanager = uimanager;
+		if (mgr->priv->loading_playlist != NULL) {
+			char *name = NULL;
 
-	if (mgr->priv->actiongroup == NULL) {
-		mgr->priv->actiongroup = gtk_action_group_new ("PlaylistManagerActions");
-		gtk_action_group_set_translation_domain (mgr->priv->actiongroup,
-							 GETTEXT_PACKAGE);
-		gtk_action_group_add_actions (mgr->priv->actiongroup,
-					      rb_playlist_manager_actions,
-					      rb_playlist_manager_n_actions,
-					      mgr);
+			/* totem-plparser may not have given us the playlist name */
+			g_object_get (mgr->priv->loading_playlist, "name", &name, NULL);
+			if (name == NULL || name[0] == '\0') {
+				char *path;
+
+				rb_debug ("setting playlist name from file name");
+				path = g_filename_from_uri (uri, NULL, NULL);
+				if (path) {
+					name = g_path_get_basename (path);
+					g_object_set (mgr->priv->loading_playlist, "name", name, NULL);
+					g_free (path);
+				}
+			}
+
+			g_free (name);
+			mgr->priv->loading_playlist = NULL;
+		}
+
+		g_object_unref (parser);
 	}
 
-	gtk_ui_manager_insert_action_group (mgr->priv->uimanager,
-					    mgr->priv->actiongroup,
-					    0);
+	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH], 0);
+	return TRUE;
 }
 
 static void
-rb_playlist_manager_set_source (RBPlaylistManager *mgr,
-				RBSource *source)
+append_new_playlist_source (RBPlaylistManager *mgr, RBPlaylistSource *source)
 {
-	gboolean playlist_active;
-	gboolean playlist_local = FALSE;
-	gboolean party_mode;
-	gboolean can_save;
-	gboolean can_delete;
-	gboolean can_edit;
-	gboolean can_rename;
-	gboolean can_shuffle;
-	GtkAction *action;
+	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_ADDED], 0,
+		       source);
+}
 
-	party_mode = rb_shell_get_party_mode (mgr->priv->shell);
+/**
+ * rb_playlist_manager_load_playlists:
+ * @mgr: the #RBPlaylistManager
+ *
+ * Loads the user's playlists, or if the playlist file does not exists,
+ * reads the default playlist file.  Should be called only once on startup.
+ **/
+void
+rb_playlist_manager_load_playlists (RBPlaylistManager *mgr)
+{
+	char *file;
+	xmlDocPtr doc;
+	xmlNodePtr root;
+	xmlNodePtr child;
+	gboolean exists;
 
-	if (mgr->priv->selected_source != NULL) {
-		g_object_unref (mgr->priv->selected_source);
+	exists = FALSE;
+	file = g_strdup (mgr->priv->playlists_file);
+
+	/* block saves until the playlists have loaded */
+	g_mutex_lock (mgr->priv->saving_mutex);
+
+	exists = g_file_test (file, G_FILE_TEST_EXISTS);
+	if (! exists) {
+		rb_debug ("personal playlists not found, loading defaults");
+
+		/* try global playlists */
+		g_free (file);
+		file = g_strdup (rb_file ("playlists.xml"));
+		exists = g_file_test (file, G_FILE_TEST_EXISTS);
 	}
-	mgr->priv->selected_source = g_object_ref (source);
 
-	playlist_active = RB_IS_PLAYLIST_SOURCE (mgr->priv->selected_source);
-	if (playlist_active) {
-		g_object_get (mgr->priv->selected_source, "is-local", &playlist_local, NULL);
+	if (! exists) {
+		rb_debug ("default playlists file not found");
+		goto out;
 	}
 
-	can_save = playlist_local && !party_mode;
-	action = gtk_action_group_get_action (mgr->priv->actiongroup,
-					      "MusicPlaylistSavePlaylist");
-	gtk_action_set_visible (action, can_save);
+	doc = xmlParseFile (file);
+	if (doc == NULL)
+		goto out;
 
-	can_delete = (playlist_local && !party_mode &&
-		      !RB_IS_PLAY_QUEUE_SOURCE (mgr->priv->selected_source));
-	action = gtk_action_group_get_action (mgr->priv->actiongroup,
-					      "MusicPlaylistDeletePlaylist");
-	gtk_action_set_visible (action, can_delete);
+	root = xmlDocGetRootElement (doc);
 
-	can_edit = (playlist_local && RB_IS_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source) &&
-		    !party_mode);
-	action = gtk_action_group_get_action (mgr->priv->actiongroup,
-					      "EditAutomaticPlaylist");
-	gtk_action_set_visible (action, can_edit);
+	for (child = root->children; child; child = child->next) {
+		RBSource *playlist;
 
-	can_rename = playlist_local && rb_source_can_rename (mgr->priv->selected_source);
-	action = gtk_action_group_get_action (mgr->priv->actiongroup,
-					      "MusicPlaylistRenamePlaylist");
-	gtk_action_set_visible (action, can_rename);
+		if (xmlNodeIsText (child))
+			continue;
 
-	can_shuffle = RB_IS_STATIC_PLAYLIST_SOURCE (mgr->priv->selected_source);
-	action = gtk_action_group_get_action (mgr->priv->actiongroup,
-					      "ShufflePlaylist");
-	gtk_action_set_sensitive (action, can_shuffle);
+		playlist = rb_playlist_source_new_from_xml (mgr->priv->shell,
+							    child);
+		if (playlist)
+			append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
+	}
+
+	xmlFreeDoc (doc);
+out:
+	g_mutex_unlock (mgr->priv->saving_mutex);
+	g_free (file);
 }
 
 static void
-rb_playlist_manager_set_shell_internal (RBPlaylistManager *mgr,
-					RBShell           *shell)
+rb_playlist_manager_set_dirty (RBPlaylistManager *mgr, gboolean dirty)
 {
-	GtkUIManager *uimanager = NULL;
-	RhythmDB     *db = NULL;
+	g_atomic_int_compare_and_exchange (&mgr->priv->dirty, dirty == FALSE, dirty == TRUE);
+}
 
-	if (mgr->priv->db != NULL) {
-		g_object_unref (mgr->priv->db);
+static gboolean
+_is_dirty_playlist (GtkTreeModel *model,
+		    GtkTreePath *path,
+		    GtkTreeIter *iter,
+		    gboolean *dirty)
+{
+	RBDisplayPage *page;
+	gboolean local;
+	gboolean ret;
+
+	gtk_tree_model_get (model,
+			    iter,
+			    RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE,
+			    &page,
+			    -1);
+	if (page == NULL) {
+		return FALSE;
+	}
+	if (RB_IS_PLAYLIST_SOURCE (page) == FALSE) {
+		g_object_unref (page);
+		return FALSE;
 	}
 
-	mgr->priv->shell = shell;
+	ret = FALSE;
+	g_object_get (page, "is-local", &local, NULL);
+	if (local) {
+		gboolean pdirty;
 
-	if (mgr->priv->shell != NULL) {
-		g_object_get (mgr->priv->shell,
-			      "ui-manager", &uimanager,
-			      "db", &db,
-			      NULL);
+		g_object_get (page, "dirty", &pdirty, NULL);
+		if (pdirty) {
+			*dirty = TRUE;
+			ret = TRUE;
+		}
 	}
+	g_object_unref (page);
 
-	mgr->priv->db = db;
-	rb_playlist_manager_set_uimanager (mgr, uimanager);
+	return ret;
 }
 
-static void
-rb_playlist_manager_set_property (GObject *object,
-				  guint prop_id,
-				  const GValue *value,
-				  GParamSpec *pspec)
+/* returns TRUE if a playlist has been created, modified, or deleted since last save */
+static gboolean
+rb_playlist_manager_is_dirty (RBPlaylistManager *mgr)
 {
-	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
+	gboolean dirty = FALSE;
 
-	switch (prop_id) {
-	case PROP_PLAYLIST_NAME:
-		g_free (mgr->priv->playlists_file);
-		mgr->priv->playlists_file = g_strdup (g_value_get_string (value));
-                break;
-	case PROP_SOURCE:
-		rb_playlist_manager_set_source (mgr, g_value_get_object (value));
-		break;
-	case PROP_SHELL:
-		rb_playlist_manager_set_shell_internal (mgr, g_value_get_object (value));
-		break;
-	case PROP_DISPLAY_PAGE_MODEL:
-		mgr->priv->page_model = g_value_dup_object (value);
-		break;
-	case PROP_DISPLAY_PAGE_TREE:
-		mgr->priv->display_page_tree = g_value_dup_object (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
+				(GtkTreeModelForeachFunc) _is_dirty_playlist,
+				&dirty);
+
+	if (!dirty)
+		dirty = g_atomic_int_get (&mgr->priv->dirty);
+
+	return dirty;
 }
 
-static void
-rb_playlist_manager_get_property (GObject *object,
-			      guint prop_id,
-			      GValue *value,
-			      GParamSpec *pspec)
+struct RBPlaylistManagerSaveData
 {
-	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
+	RBPlaylistManager *mgr;
+	xmlDocPtr doc;
+};
 
-	switch (prop_id) {
-        case PROP_PLAYLIST_NAME:
-                g_value_set_string (value, mgr->priv->playlists_file);
-                break;
-	case PROP_SOURCE:
-		g_value_set_object (value, mgr->priv->selected_source);
-		break;
-	case PROP_SHELL:
-		g_value_set_object (value, mgr->priv->shell);
-		break;
-	case PROP_DISPLAY_PAGE_MODEL:
-		g_value_set_object (value, mgr->priv->page_model);
-		break;
-	case PROP_DISPLAY_PAGE_TREE:
-		g_value_set_object (value, mgr->priv->display_page_tree);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-/**
- * rb_playlist_manager_new:
- * @shell: the #RBShell
- * @page_model: the #RBDisplayPageModel
- * @page_tree: the #RBDisplayPageTree
- * @playlists_file: the full path to the playlist file to load
- *
- * Creates the #RBPlaylistManager instance
- *
- * Return value: the #RBPlaylistManager
- */
-RBPlaylistManager *
-rb_playlist_manager_new (RBShell *shell,
-			 RBDisplayPageModel *page_model,
-			 RBDisplayPageTree *page_tree,
-			 const char *playlists_file)
-{
-	return g_object_new (RB_TYPE_PLAYLIST_MANAGER,
-			     "shell", shell,
-			     "display-page-model", page_model,
-			     "display-page-tree", page_tree,
-			     "playlists_file", playlists_file,
-			     NULL);
-}
-
-GQuark
-rb_playlist_manager_error_quark (void)
+static gpointer
+rb_playlist_manager_save_data (struct RBPlaylistManagerSaveData *data)
 {
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("rb_playlist_manager_error");
-
-	return quark;
-}
+	char *file;
+	char *tmpname;
 
-static void
-handle_playlist_entry_cb (TotemPlParser *playlist,
-			  const char *uri_maybe,
-			  GHashTable *metadata,
-			  RBPlaylistManager *mgr)
-{
-	char *uri;
-	const char *title, *genre;
+	g_mutex_lock (data->mgr->priv->saving_mutex);
 
-	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
-	genre = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_GENRE);
+	file = g_strdup (data->mgr->priv->playlists_file);
+	tmpname = g_strconcat (file, ".tmp", NULL);
 
-	uri = rb_canonicalise_uri (uri_maybe);
-	g_return_if_fail (uri != NULL);
+	if (xmlSaveFormatFile (tmpname, data->doc, 1) != -1) {
+		rename (tmpname, file);
+	} else {
+		rb_debug ("error in xmlSaveFormatFile(), not saving");
+		unlink (tmpname);
+		rb_playlist_manager_set_dirty (data->mgr, TRUE);
+	}
+	xmlFreeDoc (data->doc);
+	g_free (tmpname);
+	g_free (file);
 
-	rb_debug ("adding uri %s (title %s, genre %s) from playlist",
-		  uri, title, genre);
-	if (!rb_shell_add_uri (mgr->priv->shell,
-			       uri,
-			       title,
-			       genre,
-			       NULL))
-		return;
+	g_atomic_int_compare_and_exchange (&data->mgr->priv->saving, 1, 0);
+	g_mutex_unlock (data->mgr->priv->saving_mutex);
 
-	if (!mgr->priv->loading_playlist) {
-		mgr->priv->loading_playlist =
-			RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, NULL, FALSE));
-	}
-	if (rb_source_want_uri (RB_SOURCE (mgr->priv->loading_playlist), uri) > 0) {
-		rb_debug ("adding uri %s to playlist", uri);
-		rb_static_playlist_source_add_location (mgr->priv->loading_playlist, uri, -1);
-	}
+	g_object_unref (data->mgr);
 
-	g_free (uri);
+	g_free (data);
+	return NULL;
 }
 
-static void
-playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr)
+static gboolean
+save_playlist_cb (GtkTreeModel *model,
+		  GtkTreePath  *path,
+		  GtkTreeIter  *iter,
+		  xmlNodePtr    root)
 {
-	const char *title;
+	RBDisplayPage *page;
+	gboolean  local;
 
-	rb_debug ("loading new playlist %s", uri);
+	gtk_tree_model_get (model,
+			    iter,
+			    RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
+			    -1);
+	if (page == NULL) {
+		goto out;
+	}
+	if (RB_IS_PLAYLIST_SOURCE (page) == FALSE) {
+		goto out;
+	}
 
-	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
-	if (title == NULL)
-		title = _("Unnamed playlist");
+	g_object_get (page, "is-local", &local, NULL);
+	if (local) {
+		rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (page), root);
+	}
+ out:
+	if (page != NULL) {
+		g_object_unref (page);
+	}
 
-	mgr->priv->loading_playlist =
-			RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
+	return FALSE;
 }
 
 /**
- * rb_playlist_manager_parse_file:
+ * rb_playlist_manager_save_playlists:
  * @mgr: the #RBPlaylistManager
- * @uri: URI of the playlist to load
- * @error: returns a GError in case of error
+ * @force: if TRUE, save playlists synchronously and unconditionally
  *
- * Parses a playlist file, adding entries to the database and to a new
- * static playlist.  If the playlist file includes a title, the static
- * playlist created will have the same title.
+ * Saves the user's playlists.  If the force flag is
+ * TRUE, the playlists will always be saved.  Otherwise, the playlists
+ * will only be saved if a playlist has been created, modified, or deleted
+ * since the last time the playlists were saved, and no save operation is
+ * currently taking place.
  *
- * Return value: TRUE on success
+ * Return value: TRUE if a playlist save operation has been started
  **/
 gboolean
-rb_playlist_manager_parse_file (RBPlaylistManager *mgr, const char *uri, GError **error)
+rb_playlist_manager_save_playlists (RBPlaylistManager *mgr, gboolean force)
 {
-	rb_debug ("loading playlist from %s", uri);
-
-	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_START], 0);
-
-	{
-		TotemPlParser *parser = totem_pl_parser_new ();
-
-		g_signal_connect_object (parser, "entry-parsed",
-					 G_CALLBACK (handle_playlist_entry_cb),
-					 mgr, 0);
-
-		g_signal_connect_object (parser, "playlist-started",
-					 G_CALLBACK (playlist_load_started_cb),
-					 mgr, 0);
+	xmlNodePtr root;
+	struct RBPlaylistManagerSaveData *data;
 
-		g_object_set (parser, "recurse", FALSE, NULL);
+	if (!force && !rb_playlist_manager_is_dirty (mgr)) {
+		/* playlists already in sync, so don't bother */
+		return FALSE;
+	}
 
-		if (totem_pl_parser_parse (parser, uri, TRUE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
-			g_set_error (error,
-				     RB_PLAYLIST_MANAGER_ERROR,
-				     RB_PLAYLIST_MANAGER_ERROR_PARSE,
-				     "%s",
-				     _("The playlist file may be in an unknown format or corrupted."));
-			return FALSE;
-		}
+	if (!g_atomic_int_compare_and_exchange (&mgr->priv->saving, 0, 1) && !force) {
+		/* already saving, so don't bother */
+		return FALSE;
+	}
 
-		if (mgr->priv->loading_playlist != NULL) {
-			char *name = NULL;
+	data = g_new0 (struct RBPlaylistManagerSaveData, 1);
+	data->mgr = mgr;
+	data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
+	g_object_ref (mgr);
 
-			/* totem-plparser may not have given us the playlist name */
-			g_object_get (mgr->priv->loading_playlist, "name", &name, NULL);
-			if (name == NULL || name[0] == '\0') {
-				char *path;
+	root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
+	xmlDocSetRootElement (data->doc, root);
 
-				rb_debug ("setting playlist name from file name");
-				path = g_filename_from_uri (uri, NULL, NULL);
-				if (path) {
-					name = g_path_get_basename (path);
-					g_object_set (mgr->priv->loading_playlist, "name", name, NULL);
-					g_free (path);
-				}
-			}
+	gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
+				(GtkTreeModelForeachFunc)save_playlist_cb,
+				root);
 
-			g_free (name);
-			mgr->priv->loading_playlist = NULL;
-		}
+	/* mark clean here.  if the save fails, we'll mark it dirty again */
+	rb_playlist_manager_set_dirty (data->mgr, FALSE);
 
-		g_object_unref (parser);
-	}
+	if (force)
+		rb_playlist_manager_save_data (data);
+	else
+		g_thread_create ((GThreadFunc) rb_playlist_manager_save_data, data, FALSE, NULL);
 
-	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH], 0);
 	return TRUE;
 }
 
-static void
-append_new_playlist_source (RBPlaylistManager *mgr, RBPlaylistSource *source)
-{
-	g_signal_emit (mgr, rb_playlist_manager_signals[PLAYLIST_ADDED], 0,
-		       source);
-}
-
 /**
- * rb_playlist_manager_load_playlists:
+ * rb_playlist_manager_new_playlist:
  * @mgr: the #RBPlaylistManager
+ * @suggested_name: optional name to use for the new playlist
+ * @automatic: if TRUE, create an auto playlist
  *
- * Loads the user's playlists, or if the playlist file does not exists,
- * reads the default playlist file.  Should be called only once on startup.
- **/
-void
-rb_playlist_manager_load_playlists (RBPlaylistManager *mgr)
-{
-	char *file;
-	xmlDocPtr doc;
-	xmlNodePtr root;
-	xmlNodePtr child;
-	gboolean exists;
-
-	exists = FALSE;
-	file = g_strdup (mgr->priv->playlists_file);
-
-	/* block saves until the playlists have loaded */
-	g_mutex_lock (mgr->priv->saving_mutex);
-
-	exists = g_file_test (file, G_FILE_TEST_EXISTS);
-	if (! exists) {
-		rb_debug ("personal playlists not found, loading defaults");
-
-		/* try global playlists */
-		g_free (file);
-		file = g_strdup (rb_file ("playlists.xml"));
-		exists = g_file_test (file, G_FILE_TEST_EXISTS);
-	}
-
-	if (! exists) {
-		rb_debug ("default playlists file not found");
-		goto out;
-	}
-
-	doc = xmlParseFile (file);
-	if (doc == NULL)
-		goto out;
-
-	root = xmlDocGetRootElement (doc);
-
-	for (child = root->children; child; child = child->next) {
-		RBSource *playlist;
-
-		if (xmlNodeIsText (child))
-			continue;
-
-		playlist = rb_playlist_source_new_from_xml (mgr->priv->shell,
-							    child);
-		if (playlist)
-			append_new_playlist_source (mgr, RB_PLAYLIST_SOURCE (playlist));
-	}
-
-	xmlFreeDoc (doc);
-out:
-	g_mutex_unlock (mgr->priv->saving_mutex);
-	g_free (file);
-}
-
-static void
-rb_playlist_manager_set_dirty (RBPlaylistManager *mgr, gboolean dirty)
-{
-	g_atomic_int_compare_and_exchange (&mgr->priv->dirty, dirty == FALSE, dirty == TRUE);
-}
-
-static gboolean
-_is_dirty_playlist (GtkTreeModel *model,
-		    GtkTreePath *path,
-		    GtkTreeIter *iter,
-		    gboolean *dirty)
-{
-	RBDisplayPage *page;
-	gboolean local;
-	gboolean ret;
-
-	gtk_tree_model_get (model,
-			    iter,
-			    RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE,
-			    &page,
-			    -1);
-	if (page == NULL) {
-		return FALSE;
-	}
-	if (RB_IS_PLAYLIST_SOURCE (page) == FALSE) {
-		g_object_unref (page);
-		return FALSE;
-	}
-
-	ret = FALSE;
-	g_object_get (page, "is-local", &local, NULL);
-	if (local) {
-		gboolean pdirty;
-
-		g_object_get (page, "dirty", &pdirty, NULL);
-		if (pdirty) {
-			*dirty = TRUE;
-			ret = TRUE;
-		}
-	}
-	g_object_unref (page);
-
-	return ret;
-}
-
-/* returns TRUE if a playlist has been created, modified, or deleted since last save */
-static gboolean
-rb_playlist_manager_is_dirty (RBPlaylistManager *mgr)
-{
-	gboolean dirty = FALSE;
-
-	gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
-				(GtkTreeModelForeachFunc) _is_dirty_playlist,
-				&dirty);
-
-	if (!dirty)
-		dirty = g_atomic_int_get (&mgr->priv->dirty);
-
-	return dirty;
-}
-
-struct RBPlaylistManagerSaveData
-{
-	RBPlaylistManager *mgr;
-	xmlDocPtr doc;
-};
-
-static gpointer
-rb_playlist_manager_save_data (struct RBPlaylistManagerSaveData *data)
-{
-	char *file;
-	char *tmpname;
-
-	g_mutex_lock (data->mgr->priv->saving_mutex);
-
-	file = g_strdup (data->mgr->priv->playlists_file);
-	tmpname = g_strconcat (file, ".tmp", NULL);
-
-	if (xmlSaveFormatFile (tmpname, data->doc, 1) != -1) {
-		rename (tmpname, file);
-	} else {
-		rb_debug ("error in xmlSaveFormatFile(), not saving");
-		unlink (tmpname);
-		rb_playlist_manager_set_dirty (data->mgr, TRUE);
-	}
-	xmlFreeDoc (data->doc);
-	g_free (tmpname);
-	g_free (file);
-
-	g_atomic_int_compare_and_exchange (&data->mgr->priv->saving, 1, 0);
-	g_mutex_unlock (data->mgr->priv->saving_mutex);
-
-	g_object_unref (data->mgr);
-
-	g_free (data);
-	return NULL;
-}
-
-static gboolean
-save_playlist_cb (GtkTreeModel *model,
-		  GtkTreePath  *path,
-		  GtkTreeIter  *iter,
-		  xmlNodePtr    root)
-{
-	RBDisplayPage *page;
-	gboolean  local;
-
-	gtk_tree_model_get (model,
-			    iter,
-			    RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
-			    -1);
-	if (page == NULL) {
-		goto out;
-	}
-	if (RB_IS_PLAYLIST_SOURCE (page) == FALSE) {
-		goto out;
-	}
-
-	g_object_get (page, "is-local", &local, NULL);
-	if (local) {
-		rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (page), root);
-	}
- out:
-	if (page != NULL) {
-		g_object_unref (page);
-	}
-
-	return FALSE;
-}
-
-/**
- * rb_playlist_manager_save_playlists:
- * @mgr: the #RBPlaylistManager
- * @force: if TRUE, save playlists synchronously and unconditionally
- *
- * Saves the user's playlists.  If the force flag is
- * TRUE, the playlists will always be saved.  Otherwise, the playlists
- * will only be saved if a playlist has been created, modified, or deleted
- * since the last time the playlists were saved, and no save operation is
- * currently taking place.
- *
- * Return value: TRUE if a playlist save operation has been started
- **/
-gboolean
-rb_playlist_manager_save_playlists (RBPlaylistManager *mgr, gboolean force)
-{
-	xmlNodePtr root;
-	struct RBPlaylistManagerSaveData *data;
-
-	if (!force && !rb_playlist_manager_is_dirty (mgr)) {
-		/* playlists already in sync, so don't bother */
-		return FALSE;
-	}
-
-	if (!g_atomic_int_compare_and_exchange (&mgr->priv->saving, 0, 1) && !force) {
-		/* already saving, so don't bother */
-		return FALSE;
-	}
-
-	data = g_new0 (struct RBPlaylistManagerSaveData, 1);
-	data->mgr = mgr;
-	data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
-	g_object_ref (mgr);
-
-	root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
-	xmlDocSetRootElement (data->doc, root);
-
-	gtk_tree_model_foreach (GTK_TREE_MODEL (mgr->priv->page_model),
-				(GtkTreeModelForeachFunc)save_playlist_cb,
-				root);
-
-	/* mark clean here.  if the save fails, we'll mark it dirty again */
-	rb_playlist_manager_set_dirty (data->mgr, FALSE);
-
-	if (force)
-		rb_playlist_manager_save_data (data);
-	else
-		g_thread_create ((GThreadFunc) rb_playlist_manager_save_data, data, FALSE, NULL);
-
-	return TRUE;
-}
-
-/**
- * rb_playlist_manager_new_playlist:
- * @mgr: the #RBPlaylistManager
- * @suggested_name: optional name to use for the new playlist
- * @automatic: if TRUE, create an auto playlist
- *
- * Creates a new playlist and adds it to the source list.
- *
- * Return value: (transfer none): the new playlist object.
- */
-RBSource *
-rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
-				  const char *suggested_name,
-				  gboolean automatic)
+ * Creates a new playlist and adds it to the source list.
+ *
+ * Return value: (transfer none): the new playlist object.
+ */
+RBSource *
+rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
+				  const char *suggested_name,
+				  gboolean automatic)
 {
 	RBSource *playlist;
 	if (automatic)
@@ -1930,3 +1592,499 @@ rb_playlist_manager_export_playlist (RBPlaylistManager *mgr,
 					  m3u_format);
 	return TRUE;
 }
+
+static void
+playlist_manager_method_call (GDBusConnection *connection,
+			      const char *sender,
+			      const char *object_path,
+			      const char *interface_name,
+			      const char *method_name,
+			      GVariant *parameters,
+			      GDBusMethodInvocation *invocation,
+			      RBPlaylistManager *mgr)
+{
+	GError *error = NULL;
+	const char *name;
+	const char *uri;
+
+	if (g_strcmp0 (interface_name, RB_PLAYLIST_MANAGER_IFACE_NAME) != 0) {
+		rb_debug ("method call on unexpected interface %s", interface_name);
+		g_dbus_method_invocation_return_error (invocation,
+						       G_DBUS_ERROR,
+						       G_DBUS_ERROR_NOT_SUPPORTED,
+						       "Method %s.%s not supported",
+						       interface_name,
+						       method_name);
+		return;
+	}
+
+	if (g_strcmp0 (method_name, "getPlaylists") == 0) {
+		char **names;
+
+		rb_playlist_manager_get_playlist_names (mgr, &names, NULL);
+		g_dbus_method_invocation_return_value (invocation,
+						       g_variant_new_strv ((const char * const *)names, -1));
+		g_strfreev (names);
+	} else if (g_strcmp0 (method_name, "createPlaylist") == 0) {
+		g_variant_get (parameters, "(&s)", &name);
+		if (rb_playlist_manager_create_static_playlist (mgr, name, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else if (g_strcmp0 (method_name, "deletePlaylist") == 0) {
+		g_variant_get (parameters, "(&s)", &name);
+		if (rb_playlist_manager_delete_playlist (mgr, name, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else if (g_strcmp0 (method_name, "addToPlaylist") == 0) {
+		g_variant_get (parameters, "(ss)", &name, &uri);
+		if (rb_playlist_manager_add_to_playlist (mgr, name, uri, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else if (g_strcmp0 (method_name, "removeFromPlaylist") == 0) {
+		g_variant_get (parameters, "(ss)", &name, &uri);
+		if (rb_playlist_manager_remove_from_playlist (mgr, name, uri, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else if (g_strcmp0 (method_name, "exportPlaylist") == 0) {
+		gboolean m3u_format;
+		g_variant_get (parameters, "(ssb)", &name, &uri, &m3u_format);
+		if (rb_playlist_manager_export_playlist (mgr, name, uri, m3u_format, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else if (g_strcmp0 (method_name, "importPlaylist") == 0) {
+		g_variant_get (parameters, "(s)", &uri);
+		if (rb_playlist_manager_parse_file (mgr, uri, &error)) {
+			g_dbus_method_invocation_return_value (invocation, NULL);
+		} else {
+			g_dbus_method_invocation_return_gerror (invocation, error);
+			g_clear_error (&error);
+		}
+	} else {
+		g_dbus_method_invocation_return_error (invocation,
+						       G_DBUS_ERROR,
+						       G_DBUS_ERROR_NOT_SUPPORTED,
+						       "Method %s.%s not supported",
+						       interface_name,
+						       method_name);
+	}
+}
+
+static const GDBusInterfaceVTable playlist_manager_vtable = {
+	(GDBusInterfaceMethodCallFunc) playlist_manager_method_call,
+	NULL,
+	NULL
+};
+
+static void
+rb_playlist_manager_set_uimanager (RBPlaylistManager *mgr,
+				   GtkUIManager *uimanager)
+{
+	if (mgr->priv->uimanager != NULL) {
+		if (mgr->priv->actiongroup != NULL) {
+			gtk_ui_manager_remove_action_group (mgr->priv->uimanager,
+							    mgr->priv->actiongroup);
+		}
+		g_object_unref (mgr->priv->uimanager);
+	}
+
+	mgr->priv->uimanager = uimanager;
+
+	if (mgr->priv->actiongroup == NULL) {
+		mgr->priv->actiongroup = gtk_action_group_new ("PlaylistManagerActions");
+		gtk_action_group_set_translation_domain (mgr->priv->actiongroup,
+							 GETTEXT_PACKAGE);
+		gtk_action_group_add_actions (mgr->priv->actiongroup,
+					      rb_playlist_manager_actions,
+					      rb_playlist_manager_n_actions,
+					      mgr);
+	}
+
+	gtk_ui_manager_insert_action_group (mgr->priv->uimanager,
+					    mgr->priv->actiongroup,
+					    0);
+}
+
+static void
+rb_playlist_manager_set_source (RBPlaylistManager *mgr,
+				RBSource *source)
+{
+	gboolean playlist_active;
+	gboolean playlist_local = FALSE;
+	gboolean party_mode;
+	gboolean can_save;
+	gboolean can_delete;
+	gboolean can_edit;
+	gboolean can_rename;
+	gboolean can_shuffle;
+	GtkAction *action;
+
+	party_mode = rb_shell_get_party_mode (mgr->priv->shell);
+
+	if (mgr->priv->selected_source != NULL) {
+		g_object_unref (mgr->priv->selected_source);
+	}
+	mgr->priv->selected_source = g_object_ref (source);
+
+	playlist_active = RB_IS_PLAYLIST_SOURCE (mgr->priv->selected_source);
+	if (playlist_active) {
+		g_object_get (mgr->priv->selected_source, "is-local", &playlist_local, NULL);
+	}
+
+	can_save = playlist_local && !party_mode;
+	action = gtk_action_group_get_action (mgr->priv->actiongroup,
+					      "MusicPlaylistSavePlaylist");
+	gtk_action_set_visible (action, can_save);
+
+	can_delete = (playlist_local && !party_mode &&
+		      !RB_IS_PLAY_QUEUE_SOURCE (mgr->priv->selected_source));
+	action = gtk_action_group_get_action (mgr->priv->actiongroup,
+					      "MusicPlaylistDeletePlaylist");
+	gtk_action_set_visible (action, can_delete);
+
+	can_edit = (playlist_local && RB_IS_AUTO_PLAYLIST_SOURCE (mgr->priv->selected_source) &&
+		    !party_mode);
+	action = gtk_action_group_get_action (mgr->priv->actiongroup,
+					      "EditAutomaticPlaylist");
+	gtk_action_set_visible (action, can_edit);
+
+	can_rename = playlist_local && rb_source_can_rename (mgr->priv->selected_source);
+	action = gtk_action_group_get_action (mgr->priv->actiongroup,
+					      "MusicPlaylistRenamePlaylist");
+	gtk_action_set_visible (action, can_rename);
+
+	can_shuffle = RB_IS_STATIC_PLAYLIST_SOURCE (mgr->priv->selected_source);
+	action = gtk_action_group_get_action (mgr->priv->actiongroup,
+					      "ShufflePlaylist");
+	gtk_action_set_sensitive (action, can_shuffle);
+}
+
+static void
+rb_playlist_manager_set_shell_internal (RBPlaylistManager *mgr,
+					RBShell           *shell)
+{
+	GtkUIManager *uimanager = NULL;
+	RhythmDB     *db = NULL;
+
+	if (mgr->priv->db != NULL) {
+		g_object_unref (mgr->priv->db);
+	}
+
+	mgr->priv->shell = shell;
+
+	if (mgr->priv->shell != NULL) {
+		g_object_get (mgr->priv->shell,
+			      "ui-manager", &uimanager,
+			      "db", &db,
+			      NULL);
+	}
+
+	mgr->priv->db = db;
+	rb_playlist_manager_set_uimanager (mgr, uimanager);
+}
+
+static void
+rb_playlist_manager_set_property (GObject *object,
+				  guint prop_id,
+				  const GValue *value,
+				  GParamSpec *pspec)
+{
+	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
+
+	switch (prop_id) {
+	case PROP_PLAYLIST_NAME:
+		g_free (mgr->priv->playlists_file);
+		mgr->priv->playlists_file = g_strdup (g_value_get_string (value));
+                break;
+	case PROP_SOURCE:
+		rb_playlist_manager_set_source (mgr, g_value_get_object (value));
+		break;
+	case PROP_SHELL:
+		rb_playlist_manager_set_shell_internal (mgr, g_value_get_object (value));
+		break;
+	case PROP_DISPLAY_PAGE_MODEL:
+		mgr->priv->page_model = g_value_dup_object (value);
+		break;
+	case PROP_DISPLAY_PAGE_TREE:
+		mgr->priv->display_page_tree = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_playlist_manager_get_property (GObject *object,
+			      guint prop_id,
+			      GValue *value,
+			      GParamSpec *pspec)
+{
+	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
+
+	switch (prop_id) {
+        case PROP_PLAYLIST_NAME:
+                g_value_set_string (value, mgr->priv->playlists_file);
+                break;
+	case PROP_SOURCE:
+		g_value_set_object (value, mgr->priv->selected_source);
+		break;
+	case PROP_SHELL:
+		g_value_set_object (value, mgr->priv->shell);
+		break;
+	case PROP_DISPLAY_PAGE_MODEL:
+		g_value_set_object (value, mgr->priv->page_model);
+		break;
+	case PROP_DISPLAY_PAGE_TREE:
+		g_value_set_object (value, mgr->priv->display_page_tree);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_playlist_manager_constructed (GObject *object)
+{
+	GDBusConnection *bus;
+	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
+
+	RB_CHAIN_GOBJECT_METHOD(rb_playlist_manager_parent_class, constructed, G_OBJECT (mgr));
+
+	bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+	if (bus) {
+		GDBusNodeInfo *node_info;
+		GError *error = NULL;
+
+		node_info = g_dbus_node_info_new_for_xml (rb_playlist_manager_dbus_spec, &error);
+		if (error != NULL) {
+			g_warning ("Unable to parse playlist manager dbus spec: %s", error->message);
+			g_clear_error (&error);
+			return;
+		}
+
+		g_dbus_connection_register_object (bus,
+						   RB_PLAYLIST_MANAGER_DBUS_PATH,
+						   g_dbus_node_info_lookup_interface (node_info, RB_PLAYLIST_MANAGER_IFACE_NAME),
+						   &playlist_manager_vtable,
+						   g_object_ref (mgr),
+						   g_object_unref,
+						   &error);
+		if (error != NULL) {
+			g_warning ("Unable to register playlist manager dbus object: %s", error->message);
+			g_clear_error (&error);
+		}
+	}
+}
+
+static void
+rb_playlist_manager_init (RBPlaylistManager *mgr)
+{
+	mgr->priv = G_TYPE_INSTANCE_GET_PRIVATE (mgr,
+						 RB_TYPE_PLAYLIST_MANAGER,
+						 RBPlaylistManagerPrivate);
+
+	mgr->priv->saving_mutex = g_mutex_new ();
+	mgr->priv->dirty = 0;
+	mgr->priv->saving = 0;
+}
+
+static void
+rb_playlist_manager_dispose (GObject *object)
+{
+	RBPlaylistManager *mgr;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
+
+	rb_debug ("Disposing playlist manager");
+
+	mgr = RB_PLAYLIST_MANAGER (object);
+
+	g_return_if_fail (mgr->priv != NULL);
+
+	if (mgr->priv->db != NULL) {
+		g_object_unref (mgr->priv->db);
+		mgr->priv->db = NULL;
+	}
+
+	if (mgr->priv->uimanager != NULL) {
+		g_object_unref (mgr->priv->uimanager);
+		mgr->priv->uimanager = NULL;
+	}
+
+	if (mgr->priv->page_model != NULL) {
+		g_object_unref (mgr->priv->page_model);
+		mgr->priv->page_model = NULL;
+	}
+
+	if (mgr->priv->display_page_tree != NULL) {
+		g_object_unref (mgr->priv->display_page_tree);
+		mgr->priv->display_page_tree = NULL;
+	}
+
+	if (mgr->priv->selected_source != NULL) {
+		g_object_unref (mgr->priv->selected_source);
+		mgr->priv->selected_source = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_playlist_manager_parent_class)->dispose (object);
+}
+
+static void
+rb_playlist_manager_finalize (GObject *object)
+{
+	RBPlaylistManager *mgr;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (RB_IS_PLAYLIST_MANAGER (object));
+
+	rb_debug ("Finalizing playlist manager");
+
+	mgr = RB_PLAYLIST_MANAGER (object);
+
+	g_return_if_fail (mgr->priv != NULL);
+
+	g_mutex_free (mgr->priv->saving_mutex);
+
+	g_free (mgr->priv->playlists_file);
+
+	G_OBJECT_CLASS (rb_playlist_manager_parent_class)->finalize (object);
+}
+
+
+static void
+rb_playlist_manager_class_init (RBPlaylistManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->constructed = rb_playlist_manager_constructed;
+	object_class->dispose = rb_playlist_manager_dispose;
+	object_class->finalize = rb_playlist_manager_finalize;
+
+	object_class->set_property = rb_playlist_manager_set_property;
+	object_class->get_property = rb_playlist_manager_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_PLAYLIST_NAME,
+                                         g_param_spec_string ("playlists_file",
+                                                              "name",
+                                                              "playlists file",
+                                                              NULL,
+                                                              G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_SOURCE,
+					 g_param_spec_object ("source",
+							      "RBSource",
+							      "RBSource object",
+							      RB_TYPE_SOURCE,
+							      G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_SHELL,
+					 g_param_spec_object ("shell",
+							      "RBShell",
+							      "RBShell object",
+							      RB_TYPE_SHELL,
+							      G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_DISPLAY_PAGE_MODEL,
+					 g_param_spec_object ("display-page-model",
+							      "RBDisplayPageModel",
+							      "RBDisplayPageModel",
+							      RB_TYPE_DISPLAY_PAGE_MODEL,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (object_class,
+					 PROP_DISPLAY_PAGE_TREE,
+					 g_param_spec_object ("display-page-tree",
+							      "RBDisplayPageTree",
+							      "RBDisplayPageTree",
+							      RB_TYPE_DISPLAY_PAGE_TREE,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * RBPlaylistManager::playlist-added:
+	 * @manager: the #RBPlaylistManager
+	 * @source: the new #RBSource
+	 *
+	 * Emitted when a playlist is added, including when being loaded
+	 * from the user's playlist file.
+	 */
+	rb_playlist_manager_signals[PLAYLIST_ADDED] =
+		g_signal_new ("playlist_added",
+			      RB_TYPE_PLAYLIST_MANAGER,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_added),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__OBJECT,
+			      G_TYPE_NONE,
+			      1, G_TYPE_OBJECT);
+
+	/**
+	 * RBPlaylistManager::playlist-created:
+	 * @manager: the #RBPlaylistManager
+	 * @source: the newly created playlist #RBSource
+	 *
+	 * Emitted when a new playlist is created.
+	 */
+	rb_playlist_manager_signals[PLAYLIST_CREATED] =
+		g_signal_new ("playlist_created",
+			      RB_TYPE_PLAYLIST_MANAGER,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBPlaylistManagerClass, playlist_created),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__OBJECT,
+			      G_TYPE_NONE,
+			      1, G_TYPE_OBJECT);
+
+	/**
+	 * RBPlaylistManager::load-start:
+	 * @manager: the #RBPlaylistManager
+	 *
+	 * Emitted when the playlist manager starts loading the user's
+	 * playlist file.
+	 */
+	rb_playlist_manager_signals[PLAYLIST_LOAD_START] =
+		g_signal_new ("load_start",
+			      RB_TYPE_PLAYLIST_MANAGER,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBPlaylistManagerClass, load_start),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0, G_TYPE_NONE);
+	/**
+	 * RBPlaylistManager::load-finish
+	 * @manager: the #RBPlaylistManager
+	 *
+	 * Emitted when the playlist manager finishes loading the user's
+	 * playlist file.
+	 */
+	rb_playlist_manager_signals[PLAYLIST_LOAD_FINISH] =
+		g_signal_new ("load_finish",
+			      RB_TYPE_PLAYLIST_MANAGER,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBPlaylistManagerClass, load_finish),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0, G_TYPE_NONE);
+
+	g_type_class_add_private (klass, sizeof (RBPlaylistManagerPrivate));
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]