Index: player/rb-player-gst.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/player/rb-player-gst.c,v
retrieving revision 1.37
diff -u -r1.37 rb-player-gst.c
--- player/rb-player-gst.c	21 Jul 2005 14:10:21 -0000	1.37
+++ player/rb-player-gst.c	29 Jul 2005 10:34:18 -0000
@@ -52,6 +52,7 @@
 	gboolean playing;
 
 	guint error_signal_id;
+	guint buffering_signal_id;
 
 	float cur_volume;
 
@@ -64,11 +65,13 @@
 	INFO,
 	ERROR,
 	TICK,
+	BUFFERING,
 	LAST_SIGNAL
 } RBPlayerSignalType;
 
 typedef struct
 {
+	int type;
 	RBPlayer *object;
 	RBMetaDataField info_field;
 	GError *error;
@@ -125,6 +128,16 @@
 			      G_TYPE_NONE,
 			      1,
 			      G_TYPE_LONG);
+	rb_player_signals[BUFFERING] =
+		g_signal_new ("buffering",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBPlayerClass, buffering),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__UINT,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_UINT);
 }
 
 static gboolean
@@ -161,6 +174,8 @@
 	if (mp->priv->playbin) {
 		g_signal_handler_disconnect (G_OBJECT (mp->priv->playbin),
 					     mp->priv->error_signal_id);
+		g_signal_handler_disconnect (G_OBJECT (mp->priv->playbin),
+					     mp->priv->buffering_signal_id);
 		
 		gst_element_set_state (mp->priv->playbin,
 				       GST_STATE_NULL);
@@ -184,28 +199,46 @@
 }
 
 static gboolean
-eos_signal_idle (RBPlayer *mp)
-{
-	g_signal_emit (G_OBJECT (mp), rb_player_signals[EOS], 0);
-
-	g_object_unref (G_OBJECT (mp));
-
-	return FALSE;
-}
-
-static gboolean
-error_signal_idle (RBPlayerSignal *signal)
+emit_signal_idle (RBPlayerSignal *signal)
 {
-	g_signal_emit (G_OBJECT (signal->object),
-		       rb_player_signals[ERROR], 0,
-		       signal->error);
-
-	/* close if not already closing */
-	if (signal->object->priv->uri != NULL)
-		rb_player_close (signal->object, NULL);
-
+	switch (signal->type) {
+	case ERROR:
+		g_signal_emit (G_OBJECT (signal->object),
+			       rb_player_signals[ERROR], 0,
+			       signal->error);
+
+		/* close if not already closing */
+		if (signal->object->priv->uri != NULL)
+			rb_player_close (signal->object, NULL);
+
+		break;
+
+	case EOS:
+		g_signal_emit (G_OBJECT (signal->object), rb_player_signals[EOS], 0);
+		break;
+
+	case INFO:
+		g_signal_emit (G_OBJECT (signal->object),
+			       rb_player_signals[INFO], 0,
+			       signal->info_field, signal->info);
+		break;
+
+	case BUFFERING:
+		g_signal_emit (G_OBJECT (signal->object),
+			       rb_player_signals[BUFFERING], 0,
+			       g_value_get_uint (signal->info));
+		break;
+	}
+
+	if (signal->error)
+		g_error_free (signal->error);
+
+	if (signal->info) {
+		g_value_unset (signal->info);
+		g_free (signal->info);
+	}
+	
 	g_object_unref (G_OBJECT (signal->object));
-	g_error_free (signal->error);
 	g_free (signal);
 
 	return FALSE;
@@ -215,24 +248,12 @@
 eos_cb (GstElement *element,
 	RBPlayer *mp)
 {
-	g_object_ref (G_OBJECT (mp));
-	g_idle_add ((GSourceFunc) eos_signal_idle, mp);
-}
-
-static void
-rb_player_gst_signal_error (RBPlayer *mp, int code, const char *msg)
-{
 	RBPlayerSignal *signal;
-
 	signal = g_new0 (RBPlayerSignal, 1);
+	signal->type = EOS;
 	signal->object = mp;
-	signal->error = g_error_new_literal (RB_PLAYER_ERROR,
-					     code,
-					     msg);
-
 	g_object_ref (G_OBJECT (mp));
-
-	g_idle_add ((GSourceFunc) error_signal_idle, signal);
+	g_idle_add ((GSourceFunc) emit_signal_idle, signal);
 }
 
 static void
@@ -243,6 +264,7 @@
 	  RBPlayer *mp)
 {
 	int code;
+	RBPlayerSignal *signal;
 
 	if ((error->domain == GST_CORE_ERROR)
 	    || (error->domain == GST_LIBRARY_ERROR)
@@ -252,21 +274,16 @@
 		code = RB_PLAYER_ERROR_GENERAL;
 	}
 
-	rb_player_gst_signal_error (mp, code, error->message);
-}
-
-static gboolean
-info_signal_idle (RBPlayerSignal *signal)
-{
-	g_signal_emit (G_OBJECT (signal->object),
-		       rb_player_signals[INFO], 0,
-		       signal->info_field, signal->info);
+	signal = g_new0 (RBPlayerSignal, 1);
+	signal->type = ERROR;
+	signal->object = mp;
+	signal->error = g_error_new_literal (RB_PLAYER_ERROR,
+					     code,
+					     error->message);
 
-	g_object_unref (G_OBJECT (signal->object));
-	g_free (signal->info);
-	g_free (signal);
+	g_object_ref (G_OBJECT (mp));
 
-	return FALSE;
+	g_idle_add ((GSourceFunc) emit_signal_idle, signal);
 }
 
 static void
