hythmbox-2.98/shell/rb-playlist-manager.c

No issues found

Incomplete coverage

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