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-iradio-source.h"
58 #include "rb-library-source.h"
59 #include "eel-gconf-extensions.h"
61 #include "rb-play-order.h"
62 #include "rb-statusbar.h"
63 #include "rb-playlist-source.h"
64 #include "rb-play-queue-source.h"
66 #include "rb-podcast-manager.h"
67 #include "rb-marshal.h"
69 #ifdef HAVE_XIDLE_EXTENSION
70 #include <X11/extensions/xidle.h>
71 #endif /* HAVE_XIDLE_EXTENSION */
73 static const char* const state_to_play_order
[2][2] =
74 {{"linear", "linear-loop"},
75 {"shuffle", "random-by-age-and-rating"}};
77 static void rb_shell_player_class_init (RBShellPlayerClass
*klass
);
78 static void rb_shell_player_init (RBShellPlayer
*shell_player
);
79 static GObject
*rb_shell_player_constructor (GType type
, guint n_construct_properties
,
80 GObjectConstructParam
*construct_properties
);
81 static void rb_shell_player_finalize (GObject
*object
);
82 static void rb_shell_player_set_property (GObject
*object
,
86 static void rb_shell_player_get_property (GObject
*object
,
91 static void rb_shell_player_cmd_previous (GtkAction
*action
,
92 RBShellPlayer
*player
);
93 static void rb_shell_player_cmd_play (GtkAction
*action
,
94 RBShellPlayer
*player
);
95 static void rb_shell_player_cmd_next (GtkAction
*action
,
96 RBShellPlayer
*player
);
97 static void rb_shell_player_cmd_volume_up (GtkAction
*action
,
98 RBShellPlayer
*player
);
99 static void rb_shell_player_cmd_volume_down (GtkAction
*action
,
100 RBShellPlayer
*player
);
101 static void rb_shell_player_shuffle_changed_cb (GtkAction
*action
,
102 RBShellPlayer
*player
);
103 static void rb_shell_player_repeat_changed_cb (GtkAction
*action
,
104 RBShellPlayer
*player
);
105 static void rb_shell_player_view_song_position_slider_changed_cb (GtkAction
*action
,
106 RBShellPlayer
*player
);
107 static void rb_shell_player_set_playing_source_internal (RBShellPlayer
*player
,
109 gboolean sync_entry_view
);
110 static void rb_shell_player_sync_with_source (RBShellPlayer
*player
);
111 static void rb_shell_player_sync_with_selected_source (RBShellPlayer
*player
);
112 static void rb_shell_player_entry_changed_cb (RhythmDB
*db
,
113 RhythmDBEntry
*entry
,
115 RBShellPlayer
*player
);
117 static void rb_shell_player_entry_activated_cb (RBEntryView
*view
,
118 RhythmDBEntry
*entry
,
119 RBShellPlayer
*playa
);
120 static void rb_shell_player_property_row_activated_cb (RBPropertyView
*view
,
122 RBShellPlayer
*playa
);
123 static void rb_shell_player_sync_volume (RBShellPlayer
*player
, gboolean notify
);
124 static void rb_shell_player_sync_replaygain (RBShellPlayer
*player
,
125 RhythmDBEntry
*entry
);
126 static void tick_cb (RBPlayer
*player
, long elapsed
, gpointer data
);
127 static void error_cb (RBPlayer
*player
, const GError
*err
, gpointer data
);
128 static void buffering_cb (RBPlayer
*player
, guint progress
, gpointer data
);
129 static void rb_shell_player_error (RBShellPlayer
*player
, gboolean async
, const GError
*err
);
131 static void info_available_cb (RBPlayer
*player
,
132 RBMetaDataField field
,
135 static void rb_shell_player_set_play_order (RBShellPlayer
*player
,
136 const gchar
*new_val
);
137 static void rb_shell_player_play_order_update_cb (RBPlayOrder
*porder
,
139 gboolean has_previous
,
140 RBShellPlayer
*player
);
142 static void rb_shell_player_sync_play_order (RBShellPlayer
*player
);
143 static void rb_shell_player_sync_control_state (RBShellPlayer
*player
);
144 static void rb_shell_player_sync_song_position_slider_visibility (RBShellPlayer
*player
);
145 static void rb_shell_player_sync_buttons (RBShellPlayer
*player
);
147 static void gconf_play_order_changed (GConfClient
*client
,guint cnxn_id
,
148 GConfEntry
*entry
, RBShellPlayer
*player
);
149 static void gconf_song_position_slider_visibility_changed (GConfClient
*client
,guint cnxn_id
,
150 GConfEntry
*entry
, RBShellPlayer
*player
);
151 static void rb_shell_player_playing_changed_cb (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
;
194 RBPlayOrder
*play_order
;
195 RBPlayOrder
*queue_play_order
;
197 GQueue
*playlist_urls
;
199 RBHeader
*header_widget
;
200 RBStatusbar
*statusbar_widget
;
202 guint gconf_play_order_id
;
203 guint gconf_song_position_slider_visibility_id
;
208 guint do_next_idle_id
;
211 #define RB_SHELL_PLAYER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL_PLAYER, RBShellPlayerPrivate))
227 PROP_PLAYING_FROM_QUEUE
,
233 WINDOW_TITLE_CHANGED
,
235 PLAYING_SOURCE_CHANGED
,
237 PLAYING_SONG_CHANGED
,
239 PLAYING_SONG_PROPERTY_CHANGED
,
243 static GtkActionEntry rb_shell_player_actions
[] =
245 { "ControlPrevious", GTK_STOCK_MEDIA_PREVIOUS
, N_("P_revious"), "<alt>Left",
246 N_("Start playing the previous song"),
247 G_CALLBACK (rb_shell_player_cmd_previous
) },
248 { "ControlNext", GTK_STOCK_MEDIA_NEXT
, N_("_Next"), "<alt>Right",
249 N_("Start playing the next song"),
250 G_CALLBACK (rb_shell_player_cmd_next
) },
251 { "ControlVolumeUp", NULL
, N_("_Increase Volume"), "<control>Up",
252 N_("Increase playback volume"),
253 G_CALLBACK (rb_shell_player_cmd_volume_up
) },
254 { "ControlVolumeDown", NULL
, N_("_Decrease Volume"), "<control>Down",
255 N_("Decrease playback volume"),
256 G_CALLBACK (rb_shell_player_cmd_volume_down
) },
258 static guint rb_shell_player_n_actions
= G_N_ELEMENTS (rb_shell_player_actions
);
260 static GtkToggleActionEntry rb_shell_player_toggle_entries
[] =
262 { "ControlPlay", GTK_STOCK_MEDIA_PLAY
, N_("_Play"), "<control>space",
263 N_("Start playback"),
264 G_CALLBACK (rb_shell_player_cmd_play
) },
265 { "ControlShuffle", GNOME_MEDIA_SHUFFLE
, N_("Sh_uffle"), "<control>U",
266 N_("Play songs in a random order"),
267 G_CALLBACK (rb_shell_player_shuffle_changed_cb
) },
268 { "ControlRepeat", GNOME_MEDIA_REPEAT
, N_("_Repeat"), "<control>R",
269 N_("Play first song again after all songs are played"),
270 G_CALLBACK (rb_shell_player_repeat_changed_cb
) },
271 { "ViewSongPositionSlider", NULL
, N_("_Song Position Slider"), NULL
,
272 N_("Change the visibility of the song position slider"),
273 G_CALLBACK (rb_shell_player_view_song_position_slider_changed_cb
), TRUE
},
275 static guint rb_shell_player_n_toggle_entries
= G_N_ELEMENTS (rb_shell_player_toggle_entries
);
277 static guint rb_shell_player_signals
[LAST_SIGNAL
] = { 0 };
279 G_DEFINE_TYPE (RBShellPlayer
, rb_shell_player
, GTK_TYPE_HBOX
)
282 rb_shell_player_class_init (RBShellPlayerClass
*klass
)
284 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
286 object_class
->finalize
= rb_shell_player_finalize
;
287 object_class
->constructor
= rb_shell_player_constructor
;
289 object_class
->set_property
= rb_shell_player_set_property
;
290 object_class
->get_property
= rb_shell_player_get_property
;
292 g_object_class_install_property (object_class
,
294 g_param_spec_object ("source",
300 g_object_class_install_property (object_class
,
302 g_param_spec_object ("ui-manager",
304 "GtkUIManager object",
306 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
308 g_object_class_install_property (object_class
,
310 g_param_spec_object ("db",
314 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
316 g_object_class_install_property (object_class
,
318 g_param_spec_object ("action-group",
320 "GtkActionGroup object",
321 GTK_TYPE_ACTION_GROUP
,
322 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
324 g_object_class_install_property (object_class
,
326 g_param_spec_object ("queue-source",
328 "RBPlaylistSource object",
329 RB_TYPE_PLAYLIST_SOURCE
,
332 g_object_class_install_property (object_class
,
334 g_param_spec_boolean ("queue-only",
336 "Activation only adds to queue",
340 g_object_class_install_property (object_class
,
341 PROP_PLAYING_FROM_QUEUE
,
342 g_param_spec_boolean ("playing-from-queue",
343 "Playing from queue",
344 "Whether playing from the play queue or not",
348 g_object_class_install_property (object_class
,
350 g_param_spec_object ("player",
356 /* If you change these, be sure to update the CORBA interface
357 * in rb-remote-bonobo.c! */
358 g_object_class_install_property (object_class
,
360 g_param_spec_string ("play-order",
362 "What play order to use",
365 g_object_class_install_property (object_class
,
367 g_param_spec_boolean ("playing",
369 "Whether Rhythmbox is currently playing",
373 g_object_class_install_property (object_class
,
375 g_param_spec_float ("volume",
377 "Current playback volume",
381 g_object_class_install_property (object_class
,
383 g_param_spec_object ("statusbar",
385 "RBStatusbar object",
389 g_object_class_install_property (object_class
,
391 g_param_spec_string ("stream-song",
393 "Current stream song title",
397 rb_shell_player_signals
[WINDOW_TITLE_CHANGED
] =
398 g_signal_new ("window_title_changed",
399 G_OBJECT_CLASS_TYPE (object_class
),
401 G_STRUCT_OFFSET (RBShellPlayerClass
, window_title_changed
),
403 g_cclosure_marshal_VOID__STRING
,
408 rb_shell_player_signals
[ELAPSED_CHANGED
] =
409 g_signal_new ("elapsed_changed",
410 G_OBJECT_CLASS_TYPE (object_class
),
412 G_STRUCT_OFFSET (RBShellPlayerClass
, elapsed_changed
),
414 g_cclosure_marshal_VOID__UINT
,
419 rb_shell_player_signals
[PLAYING_SOURCE_CHANGED
] =
420 g_signal_new ("playing-source-changed",
421 G_OBJECT_CLASS_TYPE (object_class
),
423 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_source_changed
),
425 g_cclosure_marshal_VOID__OBJECT
,
430 rb_shell_player_signals
[PLAYING_CHANGED
] =
431 g_signal_new ("playing-changed",
432 G_OBJECT_CLASS_TYPE (object_class
),
434 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_changed
),
436 g_cclosure_marshal_VOID__BOOLEAN
,
441 rb_shell_player_signals
[PLAYING_SONG_CHANGED
] =
442 g_signal_new ("playing-song-changed",
443 G_OBJECT_CLASS_TYPE (object_class
),
445 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_song_changed
),
447 g_cclosure_marshal_VOID__BOXED
,
450 RHYTHMDB_TYPE_ENTRY
);
452 rb_shell_player_signals
[PLAYING_URI_CHANGED
] =
453 g_signal_new ("playing-uri-changed",
454 G_OBJECT_CLASS_TYPE (object_class
),
456 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_uri_changed
),
458 g_cclosure_marshal_VOID__STRING
,
463 rb_shell_player_signals
[PLAYING_SONG_PROPERTY_CHANGED
] =
464 g_signal_new ("playing-song-property-changed",
465 G_OBJECT_CLASS_TYPE (object_class
),
467 G_STRUCT_OFFSET (RBShellPlayerClass
, playing_song_property_changed
),
469 rb_marshal_VOID__STRING_STRING_POINTER_POINTER
,
472 G_TYPE_STRING
, G_TYPE_STRING
,
473 G_TYPE_VALUE
, G_TYPE_VALUE
);
475 g_type_class_add_private (klass
, sizeof (RBShellPlayerPrivate
));
479 rb_shell_player_constructor (GType type
,
480 guint n_construct_properties
,
481 GObjectConstructParam
*construct_properties
)
483 RBShellPlayer
*player
;
484 RBShellPlayerClass
*klass
;
487 klass
= RB_SHELL_PLAYER_CLASS (g_type_class_peek (RB_TYPE_SHELL_PLAYER
));
489 player
= RB_SHELL_PLAYER (G_OBJECT_CLASS (rb_shell_player_parent_class
)->
490 constructor (type
, n_construct_properties
, construct_properties
));
492 gtk_action_group_add_actions (player
->priv
->actiongroup
,
493 rb_shell_player_actions
,
494 rb_shell_player_n_actions
,
496 gtk_action_group_add_toggle_actions (player
->priv
->actiongroup
,
497 rb_shell_player_toggle_entries
,
498 rb_shell_player_n_toggle_entries
,
501 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
503 g_object_set (action
, "is-important", TRUE
, NULL
);
505 player
->priv
->syncing_state
= TRUE
;
506 rb_shell_player_set_playing_source (player
, NULL
);
507 rb_shell_player_sync_play_order (player
);
508 rb_shell_player_sync_control_state (player
);
509 rb_shell_player_sync_volume (player
, FALSE
);
510 player
->priv
->syncing_state
= FALSE
;
512 rb_shell_player_sync_song_position_slider_visibility (player
);
514 g_signal_connect (G_OBJECT (player
),
516 G_CALLBACK (rb_shell_player_playing_changed_cb
),
519 return G_OBJECT (player
);
523 volume_pre_unmount_cb (GnomeVFSVolumeMonitor
*monitor
,
524 GnomeVFSVolume
*volume
,
525 RBShellPlayer
*player
)
527 gchar
*uri_mount_point
;
528 gchar
*volume_mount_point
;
529 RhythmDBEntry
*entry
;
533 rb_shell_player_get_playing (player
, &playing
, NULL
);
538 entry
= rb_shell_player_get_playing_entry (player
);
540 /* At startup for example, playing path can be NULL */
544 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
545 uri_mount_point
= rb_uri_get_mount_point (uri
);
546 volume_mount_point
= gnome_vfs_volume_get_activation_uri (volume
);
548 if (uri_mount_point
&& volume_mount_point
&&
549 (strcmp (uri_mount_point
, volume_mount_point
) == 0)) {
550 rb_shell_player_stop (player
);
552 g_free (uri_mount_point
);
553 g_free (volume_mount_point
);
556 rhythmdb_entry_unref (entry
);
561 reemit_playing_signal (RBShellPlayer
*player
,
565 g_signal_emit (player
, rb_shell_player_signals
[PLAYING_CHANGED
], 0,
566 rb_player_playing (player
->priv
->mmplayer
));
570 notify_playing_idle (RBShellPlayer
*player
)
572 rb_debug ("emitting playing notification: %d", rb_player_playing (player
->priv
->mmplayer
));
573 g_object_notify (G_OBJECT (player
), "playing");
574 rb_shell_player_sync_buttons (player
);
579 rb_shell_player_open_playlist_url (RBShellPlayer
*player
,
580 const char *location
)
582 GError
*error
= NULL
;
584 rb_debug ("playing stream url %s", location
);
585 rb_player_open (player
->priv
->mmplayer
, location
, &error
);
587 rb_player_play (player
->priv
->mmplayer
, &error
);
590 GDK_THREADS_ENTER ();
591 rb_shell_player_error (player
, TRUE
, error
);
592 g_error_free (error
);
593 GDK_THREADS_LEAVE ();
595 g_idle_add ((GSourceFunc
) notify_playing_idle
, player
);
599 rb_shell_player_handle_eos_unlocked (RBShellPlayer
*player
)
601 RhythmDBEntry
*entry
;
604 source
= player
->priv
->current_playing_source
;
607 if (source
== NULL
) {
611 entry
= rb_shell_player_get_playing_entry (player
);
613 switch (rb_source_handle_eos (source
)) {
614 case RB_SOURCE_EOF_ERROR
:
615 rb_error_dialog (NULL
, _("Stream error"),
616 _("Unexpected end of stream!"));
617 rb_shell_player_set_playing_source (player
, NULL
);
619 case RB_SOURCE_EOF_STOP
:
620 rb_shell_player_set_playing_source (player
, NULL
);
622 case RB_SOURCE_EOF_RETRY
: {
626 g_get_current_time (¤t
);
627 diff
= current
.tv_sec
- player
->priv
->last_retry
.tv_sec
;
628 player
->priv
->last_retry
= current
;
630 if (rb_source_try_playlist (source
) &&
631 !g_queue_is_empty (player
->priv
->playlist_urls
)) {
632 char *location
= g_queue_pop_head (player
->priv
->playlist_urls
);
633 rb_debug ("trying next radio stream url: %s", location
);
635 rb_shell_player_open_playlist_url (player
, location
);
641 rb_debug ("Last retry was less than 4 seconds ago...aborting retry playback");
642 rb_shell_player_set_playing_source (player
, NULL
);
644 rb_shell_player_play_entry (player
, entry
, NULL
);
648 case RB_SOURCE_EOF_NEXT
:
650 GError
*error
= NULL
;
652 if (!rb_shell_player_do_next (player
, &error
)) {
653 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
654 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
655 g_warning ("Unhandled error: %s", error
->message
);
656 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
657 rb_shell_player_set_playing_source (player
, NULL
);
663 if (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
) == NULL
) {
664 rb_debug ("updating play statistics");
665 rb_source_update_play_statistics (source
,
671 rhythmdb_entry_unref (entry
);
676 rb_shell_player_handle_eos (RBShellPlayer
*player
)
678 rb_debug ("handling eos!");
680 GDK_THREADS_ENTER ();
682 rb_shell_player_handle_eos_unlocked (player
);
684 GDK_THREADS_LEAVE ();
688 rb_shell_player_init (RBShellPlayer
*player
)
690 GError
*error
= NULL
;
692 player
->priv
= RB_SHELL_PLAYER_GET_PRIVATE (player
);
694 player
->priv
->mmplayer
= rb_player_new (&error
);
697 dialog
= gtk_message_dialog_new (NULL
, GTK_DIALOG_MODAL
,
700 _("Failed to create the player: %s"),
702 gtk_dialog_run (GTK_DIALOG (dialog
));
706 gtk_box_set_spacing (GTK_BOX (player
), 12);
707 gtk_container_set_border_width (GTK_CONTAINER (player
), 3);
709 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
711 G_CALLBACK (info_available_cb
),
714 g_signal_connect_object (player
->priv
->mmplayer
,
716 G_CALLBACK (rb_shell_player_handle_eos
),
717 player
, G_CONNECT_SWAPPED
);
719 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
721 G_CALLBACK (tick_cb
),
724 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
726 G_CALLBACK (error_cb
),
729 g_signal_connect_object (G_OBJECT (player
->priv
->mmplayer
),
731 G_CALLBACK (buffering_cb
),
734 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
735 "volume-pre-unmount",
736 G_CALLBACK (volume_pre_unmount_cb
),
739 player
->priv
->gconf_play_order_id
=
740 eel_gconf_notification_add (CONF_STATE_PLAY_ORDER
,
741 (GConfClientNotifyFunc
)gconf_play_order_changed
,
744 player
->priv
->header_widget
= rb_header_new (player
);
745 gtk_widget_show (GTK_WIDGET (player
->priv
->header_widget
));
746 gtk_box_pack_start (GTK_BOX (player
), GTK_WIDGET (player
->priv
->header_widget
), TRUE
, TRUE
, 0);
748 player
->priv
->volume
= eel_gconf_get_float (CONF_STATE_VOLUME
);
750 g_signal_connect (player
, "notify::playing",
751 G_CALLBACK (reemit_playing_signal
), NULL
);
753 player
->priv
->gconf_song_position_slider_visibility_id
=
754 eel_gconf_notification_add (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
,
755 (GConfClientNotifyFunc
) gconf_song_position_slider_visibility_changed
,
759 /* Enable Multimedia Keys */
760 rb_shell_player_init_mmkeys (player
);
761 #endif /* HAVE_MMKEYS */
765 rb_shell_player_set_source_internal (RBShellPlayer
*player
,
768 if (player
->priv
->selected_source
!= NULL
) {
769 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->selected_source
);
770 GList
*property_views
= rb_source_get_property_views (player
->priv
->selected_source
);
773 g_signal_handlers_disconnect_by_func (G_OBJECT (songs
),
774 G_CALLBACK (rb_shell_player_entry_activated_cb
),
778 for (; property_views
; property_views
= property_views
->next
) {
779 g_signal_handlers_disconnect_by_func (G_OBJECT (property_views
->data
),
780 G_CALLBACK (rb_shell_player_property_row_activated_cb
),
784 g_list_free (property_views
);
787 player
->priv
->selected_source
= source
;
789 rb_debug ("selected source %p", player
->priv
->selected_source
);
791 rb_shell_player_sync_with_selected_source (player
);
792 rb_shell_player_sync_buttons (player
);
794 if (player
->priv
->selected_source
!= NULL
) {
795 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->selected_source
);
796 GList
*property_views
= rb_source_get_property_views (player
->priv
->selected_source
);
799 g_signal_connect_object (G_OBJECT (songs
),
801 G_CALLBACK (rb_shell_player_entry_activated_cb
),
803 for (; property_views
; property_views
= property_views
->next
)
804 g_signal_connect_object (G_OBJECT (property_views
->data
),
805 "property-activated",
806 G_CALLBACK (rb_shell_player_property_row_activated_cb
),
809 g_list_free (property_views
);
812 /* If we're not playing, change the play order's view of the current source;
813 * if the selected source is the queue, however, set it to NULL so it'll stop
814 * once the queue is empty.
816 if (player
->priv
->current_playing_source
== NULL
) {
817 RBSource
*source
= player
->priv
->selected_source
;
818 if (source
== RB_SOURCE (player
->priv
->queue_source
))
821 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
826 rb_shell_player_set_db_internal (RBShellPlayer
*player
,
829 if (player
->priv
->db
!= NULL
) {
830 g_signal_handlers_disconnect_by_func (player
->priv
->db
,
831 G_CALLBACK (rb_shell_player_entry_changed_cb
),
835 player
->priv
->db
= db
;
837 if (player
->priv
->db
!= NULL
) {
838 /* Listen for changed entries to update metadata display */
839 g_signal_connect_object (G_OBJECT (player
->priv
->db
),
841 G_CALLBACK (rb_shell_player_entry_changed_cb
),
847 rb_shell_player_set_queue_source_internal (RBShellPlayer
*player
,
848 RBPlayQueueSource
*source
)
850 if (player
->priv
->queue_source
!= NULL
) {
851 RBEntryView
*sidebar
;
853 g_object_get (player
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
854 g_signal_handlers_disconnect_by_func (sidebar
,
855 G_CALLBACK (rb_shell_player_entry_activated_cb
),
857 g_object_unref (sidebar
);
859 if (player
->priv
->queue_play_order
!= NULL
) {
860 g_signal_handlers_disconnect_by_func (player
->priv
->queue_play_order
,
861 G_CALLBACK (rb_shell_player_play_order_update_cb
),
863 g_object_unref (player
->priv
->queue_play_order
);
868 player
->priv
->queue_source
= source
;
870 if (player
->priv
->queue_source
!= NULL
) {
871 RBEntryView
*sidebar
;
873 player
->priv
->queue_play_order
= rb_play_order_new ("queue", player
);
874 g_signal_connect_object (G_OBJECT (player
->priv
->queue_play_order
),
875 "have_next_previous_changed",
876 G_CALLBACK (rb_shell_player_play_order_update_cb
),
878 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
881 rb_play_order_playing_source_changed (player
->priv
->queue_play_order
,
882 RB_SOURCE (player
->priv
->queue_source
));
884 g_object_get (player
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
885 g_signal_connect_object (G_OBJECT (sidebar
),
887 G_CALLBACK (rb_shell_player_entry_activated_cb
),
889 g_object_unref (sidebar
);
894 rb_shell_player_finalize (GObject
*object
)
896 RBShellPlayer
*player
;
898 g_return_if_fail (object
!= NULL
);
899 g_return_if_fail (RB_IS_SHELL_PLAYER (object
));
901 player
= RB_SHELL_PLAYER (object
);
903 g_return_if_fail (player
->priv
!= NULL
);
905 eel_gconf_notification_remove (player
->priv
->gconf_play_order_id
);
907 eel_gconf_set_float (CONF_STATE_VOLUME
, player
->priv
->volume
);
909 g_object_unref (player
->priv
->mmplayer
);
910 g_object_unref (player
->priv
->play_order
);
911 g_object_unref (player
->priv
->queue_play_order
);
913 G_OBJECT_CLASS (rb_shell_player_parent_class
)->finalize (object
);
917 rb_shell_player_set_property (GObject
*object
,
922 RBShellPlayer
*player
= RB_SHELL_PLAYER (object
);
926 rb_shell_player_set_source_internal (player
, g_value_get_object (value
));
928 case PROP_UI_MANAGER
:
929 player
->priv
->ui_manager
= g_value_get_object (value
);
932 rb_shell_player_set_db_internal (player
, g_value_get_object (value
));
934 case PROP_ACTION_GROUP
:
935 player
->priv
->actiongroup
= g_value_get_object (value
);
937 case PROP_PLAY_ORDER
:
938 eel_gconf_set_string (CONF_STATE_PLAY_ORDER
,
939 g_value_get_string (value
));
942 player
->priv
->volume
= g_value_get_float (value
);
943 rb_shell_player_sync_volume (player
, FALSE
);
946 player
->priv
->statusbar_widget
= g_value_get_object (value
);
948 case PROP_QUEUE_SOURCE
:
949 rb_shell_player_set_queue_source_internal (player
, g_value_get_object (value
));
951 case PROP_QUEUE_ONLY
:
952 player
->priv
->queue_only
= g_value_get_boolean (value
);
955 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
961 rb_shell_player_get_property (GObject
*object
,
966 RBShellPlayer
*player
= RB_SHELL_PLAYER (object
);
970 g_value_set_object (value
, player
->priv
->selected_source
);
972 case PROP_UI_MANAGER
:
973 g_value_set_object (value
, player
->priv
->ui_manager
);
976 g_value_set_object (value
, player
->priv
->db
);
978 case PROP_ACTION_GROUP
:
979 g_value_set_object (value
, player
->priv
->actiongroup
);
981 case PROP_PLAY_ORDER
:
983 char *play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
985 play_order
= g_strdup ("linear");
986 g_value_set_string_take_ownership (value
, play_order
);
990 g_value_set_boolean (value
, rb_player_playing (player
->priv
->mmplayer
));
993 g_value_set_float (value
, player
->priv
->volume
);
996 g_value_set_object (value
, player
->priv
->statusbar_widget
);
998 case PROP_QUEUE_SOURCE
:
999 g_value_set_object (value
, player
->priv
->queue_source
);
1001 case PROP_QUEUE_ONLY
:
1002 g_value_set_boolean (value
, player
->priv
->queue_only
);
1004 case PROP_STREAM_SONG
:
1005 g_value_set_string (value
, player
->priv
->song
);
1007 case PROP_PLAYING_FROM_QUEUE
:
1008 g_value_set_boolean (value
, player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
));
1011 g_value_set_object (value
, player
->priv
->mmplayer
);
1014 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1020 rb_shell_player_error_quark (void)
1022 static GQuark quark
= 0;
1024 quark
= g_quark_from_static_string ("rb_shell_player_error");
1030 rb_shell_player_set_selected_source (RBShellPlayer
*player
,
1033 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
1034 g_return_if_fail (RB_IS_SOURCE (source
));
1036 g_object_set (G_OBJECT (player
),
1042 rb_shell_player_get_playing_source (RBShellPlayer
*player
)
1044 return player
->priv
->current_playing_source
;
1048 rb_shell_player_get_active_source (RBShellPlayer
*player
)
1050 return player
->priv
->source
;
1054 rb_shell_player_new (RhythmDB
*db
,
1056 GtkActionGroup
*actiongroup
)
1058 return g_object_new (RB_TYPE_SHELL_PLAYER
,
1060 "action-group", actiongroup
,
1066 rb_shell_player_get_playing_entry (RBShellPlayer
*player
)
1068 RBPlayOrder
*porder
;
1069 RhythmDBEntry
*entry
;
1071 if (player
->priv
->current_playing_source
== NULL
)
1074 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
1075 porder
= player
->priv
->queue_play_order
;
1077 porder
= player
->priv
->play_order
;
1079 if (porder
== NULL
) {
1083 entry
= rb_play_order_get_playing_entry (porder
);
1089 RBShellPlayer
*player
;
1091 } OpenLocationThreadData
;
1094 playlist_entry_cb (TotemPlParser
*playlist
,
1098 RBShellPlayer
*player
)
1100 rb_debug ("adding stream url %s", uri
);
1101 g_queue_push_tail (player
->priv
->playlist_urls
, g_strdup (uri
));
1105 open_location_thread (OpenLocationThreadData
*data
)
1107 TotemPlParser
*playlist
;
1108 TotemPlParserResult playlist_result
;
1110 GDK_THREADS_ENTER ();
1111 rb_statusbar_set_progress (data
->player
->priv
->statusbar_widget
, 0.0, _("Connecting"));
1112 GDK_THREADS_LEAVE ();
1114 playlist
= totem_pl_parser_new ();
1115 g_signal_connect_data (G_OBJECT (playlist
), "entry",
1116 G_CALLBACK (playlist_entry_cb
),
1117 data
->player
, NULL
, 0);
1118 totem_pl_parser_add_ignored_mimetype (playlist
, "x-directory/normal");
1120 playlist_result
= totem_pl_parser_parse (playlist
, data
->location
, FALSE
);
1121 g_object_unref (playlist
);
1123 if (playlist_result
== TOTEM_PL_PARSER_RESULT_SUCCESS
) {
1124 if (g_queue_is_empty (data
->player
->priv
->playlist_urls
)) {
1125 GError
*error
= g_error_new (RB_SHELL_PLAYER_ERROR
,
1126 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1127 _("Playlist was empty"));
1128 GDK_THREADS_ENTER ();
1129 rb_shell_player_error (data
->player
, TRUE
, error
);
1130 g_error_free (error
);
1131 GDK_THREADS_LEAVE ();
1135 location
= g_queue_pop_head (data
->player
->priv
->playlist_urls
);
1136 rb_debug ("playing first stream url %s", data
->location
);
1137 rb_shell_player_open_playlist_url (data
->player
, location
);
1141 /* if we can't parse it as a playlist, just try playing it */
1142 rb_debug ("playlist parser failed, playing %s directly", data
->location
);
1143 rb_shell_player_open_playlist_url (data
->player
, data
->location
);
1151 rb_shell_player_open_location (RBShellPlayer
*player
,
1152 const char *location
,
1156 gboolean was_playing
;
1158 unescaped
= gnome_vfs_unescape_string_for_display (location
);
1159 rb_debug ("Opening %s...", unescaped
);
1162 was_playing
= rb_player_playing (player
->priv
->mmplayer
);
1164 g_free (player
->priv
->song
);
1165 player
->priv
->song
= NULL
;
1166 g_object_notify (G_OBJECT (player
), "stream-song");
1168 if (rb_source_try_playlist (player
->priv
->source
)) {
1169 OpenLocationThreadData
*data
;
1171 data
= g_new0 (OpenLocationThreadData
, 1);
1172 data
->player
= player
;
1174 /* dispose of any existing playlist urls */
1175 if (player
->priv
->playlist_urls
) {
1176 g_queue_foreach (player
->priv
->playlist_urls
,
1179 g_queue_free (player
->priv
->playlist_urls
);
1180 player
->priv
->playlist_urls
= NULL
;
1182 player
->priv
->playlist_urls
= g_queue_new ();
1184 /* add http:// as a prefix, if it doesn't have a URI scheme */
1185 if (strstr (location
, "://"))
1186 data
->location
= g_strdup (location
);
1188 data
->location
= g_strconcat ("http://", location
, NULL
);
1190 g_thread_create ((GThreadFunc
)open_location_thread
, data
, FALSE
, NULL
);
1193 if (!rb_player_open (player
->priv
->mmplayer
, location
, error
))
1196 if (!rb_player_play (player
->priv
->mmplayer
, error
))
1199 g_object_notify (G_OBJECT (player
), "playing");
1206 rb_shell_player_open_entry (RBShellPlayer
*player
,
1207 RhythmDBEntry
*entry
,
1213 location
= rhythmdb_entry_get_playback_uri (entry
);
1214 if (location
== NULL
)
1217 result
= rb_shell_player_open_location (player
, location
, error
);
1224 * rb_shell_player_play:
1225 * @player: a #RBShellPlayer
1226 * @error: error return
1228 * Starts playback, if it is not already playing.
1230 * @return: whether playback is now occurring (TRUE when successfully started
1231 * or already playing).
1234 rb_shell_player_play (RBShellPlayer
*player
,
1239 if (rb_player_playing (player
->priv
->mmplayer
))
1242 if (!rb_player_play (player
->priv
->mmplayer
, error
))
1245 songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1247 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PLAYING
);
1249 rb_shell_player_sync_with_source (player
);
1250 rb_shell_player_sync_buttons (player
);
1256 rb_shell_player_set_entry_playback_error (RBShellPlayer
*player
,
1257 RhythmDBEntry
*entry
,
1260 GValue value
= { 0, };
1262 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
1264 g_value_init (&value
, G_TYPE_STRING
);
1265 g_value_set_string (&value
, message
);
1266 rhythmdb_entry_set (player
->priv
->db
,
1268 RHYTHMDB_PROP_PLAYBACK_ERROR
,
1270 g_value_unset (&value
);
1271 rhythmdb_commit (player
->priv
->db
);
1275 do_next_idle (RBShellPlayer
*player
)
1277 /* use the EOS callback, so that EOF_SOURCE_ conditions are handled properly */
1278 rb_shell_player_handle_eos (player
);
1279 player
->priv
->do_next_idle_id
= 0;
1285 rb_shell_player_set_playing_entry (RBShellPlayer
*player
,
1286 RhythmDBEntry
*entry
,
1287 gboolean out_of_order
,
1290 GError
*tmp_error
= NULL
;
1291 const char *location
;
1294 g_return_val_if_fail (player
->priv
->current_playing_source
!= NULL
, TRUE
);
1295 g_return_val_if_fail (entry
!= NULL
, TRUE
);
1298 RBPlayOrder
*porder
;
1299 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
1300 porder
= player
->priv
->queue_play_order
;
1302 porder
= player
->priv
->play_order
;
1303 rb_play_order_set_playing_entry (porder
, entry
);
1306 if (!rb_shell_player_open_entry (player
, entry
, &tmp_error
))
1308 rb_shell_player_sync_replaygain (player
, entry
);
1310 rb_debug ("Success!");
1311 /* clear error on successful playback */
1312 g_value_init (&val
, G_TYPE_STRING
);
1313 g_value_set_string (&val
, NULL
);
1314 rhythmdb_entry_set (player
->priv
->db
, entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
1315 g_value_unset (&val
);
1317 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
1318 g_signal_emit (G_OBJECT (player
),
1319 rb_shell_player_signals
[PLAYING_SONG_CHANGED
], 0,
1321 g_signal_emit (G_OBJECT (player
),
1322 rb_shell_player_signals
[PLAYING_URI_CHANGED
], 0,
1325 rb_shell_player_sync_with_source (player
);
1326 rb_shell_player_sync_buttons (player
);
1330 /* Ignore errors, shutdown the player */
1331 rb_player_close (player
->priv
->mmplayer
, NULL
);
1332 if (tmp_error
== NULL
)
1333 tmp_error
= g_error_new (RB_SHELL_PLAYER_ERROR
,
1334 RB_SHELL_PLAYER_ERROR_NOT_PLAYING
,
1335 "Problem occurred without error being set. "
1336 "This is a bug in Rhythmbox or GStreamer.");
1337 /* Mark this song as failed */
1338 rb_shell_player_set_entry_playback_error (player
, entry
, tmp_error
->message
);
1339 g_propagate_error (error
, tmp_error
);
1341 rb_shell_player_sync_with_source (player
);
1342 rb_shell_player_sync_buttons (player
);
1343 g_object_notify (G_OBJECT (player
), "playing");
1349 gconf_play_order_changed (GConfClient
*client
,
1352 RBShellPlayer
*player
)
1354 rb_debug ("gconf play order changed");
1355 player
->priv
->syncing_state
= TRUE
;
1356 rb_shell_player_sync_play_order (player
);
1357 rb_shell_player_sync_buttons (player
);
1358 rb_shell_player_sync_control_state (player
);
1359 g_object_notify (G_OBJECT (player
), "play-order");
1360 player
->priv
->syncing_state
= FALSE
;
1364 rb_shell_player_get_playback_state (RBShellPlayer
*player
,
1371 play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
1373 g_warning (CONF_STATE_PLAY_ORDER
" gconf key not found!");
1377 for (i
= 0; i
< G_N_ELEMENTS(state_to_play_order
); i
++)
1378 for (j
= 0; j
< G_N_ELEMENTS(state_to_play_order
[0]); j
++)
1379 if (!strcmp (play_order
, state_to_play_order
[i
][j
]))
1382 g_free (play_order
);
1388 g_free (play_order
);
1393 rb_shell_player_set_play_order (RBShellPlayer
*player
,
1394 const gchar
*new_val
)
1398 g_object_get (player
, "play-order", &old_val
, NULL
);
1399 if (strcmp (old_val
, new_val
) != 0) {
1400 /* The notify signal will be emitted by the gconf notifier */
1401 eel_gconf_set_string (CONF_STATE_PLAY_ORDER
, new_val
);
1407 rb_shell_player_set_playback_state (RBShellPlayer
*player
,
1411 const char *neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
1412 rb_shell_player_set_play_order (player
, neworder
);
1416 rb_shell_player_sync_play_order (RBShellPlayer
*player
)
1418 char *new_play_order
;
1419 RhythmDBEntry
*playing_entry
= NULL
;
1422 new_play_order
= eel_gconf_get_string (CONF_STATE_PLAY_ORDER
);
1423 if (new_play_order
== NULL
) {
1424 g_warning (CONF_STATE_PLAY_ORDER
" gconf key not found!");
1425 new_play_order
= g_strdup ("linear");
1428 if (player
->priv
->play_order
!= NULL
) {
1429 playing_entry
= rb_play_order_get_playing_entry (player
->priv
->play_order
);
1430 g_signal_handlers_disconnect_by_func (player
->priv
->play_order
,
1431 G_CALLBACK (rb_shell_player_play_order_update_cb
),
1433 g_object_unref (player
->priv
->play_order
);
1436 player
->priv
->play_order
= rb_play_order_new (new_play_order
, player
);
1437 g_signal_connect_object (player
->priv
->play_order
,
1438 "have_next_previous_changed",
1439 G_CALLBACK (rb_shell_player_play_order_update_cb
),
1441 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
1445 source
= player
->priv
->current_playing_source
;
1446 if (source
== NULL
) {
1447 source
= player
->priv
->selected_source
;
1449 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
1451 if (playing_entry
!= NULL
) {
1452 rb_play_order_set_playing_entry (player
->priv
->play_order
, playing_entry
);
1453 rhythmdb_entry_unref (playing_entry
);
1456 g_free (new_play_order
);
1460 rb_shell_player_play_order_update_cb (RBPlayOrder
*porder
,
1462 gboolean has_previous
,
1463 RBShellPlayer
*player
)
1465 /* we cannot depend on the values of has_next, has_previous or porder
1466 * since this can be called for the main porder, queue porder, etc
1468 gboolean have_next
= FALSE
;
1469 gboolean have_previous
= FALSE
;
1471 RhythmDBEntry
*entry
;
1473 entry
= rb_shell_player_get_playing_entry (player
);
1474 if (entry
!= NULL
) {
1476 have_previous
= TRUE
;
1477 rhythmdb_entry_unref (entry
);
1479 if (player
->priv
->current_playing_source
&&
1480 (rb_source_handle_eos (player
->priv
->current_playing_source
) == RB_SOURCE_EOF_NEXT
)) {
1481 have_next
= rb_play_order_has_next (player
->priv
->play_order
);
1482 have_previous
= rb_play_order_has_previous (player
->priv
->play_order
);
1484 if (player
->priv
->queue_play_order
) {
1485 have_next
|= rb_play_order_has_next (player
->priv
->queue_play_order
);
1486 have_previous
|= rb_play_order_has_previous (player
->priv
->queue_play_order
);
1490 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1492 g_object_set (action
, "sensitive", have_previous
, NULL
);
1493 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1495 g_object_set (action
, "sensitive", have_next
, NULL
);
1499 rb_shell_player_jump_to_current_idle (RBShellPlayer
*player
)
1501 rb_shell_player_jump_to_current (player
);
1506 rb_shell_player_sync_song_position_slider_visibility (RBShellPlayer
*player
)
1511 visible
= !eel_gconf_get_boolean (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
);
1513 rb_header_set_show_position_slider (player
->priv
->header_widget
,
1516 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1517 "ViewSongPositionSlider");
1518 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
),
1523 rb_shell_player_jump_to_current (RBShellPlayer
*player
)
1526 RhythmDBEntry
*entry
;
1529 source
= player
->priv
->current_playing_source
? player
->priv
->current_playing_source
:
1530 player
->priv
->selected_source
;
1532 songs
= rb_source_get_entry_view (source
);
1533 entry
= rb_shell_player_get_playing_entry (player
);
1534 if (songs
!= NULL
) {
1535 if (entry
!= NULL
) {
1536 rb_entry_view_scroll_to_entry (songs
, entry
);
1537 rb_entry_view_select_entry (songs
, entry
);
1539 rb_entry_view_select_none (songs
);
1543 if (entry
!= NULL
) {
1544 rhythmdb_entry_unref (entry
);
1549 swap_playing_source (RBShellPlayer
*player
,
1550 RBSource
*new_source
)
1552 if (player
->priv
->current_playing_source
!= NULL
) {
1553 RBEntryView
*old_songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1555 rb_entry_view_set_state (old_songs
, RB_ENTRY_VIEW_NOT_PLAYING
);
1557 if (new_source
!= NULL
) {
1558 RBEntryView
*new_songs
= rb_source_get_entry_view (new_source
);
1561 rb_entry_view_set_state (new_songs
, RB_ENTRY_VIEW_PLAYING
);
1562 rb_shell_player_set_playing_source (player
, new_source
);
1568 rb_shell_player_do_previous (RBShellPlayer
*player
,
1571 RhythmDBEntry
*entry
= NULL
;
1572 RBSource
*new_source
;
1574 if (player
->priv
->current_playing_source
== NULL
) {
1576 RB_SHELL_PLAYER_ERROR
,
1577 RB_SHELL_PLAYER_ERROR_NOT_PLAYING
,
1578 _("Not currently playing"));
1582 rb_debug ("going to previous");
1584 if (player
->priv
->queue_play_order
) {
1585 entry
= rb_play_order_get_previous (player
->priv
->queue_play_order
);
1586 if (entry
!= NULL
) {
1587 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1588 rb_play_order_go_previous (player
->priv
->queue_play_order
);
1592 if (entry
== NULL
) {
1593 new_source
= player
->priv
->source
;
1594 entry
= rb_play_order_get_previous (player
->priv
->play_order
);
1596 rb_play_order_go_previous (player
->priv
->play_order
);
1599 if (entry
!= NULL
) {
1600 rb_debug ("previous song found, doing previous");
1601 if (new_source
!= player
->priv
->current_playing_source
)
1602 swap_playing_source (player
, new_source
);
1604 if (!rb_shell_player_set_playing_entry (player
, entry
, FALSE
, error
)) {
1605 rhythmdb_entry_unref (entry
);
1609 rb_shell_player_jump_to_current (player
);
1610 rhythmdb_entry_unref (entry
);
1612 rb_debug ("no previous song found, signaling error");
1614 RB_SHELL_PLAYER_ERROR
,
1615 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1616 _("No previous song"));
1617 rb_shell_player_set_playing_source (player
, NULL
);
1625 rb_shell_player_do_next (RBShellPlayer
*player
,
1628 RBSource
*new_source
= NULL
;
1629 RhythmDBEntry
*entry
= NULL
;
1632 if (player
->priv
->source
== NULL
)
1635 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
)) {
1636 /* Look for another entry in the queue, and fall back to the playing
1637 * source if there isn't one. Always call _get_next on the queue
1638 * so the current entry is removed.
1640 entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1641 rb_play_order_go_next (player
->priv
->queue_play_order
);
1643 if (entry
!= NULL
) {
1644 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1646 /* If we haven't played anything from the playing source yet,
1647 * _get_playing_entry will return NULL, so we'll have to advance
1648 * the play order to get an entry to play.
1650 entry
= rb_play_order_get_playing_entry (player
->priv
->play_order
);
1651 if (entry
== NULL
) {
1652 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1653 rb_play_order_go_next (player
->priv
->play_order
);
1656 new_source
= player
->priv
->source
;
1659 /* Advance the play order, and then let the queue override it. */
1660 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1661 if (entry
!= NULL
) {
1662 new_source
= player
->priv
->source
;
1663 rb_play_order_go_next (player
->priv
->play_order
);
1666 if (player
->priv
->queue_play_order
) {
1667 RhythmDBEntry
*queue_entry
;
1669 queue_entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1670 if (queue_entry
!= NULL
) {
1671 if (entry
!= NULL
) {
1672 rhythmdb_entry_unref (entry
);
1674 entry
= queue_entry
;
1675 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1676 rb_play_order_go_next (player
->priv
->queue_play_order
);
1681 /* play the new entry */
1682 if (entry
!= NULL
) {
1683 /* if the entry view containing the playing entry changed, update it */
1684 if (new_source
!= player
->priv
->current_playing_source
)
1685 swap_playing_source (player
, new_source
);
1687 if (!rb_shell_player_set_playing_entry (player
, entry
, FALSE
, error
))
1691 RB_SHELL_PLAYER_ERROR
,
1692 RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
,
1694 rb_debug ("No next entry, stopping playback");
1695 rb_shell_player_set_playing_source (player
, NULL
);
1696 rb_play_order_set_playing_entry (player
->priv
->play_order
, NULL
);
1697 g_object_notify (G_OBJECT (player
), "playing");
1701 g_idle_add ((GSourceFunc
)rb_shell_player_jump_to_current_idle
, player
);
1703 if (entry
!= NULL
) {
1704 rhythmdb_entry_unref (entry
);
1711 rb_shell_player_do_previous_or_seek (RBShellPlayer
*player
,
1714 rb_debug ("previous");
1715 /* If we're in the first 3 seconds go to the previous song,
1716 * else restart the current one.
1718 if (player
->priv
->current_playing_source
!= NULL
1719 && rb_source_can_pause (player
->priv
->source
)
1720 && rb_player_get_time (player
->priv
->mmplayer
) > 3) {
1722 /* see if there's anything to go back to */
1723 gboolean have_previous
;
1724 have_previous
= rb_play_order_has_previous (player
->priv
->play_order
);
1725 if (player
->priv
->queue_play_order
)
1726 have_previous
|= rb_play_order_has_previous (player
->priv
->queue_play_order
);
1728 if (have_previous
) {
1729 rb_debug ("after 3 second previous, restarting song");
1730 rb_player_set_time (player
->priv
->mmplayer
, 0);
1731 rb_header_sync_time (player
->priv
->header_widget
);
1736 return rb_shell_player_do_previous (player
, error
);
1740 rb_shell_player_cmd_previous (GtkAction
*action
,
1741 RBShellPlayer
*player
)
1743 GError
*error
= NULL
;
1745 if (!rb_shell_player_do_previous_or_seek (player
, &error
)) {
1746 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
1747 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1748 g_warning ("cmd_previous: Unhandled error: %s", error
->message
);
1749 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1750 rb_shell_player_set_playing_source (player
, NULL
);
1755 rb_shell_player_cmd_next (GtkAction
*action
,
1756 RBShellPlayer
*player
)
1758 GError
*error
= NULL
;
1760 if (!rb_shell_player_do_next (player
, &error
)) {
1761 if (error
->domain
!= RB_SHELL_PLAYER_ERROR
||
1762 error
->code
!= RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1763 g_warning ("cmd_next: Unhandled error: %s", error
->message
);
1764 else if (error
->code
== RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
)
1765 rb_shell_player_set_playing_source (player
, NULL
);
1770 rb_shell_player_play_entry (RBShellPlayer
*player
,
1771 RhythmDBEntry
*entry
,
1774 GError
*error
= NULL
;
1777 source
= player
->priv
->selected_source
;
1778 rb_shell_player_set_playing_source (player
, source
);
1780 if (!rb_shell_player_set_playing_entry (player
, entry
, TRUE
, &error
)) {
1781 rb_shell_player_error (player
, FALSE
, error
);
1782 g_clear_error (&error
);
1787 rb_shell_player_cmd_volume_up (GtkAction
*action
,
1788 RBShellPlayer
*player
)
1790 rb_shell_player_set_volume_relative (player
, 0.1, NULL
);
1794 rb_shell_player_cmd_volume_down (GtkAction
*action
,
1795 RBShellPlayer
*player
)
1797 rb_shell_player_set_volume_relative (player
, -0.1, NULL
);
1801 rb_shell_player_cmd_play (GtkAction
*action
,
1802 RBShellPlayer
*player
)
1804 GError
*error
= NULL
;
1806 if (!rb_shell_player_playpause (player
, FALSE
, &error
))
1807 rb_error_dialog (NULL
,
1808 _("Couldn't start playback"),
1809 "%s", (error
) ? error
->message
: "(null)");
1810 g_clear_error (&error
);
1813 /* unused parameter can't be removed without breaking dbus interface compatibility */
1815 rb_shell_player_playpause (RBShellPlayer
*player
,
1822 rb_debug ("doing playpause");
1824 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player
), TRUE
);
1828 if (rb_player_playing (player
->priv
->mmplayer
)) {
1829 if (player
->priv
->source
== NULL
) {
1830 rb_debug ("playing source is already NULL");
1831 } else if (rb_source_can_pause (player
->priv
->source
)) {
1832 rb_debug ("pausing mm player");
1833 rb_player_pause (player
->priv
->mmplayer
);
1834 songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
1836 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PAUSED
);
1838 rb_debug ("setting playing source to NULL");
1839 rb_shell_player_set_playing_source (player
, NULL
);
1842 RhythmDBEntry
*entry
;
1843 RBSource
*new_source
;
1844 gboolean out_of_order
= FALSE
;
1846 if (player
->priv
->source
== NULL
) {
1847 /* no current stream, pull one in from the currently
1848 * selected source */
1849 rb_debug ("no playing source, using selected source");
1850 rb_shell_player_set_playing_source (player
, player
->priv
->selected_source
);
1852 new_source
= player
->priv
->current_playing_source
;
1854 entry
= rb_shell_player_get_playing_entry (player
);
1855 if (entry
== NULL
) {
1856 /* queue takes precedence over selection */
1857 if (player
->priv
->queue_play_order
) {
1858 entry
= rb_play_order_get_next (player
->priv
->queue_play_order
);
1859 if (entry
!= NULL
) {
1860 new_source
= RB_SOURCE (player
->priv
->queue_source
);
1861 rb_play_order_go_next (player
->priv
->queue_play_order
);
1865 /* selection takes precedence over first item in play order */
1866 if (entry
== NULL
) {
1867 GList
*selection
= NULL
;
1869 songs
= rb_source_get_entry_view (player
->priv
->source
);
1871 selection
= rb_entry_view_get_selected_entries (songs
);
1873 if (selection
!= NULL
) {
1874 rb_debug ("choosing first selected entry");
1875 entry
= (RhythmDBEntry
*) selection
->data
;
1877 out_of_order
= TRUE
;
1879 g_list_free (selection
);
1883 /* play order is last */
1884 if (entry
== NULL
) {
1885 rb_debug ("getting entry from play order");
1886 entry
= rb_play_order_get_next (player
->priv
->play_order
);
1888 rb_play_order_go_next (player
->priv
->play_order
);
1891 if (entry
!= NULL
) {
1892 /* if the entry view containing the playing entry changed, update it */
1893 if (new_source
!= player
->priv
->current_playing_source
)
1894 swap_playing_source (player
, new_source
);
1896 if (!rb_shell_player_set_playing_entry (player
, entry
, out_of_order
, error
))
1898 rb_shell_player_jump_to_current (player
);
1901 if (!rb_shell_player_play (player
, error
)) {
1902 rb_shell_player_set_playing_source (player
, NULL
);
1907 if (entry
!= NULL
) {
1908 rhythmdb_entry_unref (entry
);
1912 rb_shell_player_sync_with_source (player
);
1913 rb_shell_player_sync_buttons (player
);
1914 g_object_notify (G_OBJECT (player
), "playing");
1920 rb_shell_player_sync_control_state (RBShellPlayer
*player
)
1922 gboolean shuffle
, repeat
;
1924 rb_debug ("syncing control state");
1926 if (!rb_shell_player_get_playback_state (player
, &shuffle
,
1930 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1932 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), shuffle
);
1933 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1935 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), repeat
);
1939 rb_shell_player_sync_volume (RBShellPlayer
*player
,
1943 RhythmDBEntry
*entry
;
1945 if (player
->priv
->volume
<= 0.0){
1946 player
->priv
->volume
= 0.0;
1947 } else if (player
->priv
->volume
>= 1.0){
1948 player
->priv
->volume
= 1.0;
1951 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1953 g_object_set (G_OBJECT (action
), "sensitive", player
->priv
->volume
< 0.9999, NULL
);
1955 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
1956 "ControlVolumeDown");
1957 g_object_set (G_OBJECT (action
), "sensitive", player
->priv
->volume
> 0.0001, NULL
);
1959 rb_player_set_volume (player
->priv
->mmplayer
,
1960 player
->priv
->mute
? 0.0 : player
->priv
->volume
);
1962 eel_gconf_set_float (CONF_STATE_VOLUME
, player
->priv
->volume
);
1965 entry
= rb_shell_player_get_playing_entry (player
);
1966 rb_shell_player_sync_replaygain (player
, entry
);
1967 if (entry
!= NULL
) {
1968 rhythmdb_entry_unref (entry
);
1972 g_object_notify (G_OBJECT (player
), "volume");
1976 rb_shell_player_toggle_mute (RBShellPlayer
*player
)
1978 player
->priv
->mute
= !player
->priv
->mute
;
1979 rb_shell_player_sync_volume (player
, FALSE
);
1983 rb_shell_player_sync_replaygain (RBShellPlayer
*player
,
1984 RhythmDBEntry
*entry
)
1986 double entry_track_gain
= 0;
1987 double entry_track_peak
= 0;
1988 double entry_album_gain
= 0;
1989 double entry_album_peak
= 0;
1991 if (entry
!= NULL
) {
1992 entry_track_gain
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_TRACK_GAIN
);
1993 entry_track_peak
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_TRACK_PEAK
);
1994 entry_album_gain
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_ALBUM_GAIN
);
1995 entry_album_peak
= rhythmdb_entry_get_double (entry
, RHYTHMDB_PROP_ALBUM_PEAK
);
1998 rb_player_set_replaygain (player
->priv
->mmplayer
, entry_track_gain
,
1999 entry_track_peak
, entry_album_gain
, entry_album_peak
);
2003 rb_shell_player_set_volume (RBShellPlayer
*player
,
2007 player
->priv
->volume
= volume
;
2008 rb_shell_player_sync_volume (player
, TRUE
);
2013 rb_shell_player_set_volume_relative (RBShellPlayer
*player
,
2017 /* rb_shell_player_sync_volume does clipping */
2018 player
->priv
->volume
+= delta
;
2019 rb_shell_player_sync_volume (player
, TRUE
);
2024 rb_shell_player_get_volume (RBShellPlayer
*player
,
2028 *volume
= player
->priv
->volume
;
2033 rb_shell_player_set_mute (RBShellPlayer
*player
,
2037 player
->priv
->mute
= mute
;
2038 rb_shell_player_sync_volume (player
, FALSE
);
2043 rb_shell_player_get_mute (RBShellPlayer
*player
,
2047 *mute
= player
->priv
->mute
;
2052 gconf_song_position_slider_visibility_changed (GConfClient
*client
,
2055 RBShellPlayer
*player
)
2057 rb_debug ("song position slider visibility visibility changed");
2058 rb_shell_player_sync_song_position_slider_visibility (player
);
2062 rb_shell_player_shuffle_changed_cb (GtkAction
*action
,
2063 RBShellPlayer
*player
)
2065 const char *neworder
;
2066 gboolean shuffle
= FALSE
;
2067 gboolean repeat
= FALSE
;
2069 if (player
->priv
->syncing_state
)
2072 rb_debug ("shuffle changed");
2074 rb_shell_player_get_playback_state (player
, &shuffle
, &repeat
);
2077 neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
2078 rb_shell_player_set_play_order (player
, neworder
);
2082 rb_shell_player_repeat_changed_cb (GtkAction
*action
,
2083 RBShellPlayer
*player
)
2085 const char *neworder
;
2086 gboolean shuffle
= FALSE
;
2087 gboolean repeat
= FALSE
;
2088 rb_debug ("repeat changed");
2090 if (player
->priv
->syncing_state
)
2093 rb_shell_player_get_playback_state (player
, &shuffle
, &repeat
);
2096 neworder
= state_to_play_order
[shuffle
? 1 : 0][repeat
? 1 : 0];
2097 rb_shell_player_set_play_order (player
, neworder
);
2101 rb_shell_player_view_song_position_slider_changed_cb (GtkAction
*action
,
2102 RBShellPlayer
*player
)
2104 eel_gconf_set_boolean (CONF_UI_SONG_POSITION_SLIDER_HIDDEN
,
2105 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)));
2109 rb_shell_player_entry_activated_cb (RBEntryView
*view
,
2110 RhythmDBEntry
*entry
,
2111 RBShellPlayer
*playa
)
2113 gboolean was_from_queue
= FALSE
;
2114 RhythmDBEntry
*prev_entry
= NULL
;
2115 GError
*error
= NULL
;
2116 gboolean source_set
= FALSE
;
2119 g_return_if_fail (entry
!= NULL
);
2121 rb_debug ("got entry %p activated", entry
);
2123 /* don't play hidden entries */
2124 if (rhythmdb_entry_get_boolean (entry
, RHYTHMDB_PROP_HIDDEN
))
2127 /* skip entries with no playback uri */
2128 playback_uri
= rhythmdb_entry_get_playback_uri (entry
);
2129 if (playback_uri
== NULL
) {
2132 g_free (playback_uri
);
2134 /* figure out where the previous entry came from */
2135 if ((playa
->priv
->queue_source
!= NULL
) &&
2136 (playa
->priv
->current_playing_source
== RB_SOURCE (playa
->priv
->queue_source
))) {
2137 prev_entry
= rb_shell_player_get_playing_entry (playa
);
2138 was_from_queue
= TRUE
;
2141 if (playa
->priv
->queue_source
) {
2142 RBEntryView
*queue_sidebar
;
2144 g_object_get (playa
->priv
->queue_source
, "sidebar", &queue_sidebar
, NULL
);
2146 if (view
== queue_sidebar
|| view
== rb_source_get_entry_view (RB_SOURCE (playa
->priv
->queue_source
))) {
2148 /* fall back to the current selected source once the queue is empty */
2149 if (view
== queue_sidebar
&& playa
->priv
->source
== NULL
) {
2150 rb_play_order_playing_source_changed (playa
->priv
->play_order
,
2151 playa
->priv
->selected_source
);
2152 playa
->priv
->source
= playa
->priv
->selected_source
;
2155 /* queue entry activated: move it to the start of the queue */
2156 rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (playa
->priv
->queue_source
), entry
, 0);
2157 rb_shell_player_set_playing_source (playa
, RB_SOURCE (playa
->priv
->queue_source
));
2159 /* since we just moved the entry, we should give it focus.
2160 * just calling rb_shell_player_jump_to_current here
2161 * looks terribly ugly, though. */
2162 g_idle_add ((GSourceFunc
)rb_shell_player_jump_to_current_idle
, playa
);
2163 was_from_queue
= FALSE
;
2166 if (playa
->priv
->queue_only
) {
2167 rb_source_add_to_queue (playa
->priv
->selected_source
,
2168 RB_SOURCE (playa
->priv
->queue_source
));
2169 rb_shell_player_set_playing_source (playa
, RB_SOURCE (playa
->priv
->queue_source
));
2174 g_object_unref (queue_sidebar
);
2177 /* bail out if queue only */
2178 if (playa
->priv
->queue_only
) {
2183 rb_shell_player_set_playing_source (playa
, playa
->priv
->selected_source
);
2187 if (!rb_shell_player_set_playing_entry (playa
, entry
, TRUE
, &error
)) {
2188 rb_shell_player_error (playa
, FALSE
, error
);
2189 g_clear_error (&error
);
2192 /* if we were previously playing from the queue, clear its playing entry,
2193 * so we'll start again from the start.
2195 if (was_from_queue
&& prev_entry
!= NULL
) {
2196 rb_play_order_set_playing_entry (playa
->priv
->queue_play_order
, NULL
);
2199 if (prev_entry
!= NULL
) {
2200 rhythmdb_entry_unref (prev_entry
);
2205 rb_shell_player_property_row_activated_cb (RBPropertyView
*view
,
2207 RBShellPlayer
*playa
)
2209 RhythmDBEntry
*entry
= NULL
;
2210 RhythmDBQueryModel
*model
;
2212 GError
*error
= NULL
;
2214 rb_debug ("got property activated");
2216 rb_shell_player_set_playing_source (playa
, playa
->priv
->selected_source
);
2218 /* RHYTHMDBFIXME - do we need to wait here until the query is finished?
2219 * in theory, yes, but in practice the query is started when the row is
2220 * selected (on the first click when doubleclicking, or when using the
2221 * keyboard to select then activate) and is pretty much always done by
2222 * the time we get in here.
2224 g_object_get (playa
->priv
->selected_source
, "query-model", &model
, NULL
);
2229 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model
), &iter
)) {
2233 entry
= rhythmdb_query_model_iter_to_entry (model
, &iter
);
2234 if (!rb_shell_player_set_playing_entry (playa
, entry
, TRUE
, &error
)) {
2235 rb_shell_player_error (playa
, FALSE
, error
);
2236 g_clear_error (&error
);
2239 if (entry
!= NULL
) {
2240 rhythmdb_entry_unref (entry
);
2244 g_object_unref (model
);
2248 rb_shell_player_entry_changed_cb (RhythmDB
*db
,
2249 RhythmDBEntry
*entry
,
2251 RBShellPlayer
*player
)
2254 gboolean synced
= FALSE
;
2255 const char *location
;
2256 RhythmDBEntry
*playing_entry
;
2258 playing_entry
= rb_shell_player_get_playing_entry (player
);
2260 /* We try to update only if the changed entry is currently playing */
2261 if (entry
!= playing_entry
) {
2262 if (playing_entry
!= NULL
) {
2263 rhythmdb_entry_unref (playing_entry
);
2268 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
2269 for (t
= changes
; t
; t
= t
->next
) {
2270 RhythmDBEntryChange
*change
= t
->data
;
2272 /* update UI if the artist, title or album has changed */
2273 switch (change
->prop
) {
2274 case RHYTHMDB_PROP_TITLE
:
2275 case RHYTHMDB_PROP_ARTIST
:
2276 case RHYTHMDB_PROP_ALBUM
:
2278 rb_shell_player_sync_with_source (player
);
2286 /* emit dbus signals for changes with easily marshallable types */
2287 switch (rhythmdb_get_property_type (db
, change
->prop
)) {
2289 case G_TYPE_BOOLEAN
:
2293 g_signal_emit (G_OBJECT (player
),
2294 rb_shell_player_signals
[PLAYING_SONG_PROPERTY_CHANGED
], 0,
2296 rhythmdb_nice_elt_name_from_propid (db
, change
->prop
),
2305 if (playing_entry
!= NULL
) {
2306 rhythmdb_entry_unref (playing_entry
);
2311 rb_shell_player_sync_with_source (RBShellPlayer
*player
)
2313 const char *entry_title
= NULL
;
2314 const char *artist
= NULL
;
2316 RhythmDBEntry
*entry
;
2318 entry
= rb_shell_player_get_playing_entry (player
);
2319 rb_debug ("playing source: %p, active entry: %p", player
->priv
->current_playing_source
, entry
);
2321 if (entry
!= NULL
) {
2322 entry_title
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE
);
2323 artist
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
);
2326 if (player
->priv
->song
&& entry_title
)
2327 title
= g_strdup_printf ("%s (%s)", player
->priv
->song
,
2329 else if (entry_title
&& artist
&& artist
[0] != '\0')
2330 title
= g_strdup_printf ("%s - %s", artist
, entry_title
);
2331 else if (entry_title
)
2332 title
= g_strdup (entry_title
);
2336 player
->priv
->elapsed
= rb_player_get_time (player
->priv
->mmplayer
);
2338 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[WINDOW_TITLE_CHANGED
], 0,
2340 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[ELAPSED_CHANGED
], 0,
2341 (guint
) player
->priv
->elapsed
);
2343 /* Sync the header */
2344 if (player
->priv
->song
)
2345 rb_header_set_title (player
->priv
->header_widget
, title
);
2347 rb_header_set_title (player
->priv
->header_widget
, entry_title
);
2350 rb_header_set_playing_entry (player
->priv
->header_widget
, entry
);
2351 rb_header_sync (player
->priv
->header_widget
);
2353 if (entry
!= NULL
) {
2354 rhythmdb_entry_unref (entry
);
2359 rb_shell_player_sync_buttons (RBShellPlayer
*player
)
2364 gboolean playing_from_queue
;
2366 int entry_view_state
;
2367 RhythmDBEntry
*entry
;
2369 entry
= rb_shell_player_get_playing_entry (player
);
2370 if (entry
!= NULL
) {
2371 source
= player
->priv
->current_playing_source
;
2372 entry_view_state
= rb_player_playing (player
->priv
->mmplayer
) ?
2373 RB_ENTRY_VIEW_PLAYING
: RB_ENTRY_VIEW_PAUSED
;
2375 source
= player
->priv
->selected_source
;
2376 entry_view_state
= RB_ENTRY_VIEW_NOT_PLAYING
;
2379 source
= (entry
== NULL
) ? player
->priv
->selected_source
: player
->priv
->current_playing_source
;
2381 playing_from_queue
= (source
== RB_SOURCE (player
->priv
->queue_source
));
2383 rb_debug ("syncing with source %p", source
);
2385 not_small
= !eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY
);
2386 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
2387 "ViewJumpToPlaying");
2388 g_object_set (action
,
2389 "sensitive", entry
!= NULL
&& not_small
, NULL
);
2391 if (source
!= NULL
) {
2392 view
= rb_source_get_entry_view (source
);
2394 rb_entry_view_set_state (view
, entry_view_state
);
2397 if (entry
!= NULL
) {
2398 rhythmdb_entry_unref (entry
);
2403 rb_shell_player_set_playing_source (RBShellPlayer
*player
,
2406 rb_shell_player_set_playing_source_internal (player
, source
, TRUE
);
2410 actually_set_playing_source (RBShellPlayer
*player
,
2412 gboolean sync_entry_view
)
2414 player
->priv
->source
= source
;
2415 player
->priv
->current_playing_source
= source
;
2417 if (source
!= NULL
) {
2418 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->source
);
2419 if (sync_entry_view
&& songs
) {
2420 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_PLAYING
);
2424 if (player
->priv
->play_order
&& source
!= RB_SOURCE (player
->priv
->queue_source
)) {
2426 source
= player
->priv
->selected_source
;
2427 rb_play_order_playing_source_changed (player
->priv
->play_order
, source
);
2430 rb_shell_player_play_order_update_cb (player
->priv
->play_order
,
2436 rb_shell_player_set_playing_source_internal (RBShellPlayer
*player
,
2438 gboolean sync_entry_view
)
2441 gboolean emit_source_changed
= TRUE
;
2442 gboolean emit_playing_from_queue_changed
= FALSE
;
2444 if (player
->priv
->source
== source
&&
2445 player
->priv
->current_playing_source
== source
&&
2449 rb_debug ("setting playing source to %p", source
);
2451 if (RB_SOURCE (player
->priv
->queue_source
) == source
) {
2453 if (player
->priv
->current_playing_source
!= source
)
2454 emit_playing_from_queue_changed
= TRUE
;
2456 if (player
->priv
->source
== NULL
) {
2457 actually_set_playing_source (player
, source
, sync_entry_view
);
2459 emit_source_changed
= FALSE
;
2460 player
->priv
->current_playing_source
= source
;
2464 if (player
->priv
->current_playing_source
!= source
) {
2465 if (player
->priv
->current_playing_source
== RB_SOURCE (player
->priv
->queue_source
))
2466 emit_playing_from_queue_changed
= TRUE
;
2468 /* stop the old source */
2469 if (player
->priv
->current_playing_source
!= NULL
) {
2470 if (sync_entry_view
) {
2471 RBEntryView
*songs
= rb_source_get_entry_view (player
->priv
->current_playing_source
);
2472 rb_debug ("source is already playing, stopping it");
2474 /* clear the playing entry if we're switching between non-queue sources */
2475 if (player
->priv
->current_playing_source
!= RB_SOURCE (player
->priv
->queue_source
))
2476 rb_play_order_set_playing_entry (player
->priv
->play_order
, NULL
);
2479 rb_entry_view_set_state (songs
, RB_ENTRY_VIEW_NOT_PLAYING
);
2483 actually_set_playing_source (player
, source
, sync_entry_view
);
2486 g_free (player
->priv
->url
);
2487 g_free (player
->priv
->song
);
2488 player
->priv
->song
= NULL
;
2489 player
->priv
->url
= NULL
;
2490 player
->priv
->have_url
= FALSE
;
2492 if (player
->priv
->current_playing_source
== NULL
)
2493 rb_shell_player_stop (player
);
2495 rb_shell_player_sync_with_source (player
);
2496 g_object_notify (G_OBJECT (player
), "playing");
2497 if (player
->priv
->selected_source
)
2498 rb_shell_player_sync_buttons (player
);
2500 if (emit_source_changed
) {
2501 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[PLAYING_SOURCE_CHANGED
],
2502 0, player
->priv
->source
);
2504 if (emit_playing_from_queue_changed
) {
2505 g_object_notify (G_OBJECT (player
), "playing-from-queue");
2510 * rb_shell_player_stop:
2511 * @player: a #RBShellPlayer.
2513 * Completely stops playback, freeing resources and unloading the file.
2515 * In general rb_shell_player_pause() should be used instead, as it stops the
2516 * audio, but does not completely free resources.
2520 rb_shell_player_stop (RBShellPlayer
*player
)
2522 GError
*error
= NULL
;
2523 rb_debug ("stopping");
2525 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
2528 rb_player_close (player
->priv
->mmplayer
, &error
);
2530 rb_error_dialog (NULL
,
2531 _("Couldn't stop playback"),
2532 "%s", error
->message
);
2533 g_error_free (error
);
2536 rb_shell_player_sync_with_source (player
);
2537 g_object_notify (G_OBJECT (player
), "playing");
2538 rb_shell_player_sync_buttons (player
);
2542 * rb_shell_player_pause:
2543 * @player: a #RBShellPlayer
2544 * @error: error return
2546 * Pauses playback if possible, completely stopping if not.
2548 * @return: whether playback is not occurring (TRUE when successfully
2549 * paused/stopped or playback was not occurring).
2553 rb_shell_player_pause (RBShellPlayer
*player
,
2556 if (rb_player_playing (player
->priv
->mmplayer
))
2557 return rb_shell_player_playpause (player
, FALSE
, error
);
2563 * rb_shell_player_get_playing:
2564 * @player: a #RBShellPlayer
2565 * @playing: playback state return
2566 * @error: error return
2568 * Reports whether playback is occuring by setting playing.
2570 * @return: whether the playback state could be reported successfully.
2573 rb_shell_player_get_playing (RBShellPlayer
*player
,
2577 if (playing
!= NULL
)
2578 *playing
= rb_player_playing (player
->priv
->mmplayer
);
2584 rb_shell_player_get_playing_time_string (RBShellPlayer
*player
)
2586 return rb_make_elapsed_time_string (player
->priv
->elapsed
,
2587 rb_shell_player_get_playing_song_duration (player
),
2588 !eel_gconf_get_boolean (CONF_UI_TIME_DISPLAY
));
2592 rb_shell_player_get_playing_time (RBShellPlayer
*player
,
2597 *time
= (guint
) rb_player_get_time (player
->priv
->mmplayer
);
2603 rb_shell_player_set_playing_time (RBShellPlayer
*player
,
2607 if (rb_player_seekable (player
->priv
->mmplayer
)) {
2608 rb_player_set_time (player
->priv
->mmplayer
, (long) time
);
2612 RB_SHELL_PLAYER_ERROR
,
2613 RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE
,
2614 _("Current song is not seekable"));
2620 rb_shell_player_seek (RBShellPlayer
*player
, long offset
)
2622 g_return_if_fail (RB_IS_SHELL_PLAYER (player
));
2624 if (rb_player_seekable (player
->priv
->mmplayer
)) {
2625 long t
= rb_player_get_time (player
->priv
->mmplayer
);
2626 rb_player_set_time (player
->priv
->mmplayer
, t
+ offset
);
2631 rb_shell_player_get_playing_song_duration (RBShellPlayer
*player
)
2633 RhythmDBEntry
*current_entry
;
2636 g_return_val_if_fail (RB_IS_SHELL_PLAYER (player
), -1);
2638 current_entry
= rb_shell_player_get_playing_entry (player
);
2640 if (current_entry
== NULL
) {
2641 rb_debug ("Did not get playing entry : return -1 as length");
2645 val
= rhythmdb_entry_get_ulong (current_entry
, RHYTHMDB_PROP_DURATION
);
2647 rhythmdb_entry_unref (current_entry
);
2653 rb_shell_player_sync_with_selected_source (RBShellPlayer
*player
)
2655 rb_debug ("syncing with selected source: %p", player
->priv
->selected_source
);
2656 if (player
->priv
->source
== NULL
)
2658 rb_debug ("no playing source, new source is %p", player
->priv
->selected_source
);
2660 player
->priv
->have_url
= rb_source_have_url (player
->priv
->selected_source
);
2662 rb_shell_player_sync_with_source (player
);
2667 rb_shell_player_error (RBShellPlayer
*player
,
2671 RhythmDBEntry
*entry
;
2673 g_return_if_fail (player
->priv
->handling_error
== FALSE
);
2675 player
->priv
->handling_error
= TRUE
;
2677 entry
= rb_shell_player_get_playing_entry (player
);
2679 rb_debug ("playback error while playing: %s", err
->message
);
2680 /* For synchronous errors the entry playback error has already been set */
2682 rb_shell_player_set_entry_playback_error (player
, entry
, err
->message
);
2684 if (err
->code
== RB_PLAYER_ERROR_NO_AUDIO
) {
2685 /* stream has completely ended */
2686 rb_shell_player_set_playing_source (player
, NULL
);
2687 } else if ((player
->priv
->current_playing_source
!= NULL
) &&
2688 (rb_source_handle_eos (player
->priv
->current_playing_source
) == RB_SOURCE_EOF_RETRY
)) {
2689 /* receiving an error means a broken stream or non-audio stream, so abort */
2690 rb_error_dialog (NULL
,
2691 _("Couldn't start playback"),
2692 "%s", (err
) ? err
->message
: "(null)");
2693 rb_shell_player_set_playing_source (player
, NULL
);
2694 } else if (player
->priv
->do_next_idle_id
== 0) {
2695 player
->priv
->do_next_idle_id
= g_idle_add ((GSourceFunc
)do_next_idle
, player
);
2698 player
->priv
->handling_error
= FALSE
;
2700 if (entry
!= NULL
) {
2701 rhythmdb_entry_unref (entry
);
2706 error_cb (RBPlayer
*mmplayer
,
2710 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2712 if (player
->priv
->handling_error
)
2715 if (player
->priv
->source
== NULL
) {
2716 rb_debug ("ignoring error (no source): %s", err
->message
);
2720 GDK_THREADS_ENTER ();
2722 rb_shell_player_error (player
, TRUE
, err
);
2724 rb_debug ("exiting error hander");
2725 GDK_THREADS_LEAVE ();
2729 tick_cb (RBPlayer
*mmplayer
,
2733 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2735 GDK_THREADS_ENTER ();
2737 if (rb_player_playing (mmplayer
)) {
2738 if (player
->priv
->elapsed
!= elapsed
) {
2739 player
->priv
->elapsed
= elapsed
;
2740 g_signal_emit (G_OBJECT (player
), rb_shell_player_signals
[ELAPSED_CHANGED
],
2741 0, (guint
) elapsed
);
2745 GDK_THREADS_LEAVE ();
2749 info_available_cb (RBPlayer
*mmplayer
,
2750 RBMetaDataField field
,
2754 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2755 RhythmDBEntry
*entry
;
2756 RhythmDBPropType entry_field
= 0;
2757 gboolean changed
= FALSE
;
2758 gboolean set_field
= FALSE
;
2759 RhythmDBEntryType type
;
2761 rb_debug ("info: %d", field
);
2763 /* Sanity check, this signal may come in after we stopped the
2765 if (player
->priv
->source
== NULL
2766 || !rb_player_opened (player
->priv
->mmplayer
)) {
2767 rb_debug ("Got info_available but no playing source!");
2771 GDK_THREADS_ENTER ();
2773 entry
= rb_shell_player_get_playing_entry (player
);
2774 if (entry
== NULL
) {
2775 rb_debug ("Got info_available but no playing entry!");
2779 type
= rhythmdb_entry_get_entry_type (entry
);
2780 if (type
!= RHYTHMDB_ENTRY_TYPE_IRADIO_STATION
) {
2781 rb_debug ("Got info_available but entry isn't an iradio station");
2786 case RB_METADATA_FIELD_TITLE
:
2788 char *song
= g_value_dup_string (value
);
2789 if (!g_utf8_validate (song
, -1, NULL
)) {
2790 g_warning ("Invalid UTF-8 from internet radio: %s", song
);
2794 if ((!song
&& player
->priv
->song
)
2795 || !player
->priv
->song
2796 || strcmp (song
, player
->priv
->song
)) {
2798 g_free (player
->priv
->song
);
2799 player
->priv
->song
= song
;
2800 g_object_notify (G_OBJECT (player
), "stream-song");
2806 case RB_METADATA_FIELD_GENRE
:
2808 const char *genre
= g_value_get_string (value
);
2809 const char *existing
;
2810 if (!g_utf8_validate (genre
, -1, NULL
)) {
2811 g_warning ("Invalid UTF-8 from internet radio: %s", genre
);
2815 /* check if the db entry already has a genre; if so, don't change it */
2816 existing
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_GENRE
);
2817 if ((existing
== NULL
) ||
2818 (strcmp (existing
, "") == 0) ||
2819 (strcmp (existing
, _("Unknown")) == 0)) {
2820 entry_field
= RHYTHMDB_PROP_GENRE
;
2821 rb_debug ("setting genre of iradio station to %s", genre
);
2824 rb_debug ("iradio station already has genre: %s; ignoring %s", existing
, genre
);
2828 case RB_METADATA_FIELD_COMMENT
:
2830 const char *name
= g_value_get_string (value
);
2831 const char *existing
;
2832 const char *location
;
2834 if (!g_utf8_validate (name
, -1, NULL
)) {
2835 g_warning ("Invalid UTF-8 from internet radio: %s", name
);
2839 /* check if the db entry already has a title; if so, don't change it.
2840 * consider title==URI to be the same as no title, since that's what
2841 * happens for stations imported by DnD or commandline args.
2842 * if the station title really is the same as the URI, then surely
2843 * the station title in the stream metadata will say that too..
2845 existing
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE
);
2846 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
2847 if ((existing
== NULL
) ||
2848 (strcmp (existing
, "") == 0) ||
2849 (strcmp (existing
, location
) == 0)) {
2850 entry_field
= RHYTHMDB_PROP_TITLE
;
2851 rb_debug ("setting title of iradio station to %s", name
);
2854 rb_debug ("iradio station already has title: %s; ignoring %s", existing
, name
);
2858 case RB_METADATA_FIELD_BITRATE
:
2859 if (!rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_BITRATE
)) {
2862 /* GStreamer sends us bitrate in bps, but we need it in kbps*/
2863 bitrate
= g_value_get_ulong (value
);
2864 g_value_set_ulong (value
, bitrate
/1000);
2866 rb_debug ("setting bitrate of iradio station to %lu",
2867 g_value_get_ulong (value
));
2868 entry_field
= RHYTHMDB_PROP_BITRATE
;
2877 rb_shell_player_sync_with_source (player
);
2879 if (set_field
&& entry_field
!= 0) {
2880 rhythmdb_entry_set (player
->priv
->db
, entry
, entry_field
, value
);
2881 rhythmdb_commit (player
->priv
->db
);
2885 if (entry
!= NULL
) {
2886 rhythmdb_entry_unref (entry
);
2889 GDK_THREADS_LEAVE ();
2893 buffering_cb (RBPlayer
*mmplayer
,
2897 RBShellPlayer
*player
= RB_SHELL_PLAYER (data
);
2899 GDK_THREADS_ENTER ();
2900 rb_statusbar_set_progress (player
->priv
->statusbar_widget
, ((double)progress
)/100, _("Buffering"));
2901 GDK_THREADS_LEAVE ();
2905 rb_shell_player_get_playing_path (RBShellPlayer
*shell_player
,
2909 RhythmDBEntry
*entry
;
2911 entry
= rb_shell_player_get_playing_entry (shell_player
);
2912 if (entry
!= NULL
) {
2913 *path
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
2918 if (entry
!= NULL
) {
2919 rhythmdb_entry_unref (entry
);
2927 grab_mmkey (int key_code
,
2930 gdk_error_trap_push ();
2932 XGrabKey (GDK_DISPLAY (), key_code
,
2934 GDK_WINDOW_XID (root
), True
,
2935 GrabModeAsync
, GrabModeAsync
);
2936 XGrabKey (GDK_DISPLAY (), key_code
,
2938 GDK_WINDOW_XID (root
), True
,
2939 GrabModeAsync
, GrabModeAsync
);
2940 XGrabKey (GDK_DISPLAY (), key_code
,
2942 GDK_WINDOW_XID (root
), True
,
2943 GrabModeAsync
, GrabModeAsync
);
2944 XGrabKey (GDK_DISPLAY (), key_code
,
2946 GDK_WINDOW_XID (root
), True
,
2947 GrabModeAsync
, GrabModeAsync
);
2948 XGrabKey (GDK_DISPLAY (), key_code
,
2949 Mod2Mask
| Mod5Mask
,
2950 GDK_WINDOW_XID (root
), True
,
2951 GrabModeAsync
, GrabModeAsync
);
2952 XGrabKey (GDK_DISPLAY (), key_code
,
2953 Mod2Mask
| LockMask
,
2954 GDK_WINDOW_XID (root
), True
,
2955 GrabModeAsync
, GrabModeAsync
);
2956 XGrabKey (GDK_DISPLAY (), key_code
,
2957 Mod5Mask
| LockMask
,
2958 GDK_WINDOW_XID (root
), True
,
2959 GrabModeAsync
, GrabModeAsync
);
2960 XGrabKey (GDK_DISPLAY (), key_code
,
2961 Mod2Mask
| Mod5Mask
| LockMask
,
2962 GDK_WINDOW_XID (root
), True
,
2963 GrabModeAsync
, GrabModeAsync
);
2966 if (gdk_error_trap_pop ()) {
2967 rb_debug ("Error grabbing key");
2971 static GdkFilterReturn
2972 filter_mmkeys (GdkXEvent
*xevent
,
2978 RBShellPlayer
*player
;
2979 xev
= (XEvent
*) xevent
;
2980 if (xev
->type
!= KeyPress
) {
2981 return GDK_FILTER_CONTINUE
;
2984 key
= (XKeyEvent
*) xevent
;
2986 player
= (RBShellPlayer
*)data
;
2988 if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay
) == key
->keycode
) {
2989 rb_shell_player_playpause (player
, FALSE
, NULL
);
2990 return GDK_FILTER_REMOVE
;
2991 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause
) == key
->keycode
) {
2992 rb_shell_player_pause (player
, NULL
);
2993 return GDK_FILTER_REMOVE
;
2994 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop
) == key
->keycode
) {
2995 rb_shell_player_stop (player
);
2996 return GDK_FILTER_REMOVE
;
2997 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev
) == key
->keycode
) {
2998 rb_shell_player_cmd_previous (NULL
, player
);
2999 return GDK_FILTER_REMOVE
;
3000 } else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext
) == key
->keycode
) {
3001 rb_shell_player_cmd_next (NULL
, player
);
3002 return GDK_FILTER_REMOVE
;
3004 return GDK_FILTER_CONTINUE
;
3009 rb_shell_player_init_mmkeys (RBShellPlayer
*shell_player
)
3011 gint keycodes
[] = {0, 0, 0, 0, 0};
3012 GdkDisplay
*display
;
3017 keycodes
[0] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay
);
3018 keycodes
[1] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop
);
3019 keycodes
[2] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev
);
3020 keycodes
[3] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext
);
3021 keycodes
[4] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause
);
3023 display
= gdk_display_get_default ();
3025 for (i
= 0; i
< gdk_display_get_n_screens (display
); i
++) {
3026 screen
= gdk_display_get_screen (display
, i
);
3028 if (screen
!= NULL
) {
3029 root
= gdk_screen_get_root_window (screen
);
3031 for (j
= 0; j
< G_N_ELEMENTS (keycodes
) ; j
++) {
3032 if (keycodes
[j
] != 0)
3033 grab_mmkey (keycodes
[j
], root
);
3036 gdk_window_add_filter (root
, filter_mmkeys
,
3037 (gpointer
) shell_player
);
3041 #endif /* HAVE_MMKEYS */
3044 _idle_unblock_signal_cb (gpointer data
)
3046 RBShellPlayer
*player
= (RBShellPlayer
*)data
;
3050 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
3053 /* sync the active state of the action again */
3054 g_object_get (player
, "playing", &playing
, NULL
);
3055 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), playing
);
3057 g_signal_handlers_unblock_by_func (action
, rb_shell_player_cmd_play
, player
);
3062 rb_shell_player_playing_changed_cb (RBShellPlayer
*player
,
3070 g_object_get (player
, "playing", &playing
, NULL
);
3071 action
= gtk_action_group_get_action (player
->priv
->actiongroup
,
3074 tooltip
= g_strdup (_("Stop playback"));
3076 tooltip
= g_strdup (_("Start playback"));
3078 g_object_set (action
, "tooltip", tooltip
, NULL
);
3081 /* block the signal, so that it doesn't get stuck by triggering recursively,
3082 * and don't unblock it until whatever else is happening has finished.
3084 g_signal_handlers_block_by_func (action
, rb_shell_player_cmd_play
, player
);
3085 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), playing
);
3086 g_idle_add (_idle_unblock_signal_cb
, player
);
3089 /* This should really be standard. */
3090 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
3093 rb_shell_player_error_get_type (void)
3095 static GType etype
= 0;
3098 static const GEnumValue values
[] = {
3099 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_PLAYLIST_PARSE_ERROR
, "Playing parsing error"),
3100 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_END_OF_PLAYLIST
, "End of playlist reached"),
3101 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_PLAYING
, "Not playing"),
3102 ENUM_ENTRY (RB_SHELL_PLAYER_ERROR_NOT_SEEKABLE
, "Not seekable"),
3106 etype
= g_enum_register_static ("RBShellPlayerError", values
);