@@ -282,6 +299,7 @@
 
 		signal = g_new0 (RBPlayerSignal, 1);
 
+		signal->type = INFO;
 		signal->info_field = RB_METADATA_FIELD_TITLE;
 		signal->info = g_new0 (GValue, 1);
 		g_value_init (signal->info, G_TYPE_STRING); 
@@ -291,12 +309,29 @@
 
 		g_object_ref (G_OBJECT (player));
 
-		g_idle_add ((GSourceFunc) info_signal_idle, signal);
+		g_idle_add ((GSourceFunc) emit_signal_idle, signal);
 		return;
 	}
 }
 
 static void
+buffering_cb (GstElement *element, gint progress, RBPlayer *mp)
+{
+	RBPlayerSignal *signal;
+
+	signal = g_new0 (RBPlayerSignal, 1);
+	signal->type = BUFFERING;
+
+	g_object_ref (G_OBJECT (mp));
+	signal->object = mp;
+
+	signal->info = g_new0 (GValue, 1);
+	g_value_init (signal->info, G_TYPE_UINT);
+	g_value_set_uint (signal->info, (guint)progress);
+	g_idle_add ((GSourceFunc) emit_signal_idle, signal);
+}
+
+static void
 rb_player_construct (RBPlayer *mp, GError **error)
 {
 	char *element_name = NULL;
@@ -313,6 +348,12 @@
 				 G_CALLBACK (deep_notify_cb),
 				 mp, 0);
 
+	mp->priv->buffering_signal_id =
+		g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+					 "buffering",
+					 G_CALLBACK (buffering_cb),
+					 mp, 0);
+	
 	mp->priv->error_signal_id =
 		g_signal_connect_object (G_OBJECT (mp->priv->playbin),
 					 "error",
Index: player/rb-player.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/player/rb-player.h,v
retrieving revision 1.8
diff -u -r1.8 rb-player.h
--- player/rb-player.h	26 Mar 2004 02:41:54 -0000	1.8
+++ player/rb-player.h	29 Jul 2005 10:34:18 -0000
@@ -69,9 +69,7 @@
 
 	void (*eos)             	(RBPlayer *mp);
 	void (*info)            	(RBPlayer *mp, RBMetaDataField field, GValue *value);
-	void (*buffering_begin) 	(RBPlayer *mp);
-	void (*buffering_end)   	(RBPlayer *mp);
-	void (*buffering_progress)	(RBPlayer *mp, int progress);
+	void (*buffering)		(RBPlayer *mp, guint progress);
 	void (*error)           	(RBPlayer *mp, GError *error);
 	void (*tick)            	(RBPlayer *mp, long elapsed);
 } RBPlayerClass;
Index: rhythmdb/rhythmdb.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.c,v
retrieving revision 1.102
diff -u -r1.102 rhythmdb.c
--- rhythmdb/rhythmdb.c	27 Jul 2005 16:44:41 -0000	1.102
+++ rhythmdb/rhythmdb.c	29 Jul 2005 10:34:25 -0000
@@ -2716,15 +2716,10 @@
 	return g_async_queue_length (queue) <= 0;
 }
 
-char *
-rhythmdb_get_status (RhythmDB *db)
+gboolean
+rhythmdb_is_busy (RhythmDB *db)
 {
-	char *ret = NULL;
-
-	if (!queue_is_empty (db->priv->event_queue))
-		ret = g_strdup_printf ("<b>%s</b>", _("Loading..."));
-
-	return ret;
+	return (!queue_is_empty (db->priv->event_queue));
 }
 
 char *
