hythmbox-2.98/shell/rb-shell-player.c

Location Tool Test ID Function Issue
rb-shell-player.c:2765:3 clang-analyzer Value stored to 'source_set' is never read
rb-shell-player.c:3028:3 clang-analyzer Value stored to 'source' is never read
rb-shell-player.c:3032:3 clang-analyzer Value stored to 'source' is never read
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2002, 2003 Jorn Baayen <jorn@nl.linux.org>
   4  *  Copyright (C) 2002,2003 Colin Walters <walters@debian.org>
   5  *
   6  *  This program is free software; you can redistribute it and/or modify
   7  *  it under the terms of the GNU General Public License as published by
   8  *  the Free Software Foundation; either version 2 of the License, or
   9  *  (at your option) any later version.
  10  *
  11  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  12  *  GStreamer plugins to be used and distributed together with GStreamer
  13  *  and Rhythmbox. This permission is above and beyond the permissions granted
  14  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  15  *  you may extend this exception to your version of the code, but you are not
  16  *  obligated to do so. If you do not wish to do so, delete this exception
  17  *  statement from your version.
  18  *
  19  *  This program is distributed in the hope that it will be useful,
  20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22  *  GNU General Public License for more details.
  23  *
  24  *  You should have received a copy of the GNU General Public License
  25  *  along with this program; if not, write to the Free Software
  26  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  27  *
  28  */
  29 
  30 /**
  31  * SECTION:rb-shell-player
  32  * @short_description: playback state management
  33  *
  34  * The shell player (or player shell, depending on who you're talking to)
  35  * manages the #RBPlayer instance, tracks the current playing #RhythmDBEntry,
  36  * and manages the various #RBPlayOrder instances.  It provides simple operations
  37  * such as next, previous, play/pause, and seek.
  38  *
  39  * When playing internet radio streams, it first attempts to read the stream URL
  40  * as a playlist.  If this succeeds, the URLs from the playlist are stored in a
  41  * list and tried in turn in case of errors.  If the playlist parsing fails, the
  42  * stream URL is played directly.
  43  *
  44  * The mapping from the separate shuffle and repeat settings to an #RBPlayOrder
  45  * instance occurs in here.  The play order logic can also support a number of
  46  * additional play orders not accessible via the shuffle and repeat buttons.
  47  *
  48  * If the player backend supports multiple streams, the shell player crossfades
  49  * between streams by watching the elapsed time of the current stream and simulating
  50  * an end-of-stream event when it gets within the crossfade duration of the actual
  51  * end.
  52  */
  53 
  54 #include "config.h"
  55 
  56 #include <unistd.h>
  57 #include <stdlib.h>
  58 #include <time.h>
  59 #include <string.h>
  60 
  61 #include <glib.h>
  62 #include <glib/gi18n.h>
  63 #include <gtk/gtk.h>
  64 
  65 #include "rb-property-view.h"
  66 #include "rb-shell-player.h"
  67 #include "rb-stock-icons.h"
  68 #include "rb-builder-helpers.h"
  69 #include "rb-file-helpers.h"
  70 #include "rb-cut-and-paste-code.h"
  71 #include "rb-dialog.h"
  72 #include "rb-debug.h"
  73 #include "rb-player.h"
  74 #include "rb-header.h"
  75 #include "totem-pl-parser.h"
  76 #include "rb-metadata.h"
  77 #include "rb-library-source.h"
  78 #include "rb-util.h"
  79 #include "rb-play-order.h"
  80 #include "rb-playlist-source.h"
  81 #include "rb-play-queue-source.h"
  82 #include "rhythmdb.h"
  83 #include "rb-podcast-manager.h"
  84 #include "rb-marshal.h"
  85 #include "rb-missing-plugins.h"
  86 #include "rb-ext-db.h"
  87 
  88 /* Play Orders */
  89 #include "rb-play-order-linear.h"
  90 #include "rb-play-order-linear-loop.h"
  91 #include "rb-play-order-shuffle.h"
  92 #include "rb-play-order-random-equal-weights.h"
  93 #include "rb-play-order-random-by-age.h"
  94 #include "rb-play-order-random-by-rating.h"
  95 #include "rb-play-order-random-by-age-and-rating.h"
  96 #include "rb-play-order-queue.h"
  97 
  98 static const char* const state_to_play_order[2][2] =
  99 	{{"linear",	"linear-loop"},
 100 	 {"shuffle",	"random-by-age-and-rating"}};
 101 
 102 static void rb_shell_player_class_init (RBShellPlayerClass *klass);
 103 static void rb_shell_player_init (RBShellPlayer *shell_player);
 104 static void rb_shell_player_constructed (GObject *object);
 105 static void rb_shell_player_dispose (GObject *object);
 106 static void rb_shell_player_finalize (GObject *object);
 107 static void rb_shell_player_set_property (GObject *object,
 108 					  guint prop_id,
 109 					  const GValue *value,
 110 					  GParamSpec *pspec);
 111 static void rb_shell_player_get_property (GObject *object,
 112 					  guint prop_id,
 113 					  GValue *value,
 114 					  GParamSpec *pspec);
 115 
 116 static void rb_shell_player_cmd_previous (GtkAction *action,
 117 			                  RBShellPlayer *player);
 118 static void rb_shell_player_cmd_play (GtkAction *action,
 119 			              RBShellPlayer *player);
 120 static void rb_shell_player_cmd_next (GtkAction *action,
 121 			              RBShellPlayer *player);
 122 static void rb_shell_player_cmd_volume_up (GtkAction *action,
 123 					   RBShellPlayer *player);
 124 static void rb_shell_player_cmd_volume_down (GtkAction *action,
 125 					     RBShellPlayer *player);
 126 static void rb_shell_player_shuffle_changed_cb (GtkAction *action,
 127 						RBShellPlayer *player);
 128 static void rb_shell_player_repeat_changed_cb (GtkAction *action,
 129 					       RBShellPlayer *player);
 130 static void rb_shell_player_set_playing_source_internal (RBShellPlayer *player,
 131 							 RBSource *source,
 132 							 gboolean sync_entry_view);
 133 static void rb_shell_player_sync_with_source (RBShellPlayer *player);
 134 static void rb_shell_player_sync_with_selected_source (RBShellPlayer *player);
 135 static void rb_shell_player_entry_changed_cb (RhythmDB *db,
 136 					      RhythmDBEntry *entry,
 137 					      GArray *changes,
 138 					      RBShellPlayer *player);
 139 
 140 static void rb_shell_player_entry_activated_cb (RBEntryView *view,
 141 						RhythmDBEntry *entry,
 142 						RBShellPlayer *player);
 143 static void rb_shell_player_property_row_activated_cb (RBPropertyView *view,
 144 						       const char *name,
 145 						       RBShellPlayer *player);
 146 static void rb_shell_player_sync_volume (RBShellPlayer *player, gboolean notify, gboolean set_volume);
 147 static void tick_cb (RBPlayer *player, RhythmDBEntry *entry, gint64 elapsed, gint64 duration, gpointer data);
 148 static void error_cb (RBPlayer *player, RhythmDBEntry *entry, const GError *err, gpointer data);
 149 static void missing_plugins_cb (RBPlayer *player, RhythmDBEntry *entry, const char **details, const char **descriptions, RBShellPlayer *sp);
 150 static void playing_stream_cb (RBPlayer *player, RhythmDBEntry *entry, RBShellPlayer *shell_player);
 151 static void player_image_cb (RBPlayer *player, RhythmDBEntry *entry, GdkPixbuf *image, RBShellPlayer *shell_player);
 152 static void rb_shell_player_error (RBShellPlayer *player, gboolean async, const GError *err);
 153 
 154 static void rb_shell_player_play_order_update_cb (RBPlayOrder *porder,
 155 						  gboolean has_next,
 156 						  gboolean has_previous,
 157 						  RBShellPlayer *player);
 158 
 159 static void rb_shell_player_sync_play_order (RBShellPlayer *player);
 160 static void rb_shell_player_sync_control_state (RBShellPlayer *player);
 161 static void rb_shell_player_sync_buttons (RBShellPlayer *player);
 162 
 163 static void player_settings_changed_cb (GSettings *settings,
 164 					const char *key,
 165 					RBShellPlayer *player);
 166 static void rb_shell_player_playing_changed_cb (RBShellPlayer *player,
 167 						GParamSpec *arg1,
 168 						gpointer user_data);
 169 static void rb_shell_player_extra_metadata_cb (RhythmDB *db,
 170 					       RhythmDBEntry *entry,
 171 					       const char *field,
 172 					       GValue *metadata,
 173 					       RBShellPlayer *player);
 174 
 175 static gboolean rb_shell_player_open_location (RBShellPlayer *player,
 176 					       RhythmDBEntry *entry,
 177 					       RBPlayerPlayType play_type,
 178 					       GError **error);
 179 static gboolean rb_shell_player_do_next_internal (RBShellPlayer *player,
 180 						  gboolean from_eos,
 181 						  gboolean allow_stop,
 182 						  GError **error);
 183 static void rb_shell_player_slider_dragging_cb (GObject *header,
 184 						GParamSpec *pspec,
 185 						RBShellPlayer *player);
 186 static void rb_shell_player_volume_changed_cb (RBPlayer *player,
 187 					       float volume,
 188 					       RBShellPlayer *shell_player);
 189 
 190 
 191 
 192 typedef struct {
 193 	/** Value of the state/play-order setting */
 194 	char *name;
 195 	/** Contents of the play order dropdown; should be gettext()ed before use. */
 196 	char *description;
 197 	/** the play order's gtype id */
 198 	GType order_type;
 199 	/** TRUE if the play order should appear in the dropdown */
 200 	gboolean is_in_dropdown;
 201 } RBPlayOrderDescription;
 202 
 203 static void _play_order_description_free (RBPlayOrderDescription *order);
 204 
 205 static RBPlayOrder* rb_play_order_new (RBShellPlayer *player, const char* porder_name);
 206 
 207 /* number of nanoseconds before the end of a track to start prerolling the next */
 208 #define PREROLL_TIME		RB_PLAYER_SECOND
 209 
 210 struct RBShellPlayerPrivate
 211 {
 212 	RhythmDB *db;
 213 
 214 	gboolean syncing_state;
 215 	gboolean queue_only;
 216 
 217 	RBSource *selected_source;
 218 	RBSource *source;
 219 	RBPlayQueueSource *queue_source;
 220 	RBSource *current_playing_source;
 221 
 222 	GHashTable *play_orders; /* char* -> RBPlayOrderDescription* map */
 223 
 224 	gboolean did_retry;
 225 	GTimeVal last_retry;
 226 
 227 	GtkUIManager *ui_manager;
 228 	GtkActionGroup *actiongroup;
 229 
 230 	gboolean handling_error;
 231 
 232 	RBPlayer *mmplayer;
 233 
 234 	guint elapsed;
 235 	gint64 track_transition_time;
 236 	RhythmDBEntry *playing_entry;
 237 	gboolean playing_entry_eos;
 238 	gboolean jump_to_playing_entry;
 239 
 240 	RBPlayOrder *play_order;
 241 	RBPlayOrder *queue_play_order;
 242 
 243 	GQueue *playlist_urls;
 244 	GCancellable *parser_cancellable;
 245 
 246 	RBHeader *header_widget;
 247 
 248 	GSettings *settings;
 249 	GSettings *ui_settings;
 250 
 251 	gboolean has_prev;
 252 	gboolean has_next;
 253 	gboolean mute;
 254 	float volume;
 255 
 256 	guint do_next_idle_id;
 257 	guint unblock_play_id;
 258 };
 259 
 260 #define RB_SHELL_PLAYER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL_PLAYER, RBShellPlayerPrivate))
 261 
 262 enum
 263 {
 264 	PROP_0,
 265 	PROP_SOURCE,
 266 	PROP_DB,
 267 	PROP_UI_MANAGER,
 268 	PROP_ACTION_GROUP,
 269 	PROP_PLAY_ORDER,
 270 	PROP_PLAYING,
 271 	PROP_VOLUME,
 272 	PROP_HEADER,
 273 	PROP_QUEUE_SOURCE,
 274 	PROP_QUEUE_ONLY,
 275 	PROP_PLAYING_FROM_QUEUE,
 276 	PROP_PLAYER,
 277 	PROP_MUTE,
 278 	PROP_HAS_NEXT,
 279 	PROP_HAS_PREV
 280 };
 281 
 282 enum
 283 {
 284 	WINDOW_TITLE_CHANGED,
 285 	ELAPSED_CHANGED,
 286 	PLAYING_SOURCE_CHANGED,
 287 	PLAYING_CHANGED,
 288 	PLAYING_SONG_CHANGED,
 289 	PLAYING_URI_CHANGED,
 290 	PLAYING_SONG_PROPERTY_CHANGED,
 291 	ELAPSED_NANO_CHANGED,
 292 	LAST_SIGNAL
 293 };
 294 
 295 static GtkActionEntry rb_shell_player_actions [] =
 296 {
 297 	{ "ControlPrevious", GTK_STOCK_MEDIA_PREVIOUS, N_("Pre_vious"), "<alt>Left",
 298 	  N_("Start playing the previous song"),
 299 	  G_CALLBACK (rb_shell_player_cmd_previous) },
 300 	{ "ControlNext", GTK_STOCK_MEDIA_NEXT, N_("_Next"), "<alt>Right",
 301 	  N_("Start playing the next song"),
 302 	  G_CALLBACK (rb_shell_player_cmd_next) },
 303 	{ "ControlVolumeUp", NULL, N_("_Increase Volume"), "<control>Up",
 304 	  N_("Increase playback volume"),
 305 	  G_CALLBACK (rb_shell_player_cmd_volume_up) },
 306 	{ "ControlVolumeDown", NULL, N_("_Decrease Volume"), "<control>Down",
 307 	  N_("Decrease playback volume"),
 308 	  G_CALLBACK (rb_shell_player_cmd_volume_down) },
 309 };
 310 static guint rb_shell_player_n_actions = G_N_ELEMENTS (rb_shell_player_actions);
 311 
 312 static GtkToggleActionEntry rb_shell_player_toggle_entries [] =
 313 {
 314 	{ "ControlPlay", GTK_STOCK_MEDIA_PLAY, N_("_Play"), "<control>space",
 315 	  N_("Start playback"),
 316 	  G_CALLBACK (rb_shell_player_cmd_play) },
 317 	{ "ControlShuffle", GNOME_MEDIA_SHUFFLE, N_("Sh_uffle"), "<control>U",
 318 	  N_("Play songs in a random order"),
 319 	  G_CALLBACK (rb_shell_player_shuffle_changed_cb) },
 320 	{ "ControlRepeat", GNOME_MEDIA_REPEAT, N_("_Repeat"), "<control>R",
 321 	  N_("Play first song again after all songs are played"),
 322 	  G_CALLBACK (rb_shell_player_repeat_changed_cb) },
 323 };
 324 static guint rb_shell_player_n_toggle_entries = G_N_ELEMENTS (rb_shell_player_toggle_entries);
 325 
 326 static guint rb_shell_player_signals[LAST_SIGNAL] = { 0 };
 327 
 328 G_DEFINE_TYPE (RBShellPlayer, rb_shell_player, G_TYPE_OBJECT)
 329 
 330 static void
 331 rb_shell_player_class_init (RBShellPlayerClass *klass)
 332 {
 333 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 334 
 335 	object_class->dispose = rb_shell_player_dispose;
 336 	object_class->finalize = rb_shell_player_finalize;
 337 	object_class->constructed = rb_shell_player_constructed;
 338 
 339 	object_class->set_property = rb_shell_player_set_property;
 340 	object_class->get_property = rb_shell_player_get_property;
 341 
 342 	/**
 343 	 * RBShellPlayer:source:
 344 	 *
 345 	 * The current source that is selected for playback.
 346 	 */
 347 	g_object_class_install_property (object_class,
 348 					 PROP_SOURCE,
 349 					 g_param_spec_object ("source",
 350 							      "RBSource",
 351 							      "RBSource object",
 352 							      RB_TYPE_SOURCE,
 353 							      G_PARAM_READWRITE));
 354 
 355 	/**
 356 	 * RBShellPlayer:ui-manager:
 357 	 *
 358 	 * The GtkUIManager
 359 	 */
 360 	g_object_class_install_property (object_class,
 361 					 PROP_UI_MANAGER,
 362 					 g_param_spec_object ("ui-manager",
 363 							      "GtkUIManager",
 364 							      "GtkUIManager object",
 365 							      GTK_TYPE_UI_MANAGER,
 366 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 367 
 368 	/**
 369 	 * RBShellPlayer:db:
 370 	 *
 371 	 * The #RhythmDB
 372 	 */
 373 	g_object_class_install_property (object_class,
 374 					 PROP_DB,
 375 					 g_param_spec_object ("db",
 376 							      "RhythmDB",
 377 							      "RhythmDB object",
 378 							      RHYTHMDB_TYPE,
 379 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 380 
 381 	/**
 382 	 * RBShellPlayer:action-group:
 383 	 *
 384 	 * The #GtkActionGroup to use for player actions
 385 	 */
 386 	g_object_class_install_property (object_class,
 387 					 PROP_ACTION_GROUP,
 388 					 g_param_spec_object ("action-group",
 389 							      "GtkActionGroup",
 390 							      "GtkActionGroup object",
 391 							      GTK_TYPE_ACTION_GROUP,
 392 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 393 
 394 	/**
 395 	 * RBShellPlayer:queue-source:
 396 	 *
 397 	 * The play queue source
 398 	 */
 399 	g_object_class_install_property (object_class,
 400 					 PROP_QUEUE_SOURCE,
 401 					 g_param_spec_object ("queue-source",
 402 							      "RBPlayQueueSource",
 403 							      "RBPlayQueueSource object",
 404 							      RB_TYPE_PLAYLIST_SOURCE,
 405 							      G_PARAM_READWRITE));
 406 
 407 	/**
 408 	 * RBShellPlayer:queue-only:
 409 	 *
 410 	 * If %TRUE, activating an entry should only add it to the play queue.
 411 	 */
 412 	g_object_class_install_property (object_class,
 413 					 PROP_QUEUE_ONLY,
 414 					 g_param_spec_boolean ("queue-only",
 415 							       "Queue only",
 416 							       "Activation only adds to queue",
 417 							       FALSE,
 418 							       G_PARAM_READWRITE));
 419 
 420 	/**
 421 	 * RBShellPlayer:playing-from-queue:
 422 	 *
 423 	 * If %TRUE, the current playing entry came from the play queue.
 424 	 */
 425 	g_object_class_install_property (object_class,
 426 					 PROP_PLAYING_FROM_QUEUE,
 427 					 g_param_spec_boolean ("playing-from-queue",
 428 							       "Playing from queue",
 429 							       "Whether playing from the play queue or not",
 430 							       FALSE,
 431 							       G_PARAM_READABLE));
 432 
 433 	/**
 434 	 * RBShellPlayer:player:
 435 	 *
 436 	 * The player backend object (an object implementing the #RBPlayer interface).
 437 	 */
 438 	g_object_class_install_property (object_class,
 439 					 PROP_PLAYER,
 440 					 g_param_spec_object ("player",
 441 							      "RBPlayer",
 442 							      "RBPlayer object",
 443 							      G_TYPE_OBJECT,
 444 							      G_PARAM_READABLE));
 445 
 446 	/**
 447 	 * RBShellPlayer:play-order:
 448 	 *
 449 	 * The current play order object.
 450 	 */
 451 	g_object_class_install_property (object_class,
 452 					 PROP_PLAY_ORDER,
 453 					 g_param_spec_string ("play-order",
 454 							      "play-order",
 455 							      "What play order to use",
 456 							      "linear",
 457 							      G_PARAM_READABLE));
 458 	/**
 459 	 * RBShellPlayer:playing:
 460 	 *
 461 	 * Whether Rhythmbox is currently playing something
 462 	 */
 463 	g_object_class_install_property (object_class,
 464 					 PROP_PLAYING,
 465 					 g_param_spec_boolean ("playing",
 466 							       "playing",
 467 							      "Whether Rhythmbox is currently playing",
 468 							       FALSE,
 469 							       G_PARAM_READABLE));
 470 	/**
 471 	 * RBShellPlayer:volume:
 472 	 *
 473 	 * The current playback volume (between 0.0 and 1.0)
 474 	 */
 475 	g_object_class_install_property (object_class,
 476 					 PROP_VOLUME,
 477 					 g_param_spec_float ("volume",
 478 							     "volume",
 479 							     "Current playback volume",
 480 							     0.0f, 1.0f, 1.0f,
 481 							     G_PARAM_READWRITE));
 482 
 483 	/**
 484 	 * RBShellPlayer:header:
 485 	 *
 486 	 * The #RBHeader object
 487 	 */
 488 	g_object_class_install_property (object_class,
 489 					 PROP_HEADER,
 490 					 g_param_spec_object ("header",
 491 							      "RBHeader",
 492 							      "RBHeader object",
 493 							      RB_TYPE_HEADER,
 494 							      G_PARAM_READWRITE));
 495 	/**
 496 	 * RBShellPlayer:mute:
 497 	 *
 498 	 * Whether playback is currently muted.
 499 	 */
 500 	g_object_class_install_property (object_class,
 501 					 PROP_MUTE,
 502 					 g_param_spec_boolean ("mute",
 503 							       "mute",
 504 							       "Whether playback is muted",
 505 							       FALSE,
 506 							       G_PARAM_READWRITE));
 507 	/**
 508 	 * RBShellPlayer:has-next:
 509 	 *
 510 	 * Whether there is a track to play after the current track.
 511 	 */
 512 	g_object_class_install_property (object_class,
 513 					 PROP_HAS_NEXT,
 514 					 g_param_spec_boolean ("has-next",
 515 							       "has-next",
 516 							       "Whether there is a next track",
 517 							       FALSE,
 518 							       G_PARAM_READABLE));
 519 	/**
 520 	 * RBShellPlayer:has-prev:
 521 	 *
 522 	 * Whether there was a previous track before the current track.
 523 	 */
 524 	g_object_class_install_property (object_class,
 525 					 PROP_HAS_PREV,
 526 					 g_param_spec_boolean ("has-prev",
 527 							       "has-prev",
 528 							       "Whether there is a previous track",
 529 							       FALSE,
 530 							       G_PARAM_READABLE));
 531 
 532 	/**
 533 	 * RBShellPlayer::window-title-changed:
 534 	 * @player: the #RBShellPlayer
 535 	 * @title: the new window title
 536 	 *
 537 	 * Emitted when the main window title text should be changed
 538 	 */
 539 	rb_shell_player_signals[WINDOW_TITLE_CHANGED] =
 540 		g_signal_new ("window_title_changed",
 541 			      G_OBJECT_CLASS_TYPE (object_class),
 542 			      G_SIGNAL_RUN_LAST,
 543 			      G_STRUCT_OFFSET (RBShellPlayerClass, window_title_changed),
 544 			      NULL, NULL,
 545 			      g_cclosure_marshal_VOID__STRING,
 546 			      G_TYPE_NONE,
 547 			      1,
 548 			      G_TYPE_STRING);
 549 
 550 	/**
 551 	 * RBShellPlayer::elapsed-changed:
 552 	 * @player: the #RBShellPlayer
 553 	 * @elapsed: the new playback position in seconds
 554 	 *
 555 	 * Emitted when the playback position changes.
 556 	 */
 557 	rb_shell_player_signals[ELAPSED_CHANGED] =
 558 		g_signal_new ("elapsed_changed",
 559 			      G_OBJECT_CLASS_TYPE (object_class),
 560 			      G_SIGNAL_RUN_LAST,
 561 			      G_STRUCT_OFFSET (RBShellPlayerClass, elapsed_changed),
 562 			      NULL, NULL,
 563 			      g_cclosure_marshal_VOID__UINT,
 564 			      G_TYPE_NONE,
 565 			      1,
 566 			      G_TYPE_UINT);
 567 
 568 	/**
 569 	 * RBShellPlayer::playing-source-changed:
 570 	 * @player: the #RBShellPlayer
 571 	 * @source: the #RBSource that is now playing
 572 	 *
 573 	 * Emitted when a new #RBSource instance starts playing
 574 	 */
 575 	rb_shell_player_signals[PLAYING_SOURCE_CHANGED] =
 576 		g_signal_new ("playing-source-changed",
 577 			      G_OBJECT_CLASS_TYPE (object_class),
 578 			      G_SIGNAL_RUN_LAST,
 579 			      G_STRUCT_OFFSET (RBShellPlayerClass, playing_source_changed),
 580 			      NULL, NULL,
 581 			      g_cclosure_marshal_VOID__OBJECT,
 582 			      G_TYPE_NONE,
 583 			      1,
 584 			      RB_TYPE_SOURCE);
 585 
 586 	/**
 587 	 * RBShellPlayer::playing-changed:
 588 	 * @player: the #RBShellPlayer
 589 	 * @playing: flag indicating playback state
 590 	 *
 591 	 * Emitted when playback either stops or starts.
 592 	 */
 593 	rb_shell_player_signals[PLAYING_CHANGED] =
 594 		g_signal_new ("playing-changed",
 595 			      G_OBJECT_CLASS_TYPE (object_class),
 596 			      G_SIGNAL_RUN_LAST,
 597 			      G_STRUCT_OFFSET (RBShellPlayerClass, playing_changed),
 598 			      NULL, NULL,
 599 			      g_cclosure_marshal_VOID__BOOLEAN,
 600 			      G_TYPE_NONE,
 601 			      1,
 602 			      G_TYPE_BOOLEAN);
 603 
 604 	/**
 605 	 * RBShellPlayer::playing-song-changed:
 606 	 * @player: the #RBShellPlayer
 607 	 * @entry: the new playing #RhythmDBEntry
 608 	 *
 609 	 * Emitted when the playing database entry changes
 610 	 */
 611 	rb_shell_player_signals[PLAYING_SONG_CHANGED] =
 612 		g_signal_new ("playing-song-changed",
 613 			      G_OBJECT_CLASS_TYPE (object_class),
 614 			      G_SIGNAL_RUN_LAST,
 615 			      G_STRUCT_OFFSET (RBShellPlayerClass, playing_song_changed),
 616 			      NULL, NULL,
 617 			      g_cclosure_marshal_VOID__BOXED,
 618 			      G_TYPE_NONE,
 619 			      1,
 620 			      RHYTHMDB_TYPE_ENTRY);
 621 
 622 	/**
 623 	 * RBShellPlayer::playing-uri-changed:
 624 	 * @player: the #RBShellPlayer
 625 	 * @uri: the URI of the new playing entry
 626 	 *
 627 	 * Emitted when the playing database entry changes, providing the
 628 	 * URI of the entry.
 629 	 */
 630 	rb_shell_player_signals[PLAYING_URI_CHANGED] =
 631 		g_signal_new ("playing-uri-changed",
 632 			      G_OBJECT_CLASS_TYPE (object_class),
 633 			      G_SIGNAL_RUN_LAST,
 634 			      G_STRUCT_OFFSET (RBShellPlayerClass, playing_uri_changed),
 635 			      NULL, NULL,
 636 			      g_cclosure_marshal_VOID__STRING,
 637 			      G_TYPE_NONE,
 638 			      1,
 639 			      G_TYPE_STRING);
 640 
 641 	/**
 642 	 * RBShellPlayer::playing-song-property-changed:
 643 	 * @player: the #RBShellPlayer
 644 	 * @uri: the URI of the playing entry
 645 	 * @property: the name of the property that changed
 646 	 * @old: the previous value for the property
 647 	 * @newvalue: the new value of the property
 648 	 *
 649 	 * Emitted when a property of the playing database entry changes.
 650 	 */
 651 	rb_shell_player_signals[PLAYING_SONG_PROPERTY_CHANGED] =
 652 		g_signal_new ("playing-song-property-changed",
 653 			      G_OBJECT_CLASS_TYPE (object_class),
 654 			      G_SIGNAL_RUN_LAST,
 655 			      G_STRUCT_OFFSET (RBShellPlayerClass, playing_song_property_changed),
 656 			      NULL, NULL,
 657 			      rb_marshal_VOID__STRING_STRING_POINTER_POINTER,
 658 			      G_TYPE_NONE,
 659 			      4,
 660 			      G_TYPE_STRING, G_TYPE_STRING,
 661 			      G_TYPE_VALUE, G_TYPE_VALUE);
 662 
 663 	/**
 664 	 * RBShellPlayer::elapsed-nano-changed:
 665 	 * @player: the #RBShellPlayer
 666 	 * @elapsed: the new playback position in nanoseconds
 667 	 *
 668 	 * Emitted when the playback position changes.  Only use this (as opposed to
 669 	 * elapsed-changed) when you require subsecond precision.  This signal will be
 670 	 * emitted multiple times per second.
 671 	 */
 672 	rb_shell_player_signals[ELAPSED_NANO_CHANGED] =
 673 		g_signal_new ("elapsed-nano-changed",
 674 			      G_OBJECT_CLASS_TYPE (object_class),
 675 			      G_SIGNAL_RUN_LAST,
 676 			      G_STRUCT_OFFSET (RBShellPlayerClass, elapsed_nano_changed),
 677 			      NULL, NULL,
 678 			      rb_marshal_VOID__INT64,
 679 			      G_TYPE_NONE,
 680 			      1,
 681 			      G_TYPE_INT64);
 682 
 683 	g_type_class_add_private (klass, sizeof (RBShellPlayerPrivate));
 684 }
 685 
 686 static void
 687 rb_shell_player_constructed (GObject *object)
 688 {
 689 	RBShellPlayer *player;
 690 	GtkAction *action;
 691 
 692 	RB_CHAIN_GOBJECT_METHOD (rb_shell_player_parent_class, constructed, object);
 693 
 694 	player = RB_SHELL_PLAYER (object);
 695 
 696 	gtk_action_group_add_actions (player->priv->actiongroup,
 697 				      rb_shell_player_actions,
 698 				      rb_shell_player_n_actions,
 699 				      player);
 700 	gtk_action_group_add_toggle_actions (player->priv->actiongroup,
 701 					     rb_shell_player_toggle_entries,
 702 					     rb_shell_player_n_toggle_entries,
 703 					     player);
 704 
 705 	player_settings_changed_cb (player->priv->settings, "transition-time", player);
 706 	player_settings_changed_cb (player->priv->settings, "play-order", player);
 707 
 708 	action = gtk_action_group_get_action (player->priv->actiongroup,
 709 					      "ControlPlay");
 710 	g_object_set (action, "is-important", TRUE, NULL);
 711 
 712 	action = gtk_action_group_get_action (player->priv->actiongroup, "ControlPrevious");
 713 	g_object_bind_property (player, "has-prev", action, "sensitive", G_BINDING_DEFAULT);
 714 	action = gtk_action_group_get_action (player->priv->actiongroup, "ControlNext");
 715 	g_object_bind_property (player, "has-next", action, "sensitive", G_BINDING_DEFAULT);
 716 
 717 	player->priv->syncing_state = TRUE;
 718 	rb_shell_player_set_playing_source (player, NULL);
 719 	rb_shell_player_sync_play_order (player);
 720 	rb_shell_player_sync_control_state (player);
 721 	rb_shell_player_sync_volume (player, FALSE, TRUE);
 722 	player->priv->syncing_state = FALSE;
 723 
 724 	g_signal_connect (player,
 725 			  "notify::playing",
 726 			  G_CALLBACK (rb_shell_player_playing_changed_cb),
 727 			  NULL);
 728 }
 729 
 730 static void
 731 volume_pre_unmount_cb (GVolumeMonitor *monitor,
 732 		       GMount *mount,
 733 		       RBShellPlayer *player)
 734 {
 735 	const char *entry_mount_point;
 736 	GFile *mount_root;
 737 	RhythmDBEntry *entry;
 738 
 739 	entry = rb_shell_player_get_playing_entry (player);
 740 	if (entry == NULL) {
 741 		return;
 742 	}
 743 
 744 	entry_mount_point = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
 745 	if (entry_mount_point == NULL) {
 746 		return;
 747 	}
 748 
 749 	mount_root = g_mount_get_root (mount);
 750 	if (mount_root != NULL) {
 751 		char *mount_point;
 752 		
 753 		mount_point = g_file_get_uri (mount_root);
 754 		if (mount_point && entry_mount_point &&
 755 		    strcmp (entry_mount_point, mount_point) == 0) {
 756 			rb_shell_player_stop (player);
 757 		}
 758 
 759 		g_free (mount_point);
 760 		g_object_unref (mount_root);
 761 	}
 762 
 763 	rhythmdb_entry_unref (entry);
 764 }
 765 
 766 static void
 767 reemit_playing_signal (RBShellPlayer *player,
 768 		       GParamSpec *pspec,
 769 		       gpointer data)
 770 {
 771 	g_signal_emit (player, rb_shell_player_signals[PLAYING_CHANGED], 0,
 772 		       rb_player_playing (player->priv->mmplayer));
 773 }
 774 
 775 static void
 776 rb_shell_player_open_playlist_url (RBShellPlayer *player,
 777 				   const char *location,
 778 				   RhythmDBEntry *entry,
 779 				   RBPlayerPlayType play_type)
 780 {
 781 	GError *error = NULL;
 782 
 783 	rb_debug ("playing stream url %s", location);
 784 	rb_player_open (player->priv->mmplayer,
 785 			location,
 786 			rhythmdb_entry_ref (entry),
 787 			(GDestroyNotify) rhythmdb_entry_unref,
 788 			&error);
 789 	if (error == NULL)
 790 		rb_player_play (player->priv->mmplayer, play_type, player->priv->track_transition_time, &error);
 791 
 792 	if (error) {
 793 		GDK_THREADS_ENTER ();
 794 		rb_shell_player_error (player, TRUE, error);
 795 		g_error_free (error);
 796 		GDK_THREADS_LEAVE ();
 797 	}
 798 }
 799 
 800 static void
 801 rb_shell_player_handle_eos_unlocked (RBShellPlayer *player, RhythmDBEntry *entry, gboolean allow_stop)
 802 {
 803 	RBSource *source;
 804 	gboolean update_stats;
 805 	gboolean dragging;
 806 
 807 	source = player->priv->current_playing_source;
 808 
 809 	/* nothing to do */
 810 	if (source == NULL) {
 811 		return;
 812 	}
 813 
 814 	if (player->priv->playing_entry_eos) {
 815 		rb_debug ("playing entry has already EOS'd");
 816 		return;
 817 	}
 818 
 819 	if (entry != NULL) {
 820 		if (player->priv->playing_entry != entry) {
 821 			rb_debug ("EOS'd entry is not the current playing entry; ignoring");
 822 			return;
 823 		}
 824 
 825 		rhythmdb_entry_ref (entry);
 826 	}
 827 
 828 	/* defer EOS handling while the position slider is being dragged */
 829 	g_object_get (player->priv->header_widget, "slider-dragging", &dragging, NULL);
 830 	if (dragging) {
 831 		rb_debug ("slider is dragging, will handle EOS (if applicable) on release");
 832 		player->priv->playing_entry_eos = TRUE;
 833 		if (entry != NULL)
 834 			rhythmdb_entry_unref (entry);
 835 		return;
 836 	}
 837 
 838 	update_stats = FALSE;
 839 	switch (rb_source_handle_eos (source)) {
 840 	case RB_SOURCE_EOF_ERROR:
 841 		if (allow_stop) {
 842 			rb_error_dialog (NULL, _("Stream error"),
 843 					 _("Unexpected end of stream!"));
 844 			rb_shell_player_stop (player);
 845 			player->priv->playing_entry_eos = TRUE;
 846 			update_stats = TRUE;
 847 		}
 848 		break;
 849 	case RB_SOURCE_EOF_STOP:
 850 		if (allow_stop) {
 851 			rb_shell_player_stop (player);
 852 			player->priv->playing_entry_eos = TRUE;
 853 			update_stats = TRUE;
 854 		}
 855 		break;
 856 	case RB_SOURCE_EOF_RETRY: {
 857 		GTimeVal current;
 858 		gint diff;
 859 
 860 		g_get_current_time (&current);
 861 		diff = current.tv_sec - player->priv->last_retry.tv_sec;
 862 		player->priv->last_retry = current;
 863 
 864 		if (rb_source_try_playlist (source) &&
 865 		    !g_queue_is_empty (player->priv->playlist_urls)) {
 866 			char *location = g_queue_pop_head (player->priv->playlist_urls);
 867 			rb_debug ("trying next radio stream url: %s", location);
 868 
 869 			/* we're handling an unexpected EOS here, so crossfading isn't
 870 			 * really possible anyway -> specify FALSE.
 871 			 */
 872 			rb_shell_player_open_playlist_url (player, location, entry, FALSE);
 873 			g_free (location);
 874 			break;
 875 		}
 876 
 877 		if (allow_stop) {
 878 			if (diff < 4) {
 879 				rb_debug ("Last retry was less than 4 seconds ago...aborting retry playback");
 880 				rb_shell_player_stop (player);
 881 			} else {
 882 				rb_shell_player_play_entry (player, entry, NULL);
 883 			}
 884 			player->priv->playing_entry_eos = TRUE;
 885 			update_stats = TRUE;
 886 		}
 887 	}
 888 		break;
 889 	case RB_SOURCE_EOF_NEXT:
 890 		{
 891 			GError *error = NULL;
 892 
 893 			player->priv->playing_entry_eos = TRUE;
 894 			update_stats = TRUE;
 895 			if (!rb_shell_player_do_next_internal (player, TRUE, allow_stop, &error)) {
 896 				if (error->domain != RB_SHELL_PLAYER_ERROR ||
 897 				    error->code != RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST) {
 898 					g_warning ("Unhandled error: %s", error->message);
 899 				} else if (allow_stop == FALSE) {
 900 					/* handle the real EOS when it happens */
 901 					player->priv->playing_entry_eos = FALSE;
 902 					update_stats = FALSE;
 903 				}
 904 			}
 905 		}
 906 		break;
 907 	}
 908 
 909 	if (update_stats &&
 910 	    rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR) == NULL) {
 911 		rb_debug ("updating play statistics");
 912 		rb_source_update_play_statistics (source,
 913 						  player->priv->db,
 914 						  entry);
 915 	}
 916 
 917 	if (entry != NULL)
 918 		rhythmdb_entry_unref (entry);
 919 }
 920 
 921 static void
 922 rb_shell_player_slider_dragging_cb (GObject *header, GParamSpec *pspec, RBShellPlayer *player)
 923 {
 924 	gboolean drag;
 925 
 926 	g_object_get (player->priv->header_widget, "slider-dragging", &drag, NULL);
 927 	rb_debug ("slider dragging? %d", drag);
 928 
 929 	/* if an EOS occurred while dragging, process it now */
 930 	if (drag == FALSE && player->priv->playing_entry_eos) {
 931 		rb_debug ("processing EOS delayed due to slider dragging");
 932 		player->priv->playing_entry_eos = FALSE;
 933 		rb_shell_player_handle_eos_unlocked (player, rb_shell_player_get_playing_entry (player), FALSE);
 934 	}
 935 }
 936 
 937 static void
 938 rb_shell_player_handle_eos (RBPlayer *player,
 939 			    RhythmDBEntry *entry,
 940 			    gboolean early,
 941 			    RBShellPlayer *shell_player)
 942 {
 943 	const char *location;
 944 	if (entry == NULL) {
 945 		/* special case: this is called with entry == NULL to simulate an EOS
 946 		 * from the current playing entry.
 947 		 */
 948 		entry = shell_player->priv->playing_entry;
 949 		if (entry == NULL) {
 950 			rb_debug ("called to simulate EOS for playing entry, but nothing is playing");
 951 			return;
 952 		}
 953 	}
 954 
 955 	GDK_THREADS_ENTER ();
 956 
 957 	location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 958 	if (entry != shell_player->priv->playing_entry) {
 959 		rb_debug ("got unexpected eos for %s", location);
 960 	} else {
 961 		rb_debug ("handling eos for %s", location);
 962 		/* don't allow playback to be stopped on early EOS notifications */
 963 		rb_shell_player_handle_eos_unlocked (shell_player, entry, (early == FALSE));
 964 	}
 965 
 966 	GDK_THREADS_LEAVE ();
 967 }
 968 
 969 
 970 static void
 971 rb_shell_player_handle_redirect (RBPlayer *player,
 972 				 RhythmDBEntry *entry,
 973 				 const gchar *uri,
 974 				 RBShellPlayer *shell_player)
 975 {
 976 	GValue val = { 0 };
 977 
 978 	rb_debug ("redirect to %s", uri);
 979 
 980 	/* Stop existing stream */
 981 	rb_player_close (shell_player->priv->mmplayer, NULL, NULL);
 982 
 983 	/* Update entry */
 984 	g_value_init (&val, G_TYPE_STRING);
 985 	g_value_set_string (&val, uri);
 986 	rhythmdb_entry_set (shell_player->priv->db, entry, RHYTHMDB_PROP_LOCATION, &val);
 987 	g_value_unset (&val);
 988 	rhythmdb_commit (shell_player->priv->db);
 989 
 990 	/* Play new URI */
 991 	rb_shell_player_open_location (shell_player, entry, RB_PLAYER_PLAY_REPLACE, NULL);
 992 }
 993 
 994 static void
 995 rb_shell_player_init (RBShellPlayer *player)
 996 {
 997 	GError *error = NULL;
 998 
 999 	player->priv = RB_SHELL_PLAYER_GET_PRIVATE (player);
1000 
1001 	player->priv->settings = g_settings_new ("org.gnome.rhythmbox.player");
1002 	player->priv->ui_settings = g_settings_new ("org.gnome.rhythmbox");
1003 	g_signal_connect_object (player->priv->settings,
1004 				 "changed",
1005 				 G_CALLBACK (player_settings_changed_cb),
1006 				 player, 0);
1007 
1008 	player->priv->play_orders = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)_play_order_description_free);
1009 	
1010 	rb_shell_player_add_play_order (player, "linear", N_("Linear"),
1011 					RB_TYPE_LINEAR_PLAY_ORDER, FALSE);
1012 	rb_shell_player_add_play_order (player, "linear-loop", N_("Linear looping"),
1013 					RB_TYPE_LINEAR_PLAY_ORDER_LOOP, FALSE);
1014 	rb_shell_player_add_play_order (player, "shuffle", N_("Shuffle"),
1015 					RB_TYPE_SHUFFLE_PLAY_ORDER, FALSE);
1016 	rb_shell_player_add_play_order (player, "random-equal-weights", N_("Random with equal weights"),
1017 					RB_TYPE_RANDOM_PLAY_ORDER_EQUAL_WEIGHTS, FALSE);
1018 	rb_shell_player_add_play_order (player, "random-by-age", N_("Random by time since last play"),
1019 					RB_TYPE_RANDOM_PLAY_ORDER_BY_AGE, FALSE);
1020 	rb_shell_player_add_play_order (player, "random-by-rating", N_("Random by rating"),
1021 					RB_TYPE_RANDOM_PLAY_ORDER_BY_RATING, FALSE);
1022 	rb_shell_player_add_play_order (player, "random-by-age-and-rating", N_("Random by time since last play and rating"),
1023 					RB_TYPE_RANDOM_PLAY_ORDER_BY_AGE_AND_RATING, FALSE);
1024 	rb_shell_player_add_play_order (player, "queue", N_("Linear, removing entries once played"),
1025 					RB_TYPE_QUEUE_PLAY_ORDER, TRUE);
1026 
1027 	player->priv->mmplayer = rb_player_new (g_settings_get_boolean (player->priv->settings, "use-xfade-backend"),
1028 					        &error);
1029 	if (error != NULL) {
1030 		GtkWidget *dialog;
1031 		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
1032 						 GTK_MESSAGE_ERROR,
1033 						 GTK_BUTTONS_CLOSE,
1034 						 _("Failed to create the player: %s"),
1035 						 error->message);
1036 		gtk_dialog_run (GTK_DIALOG (dialog));
1037 		exit (1);
1038 	}
1039 
1040 	g_signal_connect_object (player->priv->mmplayer,
1041 				 "eos",
1042 				 G_CALLBACK (rb_shell_player_handle_eos),
1043 				 player, 0);
1044 
1045 	g_signal_connect_object (player->priv->mmplayer,
1046 				 "redirect",
1047 				 G_CALLBACK (rb_shell_player_handle_redirect),
1048 				 player, 0);
1049 
1050 	g_signal_connect_object (player->priv->mmplayer,
1051 				 "tick",
1052 				 G_CALLBACK (tick_cb),
1053 				 player, 0);
1054 
1055 	g_signal_connect_object (player->priv->mmplayer,
1056 				 "error",
1057 				 G_CALLBACK (error_cb),
1058 				 player, 0);
1059 
1060 	g_signal_connect_object (player->priv->mmplayer,
1061 				 "playing-stream",
1062 				 G_CALLBACK (playing_stream_cb),
1063 				 player, 0);
1064 
1065 	g_signal_connect_object (player->priv->mmplayer,
1066 				 "missing-plugins",
1067 				 G_CALLBACK (missing_plugins_cb),
1068 				 player, 0);
1069 	g_signal_connect_object (player->priv->mmplayer,
1070 				 "volume-changed",
1071 				 G_CALLBACK (rb_shell_player_volume_changed_cb),
1072 				 player, 0);
1073 
1074 	g_signal_connect_object (player->priv->mmplayer,
1075 				 "image",
1076 				 G_CALLBACK (player_image_cb),
1077 				 player, 0);
1078 
1079 	{
1080 		GVolumeMonitor *monitor = g_volume_monitor_get ();
1081 		g_signal_connect (G_OBJECT (monitor),
1082 				  "mount-pre-unmount",
1083 				  G_CALLBACK (volume_pre_unmount_cb),
1084 				  player);
1085 		g_object_unref (monitor);	/* hmm */
1086 	}
1087 
1088 	player->priv->volume = g_settings_get_double (player->priv->settings, "volume");
1089 
1090 	g_signal_connect (player, "notify::playing",
1091 			  G_CALLBACK (reemit_playing_signal), NULL);
1092 }
1093 
1094 static void
1095 rb_shell_player_set_source_internal (RBShellPlayer *player,
1096 				     RBSource      *source)
1097 {
1098 	if (player->priv->selected_source != NULL) {
1099 		RBEntryView *songs = rb_source_get_entry_view (player->priv->selected_source);
1100 		GList *property_views = rb_source_get_property_views (player->priv->selected_source);
1101 		GList *l;
1102 
1103 		if (songs != NULL) {
1104 			g_signal_handlers_disconnect_by_func (G_OBJECT (songs),
1105 							      G_CALLBACK (rb_shell_player_entry_activated_cb),
1106 							      player);
1107 		}
1108 
1109 		for (l = property_views; l != NULL; l = g_list_next (l)) {
1110 			g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
1111 							      G_CALLBACK (rb_shell_player_property_row_activated_cb),
1112 							      player);
1113 		}
1114 
1115 		g_list_free (property_views);
1116 	}
1117 
1118 	player->priv->selected_source = source;
1119 
1120 	rb_debug ("selected source %p", player->priv->selected_source);
1121 
1122 	rb_shell_player_sync_with_selected_source (player);
1123 	rb_shell_player_sync_buttons (player);
1124 
1125 	if (player->priv->selected_source != NULL) {
1126 		RBEntryView *songs = rb_source_get_entry_view (player->priv->selected_source);
1127 		GList *property_views = rb_source_get_property_views (player->priv->selected_source);
1128 		GList *l;
1129 
1130 		if (songs)
1131 			g_signal_connect_object (G_OBJECT (songs),
1132 						 "entry-activated",
1133 						 G_CALLBACK (rb_shell_player_entry_activated_cb),
1134 						 player, 0);
1135 		for (l = property_views; l != NULL; l = g_list_next (l)) {
1136 			g_signal_connect_object (G_OBJECT (l->data),
1137 						 "property-activated",
1138 						 G_CALLBACK (rb_shell_player_property_row_activated_cb),
1139 						 player, 0);
1140 		}
1141 
1142 		g_list_free (property_views);
1143 	}
1144 
1145 	/* If we're not playing, change the play order's view of the current source;
1146 	 * if the selected source is the queue, however, set it to NULL so it'll stop
1147 	 * once the queue is empty.
1148 	 */
1149 	if (player->priv->current_playing_source == NULL) {
1150 		RBPlayOrder *porder = NULL;
1151 		RBSource *source = player->priv->selected_source;
1152 		if (source == RB_SOURCE (player->priv->queue_source)) {
1153 			source = NULL;
1154 		} else if (source != NULL) {
1155 			g_object_get (source, "play-order", &porder, NULL);
1156 		}
1157 
1158 		if (porder == NULL)
1159 			porder = g_object_ref (player->priv->play_order);
1160 
1161 		rb_play_order_playing_source_changed (porder, source);
1162 		g_object_unref (porder);
1163 	}
1164 }
1165 
1166 static void
1167 rb_shell_player_set_db_internal (RBShellPlayer *player,
1168 				 RhythmDB      *db)
1169 {
1170 	if (player->priv->db != NULL) {
1171 		g_signal_handlers_disconnect_by_func (player->priv->db,
1172 						      G_CALLBACK (rb_shell_player_entry_changed_cb),
1173 						      player);
1174 		g_signal_handlers_disconnect_by_func (player->priv->db,
1175 						      G_CALLBACK (rb_shell_player_extra_metadata_cb),
1176 						      player);
1177 	}
1178 
1179 	player->priv->db = db;
1180 
1181 	if (player->priv->db != NULL) {
1182 		/* Listen for changed entries to update metadata display */
1183 		g_signal_connect_object (G_OBJECT (player->priv->db),
1184 					 "entry_changed",
1185 					 G_CALLBACK (rb_shell_player_entry_changed_cb),
1186 					 player, 0);
1187 		g_signal_connect_object (G_OBJECT (player->priv->db),
1188 					 "entry_extra_metadata_notify",
1189 					 G_CALLBACK (rb_shell_player_extra_metadata_cb),
1190 					 player, 0);
1191 	}
1192 }
1193 
1194 static void
1195 rb_shell_player_set_queue_source_internal (RBShellPlayer     *player,
1196 					   RBPlayQueueSource *source)
1197 {
1198 	if (player->priv->queue_source != NULL) {
1199 		RBEntryView *sidebar;
1200 
1201 		g_object_get (player->priv->queue_source, "sidebar", &sidebar, NULL);
1202 		g_signal_handlers_disconnect_by_func (sidebar,
1203 						      G_CALLBACK (rb_shell_player_entry_activated_cb),
1204 						      player);
1205 		g_object_unref (sidebar);
1206 
1207 		if (player->priv->queue_play_order != NULL) {
1208 			g_signal_handlers_disconnect_by_func (player->priv->queue_play_order,
1209 							      G_CALLBACK (rb_shell_player_play_order_update_cb),
1210 							      player);
1211 			g_object_unref (player->priv->queue_play_order);
1212 		}
1213 
1214 	}
1215 
1216 	player->priv->queue_source = source;
1217 
1218 	if (player->priv->queue_source != NULL) {
1219 		RBEntryView *sidebar;
1220 
1221 		g_object_get (player->priv->queue_source, "play-order", &player->priv->queue_play_order, NULL);
1222 
1223 		g_signal_connect_object (G_OBJECT (player->priv->queue_play_order),
1224 					 "have_next_previous_changed",
1225 					 G_CALLBACK (rb_shell_player_play_order_update_cb),
1226 					 player, 0);
1227 		rb_shell_player_play_order_update_cb (player->priv->play_order,
1228 						      FALSE, FALSE,
1229 						      player);
1230 		rb_play_order_playing_source_changed (player->priv->queue_play_order,
1231 						      RB_SOURCE (player->priv->queue_source));
1232 
1233 		g_object_get (player->priv->queue_source, "sidebar", &sidebar, NULL);
1234 		g_signal_connect_object (G_OBJECT (sidebar),
1235 					 "entry-activated",
1236 					 G_CALLBACK (rb_shell_player_entry_activated_cb),
1237 					 player, 0);
1238 		g_object_unref (sidebar);
1239 	}
1240 }
1241 
1242 static void
1243 rb_shell_player_dispose (GObject *object)
1244 {
1245 	RBShellPlayer *player;
1246 
1247 	g_return_if_fail (object != NULL);
1248 	g_return_if_fail (RB_IS_SHELL_PLAYER (object));
1249 
1250 	player = RB_SHELL_PLAYER (object);
1251 
1252 	g_return_if_fail (player->priv != NULL);
1253 
1254 	if (player->priv->ui_settings != NULL) {
1255 		g_object_unref (player->priv->ui_settings);
1256 		player->priv->ui_settings = NULL;
1257 	}
1258 
1259 	if (player->priv->settings != NULL) {
1260 		/* hm, is this really the place to do this? */
1261 		g_settings_set_double (player->priv->settings,
1262 				       "volume",
1263 				       player->priv->volume);
1264 
1265 		g_object_unref (player->priv->settings);
1266 		player->priv->settings = NULL;
1267 	}
1268 
1269 	if (player->priv->mmplayer != NULL) {
1270 		g_object_unref (player->priv->mmplayer);
1271 		player->priv->mmplayer = NULL;
1272 	}
1273 
1274 	if (player->priv->play_order != NULL) {
1275 		g_object_unref (player->priv->play_order);
1276 		player->priv->play_order = NULL;
1277 	}
1278 
1279 	if (player->priv->queue_play_order != NULL) {
1280 		g_object_unref (player->priv->queue_play_order);
1281 		player->priv->queue_play_order = NULL;
1282 	}
1283 
1284 	if (player->priv->do_next_idle_id != 0) {
1285 		g_source_remove (player->priv->do_next_idle_id);
1286 		player->priv->do_next_idle_id = 0;
1287 	}
1288 
1289 	if (player->priv->unblock_play_id != 0) {
1290 		g_source_remove (player->priv->unblock_play_id);
1291 		player->priv->unblock_play_id = 0;
1292 	}
1293 
1294 	G_OBJECT_CLASS (rb_shell_player_parent_class)->dispose (object);
1295 }
1296 
1297 static void
1298 rb_shell_player_finalize (GObject *object)
1299 {
1300 	RBShellPlayer *player;
1301 
1302 	g_return_if_fail (object != NULL);
1303 	g_return_if_fail (RB_IS_SHELL_PLAYER (object));
1304 
1305 	player = RB_SHELL_PLAYER (object);
1306 
1307 	g_return_if_fail (player->priv != NULL);
1308 
1309 	g_hash_table_destroy (player->priv->play_orders);
1310 	
1311 	G_OBJECT_CLASS (rb_shell_player_parent_class)->finalize (object);
1312 }
1313 
1314 static void
1315 rb_shell_player_set_property (GObject *object,
1316 			      guint prop_id,
1317 			      const GValue *value,
1318 			      GParamSpec *pspec)
1319 {
1320 	RBShellPlayer *player = RB_SHELL_PLAYER (object);
1321 
1322 	switch (prop_id) {
1323 	case PROP_SOURCE:
1324 		rb_shell_player_set_source_internal (player, g_value_get_object (value));
1325 		break;
1326 	case PROP_UI_MANAGER:
1327 		player->priv->ui_manager = g_value_get_object (value);
1328 		break;
1329 	case PROP_DB:
1330 		rb_shell_player_set_db_internal (player, g_value_get_object (value));
1331 		break;
1332 	case PROP_ACTION_GROUP:
1333 		player->priv->actiongroup = g_value_get_object (value);
1334 		break;
1335 	case PROP_PLAY_ORDER:
1336 		g_settings_set_string (player->priv->settings,
1337 				       "play-order",
1338 				       g_value_get_string (value));
1339 		break;
1340 	case PROP_VOLUME:
1341 		player->priv->volume = g_value_get_float (value);
1342 		rb_shell_player_sync_volume (player, FALSE, TRUE);
1343 		break;
1344 	case PROP_HEADER:
1345 		player->priv->header_widget = g_value_get_object (value);
1346 		g_signal_connect_object (player->priv->header_widget,
1347 					 "notify::slider-dragging",
1348 					 G_CALLBACK (rb_shell_player_slider_dragging_cb),
1349 					 player, 0);
1350 		break;
1351 	case PROP_QUEUE_SOURCE:
1352 		rb_shell_player_set_queue_source_internal (player, g_value_get_object (value));
1353 		break;
1354 	case PROP_QUEUE_ONLY:
1355 		player->priv->queue_only = g_value_get_boolean (value);
1356 		break;
1357 	case PROP_MUTE:
1358 		player->priv->mute = g_value_get_boolean (value);
1359 		rb_shell_player_sync_volume (player, FALSE, TRUE);
1360 	default:
1361 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1362 		break;
1363 	}
1364 }
1365 
1366 static void
1367 rb_shell_player_get_property (GObject *object,
1368 			      guint prop_id,
1369 			      GValue *value,
1370 			      GParamSpec *pspec)
1371 {
1372 	RBShellPlayer *player = RB_SHELL_PLAYER (object);
1373 
1374 	switch (prop_id) {
1375 	case PROP_SOURCE:
1376 		g_value_set_object (value, player->priv->selected_source);
1377 		break;
1378 	case PROP_UI_MANAGER:
1379 		g_value_set_object (value, player->priv->ui_manager);
1380 		break;
1381 	case PROP_DB:
1382 		g_value_set_object (value, player->priv->db);
1383 		break;
1384 	case PROP_ACTION_GROUP:
1385 		g_value_set_object (value, player->priv->actiongroup);
1386 		break;
1387 	case PROP_PLAY_ORDER:
1388 	{
1389 		char *play_order = g_settings_get_string (player->priv->settings,
1390 							  "play-order");
1391 		if (play_order == NULL)
1392 			play_order = g_strdup ("linear");
1393 		g_value_take_string (value, play_order);
1394 		break;
1395 	}
1396 	case PROP_PLAYING:
1397 		if (player->priv->mmplayer != NULL)
1398 			g_value_set_boolean (value, rb_player_playing (player->priv->mmplayer));
1399 		else
1400 			g_value_set_boolean (value, FALSE);
1401 		break;
1402 	case PROP_VOLUME:
1403 		g_value_set_float (value, player->priv->volume);
1404 		break;
1405 	case PROP_HEADER:
1406 		g_value_set_object (value, player->priv->header_widget);
1407 		break;
1408 	case PROP_QUEUE_SOURCE:
1409 		g_value_set_object (value, player->priv->queue_source);
1410 		break;
1411 	case PROP_QUEUE_ONLY:
1412 		g_value_set_boolean (value, player->priv->queue_only);
1413 		break;
1414 	case PROP_PLAYING_FROM_QUEUE:
1415 		g_value_set_boolean (value, player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source));
1416 		break;
1417 	case PROP_PLAYER:
1418 		g_value_set_object (value, player->priv->mmplayer);
1419 		break;
1420 	case PROP_MUTE:
1421 		g_value_set_boolean (value, player->priv->mute);
1422 		break;
1423 	case PROP_HAS_NEXT:
1424 		g_value_set_boolean (value, player->priv->has_next);
1425 		break;
1426 	case PROP_HAS_PREV:
1427 		g_value_set_boolean (value, player->priv->has_prev);
1428 		break;
1429 	default:
1430 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1431 		break;
1432 	}
1433 }
1434 
1435 GQuark
1436 rb_shell_player_error_quark (void)
1437 {
1438 	static GQuark quark = 0;
1439 	if (!quark)
1440 		quark = g_quark_from_static_string ("rb_shell_player_error");
1441 
1442 	return quark;
1443 }
1444 
1445 /**
1446  * rb_shell_player_set_selected_source:
1447  * @player: the #RBShellPlayer
1448  * @source: the #RBSource to select
1449  *
1450  * Updates the player to reflect a new source being selected.
1451  */
1452 void
1453 rb_shell_player_set_selected_source (RBShellPlayer *player,
1454 				     RBSource *source)
1455 {
1456 	g_return_if_fail (RB_IS_SHELL_PLAYER (player));
1457 	g_return_if_fail (source == NULL || RB_IS_SOURCE (source));
1458 
1459 	g_object_set (player, "source", source, NULL);
1460 }
1461 
1462 /**
1463  * rb_shell_player_get_playing_source:
1464  * @player: the #RBShellPlayer
1465  *
1466  * Retrieves the current playing source.  That is, the source from
1467  * which the current song was drawn.  This differs from 
1468  * #rb_shell_player_get_active_source when the current song came
1469  * from the play queue.
1470  *
1471  * Return value: (transfer none): the current playing #RBSource
1472  */
1473 RBSource *
1474 rb_shell_player_get_playing_source (RBShellPlayer *player)
1475 {
1476 	return player->priv->current_playing_source;
1477 }
1478 
1479 /**
1480  * rb_shell_player_get_active_source:
1481  * @player: the #RBShellPlayer
1482  *
1483  * Retrieves the active source.  This is the source that the user
1484  * selected for playback.
1485  *
1486  * Return value: (transfer none): the active #RBSource
1487  */
1488 RBSource *
1489 rb_shell_player_get_active_source (RBShellPlayer *player)
1490 {
1491 	return player->priv->source;
1492 }
1493 
1494 /**
1495  * rb_shell_player_new:
1496  * @db: the #RhythmDB
1497  * @mgr: the #GtkUIManager
1498  * @actiongroup: the #GtkActionGroup to use
1499  *
1500  * Creates the #RBShellPlayer
1501  * 
1502  * Return value: the #RBShellPlayer instance
1503  */
1504 RBShellPlayer *
1505 rb_shell_player_new (RhythmDB *db,
1506 		     GtkUIManager *mgr,
1507 		     GtkActionGroup *actiongroup)
1508 {
1509 	return g_object_new (RB_TYPE_SHELL_PLAYER,
1510 			     "ui-manager", mgr,
1511 			     "action-group", actiongroup,
1512 			     "db", db,
1513 			     NULL);
1514 }
1515 
1516 /**
1517  * rb_shell_player_get_playing_entry:
1518  * @player: the #RBShellPlayer
1519  *
1520  * Retrieves the currently playing #RhythmDBEntry, or NULL if
1521  * nothing is playing.  The caller must unref the entry
1522  * (using #rhythmdb_entry_unref) when it is no longer needed.
1523  *
1524  * Return value: (transfer full) (allow-none): the currently playing #RhythmDBEntry, or NULL
1525  */
1526 RhythmDBEntry *
1527 rb_shell_player_get_playing_entry (RBShellPlayer *player)
1528 {
1529 	RBPlayOrder *porder;
1530 	RhythmDBEntry *entry;
1531 
1532 	if (player->priv->current_playing_source == NULL) {
1533 		return NULL;
1534 	}
1535 
1536 	g_object_get (player->priv->current_playing_source, "play-order", &porder, NULL);
1537 	if (porder == NULL)
1538 		porder = g_object_ref (player->priv->play_order);
1539 
1540 	entry = rb_play_order_get_playing_entry (porder);
1541 	g_object_unref (porder);
1542 
1543 	return entry;
1544 }
1545 
1546 typedef struct {
1547 	RBShellPlayer *player;
1548 	char *location;
1549 	RhythmDBEntry *entry;
1550 	RBPlayerPlayType play_type;
1551 	GCancellable *cancellable;
1552 } OpenLocationThreadData;
1553 
1554 static void
1555 playlist_entry_cb (TotemPlParser *playlist,
1556 		   const char *uri,
1557 		   GHashTable *metadata,
1558 		   OpenLocationThreadData *data)
1559 {
1560 	if (g_cancellable_is_cancelled (data->cancellable)) {
1561 		rb_debug ("playlist parser cancelled");
1562 	} else {
1563 		rb_debug ("adding stream url %s (%p)", uri, playlist);
1564 		g_queue_push_tail (data->player->priv->playlist_urls, g_strdup (uri));
1565 	}
1566 }
1567 
1568 static gpointer
1569 open_location_thread (OpenLocationThreadData *data)
1570 {
1571 	TotemPlParser *playlist;
1572 	TotemPlParserResult playlist_result;
1573 
1574 	playlist = totem_pl_parser_new ();
1575 
1576 	g_signal_connect_data (playlist, "entry-parsed",
1577 			       G_CALLBACK (playlist_entry_cb),
1578 			       data, NULL, 0);
1579 
1580 	totem_pl_parser_add_ignored_mimetype (playlist, "x-directory/normal");
1581 	totem_pl_parser_add_ignored_mimetype (playlist, "inode/directory");
1582 
1583 	playlist_result = totem_pl_parser_parse (playlist, data->location, FALSE);
1584 	g_object_unref (playlist);
1585 
1586 	if (g_cancellable_is_cancelled (data->cancellable)) {
1587 		playlist_result = TOTEM_PL_PARSER_RESULT_CANCELLED;
1588 	}
1589 
1590 	switch (playlist_result) {
1591 	case TOTEM_PL_PARSER_RESULT_SUCCESS:
1592 		if (g_queue_is_empty (data->player->priv->playlist_urls)) {
1593 			GError *error = g_error_new (RB_SHELL_PLAYER_ERROR,
1594 						     RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST,
1595 						     _("Playlist was empty"));
1596 			GDK_THREADS_ENTER ();
1597 			rb_shell_player_error (data->player, TRUE, error);
1598 			g_error_free (error);
1599 			GDK_THREADS_LEAVE ();
1600 		} else {
1601 			char *location;
1602 
1603 			location = g_queue_pop_head (data->player->priv->playlist_urls);
1604 			rb_debug ("playing first stream url %s", location);
1605 			rb_shell_player_open_playlist_url (data->player, location, data->entry, data->play_type);
1606 			g_free (location);
1607 		}
1608 		break;
1609 
1610 	case TOTEM_PL_PARSER_RESULT_CANCELLED:
1611 		rb_debug ("playlist parser was cancelled");
1612 		break;
1613 
1614 	default:
1615 		/* if we can't parse it as a playlist, just try playing it */
1616 		rb_debug ("playlist parser failed, playing %s directly", data->location);
1617 		rb_shell_player_open_playlist_url (data->player, data->location, data->entry, data->play_type);
1618 		break;
1619 	}
1620 
1621 	g_object_unref (data->cancellable);
1622 	g_free (data);
1623 	return NULL;
1624 }
1625 
1626 static gboolean
1627 rb_shell_player_open_location (RBShellPlayer *player,
1628 			       RhythmDBEntry *entry,
1629 			       RBPlayerPlayType play_type,
1630 			       GError **error)
1631 {
1632 	char *location;
1633 	gboolean ret = TRUE;
1634 
1635 	/* dispose of any existing playlist urls */
1636 	if (player->priv->playlist_urls) {
1637 		g_queue_foreach (player->priv->playlist_urls,
1638 				 (GFunc) g_free,
1639 				 NULL);
1640 		g_queue_free (player->priv->playlist_urls);
1641 		player->priv->playlist_urls = NULL;
1642 	}
1643 	if (rb_source_try_playlist (player->priv->source)) {
1644 		player->priv->playlist_urls = g_queue_new ();
1645 	}
1646 
1647 	location = rhythmdb_entry_get_playback_uri (entry);
1648 	if (location == NULL) {
1649 		return FALSE;
1650 	}
1651 
1652 	if (rb_source_try_playlist (player->priv->source)) {
1653 		OpenLocationThreadData *data;
1654 
1655 		data = g_new0 (OpenLocationThreadData, 1);
1656 		data->player = player;
1657 		data->play_type = play_type;
1658 		data->entry = entry;
1659 
1660 		/* add http:// as a prefix, if it doesn't have a URI scheme */
1661 		if (strstr (location, "://"))
1662 			data->location = g_strdup (location);
1663 		else
1664 			data->location = g_strconcat ("http://", location, NULL);
1665 
1666 		if (player->priv->parser_cancellable == NULL) {
1667 			player->priv->parser_cancellable = g_cancellable_new ();
1668 		}
1669 		data->cancellable = g_object_ref (player->priv->parser_cancellable);
1670 
1671 		g_thread_new ("open-location", (GThreadFunc)open_location_thread, data);
1672 	} else {
1673 		if (player->priv->parser_cancellable != NULL) {
1674 			g_object_unref (player->priv->parser_cancellable);
1675 			player->priv->parser_cancellable = NULL;
1676 		}
1677 
1678 		rhythmdb_entry_ref (entry);
1679 		ret = ret && rb_player_open (player->priv->mmplayer, location, entry, (GDestroyNotify) rhythmdb_entry_unref, error);
1680 
1681 		ret = ret && rb_player_play (player->priv->mmplayer, play_type, player->priv->track_transition_time, error);
1682 	}
1683 
1684 	g_free (location);
1685 	return ret;
1686 }
1687 
1688 /**
1689  * rb_shell_player_play:
1690  * @player: a #RBShellPlayer
1691  * @error: error return
1692  *
1693  * Starts playback, if it is not already playing.
1694  *
1695  * Return value: whether playback is now occurring (TRUE when successfully started
1696  * or already playing).
1697  **/
1698 gboolean
1699 rb_shell_player_play (RBShellPlayer *player,
1700 		      GError **error)
1701 {
1702 	RBEntryView *songs;
1703 
1704 	if (player->priv->current_playing_source == NULL) {
1705 		rb_debug ("current playing source is NULL");
1706 		g_set_error (error,
1707 			     RB_SHELL_PLAYER_ERROR,
1708 			     RB_SHELL_PLAYER_ERROR_NOT_PLAYING,
1709 			     "Current playing source is NULL");
1710 		return FALSE;
1711 	}
1712 
1713 	if (rb_player_playing (player->priv->mmplayer))
1714 		return TRUE;
1715 
1716 	if (player->priv->parser_cancellable != NULL) {
1717 		rb_debug ("currently parsing a playlist");
1718 		return TRUE;
1719 	}
1720 
1721 	/* we're obviously not playing anything, so crossfading is irrelevant */
1722 	if (!rb_player_play (player->priv->mmplayer, RB_PLAYER_PLAY_REPLACE, 0.0f, error)) {
1723 		rb_debug ("player doesn't want to");
1724 		return FALSE;
1725 	}
1726 
1727 	songs = rb_source_get_entry_view (player->priv->current_playing_source);
1728 	if (songs)
1729 		rb_entry_view_set_state (songs, RB_ENTRY_VIEW_PLAYING);
1730 
1731 	return TRUE;
1732 }
1733 
1734 static void
1735 rb_shell_player_set_entry_playback_error (RBShellPlayer *player,
1736 					  RhythmDBEntry *entry,
1737 					  char *message)
1738 {
1739 	GValue value = { 0, };
1740 
1741 	g_return_if_fail (RB_IS_SHELL_PLAYER (player));
1742 
1743 	g_value_init (&value, G_TYPE_STRING);
1744 	g_value_set_string (&value, message);
1745 	rhythmdb_entry_set (player->priv->db,
1746 			    entry,
1747 			    RHYTHMDB_PROP_PLAYBACK_ERROR,
1748 			    &value);
1749 	g_value_unset (&value);
1750 	rhythmdb_commit (player->priv->db);
1751 }
1752 
1753 static gboolean
1754 rb_shell_player_set_playing_entry (RBShellPlayer *player,
1755 				   RhythmDBEntry *entry,
1756 				   gboolean out_of_order,
1757 				   gboolean wait_for_eos,
1758 				   GError **error)
1759 {
1760 	GError *tmp_error = NULL;
1761 	GValue val = {0,};
1762 	RBPlayerPlayType play_type;
1763 
1764 	g_return_val_if_fail (player->priv->current_playing_source != NULL, TRUE);
1765 	g_return_val_if_fail (entry != NULL, TRUE);
1766 
1767 	play_type = wait_for_eos ? RB_PLAYER_PLAY_AFTER_EOS : RB_PLAYER_PLAY_REPLACE;
1768 
1769 	if (out_of_order) {
1770 		RBPlayOrder *porder;
1771 
1772 		g_object_get (player->priv->current_playing_source, "play-order", &porder, NULL);
1773 		if (porder == NULL)
1774 			porder = g_object_ref (player->priv->play_order);
1775 		rb_play_order_set_playing_entry (porder, entry);
1776 		g_object_unref (porder);
1777 	}
1778 
1779 	if (player->priv->playing_entry != NULL &&
1780 	    player->priv->track_transition_time > 0) {
1781 		const char *previous_album;
1782 		const char *album;
1783 
1784 		previous_album = rhythmdb_entry_get_string (player->priv->playing_entry, RHYTHMDB_PROP_ALBUM);
1785 		album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
1786 		/* only crossfade if we're not going from the end of one song on an
1787 		 * album to the start of another.  "Unknown" doesn't count as an album.
1788 		 */
1789 		if (wait_for_eos == FALSE ||
1790 		    strcmp (album, _("Unknown")) == 0 ||
1791 		    strcmp (album, previous_album) != 0) {
1792 			play_type = RB_PLAYER_PLAY_CROSSFADE;
1793 		}
1794 	}
1795 
1796 	if (rb_shell_player_open_location (player, entry, play_type, &tmp_error) == FALSE) {
1797 		goto lose;
1798 	}
1799 
1800 	rb_debug ("Success!");
1801 	/* clear error on successful playback */
1802 	g_value_init (&val, G_TYPE_STRING);
1803 	g_value_set_string (&val, NULL);
1804 	rhythmdb_entry_set (player->priv->db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &val);
1805 	rhythmdb_commit (player->priv->db);
1806 	g_value_unset (&val);
1807 
1808 	return TRUE;
1809  lose:
1810 	/* Ignore errors, shutdown the player */
1811 	rb_player_close (player->priv->mmplayer, NULL /* XXX specify uri? */, NULL);
1812 
1813 	if (tmp_error == NULL) {
1814 		tmp_error = g_error_new (RB_SHELL_PLAYER_ERROR,
1815 					 RB_SHELL_PLAYER_ERROR_NOT_PLAYING,
1816 					 "Problem occurred without error being set. "
1817 					 "This is a bug in Rhythmbox or GStreamer.");
1818 	}
1819 	/* Mark this song as failed */
1820 	rb_shell_player_set_entry_playback_error (player, entry, tmp_error->message);
1821 	g_propagate_error (error, tmp_error);
1822 
1823 	rb_shell_player_sync_with_source (player);
1824 	rb_shell_player_sync_buttons (player);
1825 	g_object_notify (G_OBJECT (player), "playing");
1826 
1827 	return FALSE;
1828 }
1829 
1830 static void
1831 player_settings_changed_cb (GSettings *settings, const char *key, RBShellPlayer *player)
1832 {
1833 	if (g_strcmp0 (key, "play-order") == 0) {
1834 		rb_debug ("play order setting changed");
1835 		player->priv->syncing_state = TRUE;
1836 		rb_shell_player_sync_play_order (player);
1837 		rb_shell_player_sync_buttons (player);
1838 		rb_shell_player_sync_control_state (player);
1839 		g_object_notify (G_OBJECT (player), "play-order");
1840 		player->priv->syncing_state = FALSE;
1841 	} else if (g_strcmp0 (key, "transition-time") == 0) {
1842 		double newtime;
1843 		rb_debug ("track transition time changed");
1844 		newtime = g_settings_get_double (player->priv->settings, "transition-time");
1845 		player->priv->track_transition_time = newtime * RB_PLAYER_SECOND;
1846 	}
1847 }
1848 
1849 /**
1850  * rb_shell_player_get_playback_state:
1851  * @player: the #RBShellPlayer
1852  * @shuffle: (out): returns the current shuffle setting
1853  * @repeat: (out): returns the current repeat setting
1854  *
1855  * Retrieves the current state of the shuffle and repeat settings.
1856  *
1857  * Return value: %TRUE if successful.
1858  */
1859 gboolean
1860 rb_shell_player_get_playback_state (RBShellPlayer *player,
1861 				    gboolean *shuffle,
1862 				    gboolean *repeat)
1863 {
1864 	int i, j;
1865 	char *play_order;
1866 
1867 	play_order = g_settings_get_string (player->priv->settings, "play-order");
1868 	for (i = 0; i < G_N_ELEMENTS(state_to_play_order); i++)
1869 		for (j = 0; j < G_N_ELEMENTS(state_to_play_order[0]); j++)
1870 			if (!strcmp (play_order, state_to_play_order[i][j]))
1871 				goto found;
1872 
1873 	g_free (play_order);
1874 	return FALSE;
1875 
1876 found:
1877 	if (shuffle != NULL) {
1878 		*shuffle = i > 0;
1879 	}
1880 	if (repeat != NULL) {
1881 		*repeat = j > 0;
1882 	}
1883 	g_free (play_order);
1884 	return TRUE;
1885 }
1886 
1887 /**
1888  * rb_shell_player_set_playback_state:
1889  * @player: the #RBShellPlayer
1890  * @shuffle: whether to enable the shuffle setting
1891  * @repeat: whether to enable the repeat setting
1892  *
1893  * Sets the state of the shuffle and repeat settings.
1894  */
1895 void
1896 rb_shell_player_set_playback_state (RBShellPlayer *player,
1897 				    gboolean shuffle,
1898 				    gboolean repeat)
1899 {
1900 	const char *neworder = state_to_play_order[shuffle ? 1 : 0][repeat ? 1 : 0];
1901 	g_settings_set_string (player->priv->settings, "play-order", neworder);
1902 }
1903 
1904 static void
1905 rb_shell_player_sync_play_order (RBShellPlayer *player)
1906 {
1907 	char *new_play_order;
1908 	RhythmDBEntry *playing_entry = NULL;
1909 	RBSource *source;
1910 
1911 	new_play_order = g_settings_get_string (player->priv->settings, "play-order");
1912 	if (player->priv->play_order != NULL) {
1913 		playing_entry = rb_play_order_get_playing_entry (player->priv->play_order);
1914 		g_signal_handlers_disconnect_by_func (player->priv->play_order,
1915 						      G_CALLBACK (rb_shell_player_play_order_update_cb),
1916 						      player);
1917 		g_object_unref (player->priv->play_order);
1918 	}
1919 
1920 	player->priv->play_order = rb_play_order_new (player, new_play_order);
1921 
1922 	g_signal_connect_object (player->priv->play_order,
1923 				 "have_next_previous_changed",
1924 				 G_CALLBACK (rb_shell_player_play_order_update_cb),
1925 				 player, 0);
1926 	rb_shell_player_play_order_update_cb (player->priv->play_order,
1927 					      FALSE, FALSE,
1928 					      player);
1929 
1930 	source = player->priv->current_playing_source;
1931 	if (source == NULL) {
1932 		source = player->priv->selected_source;
1933 	}
1934 	rb_play_order_playing_source_changed (player->priv->play_order, source);
1935 
1936 	if (playing_entry != NULL) {
1937 		rb_play_order_set_playing_entry (player->priv->play_order, playing_entry);
1938 		rhythmdb_entry_unref (playing_entry);
1939 	}
1940 
1941 	g_free (new_play_order);
1942 }
1943 
1944 static void
1945 rb_shell_player_play_order_update_cb (RBPlayOrder *porder,
1946 				      gboolean _has_next,
1947 				      gboolean _has_previous,
1948 				      RBShellPlayer *player)
1949 {
1950 	/* we cannot depend on the values of has_next, has_previous or porder
1951 	 * since this can be called for the main porder, queue porder, etc
1952 	 */
1953 	gboolean has_next = FALSE;
1954 	gboolean has_prev = FALSE;
1955 	RhythmDBEntry *entry;
1956 
1957 	entry = rb_shell_player_get_playing_entry (player);
1958 	if (entry != NULL) {
1959 		has_next = TRUE;
1960 		has_prev = TRUE;
1961 		rhythmdb_entry_unref (entry);
1962 	} else {
1963 		if (player->priv->current_playing_source &&
1964 		    (rb_source_handle_eos (player->priv->current_playing_source) == RB_SOURCE_EOF_NEXT)) {
1965 			RBPlayOrder *porder;
1966 			g_object_get (player->priv->current_playing_source, "play-order", &porder, NULL);
1967 			if (porder == NULL)
1968 				porder = g_object_ref (player->priv->play_order);
1969 			has_next = rb_play_order_has_next (porder);
1970 			g_object_unref (porder);
1971 		}
1972 		if (player->priv->queue_play_order) {
1973 			has_next |= rb_play_order_has_next (player->priv->queue_play_order);
1974 		}
1975 		has_prev = (player->priv->current_playing_source != NULL);
1976 	}
1977 
1978 	if (has_prev != player->priv->has_prev) {
1979 		player->priv->has_prev = has_prev;
1980 		g_object_notify (G_OBJECT (player), "has-prev");
1981 	}
1982 	if (has_next != player->priv->has_next) {
1983 		player->priv->has_next = has_next;
1984 		g_object_notify (G_OBJECT (player), "has-next");
1985 	}
1986 }
1987 
1988 /**
1989  * rb_shell_player_jump_to_current:
1990  * @player: the #RBShellPlayer
1991  *
1992  * Scrolls the #RBEntryView for the current playing source so that
1993  * the current playing entry is visible and selects the row for the
1994  * entry.  If there is no current playing entry, the selection is
1995  * cleared instead.
1996  */
1997 void
1998 rb_shell_player_jump_to_current (RBShellPlayer *player)
1999 {
2000 	RBSource *source;
2001 	RhythmDBEntry *entry;
2002 	RBEntryView *songs;
2003 
2004 	source = player->priv->current_playing_source ? player->priv->current_playing_source :
2005 		player->priv->selected_source;
2006 
2007 	songs = rb_source_get_entry_view (source);
2008 	entry = rb_shell_player_get_playing_entry (player);
2009 	if (songs != NULL) {
2010 		if (entry != NULL) {
2011 			rb_entry_view_scroll_to_entry (songs, entry);
2012 			rb_entry_view_select_entry (songs, entry);
2013 		} else {
2014 			rb_entry_view_select_none (songs);
2015 		}
2016 	}
2017 
2018 	if (entry != NULL) {
2019 		rhythmdb_entry_unref (entry);
2020 	}
2021 }
2022 
2023 static void
2024 swap_playing_source (RBShellPlayer *player,
2025 		     RBSource *new_source)
2026 {
2027 	if (player->priv->current_playing_source != NULL) {
2028 		RBEntryView *old_songs = rb_source_get_entry_view (player->priv->current_playing_source);
2029 		if (old_songs)
2030 			rb_entry_view_set_state (old_songs, RB_ENTRY_VIEW_NOT_PLAYING);
2031 	}
2032 	if (new_source != NULL) {
2033 		RBEntryView *new_songs = rb_source_get_entry_view (new_source);
2034 
2035 		if (new_songs) {
2036 			rb_entry_view_set_state (new_songs, RB_ENTRY_VIEW_PLAYING);
2037 			rb_shell_player_set_playing_source (player, new_source);
2038 		}
2039 	}
2040 }
2041 
2042 /**
2043  * rb_shell_player_do_previous:
2044  * @player: the #RBShellPlayer
2045  * @error: returns any error information
2046  *
2047  * If the current song has been playing for more than 3 seconds,
2048  * restarts it, otherwise, goes back to the previous song.
2049  * Fails if there is no current song, or if inside the first
2050  * 3 seconds of the first song in the play order.
2051  *
2052  * Return value: %TRUE if successful
2053  */
2054 gboolean
2055 rb_shell_player_do_previous (RBShellPlayer *player,
2056 			     GError **error)
2057 {
2058 	RhythmDBEntry *entry = NULL;
2059 	RBSource *new_source;
2060 
2061 	if (player->priv->current_playing_source == NULL) {
2062 		g_set_error (error,
2063 			     RB_SHELL_PLAYER_ERROR,
2064 			     RB_SHELL_PLAYER_ERROR_NOT_PLAYING,
2065 			     _("Not currently playing"));
2066 		return FALSE;
2067 	}
2068 
2069 	/* If we're in the first 3 seconds go to the previous song,
2070 	 * else restart the current one.
2071 	 */
2072 	if (player->priv->current_playing_source != NULL
2073 	    && rb_source_can_pause (player->priv->source)
2074 	    && rb_player_get_time (player->priv->mmplayer) > (G_GINT64_CONSTANT (3) * RB_PLAYER_SECOND)) {
2075 		rb_debug ("after 3 second previous, restarting song");
2076 		rb_player_set_time (player->priv->mmplayer, 0);
2077 		rb_shell_player_sync_with_source (player);
2078 		return TRUE;
2079 	}
2080 
2081 	rb_debug ("going to previous");
2082 
2083 	/* hrm, does this actually do anything at all? */
2084 	if (player->priv->queue_play_order) {
2085 		entry = rb_play_order_get_previous (player->priv->queue_play_order);
2086 		if (entry != NULL) {
2087 			new_source = RB_SOURCE (player->priv->queue_source);
2088 			rb_play_order_go_previous (player->priv->queue_play_order);
2089 		}
2090 	}
2091 
2092 	if (entry == NULL) {
2093 		RBPlayOrder *porder;
2094 
2095 		new_source = player->priv->source;
2096 		g_object_get (new_source, "play-order", &porder, NULL);
2097 		if (porder == NULL)
2098 			porder = g_object_ref (player->priv->play_order);
2099 
2100 		entry = rb_play_order_get_previous (porder);
2101 		if (entry)
2102 			rb_play_order_go_previous (porder);
2103 		g_object_unref (porder);
2104 	}
2105 
2106 	if (entry != NULL) {
2107 		rb_debug ("previous song found, doing previous");
2108 		if (new_source != player->priv->current_playing_source)
2109 			swap_playing_source (player, new_source);
2110 
2111 		player->priv->jump_to_playing_entry = TRUE;
2112 		if (!rb_shell_player_set_playing_entry (player, entry, FALSE, FALSE, error)) {
2113 			rhythmdb_entry_unref (entry);
2114 			return FALSE;
2115 		}
2116 
2117 		rhythmdb_entry_unref (entry);
2118 	} else {
2119 		rb_debug ("no previous song found, signalling error");
2120 		g_set_error (error,
2121 			     RB_SHELL_PLAYER_ERROR,
2122 			     RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST,
2123 			     _("No previous song"));
2124 		rb_shell_player_stop (player);
2125 		return FALSE;
2126 	}
2127 
2128 	return TRUE;
2129 }
2130 
2131 static gboolean
2132 rb_shell_player_do_next_internal (RBShellPlayer *player, gboolean from_eos, gboolean allow_stop, GError **error)
2133 {
2134 	RBSource *new_source = NULL;
2135 	RhythmDBEntry *entry = NULL;
2136 	gboolean rv = TRUE;
2137 
2138 	if (player->priv->source == NULL)
2139 		return TRUE;
2140 
2141 
2142 	/* try the current playing source's play order, if it has one */
2143 	if (player->priv->current_playing_source != NULL) {
2144 		RBPlayOrder *porder;
2145 		g_object_get (player->priv->current_playing_source, "play-order", &porder, NULL);
2146 		if (porder != NULL) {
2147 			entry = rb_play_order_get_next (porder);
2148 			if (entry != NULL) {
2149 				rb_play_order_go_next (porder);
2150 				new_source = player->priv->current_playing_source;
2151 			}
2152 			g_object_unref (porder);
2153 		}
2154 	}
2155 
2156 	/* if that's different to the playing source that the user selected
2157 	 * (ie we're playing from the queue), try that too
2158 	 */
2159 	if (entry == NULL) {
2160 		RBPlayOrder *porder;
2161 		g_object_get (player->priv->source, "play-order", &porder, NULL);
2162 		if (porder == NULL)
2163 			porder = g_object_ref (player->priv->play_order);
2164 
2165 		/*
2166 		 * If we interrupted this source to play from something else,
2167 		 * we should go back to whatever it wanted to play before.
2168 		 */
2169 		if (player->priv->source != player->priv->current_playing_source)
2170 			entry = rb_play_order_get_playing_entry (porder);
2171 
2172 		/* if that didn't help, advance the play order */
2173 		if (entry == NULL) {
2174 			entry = rb_play_order_get_next (porder);
2175 			if (entry != NULL) {
2176 				rb_debug ("got new entry %s from play order",
2177 					  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
2178 				rb_play_order_go_next (porder);
2179 			}
2180 		}
2181 
2182 		if (entry != NULL)
2183 			new_source = player->priv->source;
2184 		
2185 		g_object_unref (porder);
2186 	}
2187 
2188 	/* if the new entry isn't from the play queue anyway, let the play queue
2189 	 * override the regular play order.
2190 	 */
2191 	if (player->priv->queue_play_order &&
2192 	    new_source != RB_SOURCE (player->priv->queue_source)) {
2193 		RhythmDBEntry *queue_entry;
2194 
2195 		queue_entry = rb_play_order_get_next (player->priv->queue_play_order);
2196 		rb_play_order_go_next (player->priv->queue_play_order);
2197 		if (queue_entry != NULL) {
2198 			rb_debug ("got new entry %s from queue play order",
2199 				  rhythmdb_entry_get_string (queue_entry, RHYTHMDB_PROP_LOCATION));
2200 			if (entry != NULL) {
2201 				rhythmdb_entry_unref (entry);
2202 			}
2203 			entry = queue_entry;
2204 			new_source = RB_SOURCE (player->priv->queue_source);
2205 		} else {
2206 			rb_debug ("didn't get a new entry from queue play order");
2207 		}
2208 	}
2209 
2210 	/* play the new entry */
2211 	if (entry != NULL) {
2212 		/* if the entry view containing the playing entry changed, update it */
2213 		if (new_source != player->priv->current_playing_source)
2214 			swap_playing_source (player, new_source);
2215 
2216 		player->priv->jump_to_playing_entry = TRUE;
2217 		if (!rb_shell_player_set_playing_entry (player, entry, FALSE, from_eos, error))
2218 			rv = FALSE;
2219 	} else {
2220 		g_set_error (error,
2221 			     RB_SHELL_PLAYER_ERROR,
2222 			     RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST,
2223 			     _("No next song"));
2224 		rv = FALSE;
2225 
2226 		if (allow_stop) {
2227 			rb_debug ("No next entry, stopping playback");
2228 
2229 			/* hmm, need to set playing entry on the playing source's
2230 			 * play order if it has one?
2231 			 */
2232 
2233 			rb_shell_player_stop (player);
2234 			rb_play_order_set_playing_entry (player->priv->play_order, NULL);
2235 		}
2236 	}
2237 
2238 	if (entry != NULL) {
2239 		rhythmdb_entry_unref (entry);
2240 	}
2241 
2242 	return rv;
2243 }
2244 
2245 /**
2246  * rb_shell_player_do_next:
2247  * @player: the #RBShellPlayer
2248  * @error: returns error information
2249  *
2250  * Skips to the next song.  Consults the play queue and handles
2251  * transitions between the play queue and the active source.
2252  * Fails if there is no entry to play after the current one.
2253  *
2254  * Return value: %TRUE if successful
2255  */
2256 gboolean
2257 rb_shell_player_do_next (RBShellPlayer *player,
2258 			 GError **error)
2259 {
2260 	return rb_shell_player_do_next_internal (player, FALSE, TRUE, error);
2261 }
2262 
2263 static void
2264 rb_shell_player_cmd_previous (GtkAction *action,
2265 			      RBShellPlayer *player)
2266 {
2267 	GError *error = NULL;
2268 
2269 	if (!rb_shell_player_do_previous (player, &error)) {
2270 		if (error->domain != RB_SHELL_PLAYER_ERROR ||
2271 		    error->code != RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST) {
2272 			g_warning ("cmd_previous: Unhandled error: %s", error->message);
2273 		} else if (error->code == RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST) {
2274 			rb_shell_player_stop (player);
2275 		}
2276 	}
2277 }
2278 
2279 static void
2280 rb_shell_player_cmd_next (GtkAction *action,
2281 			  RBShellPlayer *player)
2282 {
2283 	GError *error = NULL;
2284 
2285 	if (!rb_shell_player_do_next (player, &error)) {
2286 		if (error->domain != RB_SHELL_PLAYER_ERROR ||
2287 		    error->code != RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST) {
2288 			g_warning ("cmd_next: Unhandled error: %s", error->message);
2289 		} else if (error->code == RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST) {
2290 			rb_shell_player_stop (player);
2291 		}
2292 	}
2293 }
2294 
2295 /**
2296  * rb_shell_player_play_entry:
2297  * @player: the #RBShellPlayer
2298  * @entry: the #RhythmDBEntry to play
2299  * @source: the new #RBSource to set as playing (or NULL to use the
2300  *   selected source)
2301  *
2302  * Plays a specified entry.
2303  */
2304 void
2305 rb_shell_player_play_entry (RBShellPlayer *player,
2306 			    RhythmDBEntry *entry,
2307 			    RBSource *source)
2308 {
2309 	GError *error = NULL;
2310 
2311 	if (source == NULL)
2312 		source = player->priv->selected_source;
2313 	rb_shell_player_set_playing_source (player, source);
2314 
2315 	player->priv->jump_to_playing_entry = FALSE;
2316 	if (!rb_shell_player_set_playing_entry (player, entry, TRUE, FALSE, &error)) {
2317 		rb_shell_player_error (player, FALSE, error);
2318 		g_clear_error (&error);
2319 	}
2320 }
2321 
2322 static void
2323 rb_shell_player_cmd_volume_up (GtkAction *action,
2324 			       RBShellPlayer *player)
2325 {
2326 	rb_shell_player_set_volume_relative (player, 0.1, NULL);
2327 }
2328 
2329 static void
2330 rb_shell_player_cmd_volume_down (GtkAction *action,
2331 				 RBShellPlayer *player)
2332 {
2333 	rb_shell_player_set_volume_relative (player, -0.1, NULL);
2334 }
2335 
2336 static void
2337 rb_shell_player_cmd_play (GtkAction *action,
2338 			  RBShellPlayer *player)
2339 {
2340 	GError *error = NULL;
2341 	rb_debug ("play!");
2342 	if (!rb_shell_player_playpause (player, FALSE, &error))
2343 		rb_error_dialog (NULL,
2344 				 _("Couldn't start playback"),
2345 				 "%s", (error) ? error->message : "(null)");
2346 	g_clear_error (&error);
2347 }
2348 
2349 /* unused parameter can't be removed without breaking dbus interface compatibility */
2350 /**
2351  * rb_shell_player_playpause:
2352  * @player: the #RBShellPlayer
2353  * @unused: nothing
2354  * @error: returns error information
2355  *
2356  * Toggles between playing and paused state.  If there is no playing
2357  * entry, chooses an entry from (in order of preference) the play queue,
2358  * the selection in the current source, or the play order.
2359  *
2360  * Return value: %TRUE if successful
2361  */
2362 gboolean
2363 rb_shell_player_playpause (RBShellPlayer *player,
2364 			   gboolean unused,
2365 			   GError **error)
2366 {
2367 	gboolean ret;
2368 	RBEntryView *songs;
2369 
2370 	rb_debug ("doing playpause");
2371 
2372 	g_return_val_if_fail (RB_IS_SHELL_PLAYER (player), TRUE);
2373 
2374 	ret = TRUE;
2375 
2376 	if (rb_player_playing (player->priv->mmplayer)) {
2377 		if (player->priv->source == NULL) {
2378 			rb_debug ("playing source is already NULL");
2379 		} else if (rb_source_can_pause (player->priv->source)) {
2380 			rb_debug ("pausing mm player");
2381 			rb_player_pause (player->priv->mmplayer);
2382 			songs = rb_source_get_entry_view (player->priv->current_playing_source);
2383 			if (songs)
2384 				rb_entry_view_set_state (songs, RB_ENTRY_VIEW_PAUSED);
2385 
2386 			/* might need a signal for when the player has actually paused here? */
2387 			g_object_notify (G_OBJECT (player), "playing");
2388 			/* mostly for that */
2389 		} else {
2390 			rb_debug ("stopping playback");
2391 			rb_shell_player_stop (player);
2392 		}
2393 	} else {
2394 		RhythmDBEntry *entry;
2395 		RBSource *new_source;
2396 		gboolean out_of_order = FALSE;
2397 
2398 		if (player->priv->source == NULL) {
2399 			/* no current stream, pull one in from the currently
2400 			 * selected source */
2401 			rb_debug ("no playing source, using selected source");
2402 			rb_shell_player_set_playing_source (player, player->priv->selected_source);
2403 		}
2404 		new_source = player->priv->current_playing_source;
2405 
2406 		entry = rb_shell_player_get_playing_entry (player);
2407 		if (entry == NULL) {
2408 			/* queue takes precedence over selection */
2409 			if (player->priv->queue_play_order) {
2410 				entry = rb_play_order_get_next (player->priv->queue_play_order);
2411 				if (entry != NULL) {
2412 					new_source = RB_SOURCE (player->priv->queue_source);
2413 					rb_play_order_go_next (player->priv->queue_play_order);
2414 				}
2415 			}
2416 
2417 			/* selection takes precedence over first item in play order */
2418 			if (entry == NULL) {
2419 				GList *selection = NULL;
2420 
2421 				songs = rb_source_get_entry_view (player->priv->source);
2422 				if (songs)
2423 					selection = rb_entry_view_get_selected_entries (songs);
2424 
2425 				if (selection != NULL) {
2426 					rb_debug ("choosing first selected entry");
2427 					entry = (RhythmDBEntry*) selection->data;
2428 					if (entry)
2429 						out_of_order = TRUE;
2430 
2431 					g_list_free (selection);
2432 				}
2433 			}
2434 
2435 			/* play order is last */
2436 			if (entry == NULL) {
2437 				RBPlayOrder *porder;
2438 
2439 				rb_debug ("getting entry from play order");
2440 				g_object_get (player->priv->source, "play-order", &porder, NULL);
2441 				if (porder == NULL)
2442 					porder = g_object_ref (player->priv->play_order);
2443 
2444 				entry = rb_play_order_get_next (porder);
2445 				if (entry != NULL)
2446 					rb_play_order_go_next (porder);
2447 				g_object_unref (porder);
2448 			}
2449 
2450 			if (entry != NULL) {
2451 				/* if the entry view containing the playing entry changed, update it */
2452 				if (new_source != player->priv->current_playing_source)
2453 					swap_playing_source (player, new_source);
2454 
2455 				player->priv->jump_to_playing_entry = TRUE;
2456 				if (!rb_shell_player_set_playing_entry (player, entry, out_of_order, FALSE, error))
2457 					ret = FALSE;
2458 			}
2459 		} else {
2460 			if (!rb_shell_player_play (player, error)) {
2461 				rb_shell_player_stop (player);
2462 				ret = FALSE;
2463 			}
2464 		}
2465 
2466 		if (entry != NULL) {
2467 			rhythmdb_entry_unref (entry);
2468 		}
2469 	}
2470 
2471 	rb_shell_player_sync_with_source (player);
2472 	rb_shell_player_sync_buttons (player);
2473 
2474 	return ret;
2475 }
2476 
2477 static void
2478 rb_shell_player_sync_control_state (RBShellPlayer *player)
2479 {
2480 	gboolean shuffle, repeat;
2481 	GtkAction *action;
2482 	rb_debug ("syncing control state");
2483 
2484 	if (!rb_shell_player_get_playback_state (player, &shuffle,
2485 						 &repeat))
2486 		return;
2487 
2488 	action = gtk_action_group_get_action (player->priv->actiongroup,
2489 					      "ControlShuffle");
2490 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), shuffle);
2491 	action = gtk_action_group_get_action (player->priv->actiongroup,
2492 					      "ControlRepeat");
2493 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), repeat);
2494 }
2495 
2496 static void
2497 sync_volume_cb (GSettings *settings, RBShellPlayer *player)
2498 {
2499 	g_settings_set_double (player->priv->settings, "volume", player->priv->volume);
2500 }
2501 
2502 static void
2503 rb_shell_player_sync_volume (RBShellPlayer *player,
2504 			     gboolean notify,
2505 			     gboolean set_volume)
2506 {
2507 	GtkAction *action;
2508 	RhythmDBEntry *entry;
2509 
2510 	if (player->priv->volume <= 0.0){
2511 		player->priv->volume = 0.0;
2512 	} else if (player->priv->volume >= 1.0){
2513 		player->priv->volume = 1.0;
2514 	}
2515 
2516 	action = gtk_action_group_get_action (player->priv->actiongroup,
2517 					      "ControlVolumeUp");
2518 	g_object_set (G_OBJECT (action), "sensitive", player->priv->volume < 0.9999, NULL);
2519 
2520 	action = gtk_action_group_get_action (player->priv->actiongroup,
2521 					      "ControlVolumeDown");
2522 	g_object_set (G_OBJECT (action), "sensitive", player->priv->volume > 0.0001, NULL);
2523 
2524 	if (set_volume) {
2525 		rb_player_set_volume (player->priv->mmplayer,
2526 				      player->priv->mute ? 0.0 : player->priv->volume);
2527 	}
2528 
2529 	if (player->priv->syncing_state == FALSE) {
2530 		rb_settings_delayed_sync (player->priv->settings,
2531 					  (RBDelayedSyncFunc) sync_volume_cb,
2532 					  g_object_ref (player),
2533 					  g_object_unref);
2534 	}
2535 
2536 	entry = rb_shell_player_get_playing_entry (player);
2537 	if (entry != NULL) {
2538 		rhythmdb_entry_unref (entry);
2539 	}
2540 
2541 	if (notify)
2542 		g_object_notify (G_OBJECT (player), "volume");
2543 }
2544 
2545 /**
2546  * rb_shell_player_set_volume:
2547  * @player: the #RBShellPlayer
2548  * @volume: the volume level (between 0 and 1)
2549  * @error: returns the error information
2550  *
2551  * Sets the playback volume level.
2552  *
2553  * Return value: %TRUE on success
2554  */
2555 gboolean
2556 rb_shell_player_set_volume (RBShellPlayer *player,
2557 			    gdouble volume,
2558 			    GError **error)
2559 {
2560 	player->priv->volume = volume;
2561 	rb_shell_player_sync_volume (player, TRUE, TRUE);
2562 	return TRUE;
2563 }
2564 
2565 /**
2566  * rb_shell_player_set_volume_relative:
2567  * @player: the #RBShellPlayer
2568  * @delta: difference to apply to the volume level (between -1 and 1)
2569  * @error: returns error information
2570  *
2571  * Adds the specified value to the current volume level.
2572  *
2573  * Return value: %TRUE on success
2574  */
2575 gboolean
2576 rb_shell_player_set_volume_relative (RBShellPlayer *player,
2577 				     gdouble delta,
2578 				     GError **error)
2579 {
2580 	/* rb_shell_player_sync_volume does clipping */
2581 	player->priv->volume += delta;
2582 	rb_shell_player_sync_volume (player, TRUE, TRUE);
2583 	return TRUE;
2584 }
2585 
2586 /**
2587  * rb_shell_player_get_volume:
2588  * @player: the #RBShellPlayer
2589  * @volume: (out): returns the volume level
2590  * @error: returns error information
2591  *
2592  * Returns the current volume level
2593  *
2594  * Return value: the current volume level.
2595  */
2596 gboolean
2597 rb_shell_player_get_volume (RBShellPlayer *player,
2598 			    gdouble *volume,
2599 			    GError **error)
2600 {
2601 	*volume = player->priv->volume;
2602 	return TRUE;
2603 }
2604 
2605 static void
2606 rb_shell_player_volume_changed_cb (RBPlayer *player,
2607 				   float volume,
2608 				   RBShellPlayer *shell_player)
2609 {
2610 	shell_player->priv->volume = volume;
2611 	rb_shell_player_sync_volume (shell_player, TRUE, FALSE);
2612 }
2613 
2614 /**
2615  * rb_shell_player_set_mute:
2616  * @player: the #RBShellPlayer
2617  * @mute: %TRUE to mute playback
2618  * @error: returns error information
2619  *
2620  * Updates the mute setting on the player.
2621  *
2622  * Return value: %TRUE if successful
2623  */
2624 gboolean
2625 rb_shell_player_set_mute (RBShellPlayer *player,
2626 			  gboolean mute,
2627 			  GError **error)
2628 {
2629 	player->priv->mute = mute;
2630 	rb_shell_player_sync_volume (player, FALSE, TRUE);
2631 	return TRUE;
2632 }
2633 
2634 /**
2635  * rb_shell_player_get_mute:
2636  * @player: the #RBShellPlayer
2637  * @mute: (out): returns the current mute setting
2638  * @error: returns error information
2639  *
2640  * Returns %TRUE if currently muted
2641  *
2642  * Return value: %TRUE if currently muted
2643  */
2644 gboolean
2645 rb_shell_player_get_mute (RBShellPlayer *player,
2646 			  gboolean *mute,
2647 			  GError **error)
2648 {
2649 	*mute = player->priv->mute;
2650 	return TRUE;
2651 }
2652 
2653 static void
2654 rb_shell_player_shuffle_changed_cb (GtkAction *action,
2655 				    RBShellPlayer *player)
2656 {
2657 	const char *neworder;
2658 	gboolean shuffle = FALSE;
2659 	gboolean repeat = FALSE;
2660 
2661 	if (player->priv->syncing_state)
2662 		return;
2663 
2664 	rb_debug ("shuffle changed");
2665 
2666 	rb_shell_player_get_playback_state (player, &shuffle, &repeat);
2667 
2668 	shuffle = !shuffle;
2669 	neworder = state_to_play_order[shuffle ? 1 : 0][repeat ? 1 : 0];
2670 	g_settings_set_string (player->priv->settings, "play-order", neworder);
2671 }
2672 
2673 static void
2674 rb_shell_player_repeat_changed_cb (GtkAction *action,
2675 				   RBShellPlayer *player)
2676 {
2677 	const char *neworder;
2678 	gboolean shuffle = FALSE;
2679 	gboolean repeat = FALSE;
2680 	rb_debug ("repeat changed");
2681 
2682 	if (player->priv->syncing_state)
2683 		return;
2684 
2685 	rb_shell_player_get_playback_state (player, &shuffle, &repeat);
2686 
2687 	repeat = !repeat;
2688 	neworder = state_to_play_order[shuffle ? 1 : 0][repeat ? 1 : 0];
2689 	g_settings_set_string (player->priv->settings, "play-order", neworder);
2690 }
2691 
2692 static void
2693 rb_shell_player_entry_activated_cb (RBEntryView *view,
2694 				    RhythmDBEntry *entry,
2695 				    RBShellPlayer *player)
2696 {
2697 	gboolean was_from_queue = FALSE;
2698 	RhythmDBEntry *prev_entry = NULL;
2699 	GError *error = NULL;
2700 	gboolean source_set = FALSE;
2701 	gboolean jump_to_entry = FALSE;
2702 	char *playback_uri;
2703 
2704 	g_return_if_fail (entry != NULL);
2705 
2706 	rb_debug  ("got entry %p activated", entry);
2707 
2708 	/* don't play hidden entries */
2709 	if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
2710 		return;
2711 
2712 	/* skip entries with no playback uri */
2713 	playback_uri = rhythmdb_entry_get_playback_uri (entry);
2714 	if (playback_uri == NULL)
2715 		return;
2716 
2717 	g_free (playback_uri);
2718 
2719 	/* figure out where the previous entry came from */
2720 	if ((player->priv->queue_source != NULL) &&
2721 	    (player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source))) {
2722 		prev_entry = rb_shell_player_get_playing_entry (player);
2723 		was_from_queue = TRUE;
2724 	}
2725 
2726 	if (player->priv->queue_source) {
2727 		RBEntryView *queue_sidebar;
2728 
2729 		g_object_get (player->priv->queue_source, "sidebar", &queue_sidebar, NULL);
2730 
2731 		if (view == queue_sidebar || view == rb_source_get_entry_view (RB_SOURCE (player->priv->queue_source))) {
2732 
2733 			/* fall back to the current selected source once the queue is empty */
2734 			if (view == queue_sidebar && player->priv->source == NULL) {
2735 				/* XXX only do this if the selected source doesn't have its own play order? */
2736 				rb_play_order_playing_source_changed (player->priv->play_order,
2737 								      player->priv->selected_source);
2738 				player->priv->source = player->priv->selected_source;
2739 			}
2740 
2741 			rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source));
2742 
2743 			was_from_queue = FALSE;
2744 			source_set = TRUE;
2745 			jump_to_entry = TRUE;
2746 		} else {
2747 			if (player->priv->queue_only) {
2748 				rb_source_add_to_queue (player->priv->selected_source,
2749 							RB_SOURCE (player->priv->queue_source));
2750 				rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source));
2751 				source_set = TRUE;
2752 			}
2753 		}
2754 
2755 		g_object_unref (queue_sidebar);
2756 	}
2757 
2758 	/* bail out if queue only */
2759 	if (player->priv->queue_only) {
2760 		return;
2761 	}
2762 
2763 	if (!source_set) {
2764 		rb_shell_player_set_playing_source (player, player->priv->selected_source);
2765 		source_set = TRUE;
Value stored to 'source_set' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2766 } 2767 2768 player->priv->jump_to_playing_entry = jump_to_entry; 2769 if (!rb_shell_player_set_playing_entry (player, entry, TRUE, FALSE, &error)) { 2770 rb_shell_player_error (player, FALSE, error); 2771 g_clear_error (&error); 2772 } 2773 2774 /* if we were previously playing from the queue, clear its playing entry, 2775 * so we'll start again from the start. 2776 */ 2777 if (was_from_queue && prev_entry != NULL) { 2778 rb_play_order_set_playing_entry (player->priv->queue_play_order, NULL); 2779 } 2780 2781 if (prev_entry != NULL) { 2782 rhythmdb_entry_unref (prev_entry); 2783 } 2784 } 2785 2786 static void 2787 rb_shell_player_property_row_activated_cb (RBPropertyView *view, 2788 const char *name, 2789 RBShellPlayer *player) 2790 { 2791 RBPlayOrder *porder; 2792 RhythmDBEntry *entry = NULL; 2793 GError *error = NULL; 2794 2795 rb_debug ("got property activated"); 2796 2797 rb_shell_player_set_playing_source (player, player->priv->selected_source); 2798 2799 /* RHYTHMDBFIXME - do we need to wait here until the query is finished? 2800 * in theory, yes, but in practice the query is started when the row is 2801 * selected (on the first click when doubleclicking, or when using the 2802 * keyboard to select then activate) and is pretty much always done by 2803 * the time we get in here. 2804 */ 2805 2806 g_object_get (player->priv->selected_source, "play-order", &porder, NULL); 2807 if (porder == NULL) 2808 porder = g_object_ref (player->priv->play_order); 2809 2810 entry = rb_play_order_get_next (porder); 2811 if (entry != NULL) { 2812 rb_play_order_go_next (porder); 2813 2814 player->priv->jump_to_playing_entry = TRUE; /* ? */ 2815 if (!rb_shell_player_set_playing_entry (player, entry, TRUE, FALSE, &error)) { 2816 rb_shell_player_error (player, FALSE, error); 2817 g_clear_error (&error); 2818 } 2819 } 2820 2821 rhythmdb_entry_unref (entry); 2822 g_object_unref (porder); 2823 } 2824 2825 static void 2826 rb_shell_player_entry_changed_cb (RhythmDB *db, 2827 RhythmDBEntry *entry, 2828 GArray *changes, 2829 RBShellPlayer *player) 2830 { 2831 gboolean synced = FALSE; 2832 const char *location; 2833 RhythmDBEntry *playing_entry; 2834 int i; 2835 2836 playing_entry = rb_shell_player_get_playing_entry (player); 2837 2838 /* We try to update only if the changed entry is currently playing */ 2839 if (entry != playing_entry) { 2840 if (playing_entry != NULL) { 2841 rhythmdb_entry_unref (playing_entry); 2842 } 2843 return; 2844 } 2845 2846 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION); 2847 for (i = 0; i < changes->len; i++) { 2848 GValue *v = &g_array_index (changes, GValue, i); 2849 RhythmDBEntryChange *change = g_value_get_boxed (v); 2850 2851 /* update UI if the artist, title or album has changed */ 2852 switch (change->prop) { 2853 case RHYTHMDB_PROP_TITLE: 2854 case RHYTHMDB_PROP_ARTIST: 2855 case RHYTHMDB_PROP_ALBUM: 2856 if (!synced) { 2857 rb_shell_player_sync_with_source (player); 2858 synced = TRUE; 2859 } 2860 break; 2861 default: 2862 break; 2863 } 2864 2865 /* emit dbus signals for changes with easily marshallable types */ 2866 switch (rhythmdb_get_property_type (db, change->prop)) { 2867 case G_TYPE_STRING: 2868 case G_TYPE_BOOLEAN: 2869 case G_TYPE_ULONG: 2870 case G_TYPE_UINT64: 2871 case G_TYPE_DOUBLE: 2872 g_signal_emit (G_OBJECT (player), 2873 rb_shell_player_signals[PLAYING_SONG_PROPERTY_CHANGED], 0, 2874 location, 2875 rhythmdb_nice_elt_name_from_propid (db, change->prop), 2876 &change->old, 2877 &change->new); 2878 break; 2879 default: 2880 break; 2881 } 2882 } 2883 2884 if (playing_entry != NULL) { 2885 rhythmdb_entry_unref (playing_entry); 2886 } 2887 } 2888 2889 static void 2890 rb_shell_player_extra_metadata_cb (RhythmDB *db, 2891 RhythmDBEntry *entry, 2892 const char *field, 2893 GValue *metadata, 2894 RBShellPlayer *player) 2895 { 2896 2897 RhythmDBEntry *playing_entry; 2898 2899 playing_entry = rb_shell_player_get_playing_entry (player); 2900 if (entry != playing_entry) { 2901 if (playing_entry != NULL) { 2902 rhythmdb_entry_unref (playing_entry); 2903 } 2904 return; 2905 } 2906 2907 rb_shell_player_sync_with_source (player); 2908 2909 /* emit dbus signals for changes with easily marshallable types */ 2910 switch (G_VALUE_TYPE (metadata)) { 2911 case G_TYPE_STRING: 2912 /* make sure it's valid utf8, otherwise dbus barfs */ 2913 if (g_utf8_validate (g_value_get_string (metadata), -1, NULL) == FALSE) { 2914 rb_debug ("not emitting extra metadata field %s as value is not valid utf8", field); 2915 return; 2916 } 2917 case G_TYPE_BOOLEAN: 2918 case G_TYPE_ULONG: 2919 case G_TYPE_UINT64: 2920 case G_TYPE_DOUBLE: 2921 g_signal_emit (G_OBJECT (player), 2922 rb_shell_player_signals[PLAYING_SONG_PROPERTY_CHANGED], 0, 2923 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), 2924 field, 2925 metadata, /* slightly silly */ 2926 metadata); 2927 break; 2928 default: 2929 break; 2930 } 2931 } 2932 2933 2934 static void 2935 rb_shell_player_sync_with_source (RBShellPlayer *player) 2936 { 2937 const char *entry_title = NULL; 2938 const char *artist = NULL; 2939 const char *stream_name = NULL; 2940 char *streaming_title = NULL; 2941 char *streaming_artist = NULL; 2942 RhythmDBEntry *entry; 2943 char *title = NULL; 2944 gint64 elapsed; 2945 2946 entry = rb_shell_player_get_playing_entry (player); 2947 rb_debug ("playing source: %p, active entry: %p", player->priv->current_playing_source, entry); 2948 2949 if (entry != NULL) { 2950 GValue *value; 2951 2952 entry_title = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE); 2953 artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST); 2954 2955 value = rhythmdb_entry_request_extra_metadata (player->priv->db, 2956 entry, 2957 RHYTHMDB_PROP_STREAM_SONG_TITLE); 2958 if (value != NULL) { 2959 streaming_title = g_value_dup_string (value); 2960 g_value_unset (value); 2961 g_free (value); 2962 2963 rb_debug ("got streaming title \"%s\"", streaming_title); 2964 /* use entry title for stream name */ 2965 stream_name = entry_title; 2966 entry_title = streaming_title; 2967 } 2968 2969 value = rhythmdb_entry_request_extra_metadata (player->priv->db, 2970 entry, 2971 RHYTHMDB_PROP_STREAM_SONG_ARTIST); 2972 if (value != NULL) { 2973 streaming_artist = g_value_dup_string (value); 2974 g_value_unset (value); 2975 g_free (value); 2976 2977 rb_debug ("got streaming artist \"%s\"", streaming_artist); 2978 /* override artist from entry */ 2979 artist = streaming_artist; 2980 } 2981 2982 rhythmdb_entry_unref (entry); 2983 } 2984 2985 if ((artist && artist[0] != '\0') || entry_title || stream_name) { 2986 2987 GString *title_str = g_string_sized_new (100); 2988 if (artist && artist[0] != '\0') { 2989 g_string_append (title_str, artist); 2990 g_string_append (title_str, " - "); 2991 } 2992 if (entry_title != NULL) 2993 g_string_append (title_str, entry_title); 2994 2995 if (stream_name != NULL) 2996 g_string_append_printf (title_str, " (%s)", stream_name); 2997 2998 title = g_string_free (title_str, FALSE); 2999 } 3000 3001 elapsed = rb_player_get_time (player->priv->mmplayer); 3002 if (elapsed < 0) 3003 elapsed = 0; 3004 player->priv->elapsed = elapsed / RB_PLAYER_SECOND; 3005 3006 g_signal_emit (G_OBJECT (player), rb_shell_player_signals[WINDOW_TITLE_CHANGED], 0, 3007 title); 3008 g_free (title); 3009 3010 g_signal_emit (G_OBJECT (player), rb_shell_player_signals[ELAPSED_CHANGED], 0, 3011 player->priv->elapsed); 3012 3013 g_free (streaming_artist); 3014 g_free (streaming_title); 3015 } 3016 3017 static void 3018 rb_shell_player_sync_buttons (RBShellPlayer *player) 3019 { 3020 GtkAction *action; 3021 RBSource *source; 3022 RBEntryView *view; 3023 int entry_view_state; 3024 RhythmDBEntry *entry; 3025 3026 entry = rb_shell_player_get_playing_entry (player); 3027 if (entry != NULL) { 3028 source = player->priv->current_playing_source;
Value stored to 'source' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3029 entry_view_state = rb_player_playing (player->priv->mmplayer) ? 3030 RB_ENTRY_VIEW_PLAYING : RB_ENTRY_VIEW_PAUSED; 3031 } else { 3032 source = player->priv->selected_source;
Value stored to 'source' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3033 entry_view_state = RB_ENTRY_VIEW_NOT_PLAYING; 3034 } 3035 3036 source = (entry == NULL) ? player->priv->selected_source : player->priv->current_playing_source; 3037 3038 rb_debug ("syncing with source %p", source); 3039 3040 action = gtk_action_group_get_action (player->priv->actiongroup, 3041 "ViewJumpToPlaying"); 3042 g_object_set (action, "sensitive", entry != NULL, NULL); 3043 3044 action = gtk_action_group_get_action (player->priv->actiongroup, 3045 "ControlPlay"); 3046 g_object_set (action, "sensitive", entry != NULL || source != NULL, NULL); 3047 3048 if (source != NULL) { 3049 view = rb_source_get_entry_view (source); 3050 if (view) 3051 rb_entry_view_set_state (view, entry_view_state); 3052 } 3053 3054 if (entry != NULL) { 3055 rhythmdb_entry_unref (entry); 3056 } 3057 } 3058 3059 /** 3060 * rb_shell_player_set_playing_source: 3061 * @player: the #RBShellPlayer 3062 * @source: the new playing #RBSource 3063 * 3064 * Replaces the current playing source. 3065 */ 3066 void 3067 rb_shell_player_set_playing_source (RBShellPlayer *player, 3068 RBSource *source) 3069 { 3070 rb_shell_player_set_playing_source_internal (player, source, TRUE); 3071 } 3072 3073 static void 3074 actually_set_playing_source (RBShellPlayer *player, 3075 RBSource *source, 3076 gboolean sync_entry_view) 3077 { 3078 RBPlayOrder *porder; 3079 3080 player->priv->source = source; 3081 player->priv->current_playing_source = source; 3082 3083 if (source != NULL) { 3084 RBEntryView *songs = rb_source_get_entry_view (player->priv->source); 3085 if (sync_entry_view && songs) { 3086 rb_entry_view_set_state (songs, RB_ENTRY_VIEW_PLAYING); 3087 } 3088 } 3089 3090 if (source != RB_SOURCE (player->priv->queue_source)) { 3091 if (source == NULL) 3092 source = player->priv->selected_source; 3093 3094 if (source != NULL) { 3095 g_object_get (source, "play-order", &porder, NULL); 3096 if (porder == NULL) 3097 porder = g_object_ref (player->priv->play_order); 3098 3099 rb_play_order_playing_source_changed (porder, source); 3100 g_object_unref (porder); 3101 } 3102 } 3103 3104 rb_shell_player_play_order_update_cb (player->priv->play_order, 3105 FALSE, FALSE, 3106 player); 3107 } 3108 3109 static void 3110 rb_shell_player_set_playing_source_internal (RBShellPlayer *player, 3111 RBSource *source, 3112 gboolean sync_entry_view) 3113 3114 { 3115 gboolean emit_source_changed = TRUE; 3116 gboolean emit_playing_from_queue_changed = FALSE; 3117 3118 if (player->priv->source == source && 3119 player->priv->current_playing_source == source && 3120 source != NULL) 3121 return; 3122 3123 rb_debug ("setting playing source to %p", source); 3124 3125 if (RB_SOURCE (player->priv->queue_source) == source) { 3126 3127 if (player->priv->current_playing_source != source) 3128 emit_playing_from_queue_changed = TRUE; 3129 3130 if (player->priv->source == NULL) { 3131 actually_set_playing_source (player, source, sync_entry_view); 3132 } else { 3133 emit_source_changed = FALSE; 3134 player->priv->current_playing_source = source; 3135 } 3136 3137 } else { 3138 if (player->priv->current_playing_source != source) { 3139 if (player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source)) 3140 emit_playing_from_queue_changed = TRUE; 3141 3142 /* stop the old source */ 3143 if (player->priv->current_playing_source != NULL) { 3144 if (sync_entry_view) { 3145 RBEntryView *songs = rb_source_get_entry_view (player->priv->current_playing_source); 3146 rb_debug ("source is already playing, stopping it"); 3147 3148 /* clear the playing entry if we're switching between non-queue sources */ 3149 if (player->priv->current_playing_source != RB_SOURCE (player->priv->queue_source)) 3150 rb_play_order_set_playing_entry (player->priv->play_order, NULL); 3151 3152 if (songs) 3153 rb_entry_view_set_state (songs, RB_ENTRY_VIEW_NOT_PLAYING); 3154 } 3155 } 3156 } 3157 actually_set_playing_source (player, source, sync_entry_view); 3158 } 3159 3160 rb_shell_player_sync_with_source (player); 3161 /*g_object_notify (G_OBJECT (player), "playing");*/ 3162 if (player->priv->selected_source) 3163 rb_shell_player_sync_buttons (player); 3164 3165 if (emit_source_changed) { 3166 g_signal_emit (G_OBJECT (player), rb_shell_player_signals[PLAYING_SOURCE_CHANGED], 3167 0, player->priv->source); 3168 } 3169 if (emit_playing_from_queue_changed) { 3170 g_object_notify (G_OBJECT (player), "playing-from-queue"); 3171 } 3172 } 3173 3174 /** 3175 * rb_shell_player_stop: 3176 * @player: a #RBShellPlayer. 3177 * 3178 * Completely stops playback, freeing resources and unloading the file. 3179 * 3180 * In general rb_shell_player_pause() should be used instead, as it stops the 3181 * audio, but does not completely free resources. 3182 **/ 3183 void 3184 rb_shell_player_stop (RBShellPlayer *player) 3185 { 3186 GError *error = NULL; 3187 rb_debug ("stopping"); 3188 3189 g_return_if_fail (RB_IS_SHELL_PLAYER (player)); 3190 3191 if (error == NULL) 3192 rb_player_close (player->priv->mmplayer, NULL, &error); 3193 if (error) { 3194 rb_error_dialog (NULL, 3195 _("Couldn't stop playback"), 3196 "%s", error->message); 3197 g_error_free (error); 3198 } 3199 3200 if (player->priv->parser_cancellable != NULL) { 3201 rb_debug ("cancelling playlist parser"); 3202 g_cancellable_cancel (player->priv->parser_cancellable); 3203 g_object_unref (player->priv->parser_cancellable); 3204 player->priv->parser_cancellable = NULL; 3205 } 3206 3207 if (player->priv->playing_entry != NULL) { 3208 rhythmdb_entry_unref (player->priv->playing_entry); 3209 player->priv->playing_entry = NULL; 3210 } 3211 3212 rb_shell_player_set_playing_source (player, NULL); 3213 rb_shell_player_sync_with_source (player); 3214 g_signal_emit (G_OBJECT (player), 3215 rb_shell_player_signals[PLAYING_SONG_CHANGED], 0, 3216 NULL); 3217 g_signal_emit (G_OBJECT (player), 3218 rb_shell_player_signals[PLAYING_URI_CHANGED], 0, 3219 NULL); 3220 g_object_notify (G_OBJECT (player), "playing"); 3221 rb_shell_player_sync_buttons (player); 3222 } 3223 3224 /** 3225 * rb_shell_player_pause: 3226 * @player: a #RBShellPlayer 3227 * @error: error return 3228 * 3229 * Pauses playback if possible, completely stopping if not. 3230 * 3231 * Return value: whether playback is not occurring (TRUE when successfully 3232 * paused/stopped or playback was not occurring). 3233 **/ 3234 3235 gboolean 3236 rb_shell_player_pause (RBShellPlayer *player, 3237 GError **error) 3238 { 3239 if (rb_player_playing (player->priv->mmplayer)) 3240 return rb_shell_player_playpause (player, FALSE, error); 3241 else 3242 return TRUE; 3243 } 3244 3245 /** 3246 * rb_shell_player_get_playing: 3247 * @player: a #RBShellPlayer 3248 * @playing: (out): playback state return 3249 * @error: error return 3250 * 3251 * Reports whether playback is occuring by setting #playing. 3252 * 3253 * Return value: %TRUE if successful 3254 **/ 3255 gboolean 3256 rb_shell_player_get_playing (RBShellPlayer *player, 3257 gboolean *playing, 3258 GError **error) 3259 { 3260 if (playing != NULL) 3261 *playing = rb_player_playing (player->priv->mmplayer); 3262 3263 return TRUE; 3264 } 3265 3266 /** 3267 * rb_shell_player_get_playing_time_string: 3268 * @player: the #RBShellPlayer 3269 * 3270 * Constructs a string showing the current playback position, 3271 * taking the time display settings into account. 3272 * 3273 * Return value: allocated playing time string 3274 */ 3275 char * 3276 rb_shell_player_get_playing_time_string (RBShellPlayer *player) 3277 { 3278 gboolean elapsed; 3279 elapsed = g_settings_get_boolean (player->priv->ui_settings, "time-display"); 3280 return rb_make_elapsed_time_string (player->priv->elapsed, 3281 rb_shell_player_get_playing_song_duration (player), 3282 elapsed); 3283 } 3284 3285 /** 3286 * rb_shell_player_get_playing_time: 3287 * @player: the #RBShellPlayer 3288 * @time: (out): returns the current playback position 3289 * @error: returns error information 3290 * 3291 * Retrieves the current playback position. Fails if 3292 * the player currently cannot provide the playback 3293 * position. 3294 * 3295 * Return value: %TRUE if successful 3296 */ 3297 gboolean 3298 rb_shell_player_get_playing_time (RBShellPlayer *player, 3299 guint *time, 3300 GError **error) 3301 { 3302 gint64 ptime; 3303 3304 ptime = rb_player_get_time (player->priv->mmplayer); 3305 if (ptime >= 0) { 3306 if (time != NULL) { 3307 *time = (guint)(ptime / RB_PLAYER_SECOND); 3308 } 3309 return TRUE; 3310 } else { 3311 g_set_error (error, 3312 RB_SHELL_PLAYER_ERROR, 3313 RB_SHELL_PLAYER_ERROR_POSITION_NOT_AVAILABLE, 3314 _("Playback position not available")); 3315 return FALSE; 3316 } 3317 } 3318 3319 /** 3320 * rb_shell_player_set_playing_time: 3321 * @player: the #RBShellPlayer 3322 * @time: the target playback position (in seconds) 3323 * @error: returns error information 3324 * 3325 * Attempts to set the playback position. Fails if the 3326 * current song is not seekable. 3327 * 3328 * Return value: %TRUE if successful 3329 */ 3330 gboolean 3331 rb_shell_player_set_playing_time (RBShellPlayer *player, 3332 guint time, 3333 GError **error) 3334 { 3335 if (rb_player_seekable (player->priv->mmplayer)) { 3336 if (player->priv->playing_entry_eos) { 3337 rb_debug ("forgetting that playing entry had EOS'd due to seek"); 3338 player->priv->playing_entry_eos = FALSE; 3339 } 3340 rb_player_set_time (player->priv->mmplayer, ((gint64) time) * RB_PLAYER_SECOND); 3341 return TRUE; 3342 } else { 3343 g_set_error (error, 3344 RB_SHELL_PLAYER_ERROR, 3345 RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE, 3346 _("Current song is not seekable")); 3347 return FALSE; 3348 } 3349 } 3350 3351 /** 3352 * rb_shell_player_seek: 3353 * @player: the #RBShellPlayer 3354 * @offset: relative seek target (in seconds) 3355 * @error: returns error information 3356 * 3357 * Seeks forwards or backwards in the current playing 3358 * song. Fails if the current song is not seekable. 3359 * 3360 * Return value: %TRUE if successful 3361 */ 3362 gboolean 3363 rb_shell_player_seek (RBShellPlayer *player, 3364 gint32 offset, 3365 GError **error) 3366 { 3367 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player), FALSE); 3368 3369 if (rb_player_seekable (player->priv->mmplayer)) { 3370 gint64 target_time = rb_player_get_time (player->priv->mmplayer) + 3371 (((gint64)offset) * RB_PLAYER_SECOND); 3372 if (target_time < 0) 3373 target_time = 0; 3374 rb_player_set_time (player->priv->mmplayer, target_time); 3375 return TRUE; 3376 } else { 3377 g_set_error (error, 3378 RB_SHELL_PLAYER_ERROR, 3379 RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE, 3380 _("Current song is not seekable")); 3381 return FALSE; 3382 } 3383 } 3384 3385 /** 3386 * rb_shell_player_get_playing_song_duration: 3387 * @player: the #RBShellPlayer 3388 * 3389 * Retrieves the duration of the current playing song. 3390 * 3391 * Return value: duration, or -1 if not playing 3392 */ 3393 long 3394 rb_shell_player_get_playing_song_duration (RBShellPlayer *player) 3395 { 3396 RhythmDBEntry *current_entry; 3397 long val; 3398 3399 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player), -1); 3400 3401 current_entry = rb_shell_player_get_playing_entry (player); 3402 3403 if (current_entry == NULL) { 3404 rb_debug ("Did not get playing entry : return -1 as length"); 3405 return -1; 3406 } 3407 3408 val = rhythmdb_entry_get_ulong (current_entry, RHYTHMDB_PROP_DURATION); 3409 3410 rhythmdb_entry_unref (current_entry); 3411 3412 return val; 3413 } 3414 3415 static void 3416 rb_shell_player_sync_with_selected_source (RBShellPlayer *player) 3417 { 3418 rb_debug ("syncing with selected source: %p", player->priv->selected_source); 3419 if (player->priv->source == NULL) 3420 { 3421 rb_debug ("no playing source, new source is %p", player->priv->selected_source); 3422 rb_shell_player_sync_with_source (player); 3423 } 3424 } 3425 3426 static gboolean 3427 do_next_idle (RBShellPlayer *player) 3428 { 3429 /* use the EOS callback, so that EOF_SOURCE_ conditions are handled properly */ 3430 rb_shell_player_handle_eos (NULL, NULL, FALSE, player); 3431 player->priv->do_next_idle_id = 0; 3432 3433 return FALSE; 3434 } 3435 3436 static gboolean 3437 do_next_not_found_idle (RBShellPlayer *player) 3438 { 3439 RhythmDBEntry *entry; 3440 entry = rb_shell_player_get_playing_entry (player); 3441 3442 do_next_idle (player); 3443 3444 if (entry != NULL) { 3445 rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_NOT_FOUND); 3446 rhythmdb_commit (player->priv->db); 3447 rhythmdb_entry_unref (entry); 3448 } 3449 3450 return FALSE; 3451 } 3452 3453 static void 3454 rb_shell_player_error (RBShellPlayer *player, 3455 gboolean async, 3456 const GError *err) 3457 { 3458 RhythmDBEntry *entry; 3459 gboolean do_next; 3460 3461 g_return_if_fail (player->priv->handling_error == FALSE); 3462 3463 player->priv->handling_error = TRUE; 3464 3465 entry = rb_shell_player_get_playing_entry (player); 3466 3467 rb_debug ("playback error while playing: %s", err->message); 3468 /* For synchronous errors the entry playback error has already been set */ 3469 if (entry && async) 3470 rb_shell_player_set_entry_playback_error (player, entry, err->message); 3471 3472 if (entry == NULL) { 3473 do_next = TRUE; 3474 } else if (err->domain == RB_PLAYER_ERROR && err->code == RB_PLAYER_ERROR_NOT_FOUND) { 3475 /* process not found errors after we've started the next track */ 3476 if (player->priv->do_next_idle_id != 0) { 3477 g_source_remove (player->priv->do_next_idle_id); 3478 } 3479 player->priv->do_next_idle_id = g_idle_add ((GSourceFunc)do_next_not_found_idle, player); 3480 do_next = FALSE; 3481 } else if (err->domain == RB_PLAYER_ERROR && err->code == RB_PLAYER_ERROR_NO_AUDIO) { 3482 3483 /* stream has completely ended */ 3484 rb_shell_player_stop (player); 3485 do_next = FALSE; 3486 } else if ((player->priv->current_playing_source != NULL) && 3487 (rb_source_handle_eos (player->priv->current_playing_source) == RB_SOURCE_EOF_RETRY)) { 3488 /* receiving an error means a broken stream or non-audio stream, so abort 3489 * unless we've got more URLs to try */ 3490 if (g_queue_is_empty (player->priv->playlist_urls)) { 3491 rb_error_dialog (NULL, 3492 _("Couldn't start playback"), 3493 "%s", (err) ? err->message : "(null)"); 3494 rb_shell_player_stop (player); 3495 do_next = FALSE; 3496 } else { 3497 rb_debug ("haven't yet exhausted the URLs from the playlist"); 3498 do_next = TRUE; 3499 } 3500 } else { 3501 do_next = TRUE; 3502 } 3503 3504 if (do_next && player->priv->do_next_idle_id == 0) { 3505 player->priv->do_next_idle_id = g_idle_add ((GSourceFunc)do_next_idle, player); 3506 } 3507 3508 player->priv->handling_error = FALSE; 3509 3510 if (entry != NULL) { 3511 rhythmdb_entry_unref (entry); 3512 } 3513 } 3514 3515 static void 3516 playing_stream_cb (RBPlayer *mmplayer, 3517 RhythmDBEntry *entry, 3518 RBShellPlayer *player) 3519 { 3520 gboolean entry_changed; 3521 3522 g_return_if_fail (entry != NULL); 3523 3524 GDK_THREADS_ENTER (); 3525 3526 entry_changed = (player->priv->playing_entry != entry); 3527 3528 /* update playing entry */ 3529 if (player->priv->playing_entry) 3530 rhythmdb_entry_unref (player->priv->playing_entry); 3531 player->priv->playing_entry = rhythmdb_entry_ref (entry); 3532 player->priv->playing_entry_eos = FALSE; 3533 3534 if (entry_changed) { 3535 const char *location; 3536 3537 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION); 3538 rb_debug ("new playing stream: %s", location); 3539 g_signal_emit (G_OBJECT (player), 3540 rb_shell_player_signals[PLAYING_SONG_CHANGED], 0, 3541 entry); 3542 g_signal_emit (G_OBJECT (player), 3543 rb_shell_player_signals[PLAYING_URI_CHANGED], 0, 3544 location); 3545 } 3546 3547 /* resync UI */ 3548 rb_shell_player_sync_with_source (player); 3549 rb_shell_player_sync_buttons (player); 3550 g_object_notify (G_OBJECT (player), "playing"); 3551 3552 if (player->priv->jump_to_playing_entry) { 3553 rb_shell_player_jump_to_current (player); 3554 player->priv->jump_to_playing_entry = FALSE; 3555 } 3556 3557 GDK_THREADS_LEAVE (); 3558 } 3559 3560 static void 3561 error_cb (RBPlayer *mmplayer, 3562 RhythmDBEntry *entry, 3563 const GError *err, 3564 gpointer data) 3565 { 3566 RBShellPlayer *player = RB_SHELL_PLAYER (data); 3567 3568 if (player->priv->handling_error) 3569 return; 3570 3571 if (player->priv->source == NULL) { 3572 rb_debug ("ignoring error (no source): %s", err->message); 3573 return; 3574 } 3575 3576 GDK_THREADS_ENTER (); 3577 3578 if (entry != player->priv->playing_entry) { 3579 rb_debug ("got error for unexpected entry %p (expected %p)", entry, player->priv->playing_entry); 3580 } else { 3581 rb_shell_player_error (player, TRUE, err); 3582 rb_debug ("exiting error hander"); 3583 } 3584 3585 GDK_THREADS_LEAVE (); 3586 } 3587 3588 static void 3589 tick_cb (RBPlayer *mmplayer, 3590 RhythmDBEntry *entry, 3591 gint64 elapsed, 3592 gint64 duration, 3593 gpointer data) 3594 { 3595 RBShellPlayer *player = RB_SHELL_PLAYER (data); 3596 gint64 remaining_check = 0; 3597 gboolean duration_from_player = TRUE; 3598 const char *uri; 3599 long elapsed_sec; 3600 3601 GDK_THREADS_ENTER (); 3602 3603 if (player->priv->playing_entry != entry) { 3604 rb_debug ("got tick for unexpected entry %p (expected %p)", entry, player->priv->playing_entry); 3605 GDK_THREADS_LEAVE (); 3606 return; 3607 } 3608 3609 /* if we aren't getting a duration value from the player, use the 3610 * value from the entry, if any. 3611 */ 3612 if (duration < 1) { 3613 duration = ((gint64)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION)) * RB_PLAYER_SECOND; 3614 duration_from_player = FALSE; 3615 } 3616 3617 uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION); 3618 rb_debug ("tick: [%s, %" G_GINT64_FORMAT ":%" G_GINT64_FORMAT "(%d)]", 3619 uri, 3620 elapsed, 3621 duration, 3622 duration_from_player); 3623 3624 if (elapsed < 0) { 3625 elapsed_sec = 0; 3626 } else { 3627 elapsed_sec = elapsed / RB_PLAYER_SECOND; 3628 } 3629 3630 if (player->priv->elapsed != elapsed_sec) { 3631 player->priv->elapsed = elapsed_sec; 3632 g_signal_emit (G_OBJECT (player), rb_shell_player_signals[ELAPSED_CHANGED], 3633 0, player->priv->elapsed); 3634 } 3635 g_signal_emit (player, rb_shell_player_signals[ELAPSED_NANO_CHANGED], 0, elapsed); 3636 3637 if (duration_from_player) { 3638 /* XXX update duration in various things? */ 3639 } 3640 3641 /* check if we should start a crossfade */ 3642 if (rb_player_multiple_open (mmplayer)) { 3643 if (player->priv->track_transition_time < PREROLL_TIME) { 3644 remaining_check = PREROLL_TIME; 3645 } else { 3646 remaining_check = player->priv->track_transition_time; 3647 } 3648 } 3649 3650 /* 3651 * just pretending we got an EOS will do exactly what we want 3652 * here. if we don't want to crossfade, we'll just leave the stream 3653 * prerolled until the current stream really ends. 3654 */ 3655 if (remaining_check > 0 && 3656 duration > 0 && 3657 elapsed > 0 && 3658 ((duration - elapsed) <= remaining_check)) { 3659 rb_debug ("%" G_GINT64_FORMAT " ns remaining in stream %s; need %" G_GINT64_FORMAT " for transition", 3660 duration - elapsed, 3661 uri, 3662 remaining_check); 3663 rb_shell_player_handle_eos_unlocked (player, entry, FALSE); 3664 } 3665 3666 GDK_THREADS_LEAVE (); 3667 } 3668 3669 typedef struct { 3670 RhythmDBEntry *entry; 3671 RBShellPlayer *player; 3672 } MissingPluginRetryData; 3673 3674 static void 3675 missing_plugins_retry_cb (gpointer inst, 3676 gboolean retry, 3677 MissingPluginRetryData *retry_data) 3678 { 3679 GError *error = NULL; 3680 if (retry == FALSE) { 3681 /* next? or stop playback? */ 3682 rb_debug ("not retrying playback; stopping player"); 3683 rb_shell_player_stop (retry_data->player); 3684 return; 3685 } 3686 3687 rb_debug ("retrying playback"); 3688 rb_shell_player_set_playing_entry (retry_data->player, 3689 retry_data->entry, 3690 FALSE, FALSE, 3691 &error); 3692 if (error != NULL) { 3693 rb_shell_player_error (retry_data->player, FALSE, error); 3694 g_clear_error (&error); 3695 } 3696 } 3697 3698 static void 3699 missing_plugins_retry_cleanup (MissingPluginRetryData *retry) 3700 { 3701 retry->player->priv->handling_error = FALSE; 3702 3703 g_object_unref (retry->player); 3704 rhythmdb_entry_unref (retry->entry); 3705 g_free (retry); 3706 } 3707 3708 3709 static void 3710 missing_plugins_cb (RBPlayer *player, 3711 RhythmDBEntry *entry, 3712 const char **details, 3713 const char **descriptions, 3714 RBShellPlayer *sp) 3715 { 3716 gboolean processing; 3717 GClosure *retry; 3718 MissingPluginRetryData *retry_data; 3719 3720 retry_data = g_new0 (MissingPluginRetryData, 1); 3721 retry_data->player = g_object_ref (sp); 3722 retry_data->entry = rhythmdb_entry_ref (entry); 3723 3724 retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb, 3725 retry_data, 3726 (GClosureNotify) missing_plugins_retry_cleanup); 3727 g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN); 3728 processing = rb_missing_plugins_install (details, FALSE, retry); 3729 if (processing) { 3730 /* don't handle any further errors */ 3731 sp->priv->handling_error = TRUE; 3732 3733 /* probably specify the URI here.. */ 3734 rb_debug ("stopping player while processing missing plugins"); 3735 rb_player_close (retry_data->player->priv->mmplayer, NULL, NULL); 3736 } else { 3737 rb_debug ("not processing missing plugins; simulating EOS"); 3738 rb_shell_player_handle_eos (NULL, NULL, FALSE, retry_data->player); 3739 } 3740 3741 g_closure_sink (retry); 3742 } 3743 3744 static void 3745 player_image_cb (RBPlayer *player, 3746 RhythmDBEntry *entry, 3747 GdkPixbuf *image, 3748 RBShellPlayer *shell_player) 3749 { 3750 RBExtDB *store; 3751 RBExtDBKey *key; 3752 const char *artist; 3753 GValue v = G_VALUE_INIT; 3754 3755 if (image == NULL) 3756 return; 3757 3758 artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST); 3759 if (artist == NULL || artist[0] == '\0' || strcmp (artist, _("Unknown")) == 0) { 3760 artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST); 3761 if (artist == NULL || artist[0] == '\0' || strcmp (artist, _("Unknown")) == 0) { 3762 return; 3763 } 3764 } 3765 3766 store = rb_ext_db_new ("album-art"); 3767 3768 key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM)); 3769 rb_ext_db_key_add_field (key, "artist", artist); 3770 3771 g_value_init (&v, GDK_TYPE_PIXBUF); 3772 g_value_set_object (&v, image); 3773 rb_ext_db_store (store, key, RB_EXT_DB_SOURCE_EMBEDDED, &v); 3774 g_value_unset (&v); 3775 3776 g_object_unref (store); 3777 rb_ext_db_key_free (key); 3778 } 3779 3780 /** 3781 * rb_shell_player_get_playing_path: 3782 * @player: the #RBShellPlayer 3783 * @path: (out callee-allocates) (transfer full): returns the URI of the current playing entry 3784 * @error: returns error information 3785 * 3786 * Retrieves the URI of the current playing entry. The 3787 * caller must not free the returned string. 3788 * 3789 * Return value: %TRUE if successful 3790 */ 3791 gboolean 3792 rb_shell_player_get_playing_path (RBShellPlayer *player, 3793 const gchar **path, 3794 GError **error) 3795 { 3796 RhythmDBEntry *entry; 3797 3798 entry = rb_shell_player_get_playing_entry (player); 3799 if (entry != NULL) { 3800 *path = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION); 3801 } else { 3802 *path = NULL; 3803 } 3804 3805 if (entry != NULL) { 3806 rhythmdb_entry_unref (entry); 3807 } 3808 3809 return TRUE; 3810 } 3811 3812 static gboolean 3813 _idle_unblock_signal_cb (gpointer data) 3814 { 3815 RBShellPlayer *player = (RBShellPlayer *)data; 3816 GtkAction *action; 3817 gboolean playing; 3818 3819 GDK_THREADS_ENTER (); 3820 3821 player->priv->unblock_play_id = 0; 3822 3823 action = gtk_action_group_get_action (player->priv->actiongroup, 3824 "ControlPlay"); 3825 3826 /* sync the active state of the action again */ 3827 g_object_get (player, "playing", &playing, NULL); 3828 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), playing); 3829 3830 g_signal_handlers_unblock_by_func (action, rb_shell_player_cmd_play, player); 3831 3832 GDK_THREADS_LEAVE (); 3833 return FALSE; 3834 } 3835 3836 static void 3837 rb_shell_player_playing_changed_cb (RBShellPlayer *player, 3838 GParamSpec *arg1, 3839 gpointer user_data) 3840 { 3841 GtkAction *action; 3842 gboolean playing; 3843 char *tooltip; 3844 3845 g_object_get (player, "playing", &playing, NULL); 3846 action = gtk_action_group_get_action (player->priv->actiongroup, 3847 "ControlPlay"); 3848 if (playing) { 3849 if (rb_source_can_pause (player->priv->source)) { 3850 tooltip = g_strdup (_("Pause playback")); 3851 } else { 3852 tooltip = g_strdup (_("Stop playback")); 3853 } 3854 } else { 3855 tooltip = g_strdup (_("Start playback")); 3856 } 3857 g_object_set (action, "tooltip", tooltip, NULL); 3858 g_free (tooltip); 3859 3860 /* block the signal, so that it doesn't get stuck by triggering recursively, 3861 * and don't unblock it until whatever else is happening has finished. 3862 * don't block it again if it's already blocked, though. 3863 */ 3864 if (player->priv->unblock_play_id == 0) { 3865 g_signal_handlers_block_by_func (action, rb_shell_player_cmd_play, player); 3866 } 3867 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), playing); 3868 3869 if (player->priv->unblock_play_id == 0) { 3870 player->priv->unblock_play_id = g_idle_add (_idle_unblock_signal_cb, player); 3871 } 3872 } 3873 3874 /* This should really be standard. */ 3875 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } 3876 3877 GType 3878 rb_shell_player_error_get_type (void) 3879 { 3880 static GType etype = 0; 3881 3882 if (etype == 0) { 3883 static const GEnumValue values[] = { 3884 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_PLAYLIST_PARSE_ERROR, "playlist-parse-failed"), 3885 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST, "end-of-playlist"), 3886 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_PLAYING, "not-playing"), 3887 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE, "not-seekable"), 3888 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_POSITION_NOT_AVAILABLE, "position-not-available"), 3889 { 0, 0, 0 } 3890 }; 3891 3892 etype = g_enum_register_static ("RBShellPlayerError", values); 3893 } 3894 3895 return etype; 3896 } 3897 3898 static void 3899 _play_order_description_free (RBPlayOrderDescription *order) 3900 { 3901 g_free (order->name); 3902 g_free (order->description); 3903 g_free (order); 3904 } 3905 3906 /** 3907 * rb_play_order_new: 3908 * @porder_name: Play order type name 3909 * @player: #RBShellPlayer instance to attach to 3910 * 3911 * Creates a new #RBPlayOrder of the specified type. 3912 * 3913 * Returns: #RBPlayOrder instance 3914 **/ 3915 3916 #define DEFAULT_PLAY_ORDER "linear" 3917 3918 static RBPlayOrder * 3919 rb_play_order_new (RBShellPlayer *player, const char* porder_name) 3920 { 3921 RBPlayOrderDescription *order; 3922 3923 g_return_val_if_fail (porder_name != NULL, NULL); 3924 g_return_val_if_fail (player != NULL, NULL); 3925 3926 order = g_hash_table_lookup (player->priv->play_orders, porder_name); 3927 3928 if (order == NULL) { 3929 g_warning ("Unknown value \"%s\" in GSettings key \"play-order" 3930 "\". Using %s play order.", porder_name, DEFAULT_PLAY_ORDER); 3931 order = g_hash_table_lookup (player->priv->play_orders, DEFAULT_PLAY_ORDER); 3932 } 3933 3934 return RB_PLAY_ORDER (g_object_new (order->order_type, "player", player, NULL)); 3935 } 3936 3937 /** 3938 * rb_shell_player_add_play_order: 3939 * @player: the #RBShellPlayer 3940 * @name: name of the new play order 3941 * @description: description of the new play order 3942 * @order_type: the #GType of the play order class 3943 * @hidden: if %TRUE, don't display the play order in the UI 3944 * 3945 * Adds a new play order to the set of available play orders. 3946 */ 3947 void 3948 rb_shell_player_add_play_order (RBShellPlayer *player, const char *name, 3949 const char *description, GType order_type, gboolean hidden) 3950 { 3951 RBPlayOrderDescription *order; 3952 3953 g_return_if_fail (g_type_is_a (order_type, RB_TYPE_PLAY_ORDER)); 3954 3955 order = g_new0(RBPlayOrderDescription, 1); 3956 order->name = g_strdup (name); 3957 order->description = g_strdup (description); 3958 order->order_type = order_type; 3959 order->is_in_dropdown = !hidden; 3960 3961 g_hash_table_insert (player->priv->play_orders, order->name, order); 3962 } 3963 3964 /** 3965 * rb_shell_player_remove_play_order: 3966 * @player: the #RBShellPlayer 3967 * @name: name of the play order to remove 3968 * 3969 * Removes a play order previously added with #rb_shell_player_add_play_order 3970 * from the set of available play orders. 3971 */ 3972 void 3973 rb_shell_player_remove_play_order (RBShellPlayer *player, const char *name) 3974 { 3975 g_hash_table_remove (player->priv->play_orders, name); 3976 }