1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of main playback logic object
5 * Copyright (C) 2002, 2003 Jorn Baayen <jorn@nl.linux.org>
6 * Copyright (C) 2002,2003 Colin Walters <walters@debian.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <glib/gi18n.h>
34 #include <glade/glade.h>
35 #include <libgnomevfs/gnome-vfs-utils.h>
36 #include <libgnomevfs/gnome-vfs-uri.h>
40 #include <X11/XF86keysym.h>
42 #endif /* HAVE_MMKEYS */
44 #include "rb-property-view.h"
45 #include "rb-shell-player.h"
46 #include "rb-stock-icons.h"
47 #include "rb-glade-helpers.h"
48 #include "rb-file-helpers.h"
49 #include "rb-cut-and-paste-code.h"
50 #include "rb-dialog.h"
51 #include "rb-preferences.h"
53 #include "rb-player.h"
54 #include "rb-header.h"
55 #include "totem-pl-parser.h"
56 #include "rb-metadata.h"
57 #include "rb-library-source.h"
58 #include "eel-gconf-extensions.h"
60 #include "rb-play-order.h"
61 #include "rb-statusbar.h"
62 #include "rb-playlist-source.h"
63 #include "rb-play-queue-source.h"
65 #include "rb-podcast-manager.h"
66 #include "rb-marshal.h"
68 #ifdef HAVE_XIDLE_EXTENSION
69 #include <X11/extensions/xidle.h>
70 #endif /* HAVE_XIDLE_EXTENSION */
72 static const char* const state_to_play_order
[2][2] =
73 {{"linear", "linear-loop"},
74 {"shuffle", "random-by-age-and-rating"}};
76 static void rb_shell_player_class_init (RBShellPlayerClass
*klass
);
77 static void rb_shell_player_init (RBShellPlayer
*shell_player
);
78 static GObject
*rb_shell_player_constructor (GType type
, guint n_construct_properties
,
79 GObjectConstructParam
*construct_properties
);
80 static void rb_shell_player_finalize (GObject
*object
);
81 static void rb_shell_player_set_property (GObject
*object
,
85 static void rb_shell_player_get_property (GObject
*object
,
90 static void rb_shell_player_cmd_previous (GtkAction
*action
,
91 RBShellPlayer
*player
);
92 static void rb_shell_player_cmd_play (GtkAction
*action
,
93 RBShellPlayer
*player
);
94 static void rb_shell_player_cmd_next (GtkAction
*action
,
95 RBShellPlayer
*player
);
96 static void rb_shell_player_cmd_volume_up (GtkAction
*action
,
97 RBShellPlayer
*player
);
98 static void rb_shell_player_cmd_volume_down (GtkAction
*action
,
99 RBShellPlayer
*player
);
100 static void rb_shell_player_shuffle_changed_cb (GtkAction
*action
,
101 RBShellPlayer
*player
);
102 static void rb_shell_player_repeat_changed_cb (GtkAction
*action
,
103 RBShellPlayer
*player
);
104 static void rb_shell_player_view_song_position_slider_changed_cb (GtkAction
*action
,
105 RBShellPlayer
*player
);
106 static void rb_shell_player_set_playing_source_internal (RBShellPlayer
*player
,
108 gboolean sync_entry_view
);
109 static void rb_shell_player_sync_with_source (RBShellPlayer
*player
);
110 static void rb_shell_player_sync_with_selected_source (RBShellPlayer
*player
);
111 static void rb_shell_player_entry_changed_cb (RhythmDB
*db
,
112 RhythmDBEntry
*entry
,
114 RBShellPlayer
*player
);
116 static void rb_shell_player_entry_activated_cb (RBEntryView
*view
,
117 RhythmDBEntry
*entry
,
118 RBShellPlayer
*playa
);
119 static void rb_shell_player_property_row_activated_cb (RBPropertyView
*view
,
121 RBShellPlayer
*playa
);
122 static void rb_shell_player_sync_volume (RBShellPlayer
*player
, gboolean notify
);
123 static void rb_shell_player_sync_replaygain (RBShellPlayer
*player
,
124 RhythmDBEntry
*entry
);
125 static void tick_cb (RBPlayer
*player
, long elapsed
, gpointer data
);
126 static void error_cb (RBPlayer
*player
, const GError
*err
, gpointer data
);
127 static void rb_shell_player_error (RBShellPlayer
*player
, gboolean async
, const GError
*err
);
129 static void rb_shell_player_set_play_order (RBShellPlayer
*player
,
130 const gchar
*new_val
);
131 static void rb_shell_player_play_order_update_cb (RBPlayOrder
*porder
,
133 gboolean has_previous
,
134 RBShellPlayer
*player
);
136 static void rb_shell_player_sync_play_order (RBShellPlayer
*player
);
137 static void rb_shell_player_sync_control_state (RBShellPlayer
*player
);
138 static void rb_shell_player_sync_song_position_slider_visibility (RBShellPlayer
*player
);
139 static void rb_shell_player_sync_buttons (RBShellPlayer
*player
);
141 static void gconf_play_order_changed (GConfClient
*client
,guint cnxn_id
,
142 GConfEntry
*entry
, RBShellPlayer
*player
);
143 static void gconf_song_position_slider_visibility_changed (GConfClient
*client
,guint cnxn_id
,
144 GConfEntry
*entry
, RBShellPlayer
*player
);
145 static void rb_shell_player_playing_changed_cb (RBShellPlayer
*player
,
148 static void rb_shell_player_extra_metadata_cb (RhythmDB
*db
,
149 RhythmDBEntry
*entry
,
152 RBShellPlayer
*player
);
155 static gboolean
rb_shell_player_jump_to_current_idle (RBShellPlayer
*player
);
158 static void grab_mmkey (int key_code
, GdkWindow
*root
);
159 static GdkFilterReturn
filter_mmkeys (GdkXEvent
*xevent
,
162 static void rb_shell_player_init_mmkeys (RBShellPlayer
*shell_player
);
163 #endif /* HAVE_MMKEYS */
165 #define CONF_STATE CONF_PREFIX "/state"
167 struct RBShellPlayerPrivate
171 gboolean syncing_state
;
174 RBSource
*selected_source
;
176 RBPlayQueueSource
*queue_source
;
177 RBSource
*current_playing_source
;
182 GtkUIManager
*ui_manager
;
183 GtkActionGroup
*actiongroup
;
185 gboolean handling_error
;
193 RBPlayOrder
*play_order
;
194 RBPlayOrder
*queue_play_order
;
196 GQueue
*playlist_urls
;
198 RBHeader
*header_widget
;
199 RBStatusbar
*statusbar_widget
;
201 guint gconf_play_order_id
;
202 guint gconf_song_position_slider_visibility_id
;
207 guint do_next_idle_id
;
210 #define RB_SHELL_PLAYER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL_PLAYER, RBShellPlayerPrivate))
225 PROP_PLAYING_FROM_QUEUE
,
231 WINDOW_TITLE_CHANGED
,
233 PLAYING_SOURCE_CHANGED
,
235 PLAYING_SONG_CHANGED
,
237 PLAYING_SONG_PROPERTY_CHANGED
,
241 static GtkActionEntry rb_shell_player_actions
[] =
243 { "ControlPrevious", GTK_STOCK_MEDIA_PREVIOUS
, N_("P_revious"), "<alt>Left",
244 N_("Start playing the previous song"),
245 G_CALLBACK (rb_shell_player_cmd_previous
) },
246 { "ControlNext", GTK_STOCK_MEDIA_NEXT
, N_("_Next"), "<alt>Right",
247 N_("Start playing the next song"),
248 G_CALLBACK (rb_shell_player_cmd_next
) },
249 { "ControlVolumeUp", NULL
, N_("_Increase Volume"), "<control>Up",
250 N_("Increase playback volume"),
251 G_CALLBACK (rb_shell_player_cmd_volume_up
) },
252 { "ControlVolumeDown", NULL
, N_("_Decrease Volume"), "<control>Down",
253 N_("Decrease playback volume"),
254 G_CALLBACK (rb_shell_player_cmd_volume_down
) },
256 static guint rb_shell_player_n_actions
= G_N_ELEMENTS (rb_shell_player_actions
);
258 static GtkToggleActionEntry rb_shell_player_toggle_entries
[] =
260 { "ControlPlay", GTK_STOCK_MEDIA_PLAY
, N_("_Play"), "<control>space",
261 N_("Start playback"),
262 G_CALLBACK (rb_shell_player_cmd_play
) },
263 { "ControlShuffle", GNOME_MEDIA_SHUFFLE
, N_("Sh_uffle"), "<control>U",
264 N_("Play songs in a random order"),
265 G_CALLBACK (rb_shell_player_shuffle_changed_cb
) },
266 { "ControlRepeat", GNOME_MEDIA_REPEAT
, N_("_Repeat"), "<control>R",
267 N_("Play first song again after all songs are played"),
268 G_CALLBACK (rb_shell_player_repeat_changed_cb
) },
269 { "ViewSongPositionSlider", NULL
, N_("_Song Position Slider"), NULL
,
270 N_("Change the visibility of the song position slider"),
271 G_CALLBACK (rb_shell_player_view_song_position_slider_changed_cb
), TRUE
},
273 static guint rb_shell_player_n_toggle_entries
= G_N_ELEMENTS (rb_shell_player_toggle_entries
);
275 static guint rb_shell_player_signals
[LAST_SIGNAL
] = { 0 };
277 G_DEFINE_TYPE (RBShellPlayer
, rb_shell_player
, GTK_TYPE_HBOX
)
280 rb_shell_player_class_init (RBShellPlayerClass
*klass
)
282 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
284 object_class
->finalize
= rb_shell_player_finalize
;
285 object_class
->constructor
= rb_shell_player_constructor
;
287 object_class
->set_property
= rb_shell_player_set_property
;
288 object_class
->get_property
= rb_shell_player_get_property
;
290 g_object_class_install_property (object_class
,
292 g_param_spec_object ("source",
298 g_object_class_install_property (object_class
,
300 g_param_spec_object ("ui-manager",
302 "GtkUIManager object",
304 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
306 g_object_class_install_property (object_class
,
308 g_param_spec_object ("db",
312 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
314 g_object_class_install_property (object_class
,
316 g_param_spec_object ("action-group",
318 "GtkActionGroup object",
319 GTK_TYPE_ACTION_GROUP
,
320 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
322 g_object_class_install_property (object_class
,
324 g_param_spec_object ("queue-source",
326 "RBPlaylistSource object",
327 RB_TYPE_PLAYLIST_SOURCE
,
330 g_object_class_install_property (object_class
,
332 g_param_spec_boolean ("queue-only",
334 "Activation only adds to queue",
338 g_object_class_install_property (object_class
,
339 PROP_PLAYING_FROM_QUEUE
,
340 g_param_spec_boolean ("playing-from-queue",
341 "Playing from queue",
342 "Whether playing from the play queue or not",
346 g_object_class_install_property (object_class
,
348 g_param_spec_object ("player",
354 g_object_class_install_property (object_class
,
356 g_param_spec_string ("play-order",
358 "What play order to use",
361 g_object_class_install_property (object_class
,
363 g_param_spec_boolean ("playing",
365 "Whether Rhythmbox is currently playing",
369 g_object_class_install_property (object_class
,
371 g_param_spec_float ("volume",
373 "Current playback volume",
377 g_object_class_install_property (object_class
,
379 g_param_spec_object ("statusbar",
381 "RBStatusbar object",
385 rb_shell_player_signals
[WINDOW_TITLE_CHANGED
] =
386 g_signal_new ("window_title_changed",
387 G_OBJECT_CLASS_TYPE (object_class
),
389 G_STRUCT_OFFSET (RBShellPlayerClass
, window_title_changed
),
391 g_cclosure_marshal_VOID__STRING
,
396 rb_shell_player_signals
[ELAPSED_CHANGED
] =
397 g_signal_new ("elapsed_changed",
398 G_OBJECT_CLASS_TYPE (object_class
),
400 G_STRUCT_OFFSET (RBShellPlayerClass
, elapsed_changed
),
402 g_cclosure_marshal_VOID__UINT
,
407 rb_shell_player_signals
[PLAYING_SOURCE_CHANGED
] =
408 g_signal_new ("playing-source-changed",
409 G_OBJECT_CLASS_TYPE (object_class
),
411 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_source_changed
),
413 g_cclosure_marshal_VOID__OBJECT
,
418 rb_shell_player_signals
[PLAYING_CHANGED
] =
419 g_signal_new ("playing-changed",
420 G_OBJECT_CLASS_TYPE (object_class
),
422 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_changed
),
424 g_cclosure_marshal_VOID__BOOLEAN
,
429 rb_shell_player_signals
[PLAYING_SONG_CHANGED
] =
430 g_signal_new ("playing-song-changed",
431 G_OBJECT_CLASS_TYPE (object_class
),
433 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_song_changed
),
435 g_cclosure_marshal_VOID__BOXED
,
438 RHYTHMDB_TYPE_ENTRY
);
440 rb_shell_player_signals
[PLAYING_URI_CHANGED
] =
441 g_signal_new ("playing-uri-changed",
442 G_OBJECT_CLASS_TYPE (object_class
),
444 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_uri_changed
),
446 g_cclosure_marshal_VOID__STRING
,
451 rb_shell_player_signals
[PLAYING_SONG_PROPERTY_CHANGED
] =
452 g_signal_new ("playing-song-property-changed",
453 G_OBJECT_CLASS_TYPE (object_class
),
455 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_song_property_changed
),
457 rb_marshal_VOID__STRING_STRING_POINTER_POINTER
,
460 G_TYPE_STRING
, G_TYPE_STRING
,
461 G_TYPE_VALUE
, G_TYPE_VALUE
);
463 g_type_class_add_private (klass
, sizeof (RBShellPlayerPrivate
));
467 rb_shell_player_constructor (GType type
,
468 guint n_construct_properties
,
469 GObjectConstructParam
*construct_properties
)
471 RBShellPlayer
*player
;
472 RBShellPlayerClass
*klass
;
475 klass
= RB_SHELL_PLAYER_CLASS (g_type_class_peek (RB_TYPE_SHELL_PLAYER
));
477 player
= RB_SHELL_PLAYER (G_OBJECT_CLASS (rb_shell_player_parent_class
)->
478 constructor (type
, n_construct_properties
, construct_properties
));
480 player
->priv
->header_widget
= rb_header_new (player
, player
->priv
->db
);
481 gtk_widget_show (GTK_WIDGET (player
->priv
->header_widget
));
482 gtk_box_pack_start (GTK_BOX (player
), GTK_WIDGET (player
->priv
->header_widget
), TRUE
, TRUE
, 0);
484 gtk_action_group_add_actions (player
->priv
->actiongroup
,
485 rb_shell_player_actions
,
486 rb_shell_player_n_actions
,
488 gtk_action_group_add_toggle_actions (player
->priv
->actiongroup
,
489 rb_shell_player_toggle_entries
,
490 rb_shell_player_n_toggle_entries
,
493 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
495 g_object_set (action
, "is-important", TRUE
, NULL
);
497 player
->priv
->syncing_state
= TRUE
;
498 rb_shell_player_set_playing_source (player
, NULL
);
499 rb_shell_player_sync_play_order (player
);
500 rb_shell_player_sync_control_state (player
);
501 rb_shell_player_sync_volume (player
, FALSE
);
502 player
->priv
->syncing_state
= FALSE
;
504 rb_shell_player_sync_song_position_slider_visibility (player
);
506 g_signal_connect (G_OBJECT (player
),
508 G_CALLBACK (rb_shell_player_playing_changed_cb
),
511 return G_OBJECT (player
);
515 volume_pre_unmount_cb (GnomeVFSVolumeMonitor
*monitor
,
516 GnomeVFSVolume
*volume
,
517 RBShellPlayer
*player
)
519 gchar
*uri_mount_point
;
520 gchar
*volume_mount_point
;
521 RhythmDBEntry
*entry
;
525 rb_shell_player_get_playing (player
, &playing
, NULL
);
530 entry
= rb_shell_player_get_playing_entry (player
);
532 /* At startup for example, playing path can be NULL */
536 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
537 uri_mount_point
= rb_uri_get_mount_point (uri
);
538 volume_mount_point
= gnome_vfs_volume_get_activation_uri (volume
);
540 if (uri_mount_point
&& volume_mount_point
&&
541 (strcmp (uri_mount_point
, volume_mount_point
) == 0)) {
542 rb_shell_player_stop (player
);
544 g_free (uri_mount_point
);
545 g_free (volume_mount_point
);
548 rhythmdb_entry_unref (entry
);
553 reemit_playing_signal (RBShellPlayer
*player
,
557 g_signal_emit (player
, rb_shell_player_signals
[PLAYING_CHANGED
], 0,
558 rb_player_playing (player
->priv
->mmplayer
));
562 notify_playing_idle (RBShellPlayer
*player
)
564 GDK_THREADS_ENTER ();
565 rb_debug ("emitting playing notification: %d", rb_player_playing (player
->priv
->mmplayer
));
566 g_object_notify (G_OBJECT (player
), "playing");
567 rb_shell_player_sync_buttons (player
);
569 GDK_THREADS_LEAVE ();
574 rb_shell_player_open_playlist_url (RBShellPlayer
*player
,
575 const char *location
)
577 GError
*error
= NULL
;
579 rb_debug ("playing stream url %s", location
);
580 rb_player_open (player
->priv
->mmplayer
, location
, &error
);
582 rb_player_play (player
->priv
->mmplayer
, &error
);
585 GDK_THREADS_ENTER ();
586 rb_shell_player_error (player
, TRUE
, error
);
587 g_error_free (error
);
588 GDK_THREADS_LEAVE ();
590 g_idle_add ((GSourceFunc
) notify_playing_idle
, player
);
594 rb_shell_player_handle_eos_unlocked (RBShellPlayer
*player
)
596 RhythmDBEntry
*entry
;
599 source
= player
->priv
->current_playing_source
;
602 if (source
== NULL
) {
606 entry
= rb_shell_player_get_playing_entry (player
);
608 switch (rb_source_handle_eos (source
)) {
609 case RB_SOURCE_EOF_ERROR
:
610 rb_error_dialog (NULL
, _("Stream error"),
611 _("Unexpected end of stream!"));
612 rb_shell_player_set_playing_source (player
, NULL
);
614 case RB_SOURCE_EOF_STOP
:
615 rb_shell_player_set_playing_source (player
, NULL
);
617 case RB_SOURCE_EOF_RETRY
: {
621 g_get_current_time (¤t
);
622 diff
= current
.tv_sec
- player
->priv
->last_retry
.tv_sec
;
623 player
->priv
->last_retry
= current
;
625 if (rb_source_try_playlist (source
) &&
626 !g_queue_is_empty (player
->priv
->playlist_urls
)) {
627 char *location
= g_queue_pop_head (player
->priv
->playlist_urls
);
628 rb_debug ("trying next radio stream url: %s", location
);
630 rb_shell_player_open_playlist_url (player
, location
);
636 rb_debug ("Last retry was less than 4 seconds ago...aborting retry playback");
637 rb_shell_player_set_playing_source (player
, NULL
);
639 rb_shell_player_play_entry (player
, entry
, NULL
);
643 case RB_SOURCE_EOF_NEXT
:
645 GError
*error
= NULL
;
647 if (!rb_shell_player_do_next (player
, &error
)) {
648 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
649 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
650 g_warning ("Unhandled error: %s", error
->message
);
651 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
652 rb_shell_player_set_playing_source (player
, NULL
);
658 if (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
) == NULL
) {
659 rb_debug ("updating play statistics");
660 rb_source_update_play_statistics (source
,
666 rhythmdb_entry_unref (entry
);
671 rb_shell_player_handle_eos (RBShellPlayer
*player
)
673 rb_debug ("handling eos!");
675 GDK_THREADS_ENTER ();
677 rb_shell_player_handle_eos_unlocked (player
);
679 GDK_THREADS_LEAVE ();
683 rb_shell_player_init (RBShellPlayer
*player
)
685 GError
*error
= NULL
;
687 player
->priv
= RB_SHELL_PLAYER_GET_PRIVATE (player
);
689 player
->priv
->mmplayer
= rb_player_new (&error
);
692 dialog
= gtk_message_dialog_new (NULL
, GTK_DIALOG_MODAL
,
695 _("Failed to create the player: %s"),
697 gtk_dialog_run (GTK_DIALOG (dialog
));
701 gtk_box_set_spacing (GTK_BOX (player
), 12);
702 gtk_container_set_border_width (GTK_CONTAINER (player
), 3);
704 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
706 G_CALLBACK (rb_shell_player_handle_eos
),
707 player
, G_CONNECT_SWAPPED
);
709 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
711 G_CALLBACK (tick_cb
),
714 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
716 G_CALLBACK (error_cb
),
719 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
720 "volume-pre-unmount",
721 G_CALLBACK (volume_pre_unmount_cb
),
724 player
->priv
->gconf_play_order_id
=
725 eel_gconf_notification_add (CONF_STATE_PLAY_ORDER
,
726 (GConfClientNotifyFunc
)gconf_play_order_changed
,
729 player
->priv
->volume
= eel_gconf_get_float (CONF_STATE_VOLUME
);
731 g_signal_connect (player
, "notify::playing",
732 G_CALLBACK (reemit_playing_signal
), NULL
);
734 player
->priv
->gconf_song_position_slider_visibility_id
=
735 eel_gconf_notification_add (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
,
736 (GConfClientNotifyFunc
) gconf_song_position_slider_visibility_changed
,
740 /* Enable Multimedia Keys */
741 rb_shell_player_init_mmkeys (player
);
742 #endif /* HAVE_MMKEYS */
746 rb_shell_player_set_source_internal (RBShellPlayer
*player
,
749 if (player
->priv
->selected_source
!= NULL
) {
750 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->selected_source
);
751 GList
*property_views
= rb_source_get_property_views (player
->priv
->selected_source
);
754 g_signal_handlers_disconnect_by_func (G_OBJECT (songs
),
755 G_CALLBACK (rb_shell_player_entry_activated_cb
),
759 for (; property_views
; property_views
= property_views
->next
) {
760 g_signal_handlers_disconnect_by_func (G_OBJECT (property_views
->data
),
761 G_CALLBACK (rb_shell_player_property_row_activated_cb
),
765 g_list_free (property_views
);
768 player
->priv
->selected_source
= source
;
770 rb_debug ("selected source %p", player
->priv
->selected_source
);
772 rb_shell_player_sync_with_selected_source (player
);
773 rb_shell_player_sync_buttons (player
);
775 if (player
->priv
->selected_source
!= NULL
) {
776 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->selected_source
);
777 GList
*property_views
= rb_source_get_property_views (player
->priv
->selected_source
);
780 g_signal_connect_object (G_OBJECT (songs
),
782 G_CALLBACK (rb_shell_player_entry_activated_cb
),
784 for (; property_views
; property_views
= property_views
->next
)
785 g_signal_connect_object (G_OBJECT (property_views
->data
),
786 "property-activated",
787 G_CALLBACK (rb_shell_player_property_row_activated_cb
),
790 g_list_free (property_views
);
793 /* If we're not playing, change the play order's view of the current source;
794 * if the selected source is the queue, however, set it to NULL so it'll stop
795 * once the queue is empty.
797 if (player
->priv
->current_playing_source
== NULL
) {
798 RBSource
*source
= player
->priv
->selected_source
;
799 if (source
== RB_SOURCE (player
->priv
->queue_source
))
802 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
807 rb_shell_player_set_db_internal (RBShellPlayer
*player
,
810 if (player
->priv
->db
!= NULL
) {
811 g_signal_handlers_disconnect_by_func (player
->priv
->db
,
812 G_CALLBACK (rb_shell_player_entry_changed_cb
),
814 g_signal_handlers_disconnect_by_func (player
->priv
->db
,
815 G_CALLBACK (rb_shell_player_extra_metadata_cb
),
819 player
->priv
->db
= db
;
821 if (player
->priv
->db
!= NULL
) {
822 /* Listen for changed entries to update metadata display */
823 g_signal_connect_object (G_OBJECT (player
->priv
->db
),
825 G_CALLBACK (rb_shell_player_entry_changed_cb
),
827 g_signal_connect_object (G_OBJECT (player
->priv
->db
),
828 "entry_extra_metadata_notify",
829 G_CALLBACK (rb_shell_player_extra_metadata_cb
),
835 rb_shell_player_set_queue_source_internal (RBShellPlayer
*player
,
836 RBPlayQueueSource
*source
)
838 if (player
->priv
->queue_source
!= NULL
) {
839 RBEntryView
*sidebar
;
841 g_object_get (player
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
842 g_signal_handlers_disconnect_by_func (sidebar
,
843 G_CALLBACK (rb_shell_player_entry_activated_cb
),
845 g_object_unref (sidebar
);
847 if (player
->priv
->queue_play_order
!= NULL
) {
848 g_signal_handlers_disconnect_by_func (player
->priv
->queue_play_order
,
849 G_CALLBACK (rb_shell_player_play_order_update_cb
),
851 g_object_unref (player
->priv
->queue_play_order
);
856 player
->priv
->queue_source
= source
;
858 if (player
->priv
->queue_source
!= NULL
) {
859 RBEntryView
*sidebar
;
861 player
->priv
->queue_play_order
= rb_play_order_new ("queue", player
);
862 g_signal_connect_object (G_OBJECT (player
->priv
->queue_play_order
),
863 "have_next_previous_changed",
864 G_CALLBACK (rb_shell_player_play_order_update_cb
),
866 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
869 rb_play_order_playing_source_changed (player
->priv
->queue_play_order
,
870 RB_SOURCE (player
->priv
->queue_source
));
872 g_object_get (player
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
873 g_signal_connect_object (G_OBJECT (sidebar
),
875 G_CALLBACK (rb_shell_player_entry_activated_cb
),
877 g_object_unref (sidebar
);
882 rb_shell_player_finalize (GObject
*object
)
884 RBShellPlayer
*player
;
886 g_return_if_fail (object
!= NULL
);
887 g_return_if_fail (RB_IS_SHELL_PLAYER (object
));
889 player
= RB_SHELL_PLAYER (object
);
891 g_return_if_fail (player
->priv
!= NULL
);
893 eel_gconf_notification_remove (player
->priv
->gconf_play_order_id
);
895 eel_gconf_set_float (CONF_STATE_VOLUME
, player
->priv
->volume
);
897 g_object_unref (player
->priv
->mmplayer
);
898 g_object_unref (player
->priv
->play_order
);
899 g_object_unref (player
->priv
->queue_play_order
);
901 G_OBJECT_CLASS (rb_shell_player_parent_class
)->finalize (object
);
905 rb_shell_player_set_property (GObject
*object
,
910 RBShellPlayer
*player
= RB_SHELL_PLAYER (object
);
914 rb_shell_player_set_source_internal (player
, g_value_get_object (value
));
916 case PROP_UI_MANAGER
:
917 player
->priv
->ui_manager
= g_value_get_object (value
);
920 rb_shell_player_set_db_internal (player
, g_value_get_object (value
));
922 case PROP_ACTION_GROUP
:
923 player
->priv
->actiongroup
= g_value_get_object (value
);
925 case PROP_PLAY_ORDER
:
926 eel_gconf_set_string (CONF_STATE_PLAY_ORDER
,
927 g_value_get_string (value
));
930 player
->priv
->volume
= g_value_get_float (value
);
931 rb_shell_player_sync_volume (player
, FALSE
);
934 player
->priv
->statusbar_widget
= g_value_get_object (value
);
936 case PROP_QUEUE_SOURCE
:
937 rb_shell_player_set_queue_source_internal (player
, g_value_get_object (value
));
939 case PROP_QUEUE_ONLY
:
940 player
->priv
->queue_only
= g_value_get_boolean (value
);
943 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
949 rb_shell_player_get_property (GObject
*object
,
954 RBShellPlayer
*player
= RB_SHELL_PLAYER (object
);
958 g_value_set_object (value
, player
->priv
->selected_source
);
960 case PROP_UI_MANAGER
:
961 g_value_set_object (value
, player
->priv
->ui_manager
);
964 g_value_set_object (value
, player
->priv
->db
);
966 case PROP_ACTION_GROUP
:
967 g_value_set_object (value
, player
->priv
->actiongroup
);
969 case PROP_PLAY_ORDER
:
971 char *play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
973 play_order
= g_strdup ("linear");
974 g_value_set_string_take_ownership (value
, play_order
);
978 g_value_set_boolean (value
, rb_player_playing (player
->priv
->mmplayer
));
981 g_value_set_float (value
, player
->priv
->volume
);
984 g_value_set_object (value
, player
->priv
->statusbar_widget
);
986 case PROP_QUEUE_SOURCE
:
987 g_value_set_object (value
, player
->priv
->queue_source
);
989 case PROP_QUEUE_ONLY
:
990 g_value_set_boolean (value
, player
->priv
->queue_only
);
992 case PROP_PLAYING_FROM_QUEUE
:
993 g_value_set_boolean (value
, player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
));
996 g_value_set_object (value
, player
->priv
->mmplayer
);
999 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1005 rb_shell_player_error_quark (void)
1007 static GQuark quark
= 0;
1009 quark
= g_quark_from_static_string ("rb_shell_player_error");
1015 rb_shell_player_set_selected_source (RBShellPlayer
*player
,
1018 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
1019 g_return_if_fail (RB_IS_SOURCE (source
));
1021 g_object_set (G_OBJECT (player
),
1027 rb_shell_player_get_playing_source (RBShellPlayer
*player
)
1029 return player
->priv
->current_playing_source
;
1033 rb_shell_player_get_active_source (RBShellPlayer
*player
)
1035 return player
->priv
->source
;
1039 rb_shell_player_new (RhythmDB
*db
,
1041 GtkActionGroup
*actiongroup
)
1043 return g_object_new (RB_TYPE_SHELL_PLAYER
,
1045 "action-group", actiongroup
,
1051 rb_shell_player_get_playing_entry (RBShellPlayer
*player
)
1053 RBPlayOrder
*porder
;
1054 RhythmDBEntry
*entry
;
1056 if (player
->priv
->current_playing_source
== NULL
)
1059 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
1060 porder
= player
->priv
->queue_play_order
;
1062 porder
= player
->priv
->play_order
;
1064 if (porder
== NULL
) {
1068 entry
= rb_play_order_get_playing_entry (porder
);
1074 RBShellPlayer
*player
;
1076 } OpenLocationThreadData
;
1079 playlist_entry_cb (TotemPlParser
*playlist
,
1083 RBShellPlayer
*player
)
1085 rb_debug ("adding stream url %s", uri
);
1086 g_queue_push_tail (player
->priv
->playlist_urls
, g_strdup (uri
));
1090 open_location_thread (OpenLocationThreadData
*data
)
1092 TotemPlParser
*playlist
;
1093 TotemPlParserResult playlist_result
;
1095 playlist
= totem_pl_parser_new ();
1096 g_signal_connect_data (G_OBJECT (playlist
), "entry",
1097 G_CALLBACK (playlist_entry_cb
),
1098 data
->player
, NULL
, 0);
1099 totem_pl_parser_add_ignored_mimetype (playlist
, "x-directory/normal");
1101 playlist_result
= totem_pl_parser_parse (playlist
, data
->location
, FALSE
);
1102 g_object_unref (playlist
);
1104 if (playlist_result
== TOTEM_PL_PARSER_RESULT_SUCCESS
) {
1105 if (g_queue_is_empty (data
->player
->priv
->playlist_urls
)) {
1106 GError
*error
= g_error_new (RB_SHELL_PLAYER_ERROR
,
1107 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1108 _("Playlist was empty"));
1109 GDK_THREADS_ENTER ();
1110 rb_shell_player_error (data
->player
, TRUE
, error
);
1111 g_error_free (error
);
1112 GDK_THREADS_LEAVE ();
1116 location
= g_queue_pop_head (data
->player
->priv
->playlist_urls
);
1117 rb_debug ("playing first stream url %s", data
->location
);
1118 rb_shell_player_open_playlist_url (data
->player
, location
);
1122 /* if we can't parse it as a playlist, just try playing it */
1123 rb_debug ("playlist parser failed, playing %s directly", data
->location
);
1124 rb_shell_player_open_playlist_url (data
->player
, data
->location
);
1132 rb_shell_player_open_location (RBShellPlayer
*player
,
1133 const char *location
,
1137 gboolean was_playing
;
1139 unescaped
= gnome_vfs_unescape_string_for_display (location
);
1140 rb_debug ("Opening %s...", unescaped
);
1143 was_playing
= rb_player_playing (player
->priv
->mmplayer
);
1145 if (rb_source_try_playlist (player
->priv
->source
)) {
1146 OpenLocationThreadData
*data
;
1148 data
= g_new0 (OpenLocationThreadData
, 1);
1149 data
->player
= player
;
1151 /* dispose of any existing playlist urls */
1152 if (player
->priv
->playlist_urls
) {
1153 g_queue_foreach (player
->priv
->playlist_urls
,
1156 g_queue_free (player
->priv
->playlist_urls
);
1157 player
->priv
->playlist_urls
= NULL
;
1159 player
->priv
->playlist_urls
= g_queue_new ();
1161 /* add http:// as a prefix, if it doesn't have a URI scheme */
1162 if (strstr (location
, "://"))
1163 data
->location
= g_strdup (location
);
1165 data
->location
= g_strconcat ("http://", location
, NULL
);
1167 g_thread_create ((GThreadFunc
)open_location_thread
, data
, FALSE
, NULL
);
1170 if (!rb_player_open (player
->priv
->mmplayer
, location
, error
))
1173 if (!rb_player_play (player
->priv
->mmplayer
, error
))
1176 g_object_notify (G_OBJECT (player
), "playing");
1183 rb_shell_player_open_entry (RBShellPlayer
*player
,
1184 RhythmDBEntry
*entry
,
1190 location
= rhythmdb_entry_get_playback_uri (entry
);
1191 if (location
== NULL
)
1194 result
= rb_shell_player_open_location (player
, location
, error
);
1201 * rb_shell_player_play:
1202 * @player: a #RBShellPlayer
1203 * @error: error return
1205 * Starts playback, if it is not already playing.
1207 * @return: whether playback is now occurring (TRUE when successfully started
1208 * or already playing).
1211 rb_shell_player_play (RBShellPlayer
*player
,
1216 if (player
->priv
->current_playing_source
== NULL
)
1219 if (rb_player_playing (player
->priv
->mmplayer
))
1222 if (!rb_player_play (player
->priv
->mmplayer
, error
))
1225 songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1227 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PLAYING
);
1229 rb_shell_player_sync_with_source (player
);
1230 rb_shell_player_sync_buttons (player
);
1236 rb_shell_player_set_entry_playback_error (RBShellPlayer
*player
,
1237 RhythmDBEntry
*entry
,
1240 GValue value
= { 0, };
1242 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
1244 g_value_init (&value
, G_TYPE_STRING
);
1245 g_value_set_string (&value
, message
);
1246 rhythmdb_entry_set (player
->priv
->db
,
1248 RHYTHMDB_PROP_PLAYBACK_ERROR
,
1250 g_value_unset (&value
);
1251 rhythmdb_commit (player
->priv
->db
);
1255 do_next_idle (RBShellPlayer
*player
)
1257 /* use the EOS callback, so that EOF_SOURCE_ conditions are handled properly */
1258 rb_shell_player_handle_eos (player
);
1259 player
->priv
->do_next_idle_id
= 0;
1265 rb_shell_player_set_playing_entry (RBShellPlayer
*player
,
1266 RhythmDBEntry
*entry
,
1267 gboolean out_of_order
,
1270 GError
*tmp_error
= NULL
;
1271 const char *location
;
1274 g_return_val_if_fail (player
->priv
->current_playing_source
!= NULL
, TRUE
);
1275 g_return_val_if_fail (entry
!= NULL
, TRUE
);
1278 RBPlayOrder
*porder
;
1279 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
1280 porder
= player
->priv
->queue_play_order
;
1282 porder
= player
->priv
->play_order
;
1283 rb_play_order_set_playing_entry (porder
, entry
);
1286 if (!rb_shell_player_open_entry (player
, entry
, &tmp_error
))
1288 rb_shell_player_sync_replaygain (player
, entry
);
1290 rb_debug ("Success!");
1291 /* clear error on successful playback */
1292 g_value_init (&val
, G_TYPE_STRING
);
1293 g_value_set_string (&val
, NULL
);
1294 rhythmdb_entry_set (player
->priv
->db
, entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
1295 g_value_unset (&val
);
1297 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
1298 g_signal_emit (G_OBJECT (player
),
1299 rb_shell_player_signals
[PLAYING_SONG_CHANGED
], 0,
1301 g_signal_emit (G_OBJECT (player
),
1302 rb_shell_player_signals
[PLAYING_URI_CHANGED
], 0,
1305 rb_shell_player_sync_with_source (player
);
1306 rb_shell_player_sync_buttons (player
);
1310 /* Ignore errors, shutdown the player */
1311 rb_player_close (player
->priv
->mmplayer
, NULL
);
1312 if (tmp_error
== NULL
)
1313 tmp_error
= g_error_new (RB_SHELL_PLAYER_ERROR
,
1314 RB_SHELL_PLAYER_ERROR_NOT_PLAYING
,
1315 "Problem occurred without error being set. "
1316 "This is a bug in Rhythmbox or GStreamer.");
1317 /* Mark this song as failed */
1318 rb_shell_player_set_entry_playback_error (player
, entry
, tmp_error
->message
);
1319 g_propagate_error (error
, tmp_error
);
1321 rb_shell_player_sync_with_source (player
);
1322 rb_shell_player_sync_buttons (player
);
1323 g_object_notify (G_OBJECT (player
), "playing");
1329 gconf_play_order_changed (GConfClient
*client
,
1332 RBShellPlayer
*player
)
1334 rb_debug ("gconf play order changed");
1335 player
->priv
->syncing_state
= TRUE
;
1336 rb_shell_player_sync_play_order (player
);
1337 rb_shell_player_sync_buttons (player
);
1338 rb_shell_player_sync_control_state (player
);
1339 g_object_notify (G_OBJECT (player
), "play-order");
1340 player
->priv
->syncing_state
= FALSE
;
1344 rb_shell_player_get_playback_state (RBShellPlayer
*player
,
1351 play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
1353 g_warning (CONF_STATE_PLAY_ORDER
" gconf key not found!");
1357 for (i
= 0; i
< G_N_ELEMENTS(state_to_play_order
); i
++)
1358 for (j
= 0; j
< G_N_ELEMENTS(state_to_play_order
[0]); j
++)
1359 if (!strcmp (play_order
, state_to_play_order
[i
][j
]))
1362 g_free (play_order
);
1368 g_free (play_order
);
1373 rb_shell_player_set_play_order (RBShellPlayer
*player
,
1374 const gchar
*new_val
)
1378 g_object_get (player
, "play-order", &old_val
, NULL
);
1379 if (strcmp (old_val
, new_val
) != 0) {
1380 /* The notify signal will be emitted by the gconf notifier */
1381 eel_gconf_set_string (CONF_STATE_PLAY_ORDER
, new_val
);
1387 rb_shell_player_set_playback_state (RBShellPlayer
*player
,
1391 const char *neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
1392 rb_shell_player_set_play_order (player
, neworder
);
1396 rb_shell_player_sync_play_order (RBShellPlayer
*player
)
1398 char *new_play_order
;
1399 RhythmDBEntry
*playing_entry
= NULL
;
1402 new_play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
1403 if (new_play_order
== NULL
) {
1404 g_warning (CONF_STATE_PLAY_ORDER
" gconf key not found!");
1405 new_play_order
= g_strdup ("linear");
1408 if (player
->priv
->play_order
!= NULL
) {
1409 playing_entry
= rb_play_order_get_playing_entry (player
->priv
->play_order
);
1410 g_signal_handlers_disconnect_by_func (player
->priv
->play_order
,
1411 G_CALLBACK (rb_shell_player_play_order_update_cb
),
1413 g_object_unref (player
->priv
->play_order
);
1416 player
->priv
->play_order
= rb_play_order_new (new_play_order
, player
);
1417 g_signal_connect_object (player
->priv
->play_order
,
1418 "have_next_previous_changed",
1419 G_CALLBACK (rb_shell_player_play_order_update_cb
),
1421 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
1425 source
= player
->priv
->current_playing_source
;
1426 if (source
== NULL
) {
1427 source
= player
->priv
->selected_source
;
1429 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
1431 if (playing_entry
!= NULL
) {
1432 rb_play_order_set_playing_entry (player
->priv
->play_order
, playing_entry
);
1433 rhythmdb_entry_unref (playing_entry
);
1436 g_free (new_play_order
);
1440 rb_shell_player_play_order_update_cb (RBPlayOrder
*porder
,
1442 gboolean has_previous
,
1443 RBShellPlayer
*player
)
1445 /* we cannot depend on the values of has_next, has_previous or porder
1446 * since this can be called for the main porder, queue porder, etc
1448 gboolean have_next
= FALSE
;
1449 gboolean have_previous
= FALSE
;
1451 RhythmDBEntry
*entry
;
1453 entry
= rb_shell_player_get_playing_entry (player
);
1454 if (entry
!= NULL
) {
1456 have_previous
= TRUE
;
1457 rhythmdb_entry_unref (entry
);
1459 if (player
->priv
->current_playing_source
&&
1460 (rb_source_handle_eos (player
->priv
->current_playing_source
) == RB_SOURCE_EOF_NEXT
)) {
1461 have_next
= rb_play_order_has_next (player
->priv
->play_order
);
1462 have_previous
= rb_play_order_has_previous (player
->priv
->play_order
);
1464 if (player
->priv
->queue_play_order
) {
1465 have_next
|= rb_play_order_has_next (player
->priv
->queue_play_order
);
1466 have_previous
|= rb_play_order_has_previous (player
->priv
->queue_play_order
);
1470 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1472 g_object_set (action
, "sensitive", have_previous
, NULL
);
1473 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1475 g_object_set (action
, "sensitive", have_next
, NULL
);
1479 rb_shell_player_jump_to_current_idle (RBShellPlayer
*player
)
1481 GDK_THREADS_ENTER ();
1482 rb_shell_player_jump_to_current (player
);
1483 GDK_THREADS_LEAVE ();
1488 rb_shell_player_sync_song_position_slider_visibility (RBShellPlayer
*player
)
1493 visible
= !eel_gconf_get_boolean (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
);
1495 rb_header_set_show_position_slider (player
->priv
->header_widget
,
1498 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1499 "ViewSongPositionSlider");
1500 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
),
1505 rb_shell_player_jump_to_current (RBShellPlayer
*player
)
1508 RhythmDBEntry
*entry
;
1511 source
= player
->priv
->current_playing_source
? player
->priv
->current_playing_source
:
1512 player
->priv
->selected_source
;
1514 songs
= rb_source_get_entry_view (source
);
1515 entry
= rb_shell_player_get_playing_entry (player
);
1516 if (songs
!= NULL
) {
1517 if (entry
!= NULL
) {
1518 rb_entry_view_scroll_to_entry (songs
, entry
);
1519 rb_entry_view_select_entry (songs
, entry
);
1521 rb_entry_view_select_none (songs
);
1525 if (entry
!= NULL
) {
1526 rhythmdb_entry_unref (entry
);
1531 swap_playing_source (RBShellPlayer
*player
,
1532 RBSource
*new_source
)
1534 if (player
->priv
->current_playing_source
!= NULL
) {
1535 RBEntryView
*old_songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1537 rb_entry_view_set_state (old_songs
, RB_ENTRY_VIEW_NOT_PLAYING
);
1539 if (new_source
!= NULL
) {
1540 RBEntryView
*new_songs
= rb_source_get_entry_view (new_source
);
1543 rb_entry_view_set_state (new_songs
, RB_ENTRY_VIEW_PLAYING
);
1544 rb_shell_player_set_playing_source (player
, new_source
);
1550 rb_shell_player_do_previous (RBShellPlayer
*player
,
1553 RhythmDBEntry
*entry
= NULL
;
1554 RBSource
*new_source
;
1556 if (player
->priv
->current_playing_source
== NULL
) {
1558 RB_SHELL_PLAYER_ERROR
,
1559 RB_SHELL_PLAYER_ERROR_NOT_PLAYING
,
1560 _("Not currently playing"));
1564 rb_debug ("going to previous");
1566 if (player
->priv
->queue_play_order
) {
1567 entry
= rb_play_order_get_previous (player
->priv
->queue_play_order
);
1568 if (entry
!= NULL
) {
1569 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1570 rb_play_order_go_previous (player
->priv
->queue_play_order
);
1574 if (entry
== NULL
) {
1575 new_source
= player
->priv
->source
;
1576 entry
= rb_play_order_get_previous (player
->priv
->play_order
);
1578 rb_play_order_go_previous (player
->priv
->play_order
);
1581 if (entry
!= NULL
) {
1582 rb_debug ("previous song found, doing previous");
1583 if (new_source
!= player
->priv
->current_playing_source
)
1584 swap_playing_source (player
, new_source
);
1586 if (!rb_shell_player_set_playing_entry (player
, entry
, FALSE
, error
)) {
1587 rhythmdb_entry_unref (entry
);
1591 rb_shell_player_jump_to_current (player
);
1592 rhythmdb_entry_unref (entry
);
1594 rb_debug ("no previous song found, signaling error");
1596 RB_SHELL_PLAYER_ERROR
,
1597 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1598 _("No previous song"));
1599 rb_shell_player_set_playing_source (player
, NULL
);
1607 rb_shell_player_do_next (RBShellPlayer
*player
,
1610 RBSource
*new_source
= NULL
;
1611 RhythmDBEntry
*entry
= NULL
;
1614 if (player
->priv
->source
== NULL
)
1617 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
)) {
1618 /* Look for another entry in the queue, and fall back to the playing
1619 * source if there isn't one. Always call _get_next on the queue
1620 * so the current entry is removed.
1622 entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1623 rb_play_order_go_next (player
->priv
->queue_play_order
);
1625 if (entry
!= NULL
) {
1626 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1628 /* If we haven't played anything from the playing source yet,
1629 * _get_playing_entry will return NULL, so we'll have to advance
1630 * the play order to get an entry to play.
1632 entry
= rb_play_order_get_playing_entry (player
->priv
->play_order
);
1633 if (entry
== NULL
) {
1634 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1635 rb_play_order_go_next (player
->priv
->play_order
);
1638 new_source
= player
->priv
->source
;
1641 /* Advance the play order, and then let the queue override it. */
1642 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1643 if (entry
!= NULL
) {
1644 new_source
= player
->priv
->source
;
1645 rb_play_order_go_next (player
->priv
->play_order
);
1648 if (player
->priv
->queue_play_order
) {
1649 RhythmDBEntry
*queue_entry
;
1651 queue_entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1652 if (queue_entry
!= NULL
) {
1653 if (entry
!= NULL
) {
1654 rhythmdb_entry_unref (entry
);
1656 entry
= queue_entry
;
1657 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1658 rb_play_order_go_next (player
->priv
->queue_play_order
);
1663 /* play the new entry */
1664 if (entry
!= NULL
) {
1665 /* if the entry view containing the playing entry changed, update it */
1666 if (new_source
!= player
->priv
->current_playing_source
)
1667 swap_playing_source (player
, new_source
);
1669 if (!rb_shell_player_set_playing_entry (player
, entry
, FALSE
, error
))
1673 RB_SHELL_PLAYER_ERROR
,
1674 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1676 rb_debug ("No next entry, stopping playback");
1677 rb_shell_player_set_playing_source (player
, NULL
);
1678 rb_play_order_set_playing_entry (player
->priv
->play_order
, NULL
);
1679 g_object_notify (G_OBJECT (player
), "playing");
1683 g_idle_add ((GSourceFunc
)rb_shell_player_jump_to_current_idle
, player
);
1685 if (entry
!= NULL
) {
1686 rhythmdb_entry_unref (entry
);
1693 rb_shell_player_do_previous_or_seek (RBShellPlayer
*player
,
1696 rb_debug ("previous");
1697 /* If we're in the first 3 seconds go to the previous song,
1698 * else restart the current one.
1700 if (player
->priv
->current_playing_source
!= NULL
1701 && rb_source_can_pause (player
->priv
->source
)
1702 && rb_player_get_time (player
->priv
->mmplayer
) > 3) {
1704 /* see if there's anything to go back to */
1705 gboolean have_previous
;
1706 have_previous
= rb_play_order_has_previous (player
->priv
->play_order
);
1707 if (player
->priv
->queue_play_order
)
1708 have_previous
|= rb_play_order_has_previous (player
->priv
->queue_play_order
);
1710 if (have_previous
) {
1711 rb_debug ("after 3 second previous, restarting song");
1712 rb_player_set_time (player
->priv
->mmplayer
, 0);
1713 rb_header_sync_time (player
->priv
->header_widget
);
1718 return rb_shell_player_do_previous (player
, error
);
1722 rb_shell_player_cmd_previous (GtkAction
*action
,
1723 RBShellPlayer
*player
)
1725 GError
*error
= NULL
;
1727 if (!rb_shell_player_do_previous_or_seek (player
, &error
)) {
1728 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
1729 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1730 g_warning ("cmd_previous: Unhandled error: %s", error
->message
);
1731 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1732 rb_shell_player_set_playing_source (player
, NULL
);
1737 rb_shell_player_cmd_next (GtkAction
*action
,
1738 RBShellPlayer
*player
)
1740 GError
*error
= NULL
;
1742 if (!rb_shell_player_do_next (player
, &error
)) {
1743 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
1744 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1745 g_warning ("cmd_next: Unhandled error: %s", error
->message
);
1746 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1747 rb_shell_player_set_playing_source (player
, NULL
);
1752 rb_shell_player_play_entry (RBShellPlayer
*player
,
1753 RhythmDBEntry
*entry
,
1756 GError
*error
= NULL
;
1759 source
= player
->priv
->selected_source
;
1760 rb_shell_player_set_playing_source (player
, source
);
1762 if (!rb_shell_player_set_playing_entry (player
, entry
, TRUE
, &error
)) {
1763 rb_shell_player_error (player
, FALSE
, error
);
1764 g_clear_error (&error
);
1769 rb_shell_player_cmd_volume_up (GtkAction
*action
,
1770 RBShellPlayer
*player
)
1772 rb_shell_player_set_volume_relative (player
, 0.1, NULL
);
1776 rb_shell_player_cmd_volume_down (GtkAction
*action
,
1777 RBShellPlayer
*player
)
1779 rb_shell_player_set_volume_relative (player
, -0.1, NULL
);
1783 rb_shell_player_cmd_play (GtkAction
*action
,
1784 RBShellPlayer
*player
)
1786 GError
*error
= NULL
;
1788 if (!rb_shell_player_playpause (player
, FALSE
, &error
))
1789 rb_error_dialog (NULL
,
1790 _("Couldn't start playback"),
1791 "%s", (error
) ? error
->message
: "(null)");
1792 g_clear_error (&error
);
1795 /* unused parameter can't be removed without breaking dbus interface compatibility */
1797 rb_shell_player_playpause (RBShellPlayer
*player
,
1804 rb_debug ("doing playpause");
1806 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player
), TRUE
);
1810 if (rb_player_playing (player
->priv
->mmplayer
)) {
1811 if (player
->priv
->source
== NULL
) {
1812 rb_debug ("playing source is already NULL");
1813 } else if (rb_source_can_pause (player
->priv
->source
)) {
1814 rb_debug ("pausing mm player");
1815 rb_player_pause (player
->priv
->mmplayer
);
1816 songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1818 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PAUSED
);
1820 rb_debug ("setting playing source to NULL");
1821 rb_shell_player_set_playing_source (player
, NULL
);
1824 RhythmDBEntry
*entry
;
1825 RBSource
*new_source
;
1826 gboolean out_of_order
= FALSE
;
1828 if (player
->priv
->source
== NULL
) {
1829 /* no current stream, pull one in from the currently
1830 * selected source */
1831 rb_debug ("no playing source, using selected source");
1832 rb_shell_player_set_playing_source (player
, player
->priv
->selected_source
);
1834 new_source
= player
->priv
->current_playing_source
;
1836 entry
= rb_shell_player_get_playing_entry (player
);
1837 if (entry
== NULL
) {
1838 /* queue takes precedence over selection */
1839 if (player
->priv
->queue_play_order
) {
1840 entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1841 if (entry
!= NULL
) {
1842 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1843 rb_play_order_go_next (player
->priv
->queue_play_order
);
1847 /* selection takes precedence over first item in play order */
1848 if (entry
== NULL
) {
1849 GList
*selection
= NULL
;
1851 songs
= rb_source_get_entry_view (player
->priv
->source
);
1853 selection
= rb_entry_view_get_selected_entries (songs
);
1855 if (selection
!= NULL
) {
1856 rb_debug ("choosing first selected entry");
1857 entry
= (RhythmDBEntry
*) selection
->data
;
1859 out_of_order
= TRUE
;
1861 g_list_free (selection
);
1865 /* play order is last */
1866 if (entry
== NULL
) {
1867 rb_debug ("getting entry from play order");
1868 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1870 rb_play_order_go_next (player
->priv
->play_order
);
1873 if (entry
!= NULL
) {
1874 /* if the entry view containing the playing entry changed, update it */
1875 if (new_source
!= player
->priv
->current_playing_source
)
1876 swap_playing_source (player
, new_source
);
1878 if (!rb_shell_player_set_playing_entry (player
, entry
, out_of_order
, error
))
1880 rb_shell_player_jump_to_current (player
);
1883 if (!rb_shell_player_play (player
, error
)) {
1884 rb_shell_player_set_playing_source (player
, NULL
);
1889 if (entry
!= NULL
) {
1890 rhythmdb_entry_unref (entry
);
1894 rb_shell_player_sync_with_source (player
);
1895 rb_shell_player_sync_buttons (player
);
1896 g_object_notify (G_OBJECT (player
), "playing");
1902 rb_shell_player_sync_control_state (RBShellPlayer
*player
)
1904 gboolean shuffle
, repeat
;
1906 rb_debug ("syncing control state");
1908 if (!rb_shell_player_get_playback_state (player
, &shuffle
,
1912 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1914 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), shuffle
);
1915 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1917 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), repeat
);
1921 rb_shell_player_sync_volume (RBShellPlayer
*player
,
1925 RhythmDBEntry
*entry
;
1927 if (player
->priv
->volume
<= 0.0){
1928 player
->priv
->volume
= 0.0;
1929 } else if (player
->priv
->volume
>= 1.0){
1930 player
->priv
->volume
= 1.0;
1933 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1935 g_object_set (G_OBJECT (action
), "sensitive", player
->priv
->volume
< 0.9999, NULL
);
1937 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1938 "ControlVolumeDown");
1939 g_object_set (G_OBJECT (action
), "sensitive", player
->priv
->volume
> 0.0001, NULL
);
1941 rb_player_set_volume (player
->priv
->mmplayer
,
1942 player
->priv
->mute
? 0.0 : player
->priv
->volume
);
1944 eel_gconf_set_float (CONF_STATE_VOLUME
, player
->priv
->volume
);
1947 entry
= rb_shell_player_get_playing_entry (player
);
1948 rb_shell_player_sync_replaygain (player
, entry
);
1949 if (entry
!= NULL
) {
1950 rhythmdb_entry_unref (entry
);
1954 g_object_notify (G_OBJECT (player
), "volume");
1958 rb_shell_player_toggle_mute (RBShellPlayer
*player
)
1960 player
->priv
->mute
= !player
->priv
->mute
;
1961 rb_shell_player_sync_volume (player
, FALSE
);
1965 rb_shell_player_sync_replaygain (RBShellPlayer
*player
,
1966 RhythmDBEntry
*entry
)
1968 double entry_track_gain
= 0;
1969 double entry_track_peak
= 0;
1970 double entry_album_gain
= 0;
1971 double entry_album_peak
= 0;
1973 if (entry
!= NULL
) {
1974 entry_track_gain
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_TRACK_GAIN
);
1975 entry_track_peak
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_TRACK_PEAK
);
1976 entry_album_gain
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_ALBUM_GAIN
);
1977 entry_album_peak
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_ALBUM_PEAK
);
1980 rb_player_set_replaygain (player
->priv
->mmplayer
, entry_track_gain
,
1981 entry_track_peak
, entry_album_gain
, entry_album_peak
);
1985 rb_shell_player_set_volume (RBShellPlayer
*player
,
1989 player
->priv
->volume
= volume
;
1990 rb_shell_player_sync_volume (player
, TRUE
);
1995 rb_shell_player_set_volume_relative (RBShellPlayer
*player
,
1999 /* rb_shell_player_sync_volume does clipping */
2000 player
->priv
->volume
+= delta
;
2001 rb_shell_player_sync_volume (player
, TRUE
);
2006 rb_shell_player_get_volume (RBShellPlayer
*player
,
2010 *volume
= player
->priv
->volume
;
2015 rb_shell_player_set_mute (RBShellPlayer
*player
,
2019 player
->priv
->mute
= mute
;
2020 rb_shell_player_sync_volume (player
, FALSE
);
2025 rb_shell_player_get_mute (RBShellPlayer
*player
,
2029 *mute
= player
->priv
->mute
;
2034 gconf_song_position_slider_visibility_changed (GConfClient
*client
,
2037 RBShellPlayer
*player
)
2039 rb_debug ("song position slider visibility visibility changed");
2040 rb_shell_player_sync_song_position_slider_visibility (player
);
2044 rb_shell_player_shuffle_changed_cb (GtkAction
*action
,
2045 RBShellPlayer
*player
)
2047 const char *neworder
;
2048 gboolean shuffle
= FALSE
;
2049 gboolean repeat
= FALSE
;
2051 if (player
->priv
->syncing_state
)
2054 rb_debug ("shuffle changed");
2056 rb_shell_player_get_playback_state (player
, &shuffle
, &repeat
);
2059 neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
2060 rb_shell_player_set_play_order (player
, neworder
);
2064 rb_shell_player_repeat_changed_cb (GtkAction
*action
,
2065 RBShellPlayer
*player
)
2067 const char *neworder
;
2068 gboolean shuffle
= FALSE
;
2069 gboolean repeat
= FALSE
;
2070 rb_debug ("repeat changed");
2072 if (player
->priv
->syncing_state
)
2075 rb_shell_player_get_playback_state (player
, &shuffle
, &repeat
);
2078 neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
2079 rb_shell_player_set_play_order (player
, neworder
);
2083 rb_shell_player_view_song_position_slider_changed_cb (GtkAction
*action
,
2084 RBShellPlayer
*player
)
2086 eel_gconf_set_boolean (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
,
2087 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)));
2091 rb_shell_player_entry_activated_cb (RBEntryView
*view
,
2092 RhythmDBEntry
*entry
,
2093 RBShellPlayer
*playa
)
2095 gboolean was_from_queue
= FALSE
;
2096 RhythmDBEntry
*prev_entry
= NULL
;
2097 GError
*error
= NULL
;
2098 gboolean source_set
= FALSE
;
2101 g_return_if_fail (entry
!= NULL
);
2103 rb_debug ("got entry %p activated", entry
);
2105 /* don't play hidden entries */
2106 if (rhythmdb_entry_get_boolean (entry
, RHYTHMDB_PROP_HIDDEN
))
2109 /* skip entries with no playback uri */
2110 playback_uri
= rhythmdb_entry_get_playback_uri (entry
);
2111 if (playback_uri
== NULL
) {
2114 g_free (playback_uri
);
2116 /* figure out where the previous entry came from */
2117 if ((playa
->priv
->queue_source
!= NULL
) &&
2118 (playa
->priv
->current_playing_source
== RB_SOURCE (playa
->priv
->queue_source
))) {
2119 prev_entry
= rb_shell_player_get_playing_entry (playa
);
2120 was_from_queue
= TRUE
;
2123 if (playa
->priv
->queue_source
) {
2124 RBEntryView
*queue_sidebar
;
2126 g_object_get (playa
->priv
->queue_source
, "sidebar", &queue_sidebar
, NULL
);
2128 if (view
== queue_sidebar
|| view
== rb_source_get_entry_view (RB_SOURCE (playa
->priv
->queue_source
))) {
2130 /* fall back to the current selected source once the queue is empty */
2131 if (view
== queue_sidebar
&& playa
->priv
->source
== NULL
) {
2132 rb_play_order_playing_source_changed (playa
->priv
->play_order
,
2133 playa
->priv
->selected_source
);
2134 playa
->priv
->source
= playa
->priv
->selected_source
;
2137 /* queue entry activated: move it to the start of the queue */
2138 rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (playa
->priv
->queue_source
), entry
, 0);
2139 rb_shell_player_set_playing_source (playa
, RB_SOURCE (playa
->priv
->queue_source
));
2141 /* since we just moved the entry, we should give it focus.
2142 * just calling rb_shell_player_jump_to_current here
2143 * looks terribly ugly, though. */
2144 g_idle_add ((GSourceFunc
)rb_shell_player_jump_to_current_idle
, playa
);
2145 was_from_queue
= FALSE
;
2148 if (playa
->priv
->queue_only
) {
2149 rb_source_add_to_queue (playa
->priv
->selected_source
,
2150 RB_SOURCE (playa
->priv
->queue_source
));
2151 rb_shell_player_set_playing_source (playa
, RB_SOURCE (playa
->priv
->queue_source
));
2156 g_object_unref (queue_sidebar
);
2159 /* bail out if queue only */
2160 if (playa
->priv
->queue_only
) {
2165 rb_shell_player_set_playing_source (playa
, playa
->priv
->selected_source
);
2169 if (!rb_shell_player_set_playing_entry (playa
, entry
, TRUE
, &error
)) {
2170 rb_shell_player_error (playa
, FALSE
, error
);
2171 g_clear_error (&error
);
2174 /* if we were previously playing from the queue, clear its playing entry,
2175 * so we'll start again from the start.
2177 if (was_from_queue
&& prev_entry
!= NULL
) {
2178 rb_play_order_set_playing_entry (playa
->priv
->queue_play_order
, NULL
);
2181 if (prev_entry
!= NULL
) {
2182 rhythmdb_entry_unref (prev_entry
);
2187 rb_shell_player_property_row_activated_cb (RBPropertyView
*view
,
2189 RBShellPlayer
*player
)
2191 RhythmDBEntry
*entry
= NULL
;
2192 GError
*error
= NULL
;
2194 rb_debug ("got property activated");
2196 rb_shell_player_set_playing_source (player
, player
->priv
->selected_source
);
2198 /* RHYTHMDBFIXME - do we need to wait here until the query is finished?
2199 * in theory, yes, but in practice the query is started when the row is
2200 * selected (on the first click when doubleclicking, or when using the
2201 * keyboard to select then activate) and is pretty much always done by
2202 * the time we get in here.
2205 entry
= rb_play_order_get_next (player
->priv
->play_order
);
2206 if (entry
== NULL
) {
2210 rb_play_order_go_next (player
->priv
->play_order
);
2212 if (!rb_shell_player_set_playing_entry (player
, entry
, TRUE
, &error
)) {
2213 rb_shell_player_error (player
, FALSE
, error
);
2214 g_clear_error (&error
);
2217 if (entry
!= NULL
) {
2218 rhythmdb_entry_unref (entry
);
2223 rb_shell_player_entry_changed_cb (RhythmDB
*db
,
2224 RhythmDBEntry
*entry
,
2226 RBShellPlayer
*player
)
2229 gboolean synced
= FALSE
;
2230 const char *location
;
2231 RhythmDBEntry
*playing_entry
;
2233 playing_entry
= rb_shell_player_get_playing_entry (player
);
2235 /* We try to update only if the changed entry is currently playing */
2236 if (entry
!= playing_entry
) {
2237 if (playing_entry
!= NULL
) {
2238 rhythmdb_entry_unref (playing_entry
);
2243 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
2244 for (t
= changes
; t
; t
= t
->next
) {
2245 RhythmDBEntryChange
*change
= t
->data
;
2247 /* update UI if the artist, title or album has changed */
2248 switch (change
->prop
) {
2249 case RHYTHMDB_PROP_TITLE
:
2250 case RHYTHMDB_PROP_ARTIST
:
2251 case RHYTHMDB_PROP_ALBUM
:
2253 rb_shell_player_sync_with_source (player
);
2261 /* emit dbus signals for changes with easily marshallable types */
2262 switch (rhythmdb_get_property_type (db
, change
->prop
)) {
2264 case G_TYPE_BOOLEAN
:
2268 g_signal_emit (G_OBJECT (player
),
2269 rb_shell_player_signals
[PLAYING_SONG_PROPERTY_CHANGED
], 0,
2271 rhythmdb_nice_elt_name_from_propid (db
, change
->prop
),
2280 if (playing_entry
!= NULL
) {
2281 rhythmdb_entry_unref (playing_entry
);
2286 rb_shell_player_extra_metadata_cb (RhythmDB
*db
,
2287 RhythmDBEntry
*entry
,
2290 RBShellPlayer
*player
)
2293 RhythmDBEntry
*playing_entry
;
2295 playing_entry
= rb_shell_player_get_playing_entry (player
);
2296 if (entry
!= playing_entry
) {
2297 if (playing_entry
!= NULL
) {
2298 rhythmdb_entry_unref (playing_entry
);
2303 rb_shell_player_sync_with_source (player
);
2305 /* emit dbus signals for changes with easily marshallable types */
2306 switch (G_VALUE_TYPE (metadata
)) {
2308 case G_TYPE_BOOLEAN
:
2312 g_signal_emit (G_OBJECT (player
),
2313 rb_shell_player_signals
[PLAYING_SONG_PROPERTY_CHANGED
], 0,
2314 rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
),
2316 metadata
, /* slightly silly */
2326 rb_shell_player_sync_with_source (RBShellPlayer
*player
)
2328 const char *entry_title
= NULL
;
2329 const char *artist
= NULL
;
2330 const char *stream_name
= NULL
;
2331 char *streaming_title
= NULL
;
2332 char *streaming_artist
= NULL
;
2333 RhythmDBEntry
*entry
;
2337 entry
= rb_shell_player_get_playing_entry (player
);
2338 rb_debug ("playing source: %p, active entry: %p", player
->priv
->current_playing_source
, entry
);
2340 if (entry
!= NULL
) {
2343 entry_title
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE
);
2344 artist
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
);
2346 value
= rhythmdb_entry_request_extra_metadata (player
->priv
->db
,
2348 RHYTHMDB_PROP_STREAM_SONG_TITLE
);
2349 if (value
!= NULL
) {
2350 streaming_title
= g_value_dup_string (value
);
2351 g_value_unset (value
);
2354 rb_debug ("got streaming title \"%s\"", streaming_title
);
2355 /* use entry title for stream name */
2356 stream_name
= entry_title
;
2357 entry_title
= streaming_title
;
2360 value
= rhythmdb_entry_request_extra_metadata (player
->priv
->db
,
2362 RHYTHMDB_PROP_STREAM_SONG_ARTIST
);
2363 if (value
!= NULL
) {
2364 streaming_artist
= g_value_dup_string (value
);
2365 g_value_unset (value
);
2368 rb_debug ("got streaming artist \"%s\"", streaming_artist
);
2369 /* override artist from entry */
2370 artist
= streaming_artist
;
2374 if ((artist
&& artist
[0] != '\0') || entry_title
|| stream_name
) {
2376 GString
*title_str
= g_string_sized_new (100);
2377 if (artist
&& artist
[0] != '\0') {
2378 g_string_append (title_str
, artist
);
2379 g_string_append (title_str
, " - ");
2381 if (entry_title
!= NULL
)
2382 g_string_append (title_str
, entry_title
);
2384 if (stream_name
!= NULL
)
2385 g_string_append_printf (title_str
, " (%s)", stream_name
);
2387 title
= g_string_free (title_str
, FALSE
);
2390 elapsed
= rb_player_get_time (player
->priv
->mmplayer
);
2393 player
->priv
->elapsed
= elapsed
;
2395 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[WINDOW_TITLE_CHANGED
], 0,
2399 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[ELAPSED_CHANGED
], 0,
2400 player
->priv
->elapsed
);
2402 /* Sync the header */
2403 rb_header_set_playing_entry (player
->priv
->header_widget
,
2405 TRUE
/*rb_player_seekable (player->priv->mmplayer)*/);
2406 rb_header_sync (player
->priv
->header_widget
);
2408 if (entry
!= NULL
) {
2409 rhythmdb_entry_unref (entry
);
2412 g_free (streaming_artist
);
2413 g_free (streaming_title
);
2417 rb_shell_player_sync_buttons (RBShellPlayer
*player
)
2422 gboolean playing_from_queue
;
2424 int entry_view_state
;
2425 RhythmDBEntry
*entry
;
2427 entry
= rb_shell_player_get_playing_entry (player
);
2428 if (entry
!= NULL
) {
2429 source
= player
->priv
->current_playing_source
;
2430 entry_view_state
= rb_player_playing (player
->priv
->mmplayer
) ?
2431 RB_ENTRY_VIEW_PLAYING
: RB_ENTRY_VIEW_PAUSED
;
2433 source
= player
->priv
->selected_source
;
2434 entry_view_state
= RB_ENTRY_VIEW_NOT_PLAYING
;
2437 source
= (entry
== NULL
) ? player
->priv
->selected_source
: player
->priv
->current_playing_source
;
2439 playing_from_queue
= (source
== RB_SOURCE (player
->priv
->queue_source
));
2441 rb_debug ("syncing with source %p", source
);
2443 not_small
= !eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY
);
2444 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
2445 "ViewJumpToPlaying");
2446 g_object_set (action
,
2447 "sensitive", entry
!= NULL
&& not_small
, NULL
);
2449 if (source
!= NULL
) {
2450 view
= rb_source_get_entry_view (source
);
2452 rb_entry_view_set_state (view
, entry_view_state
);
2455 if (entry
!= NULL
) {
2456 rhythmdb_entry_unref (entry
);
2461 rb_shell_player_set_playing_source (RBShellPlayer
*player
,
2464 rb_shell_player_set_playing_source_internal (player
, source
, TRUE
);
2468 actually_set_playing_source (RBShellPlayer
*player
,
2470 gboolean sync_entry_view
)
2472 player
->priv
->source
= source
;
2473 player
->priv
->current_playing_source
= source
;
2475 if (source
!= NULL
) {
2476 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->source
);
2477 if (sync_entry_view
&& songs
) {
2478 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PLAYING
);
2482 if (player
->priv
->play_order
&& source
!= RB_SOURCE (player
->priv
->queue_source
)) {
2484 source
= player
->priv
->selected_source
;
2485 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
2488 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
2494 rb_shell_player_set_playing_source_internal (RBShellPlayer
*player
,
2496 gboolean sync_entry_view
)
2499 gboolean emit_source_changed
= TRUE
;
2500 gboolean emit_playing_from_queue_changed
= FALSE
;
2502 if (player
->priv
->source
== source
&&
2503 player
->priv
->current_playing_source
== source
&&
2507 rb_debug ("setting playing source to %p", source
);
2509 if (RB_SOURCE (player
->priv
->queue_source
) == source
) {
2511 if (player
->priv
->current_playing_source
!= source
)
2512 emit_playing_from_queue_changed
= TRUE
;
2514 if (player
->priv
->source
== NULL
) {
2515 actually_set_playing_source (player
, source
, sync_entry_view
);
2517 emit_source_changed
= FALSE
;
2518 player
->priv
->current_playing_source
= source
;
2522 if (player
->priv
->current_playing_source
!= source
) {
2523 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
2524 emit_playing_from_queue_changed
= TRUE
;
2526 /* stop the old source */
2527 if (player
->priv
->current_playing_source
!= NULL
) {
2528 if (sync_entry_view
) {
2529 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
2530 rb_debug ("source is already playing, stopping it");
2532 /* clear the playing entry if we're switching between non-queue sources */
2533 if (player
->priv
->current_playing_source
!= RB_SOURCE (player
->priv
->queue_source
))
2534 rb_play_order_set_playing_entry (player
->priv
->play_order
, NULL
);
2537 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_NOT_PLAYING
);
2541 actually_set_playing_source (player
, source
, sync_entry_view
);
2544 g_free (player
->priv
->url
);
2545 player
->priv
->url
= NULL
;
2547 if (player
->priv
->current_playing_source
== NULL
)
2548 rb_shell_player_stop (player
);
2550 rb_shell_player_sync_with_source (player
);
2551 g_object_notify (G_OBJECT (player
), "playing");
2552 if (player
->priv
->selected_source
)
2553 rb_shell_player_sync_buttons (player
);
2555 if (emit_source_changed
) {
2556 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[PLAYING_SOURCE_CHANGED
],
2557 0, player
->priv
->source
);
2559 if (emit_playing_from_queue_changed
) {
2560 g_object_notify (G_OBJECT (player
), "playing-from-queue");
2565 * rb_shell_player_stop:
2566 * @player: a #RBShellPlayer.
2568 * Completely stops playback, freeing resources and unloading the file.
2570 * In general rb_shell_player_pause() should be used instead, as it stops the
2571 * audio, but does not completely free resources.
2575 rb_shell_player_stop (RBShellPlayer
*player
)
2577 GError
*error
= NULL
;
2578 rb_debug ("stopping");
2580 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
2583 rb_player_close (player
->priv
->mmplayer
, &error
);
2585 rb_error_dialog (NULL
,
2586 _("Couldn't stop playback"),
2587 "%s", error
->message
);
2588 g_error_free (error
);
2591 rb_shell_player_sync_with_source (player
);
2592 g_signal_emit (G_OBJECT (player
),
2593 rb_shell_player_signals
[PLAYING_SONG_CHANGED
], 0,
2595 g_signal_emit (G_OBJECT (player
),
2596 rb_shell_player_signals
[PLAYING_URI_CHANGED
], 0,
2598 g_object_notify (G_OBJECT (player
), "playing");
2599 rb_shell_player_sync_buttons (player
);
2603 * rb_shell_player_pause:
2604 * @player: a #RBShellPlayer
2605 * @error: error return
2607 * Pauses playback if possible, completely stopping if not.
2609 * @return: whether playback is not occurring (TRUE when successfully
2610 * paused/stopped or playback was not occurring).
2614 rb_shell_player_pause (RBShellPlayer
*player
,
2617 if (rb_player_playing (player
->priv
->mmplayer
))
2618 return rb_shell_player_playpause (player
, FALSE
, error
);
2624 * rb_shell_player_get_playing:
2625 * @player: a #RBShellPlayer
2626 * @playing: playback state return
2627 * @error: error return
2629 * Reports whether playback is occuring by setting playing.
2631 * @return: whether the playback state could be reported successfully.
2634 rb_shell_player_get_playing (RBShellPlayer
*player
,
2638 if (playing
!= NULL
)
2639 *playing
= rb_player_playing (player
->priv
->mmplayer
);
2645 rb_shell_player_get_playing_time_string (RBShellPlayer
*player
)
2647 return rb_make_elapsed_time_string (player
->priv
->elapsed
,
2648 rb_shell_player_get_playing_song_duration (player
),
2649 !eel_gconf_get_boolean (CONF_UI_TIME_DISPLAY
));
2653 rb_shell_player_get_playing_time (RBShellPlayer
*player
,
2658 *time
= (guint
) rb_player_get_time (player
->priv
->mmplayer
);
2664 rb_shell_player_set_playing_time (RBShellPlayer
*player
,
2668 if (rb_player_seekable (player
->priv
->mmplayer
)) {
2669 rb_player_set_time (player
->priv
->mmplayer
, (long) time
);
2673 RB_SHELL_PLAYER_ERROR
,
2674 RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE
,
2675 _("Current song is not seekable"));
2681 rb_shell_player_seek (RBShellPlayer
*player
, long offset
)
2683 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
2685 if (rb_player_seekable (player
->priv
->mmplayer
)) {
2686 long t
= rb_player_get_time (player
->priv
->mmplayer
);
2689 rb_player_set_time (player
->priv
->mmplayer
, t
+ offset
);
2694 rb_shell_player_get_playing_song_duration (RBShellPlayer
*player
)
2696 RhythmDBEntry
*current_entry
;
2699 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player
), -1);
2701 current_entry
= rb_shell_player_get_playing_entry (player
);
2703 if (current_entry
== NULL
) {
2704 rb_debug ("Did not get playing entry : return -1 as length");
2708 val
= rhythmdb_entry_get_ulong (current_entry
, RHYTHMDB_PROP_DURATION
);
2710 rhythmdb_entry_unref (current_entry
);
2716 rb_shell_player_sync_with_selected_source (RBShellPlayer
*player
)
2718 rb_debug ("syncing with selected source: %p", player
->priv
->selected_source
);
2719 if (player
->priv
->source
== NULL
)
2721 rb_debug ("no playing source, new source is %p", player
->priv
->selected_source
);
2722 rb_shell_player_sync_with_source (player
);
2727 rb_shell_player_error (RBShellPlayer
*player
,
2731 RhythmDBEntry
*entry
;
2734 g_return_if_fail (player
->priv
->handling_error
== FALSE
);
2736 player
->priv
->handling_error
= TRUE
;
2738 entry
= rb_shell_player_get_playing_entry (player
);
2740 rb_debug ("playback error while playing: %s", err
->message
);
2741 /* For synchronous errors the entry playback error has already been set */
2743 rb_shell_player_set_entry_playback_error (player
, entry
, err
->message
);
2745 if (err
->code
== RB_PLAYER_ERROR_NO_AUDIO
) {
2746 /* stream has completely ended */
2747 rb_shell_player_set_playing_source (player
, NULL
);
2749 } else if ((player
->priv
->current_playing_source
!= NULL
) &&
2750 (rb_source_handle_eos (player
->priv
->current_playing_source
) == RB_SOURCE_EOF_RETRY
)) {
2751 /* receiving an error means a broken stream or non-audio stream, so abort
2752 * unless we've got more URLs to try */
2753 if (g_queue_is_empty (player
->priv
->playlist_urls
)) {
2754 rb_error_dialog (NULL
,
2755 _("Couldn't start playback"),
2756 "%s", (err
) ? err
->message
: "(null)");
2757 rb_shell_player_set_playing_source (player
, NULL
);
2760 rb_debug ("haven't yet exhausted the URLs from the playlist");
2767 if (do_next
&& player
->priv
->do_next_idle_id
== 0) {
2768 player
->priv
->do_next_idle_id
= g_idle_add ((GSourceFunc
)do_next_idle
, player
);
2771 player
->priv
->handling_error
= FALSE
;
2773 if (entry
!= NULL
) {
2774 rhythmdb_entry_unref (entry
);
2779 error_cb (RBPlayer
*mmplayer
,
2783 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2785 if (player
->priv
->handling_error
)
2788 if (player
->priv
->source
== NULL
) {
2789 rb_debug ("ignoring error (no source): %s", err
->message
);
2793 GDK_THREADS_ENTER ();
2795 rb_shell_player_error (player
, TRUE
, err
);
2797 rb_debug ("exiting error hander");
2798 GDK_THREADS_LEAVE ();
2802 tick_cb (RBPlayer
*mmplayer
,
2806 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2808 GDK_THREADS_ENTER ();
2810 if (rb_player_playing (mmplayer
)) {
2814 if (player
->priv
->elapsed
!= elapsed
) {
2815 player
->priv
->elapsed
= elapsed
;
2816 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[ELAPSED_CHANGED
],
2817 0, player
->priv
->elapsed
);
2821 GDK_THREADS_LEAVE ();
2825 rb_shell_player_get_playing_path (RBShellPlayer
*shell_player
,
2829 RhythmDBEntry
*entry
;
2831 entry
= rb_shell_player_get_playing_entry (shell_player
);
2832 if (entry
!= NULL
) {
2833 *path
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
2838 if (entry
!= NULL
) {
2839 rhythmdb_entry_unref (entry
);
2847 grab_mmkey (int key_code
,
2850 gdk_error_trap_push ();
2852 XGrabKey (GDK_DISPLAY (), key_code
,
2854 GDK_WINDOW_XID (root
), True
,
2855 GrabModeAsync
, GrabModeAsync
);
2856 XGrabKey (GDK_DISPLAY (), key_code
,
2858 GDK_WINDOW_XID (root
), True
,
2859 GrabModeAsync
, GrabModeAsync
);
2860 XGrabKey (GDK_DISPLAY (), key_code
,
2862 GDK_WINDOW_XID (root
), True
,
2863 GrabModeAsync
, GrabModeAsync
);
2864 XGrabKey (GDK_DISPLAY (), key_code
,
2866 GDK_WINDOW_XID (root
), True
,
2867 GrabModeAsync
, GrabModeAsync
);
2868 XGrabKey (GDK_DISPLAY (), key_code
,
2869 Mod2Mask
| Mod5Mask
,
2870 GDK_WINDOW_XID (root
), True
,
2871 GrabModeAsync
, GrabModeAsync
);
2872 XGrabKey (GDK_DISPLAY (), key_code
,
2873 Mod2Mask
| LockMask
,
2874 GDK_WINDOW_XID (root
), True
,
2875 GrabModeAsync
, GrabModeAsync
);
2876 XGrabKey (GDK_DISPLAY (), key_code
,
2877 Mod5Mask
| LockMask
,
2878 GDK_WINDOW_XID (root
), True
,
2879 GrabModeAsync
, GrabModeAsync
);
2880 XGrabKey (GDK_DISPLAY (), key_code
,
2881 Mod2Mask
| Mod5Mask
| LockMask
,
2882 GDK_WINDOW_XID (root
), True
,
2883 GrabModeAsync
, GrabModeAsync
);
2886 if (gdk_error_trap_pop ()) {
2887 rb_debug ("Error grabbing key");
2891 static GdkFilterReturn
2892 filter_mmkeys (GdkXEvent
*xevent
,
2898 RBShellPlayer
*player
;
2899 xev
= (XEvent
*) xevent
;
2900 if (xev
->type
!= KeyPress
) {
2901 return GDK_FILTER_CONTINUE
;
2904 key
= (XKeyEvent
*) xevent
;
2906 player
= (RBShellPlayer
*)data
;
2908 if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay
) == key
->keycode
) {
2909 rb_shell_player_playpause (player
, FALSE
, NULL
);
2910 return GDK_FILTER_REMOVE
;
2911 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause
) == key
->keycode
) {
2912 rb_shell_player_pause (player
, NULL
);
2913 return GDK_FILTER_REMOVE
;
2914 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop
) == key
->keycode
) {
2915 rb_shell_player_stop (player
);
2916 return GDK_FILTER_REMOVE
;
2917 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev
) == key
->keycode
) {
2918 rb_shell_player_cmd_previous (NULL
, player
);
2919 return GDK_FILTER_REMOVE
;
2920 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext
) == key
->keycode
) {
2921 rb_shell_player_cmd_next (NULL
, player
);
2922 return GDK_FILTER_REMOVE
;
2924 return GDK_FILTER_CONTINUE
;
2929 rb_shell_player_init_mmkeys (RBShellPlayer
*shell_player
)
2931 gint keycodes
[] = {0, 0, 0, 0, 0};
2932 GdkDisplay
*display
;
2937 keycodes
[0] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay
);
2938 keycodes
[1] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop
);
2939 keycodes
[2] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev
);
2940 keycodes
[3] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext
);
2941 keycodes
[4] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause
);
2943 display
= gdk_display_get_default ();
2945 for (i
= 0; i
< gdk_display_get_n_screens (display
); i
++) {
2946 screen
= gdk_display_get_screen (display
, i
);
2948 if (screen
!= NULL
) {
2949 root
= gdk_screen_get_root_window (screen
);
2951 for (j
= 0; j
< G_N_ELEMENTS (keycodes
) ; j
++) {
2952 if (keycodes
[j
] != 0)
2953 grab_mmkey (keycodes
[j
], root
);
2956 gdk_window_add_filter (root
, filter_mmkeys
,
2957 (gpointer
) shell_player
);
2961 #endif /* HAVE_MMKEYS */
2964 _idle_unblock_signal_cb (gpointer data
)
2966 RBShellPlayer
*player
= (RBShellPlayer
*)data
;
2970 GDK_THREADS_ENTER ();
2972 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
2975 /* sync the active state of the action again */
2976 g_object_get (player
, "playing", &playing
, NULL
);
2977 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), playing
);
2979 g_signal_handlers_unblock_by_func (action
, rb_shell_player_cmd_play
, player
);
2981 GDK_THREADS_LEAVE ();
2986 rb_shell_player_playing_changed_cb (RBShellPlayer
*player
,
2994 g_object_get (player
, "playing", &playing
, NULL
);
2995 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
2998 tooltip
= g_strdup (_("Stop playback"));
3000 tooltip
= g_strdup (_("Start playback"));
3002 g_object_set (action
, "tooltip", tooltip
, NULL
);
3005 /* block the signal, so that it doesn't get stuck by triggering recursively,
3006 * and don't unblock it until whatever else is happening has finished.
3008 g_signal_handlers_block_by_func (action
, rb_shell_player_cmd_play
, player
);
3009 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), playing
);
3010 g_idle_add (_idle_unblock_signal_cb
, player
);
3013 /* This should really be standard. */
3014 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
3017 rb_shell_player_error_get_type (void)
3019 static GType etype
= 0;
3022 static const GEnumValue values
[] = {
3023 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_PLAYLIST_PARSE_ERROR
, "Playing parsing error"),
3024 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
, "End of playlist reached"),
3025 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_PLAYING
, "Not playing"),
3026 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE
, "Not seekable"),
3030 etype
= g_enum_register_static ("RBShellPlayerError", values
);