Index: rhythmdb/rhythmdb.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.h,v
retrieving revision 1.41
diff -u -r1.41 rhythmdb.h
--- rhythmdb/rhythmdb.h	5 Jul 2005 09:15:43 -0000	1.41
+++ rhythmdb/rhythmdb.h	29 Jul 2005 10:34:26 -0000
@@ -450,7 +450,7 @@
 void		rhythmdb_emit_entry_added		(RhythmDB *db, RhythmDBEntry *entry);
 void		rhythmdb_emit_entry_deleted		(RhythmDB *db, RhythmDBEntry *entry);
 
-char *		rhythmdb_get_status			(RhythmDB *db);
+gboolean	rhythmdb_is_busy			(RhythmDB *db);
 char *		rhythmdb_compute_status_normal		(gint n_songs, glong duration,
 							 GnomeVFSFileSize size);
 RhythmDBEntryType rhythmdb_entry_register_type          (void);
Index: shell/rb-shell-player.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell-player.c,v
retrieving revision 1.157
diff -u -r1.157 rb-shell-player.c
--- shell/rb-shell-player.c	20 Jul 2005 14:20:32 -0000	1.157
+++ shell/rb-shell-player.c	29 Jul 2005 10:34:30 -0000
@@ -58,6 +58,7 @@
 #include "eel-gconf-extensions.h"
 #include "rb-util.h"
 #include "rb-play-order.h"
+#include "rb-statusbar.h"
 
 #ifdef HAVE_XIDLE_EXTENSION
 #include <X11/extensions/xidle.h>
@@ -136,6 +137,7 @@
 static void tick_cb (RBPlayer *player, long elapsed, gpointer data);
 static void eos_cb (RBPlayer *player, gpointer data);
 static void error_cb (RBPlayer *player, GError *err, gpointer data);
+static void buffering_cb (RBPlayer *player, guint progress, gpointer data);
 static void rb_shell_player_error (RBShellPlayer *player, GError *err,
 				   gboolean lock);
 
@@ -217,6 +219,7 @@
 	GtkWidget *next_button;
 
 	RBHeader *header_widget;
+	RBStatusbar *statusbar_widget;
 
 	GtkWidget *shuffle_button;
 	GtkWidget *volume_button;
@@ -240,7 +243,8 @@
 	PROP_ACTION_GROUP,
 	PROP_PLAY_ORDER,
 	PROP_PLAYING,
-	PROP_VOLUME
+	PROP_VOLUME,
+	PROP_STATUSBAR
 };
 
 enum
@@ -398,6 +402,15 @@
 							     "Current playback volume",
 							     0.0f, 1.0f, 1.0f,
 							     G_PARAM_READWRITE));
+	
+	g_object_class_install_property (object_class,
+					 PROP_STATUSBAR,
+					 g_param_spec_object ("statusbar", 
+							      "RBStatusbar", 
+							      "RBStatusbar object", 
+							      RB_TYPE_STATUSBAR,
+							      G_PARAM_READWRITE));
+
 
 	rb_shell_player_signals[WINDOW_TITLE_CHANGED] =
 		g_signal_new ("window_title_changed",
@@ -575,6 +588,11 @@
 				 G_CALLBACK (error_cb),
 				 player, 0);
 
+	g_signal_connect_object (G_OBJECT (player->priv->mmplayer),
+				 "buffering",
+				 G_CALLBACK (buffering_cb),
+				 player, 0);
+
 	g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()), 
 			  "volume-pre-unmount",
 			  G_CALLBACK (volume_pre_unmount_cb),
@@ -779,7 +797,9 @@
 		eel_gconf_set_float (CONF_STATE_VOLUME, 
 				     g_value_get_float (value));
 		break;
-	
+	case PROP_STATUSBAR:
+		player->priv->statusbar_widget = g_value_get_object (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -822,6 +842,9 @@
 	case PROP_VOLUME:
 		g_value_set_float (value, rb_player_get_volume (player->priv->mmplayer));
 		break;
+	case PROP_STATUSBAR:
+		g_value_set_object (value, player->priv->statusbar_widget);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -969,6 +992,8 @@
 	if (error && *error)
 		return;
 
+	rb_statusbar_set_progress (player->priv->statusbar_widget, -1.0, NULL);
+	
 	rb_player_play (player->priv->mmplayer, error);
 	if (error && *error)
 		return;
@@ -2156,6 +2181,16 @@
 	GDK_THREADS_LEAVE ();
 }
 
+static void
+buffering_cb (RBPlayer *mmplayer, guint progress, gpointer data)
+{
+ 	RBShellPlayer *player = RB_SHELL_PLAYER (data);
+
+	GDK_THREADS_ENTER ();
+	rb_statusbar_set_progress (player->priv->statusbar_widget, ((double)progress)/100, _("Buffering"));
+	GDK_THREADS_LEAVE ();
+}
+
 
 const char *
 rb_shell_player_get_playing_path (RBShellPlayer *shell_player)
Index: shell/rb-shell.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell.c,v
retrieving revision 1.306
diff -u -r1.306 rb-shell.c
--- shell/rb-shell.c	26 Jul 2005 14:07:28 -0000	1.306
+++ shell/rb-shell.c	29 Jul 2005 10:34:35 -0000
@@ -930,6 +930,7 @@
 	shell->priv->statusbar = rb_statusbar_new (shell->priv->db,
 						   shell->priv->actiongroup,
 						   shell->priv->player_shell);
+	g_object_set (shell->priv->player_shell, "statusbar", shell->priv->statusbar, NULL);
 
 	rb_sourcelist_set_dnd_targets (RB_SOURCELIST (shell->priv->sourcelist), target_table,
 				       G_N_ELEMENTS (target_table));
Index: shell/rb-statusbar.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-statusbar.c,v
retrieving revision 1.38
diff -u -r1.38 rb-statusbar.c
--- shell/rb-statusbar.c	19 Jun 2005 18:30:57 -0000	1.38
+++ shell/rb-statusbar.c	29 Jul 2005 10:34:37 -0000
@@ -34,6 +34,8 @@
 #include "eel-gconf-extensions.h"
 #include "rb-play-order.h"
 
+#define EPSILON		(0.00001)
+
 static GObject* rb_statusbar_construct (GType type,
 					guint n_construct_properties,
 					GObjectConstructParam *construct_properties);
@@ -92,11 +94,15 @@
         GtkWidget *status;
 
         GtkWidget *progress;
+        double progress_fraction;
+        gboolean progress_changed;
+        gchar *progress_text;
+
+        gchar *loading_text;
 
         guint status_poll_id;
 
-        gboolean entry_view_busy;
-        gboolean library_busy;
+        gboolean idle;
         guint idle_tick_id;
 
 	guint notify_id;
@@ -247,6 +253,9 @@
         gtk_box_set_spacing (GTK_BOX (statusbar), 5);
 
         statusbar->priv->progress = gtk_progress_bar_new ();
+        statusbar->priv->progress_fraction = 1.0;
+
+        statusbar->priv->loading_text = g_strdup_printf ("<b>%s</b>", _("Loading..."));
 
         gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (statusbar->priv->progress), 1.0);
 	gtk_widget_hide (statusbar->priv->progress);
@@ -406,60 +415,87 @@
 static void
 rb_statusbar_sync_status (RBStatusbar *status)
 {
-        char *str;
-        gboolean library_busy_changed;
-        gboolean library_is_busy;
-        gboolean entry_view_busy = FALSE;
         gboolean changed = FALSE;
+        gboolean show_progress = TRUE;
+        gboolean pulse_progress = FALSE;
+        char *status_text = NULL;
+
+        /*
+         * Behaviour of status bar.
+         * progress bar:
+         * - if we have a progress fraction, display it
+         * - otherwise, if the library or selected entry view are busy, pulse
+         * - otherwise, hide.
+         * 
+         * status text:
+         * - if we have a progress fraction, display its text
+         * - otherwise, if the library is busy, display its text
+         * - otherwise, display the selected source's status text
+         */
+
+        /* 1. progress bar moving? */
+        if (status->priv->progress_fraction < (1.0 - EPSILON) ||
+            status->priv->progress_changed) {
+                status_text = status->priv->progress_text;
+                status->priv->progress_changed = FALSE;
+        }
 
-        str = rhythmdb_get_status (status->priv->db);
+        /* 2. library busy? */
+        if (status_text == NULL && rhythmdb_is_busy (status->priv->db)) {
+                status_text = status->priv->loading_text;
+                pulse_progress = TRUE;
+        }
 
-        if (!str && status->priv->selected_source) {
+        /* 3. entry view busy? */
+        if (status_text == NULL && status->priv->selected_source) {
                 RBEntryView *view;
                 view = rb_source_get_entry_view (status->priv->selected_source);
-                entry_view_busy = rb_entry_view_busy (view);
+                if (rb_entry_view_busy (view))
+                        pulse_progress = TRUE;
+                else
+                        show_progress = FALSE;
         }
 
-        library_is_busy = str != NULL;
-        library_busy_changed = status->priv->library_busy && !library_is_busy;
-        status->priv->library_busy = library_is_busy;   
-
-        /* Set up the status display */
-        if (status->priv->library_busy) {
-                gtk_label_set_markup (GTK_LABEL (status->priv->status), str);
-                g_free (str);
-                str = NULL;
+        /* set up the status text */
+        if (status_text) {
+                gtk_label_set_markup (GTK_LABEL (status->priv->status), status_text);
+
                 changed = TRUE;
-	} else if (library_busy_changed) {
+                status->priv->idle = FALSE;
+        } else if (!status->priv->idle) {
                 rb_statusbar_sync_with_source (status);
                 changed = TRUE;
+                status->priv->idle = TRUE;
+        }
+
+        if (!pulse_progress && status->priv->idle_tick_id > 0) {
+                g_source_remove (status->priv->idle_tick_id);
+                status->priv->idle_tick_id = 0;
+                changed = TRUE;
         }
-        g_free (str);
 
         /* Sync the progress bar */
-	if (library_is_busy || entry_view_busy) {
-		gtk_widget_show (status->priv->progress);
+        if (show_progress) {
+                gtk_widget_show (status->priv->progress);
 
-                if (status->priv->idle_tick_id == 0) {
-                        status->priv->idle_tick_id
-                                = g_timeout_add (250, (GSourceFunc) status_tick_cb,
-                                                 status->priv->progress);
+                if (pulse_progress) {
+                        if (status->priv->idle_tick_id == 0) {
+                                status->priv->idle_tick_id
+                                        = g_timeout_add (250, (GSourceFunc) status_tick_cb,
+                                                         status->priv->progress);
+                        }
                         changed = TRUE;
+                } else {
+                        gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress),
+                                                       status->priv->progress_fraction);
                 }
         } else {
-                if (status->priv->idle_tick_id > 0) {
-                        g_source_remove (status->priv->idle_tick_id);
-                        status->priv->idle_tick_id = 0;
-                        changed = TRUE;
-                }
-                gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress),
-                                               1.0);
-
-		gtk_widget_hide (status->priv->progress);
+                gtk_widget_hide (status->priv->progress);
         }
 
-        status->priv->status_poll_id = 
-                g_timeout_add (changed ? 350 : 750, (GSourceFunc) poll_status, status);
+        if (status->priv->status_poll_id == 0)
+                status->priv->status_poll_id = 
+                        g_timeout_add (changed ? 350 : 750, (GSourceFunc) poll_status, status);
 }
 
 static gboolean
@@ -467,6 +503,7 @@
 {
         GDK_THREADS_ENTER ();
 
+        status->priv->status_poll_id = 0;
         rb_statusbar_sync_status (status);
 
         GDK_THREADS_LEAVE ();
@@ -489,6 +526,26 @@
         return statusbar;
 }
 
+void
+rb_statusbar_set_progress (RBStatusbar *statusbar, double progress, const char *text)
+{
+        if (statusbar->priv->progress_text) {
+                g_free (statusbar->priv->progress_text);
+                statusbar->priv->progress_text = NULL;
+        }
+        
+        if (progress > 0.0) {
+                statusbar->priv->progress_fraction = progress;
+                statusbar->priv->progress_changed = TRUE;
+                statusbar->priv->progress_text = g_strdup_printf ("<b>%s</b>", text);
+        } else {
+                /* trick sync_status into hiding it */
+                statusbar->priv->progress_fraction = 1.0;
+                statusbar->priv->progress_changed = FALSE;
+        }
+        rb_statusbar_sync_status (statusbar);
+}
+
 static void
 rb_statusbar_sync_with_source (RBStatusbar *statusbar)
 {
@@ -595,6 +652,6 @@
                                     RBStatusbar *statusbar)
 {
         rb_debug ("entry view changed");
-        if (!(statusbar->priv->library_busy || statusbar->priv->entry_view_busy))
+        if (statusbar->priv->idle)
                 rb_statusbar_sync_with_source (statusbar);
 }
Index: shell/rb-statusbar.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-statusbar.h,v
retrieving revision 1.11
diff -u -r1.11 rb-statusbar.h
--- shell/rb-statusbar.h	14 Jul 2004 03:13:03 -0000	1.11
+++ shell/rb-statusbar.h	29 Jul 2005 10:34:38 -0000
@@ -61,7 +61,10 @@
 void			rb_statusbar_set_source	(RBStatusbar *bar,
 						 RBSource *player);
 
-void			rb_statusbar_set_progress(RBStatusbar *bar, double progress);
+void			rb_statusbar_set_progress(RBStatusbar *bar, 
+						  double progress,
+						  const char *text);
+
 void			rb_statusbar_sync_state (RBStatusbar *statusbar);
 
 G_END_DECLS
