--- plugins/artdisplay/artdisplay/__init__.py	2006/12/30 04:37:23	1.1
+++ plugins/artdisplay/artdisplay/__init__.py	2006/12/30 05:17:35
@@ -250,11 +250,14 @@ class ArtDisplayPlugin (rb.Plugin):
 		sp = shell.get_player ()
 		self.pec_id = sp.connect ('playing-song-changed', self.playing_entry_changed)
 		self.pc_id = sp.connect ('playing-changed', self.playing_changed)
+		db = shell.get_property ("db")
+		self.eemr_art_id = db.connect_after ('entry-extra-metadata-request::rb:coverArt', self.cover_art_request)
 		self.art_widget = FadingImage (self.find_file (ART_MISSING_ICON + ".svg"))
 		self.art_widget.set_padding (0, 5)
 		shell.add_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR)
 		self.art_db = CoverArtDatabase ()
 		self.current_entry = None
+		self.current_pixbuf = None
 		entry = sp.get_playing_entry ()
 		self.playing_entry_changed (sp, entry)
 	
@@ -263,6 +266,8 @@ class ArtDisplayPlugin (rb.Plugin):
 		sp = shell.get_player ()
 		sp.disconnect (self.pec_id)
 		sp.disconnect (self.pc_id)
+		db = shell.get_property ("db")
+		db.disconnect (self.eemr_art_id)
 		shell.remove_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR)
 		self.art_widget.disconnect_handlers ()
 		self.art_widget = None
@@ -282,9 +287,18 @@ class ArtDisplayPlugin (rb.Plugin):
 			self.art_widget.show ()
 			# Intitates search in the database (which checks art cache, internet etc.)
 			self.current_entry = entry
+			self.current_pixbuf = None
 			self.art_db.get_pixbuf(db, entry, self.on_get_pixbuf_completed)
 
 	def on_get_pixbuf_completed(self, entry, pixbuf):
 		# Set the pixbuf for the entry returned from the art db
 		if entry == self.current_entry:
 			self.art_widget.set_current_art (pixbuf, False)
+			if pixbuf:
+				db = self.shell.get_property ("db")
+				db.emit_entry_extra_metadata_notify (entry,
+						"rb:coverArt", pixbuf)
+	
+	def cover_art_request (self, db, entry):
+		if entry == self.current_entry:
+			return self.current_pixbuf
--- shell/rb-shell.c	2006/12/30 04:37:23	1.1
+++ shell/rb-shell.c	2006/12/30 14:29:57
@@ -86,6 +86,8 @@
 #include "rb-song-info.h"
 #include "rb-marshal.h"
 
+#define PLAYING_ENTRY_NOTIFY_TIME 4
+
 static void rb_shell_class_init (RBShellClass *klass);
 static void rb_shell_init (RBShell *shell);
 static GObject *rb_shell_constructor (GType type,
@@ -135,6 +137,11 @@ static void rb_shell_db_save_error_cb (R
 static void rb_shell_db_entry_added_cb (RhythmDB *db,
 					RhythmDBEntry *entry,
 					RBShell *shell);
+static void rb_shell_db_metadata_art_cb (RhythmDB *db,
+					 RhythmDBEntry *entry,
+					 const char *field,
+					 GValue *metadata,
+					 RBShell *shell);
 static void rb_shell_druid_response_cb (GtkDialog *druid,
 					guint response,
 					 RBShell *shell);
@@ -149,7 +156,7 @@ static void rb_shell_transfer_progress_c
 					   RBShell *shell);
 static void rb_shell_source_deleted_cb (RBSource *source, RBShell *shell);
 static void rb_shell_set_window_title (RBShell *shell, const char *window_title);
-static void rb_shell_set_elapsed (RBShell *shell, guint elapsed);
+static void rb_shell_update_tray_tooltip_elapsed (RBShell *shell);
 static void rb_shell_player_window_title_changed_cb (RBShellPlayer *player,
 					             const char *window_title,
 					             RBShell *shell);
@@ -222,6 +229,8 @@ static gboolean rb_shell_show_popup_cb (
 					RBSource *target,
 					RBShell *shell);
 static gboolean tray_destroy_cb (GtkObject *object, RBShell *shell);
+static void rb_shell_construct_notify_titles (RBShell *shell, 
+					      RhythmDBEntry *entry);
 
 static gboolean save_yourself_cb (GnomeClient *client,
                                   gint phase,
@@ -366,6 +375,11 @@ struct RBShellPrivate
 	char *cached_duration;
 	gboolean cached_playing;
 
+	/* markup, used for notifications and for tray tooltips */
+	char *cached_notify_primary;
+	char *cached_notify_secondary;
+	GtkWidget *cached_art_icon;
+
 	guint sidepane_visibility_notify_id;
 	guint toolbar_visibility_notify_id;
 	guint toolbar_style_notify_id;
@@ -877,6 +891,13 @@ rb_shell_finalize (GObject *object)
 
 	gtk_widget_destroy (GTK_WIDGET (shell->priv->tray_icon));
 
+	if (shell->priv->cached_notify_primary != NULL)
+		g_free (shell->priv->cached_notify_primary);
+	if (shell->priv->cached_notify_secondary != NULL)
+		g_free (shell->priv->cached_notify_secondary);
+	if (shell->priv->cached_art_icon != NULL)
+		g_object_unref (shell->priv->cached_art_icon);
+
 	if (shell->priv->save_playlist_id > 0) {
 		g_source_remove (shell->priv->save_playlist_id);
 		shell->priv->save_playlist_id = 0;
@@ -1347,6 +1368,10 @@ rb_shell_constructor (GType type,
 	g_signal_connect_object (G_OBJECT (shell->priv->db), "entry-added",
 				 G_CALLBACK (rb_shell_db_entry_added_cb), shell, 0);
 
+	g_signal_connect_object (G_OBJECT (shell->priv->db),
+				 "entry_extra_metadata_notify::rb:coverArt",
+				 G_CALLBACK (rb_shell_db_metadata_art_cb), shell, 0);
+
 	construct_sources (shell);
 
 	construct_load_ui (shell);
@@ -1708,6 +1733,46 @@ rb_shell_db_entry_added_cb (RhythmDB *db
 	}
 }
 
+static void
+rb_shell_db_metadata_art_cb (RhythmDB *db,
+			     RhythmDBEntry *entry,
+			     const char *field,
+			     GValue *metadata,
+			     RBShell *shell)
+{
+	GdkPixbuf *pixbuf, *my_pixbuf;
+	RhythmDBEntry *playing_entry;
+	gint icon_size;
+	guint time;
+
+	playing_entry = rb_shell_player_get_playing_entry (shell->priv->player_shell);
+	if (playing_entry != NULL)
+		rhythmdb_entry_unref (playing_entry);
+	if (entry != playing_entry)
+		return;
+	if  (!G_VALUE_HOLDS (metadata, GDK_TYPE_PIXBUF))
+		return;
+	if ((pixbuf = GDK_PIXBUF (g_value_get_object (metadata))) == NULL)
+		return;
+
+	gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &icon_size, NULL);
+	my_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+			icon_size, icon_size,
+			GDK_INTERP_BILINEAR);
+
+	if (shell->priv->cached_art_icon != NULL)
+		g_object_unref (shell->priv->cached_art_icon);
+	shell->priv->cached_art_icon = g_object_ref_sink (gtk_image_new_from_pixbuf (my_pixbuf));
+	g_object_unref (my_pixbuf);
+
+	rb_tray_icon_set_tooltip_icon (shell->priv->tray_icon,
+				       shell->priv->cached_art_icon);
+
+	if (rb_shell_player_get_playing_time (shell->priv->player_shell, &time, NULL))
+		if (time < PLAYING_ENTRY_NOTIFY_TIME)
+			rb_shell_notify_playing_entry (shell, entry, FALSE);
+}
+
 RBSource *
 rb_shell_get_source_by_entry_type (RBShell *shell,
 				   RhythmDBEntryType type)
@@ -1896,7 +1961,15 @@ rb_shell_playing_entry_changed_cb (RBShe
 				   RhythmDBEntry *entry,
 				   RBShell *shell)
 {
+	if (shell->priv->cached_art_icon != NULL)
+		g_object_unref (shell->priv->cached_art_icon);
+	shell->priv->cached_art_icon = NULL;
+
 	rb_shell_notify_playing_entry (shell, entry, FALSE);
+
+	rb_tray_icon_set_tooltip_primary_markup (shell->priv->tray_icon, shell->priv->cached_notify_primary);
+	rb_tray_icon_set_tooltip_icon (shell->priv->tray_icon, shell->priv->cached_art_icon);
+	rb_shell_update_tray_tooltip_elapsed (shell);
 }
 
 static void
@@ -1973,35 +2046,38 @@ rb_shell_player_elapsed_changed_cb (RBSh
 				    guint elapsed,
 				    RBShell *shell)
 {
-	rb_shell_set_elapsed (shell, elapsed);
+	rb_shell_update_tray_tooltip_elapsed (shell);
 }
 
 static void
-rb_shell_set_elapsed (RBShell *shell,
-		      guint elapsed)
+rb_shell_update_tray_tooltip_elapsed (RBShell *shell)
 {
 	gboolean playing;
 	char *elapsed_string;
-	char *tooltip;
+	GString *secondary;
 
 	rb_shell_player_get_playing (shell->priv->player_shell, &playing, NULL);
 	elapsed_string = rb_shell_player_get_playing_time_string (shell->priv->player_shell);
 
-	if (shell->priv->cached_title == NULL)
-		tooltip = g_strdup (_("Not playing"));
-	else if (!playing) {
-		/* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
-		tooltip = g_strdup_printf (_("%s\nPaused, %s"),
-					 shell->priv->cached_title, elapsed_string);
+	secondary = g_string_sized_new (100);
+	if (shell->priv->cached_notify_secondary != NULL) {
+		g_string_append (secondary, shell->priv->cached_notify_secondary);
+		if (secondary->len != 0)
+			g_string_append_c (secondary, '\n');
+	}
+	if (shell->priv->cached_notify_primary == NULL) {
+		g_string_append (secondary, _("Not playing"));
+	} else if (!playing) {
+		/* Translators: the %s is the elapsed and total time */
+		g_string_append_printf (secondary, _("Paused, %s"), elapsed_string);
 	} else {
-		/* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
-		tooltip = g_strdup_printf (_("%s\n%s"),
-					   shell->priv->cached_title, elapsed_string);
+		g_string_append (secondary, elapsed_string);
 	}
 
-	rb_tray_icon_set_tooltip (shell->priv->tray_icon, tooltip);
+	rb_tray_icon_set_tooltip_secondary_markup (shell->priv->tray_icon,
+						   secondary->str);
 	g_free (elapsed_string);
-	g_free (tooltip);
+	g_string_free (secondary, TRUE);
 }
 
 static void
@@ -2827,22 +2903,33 @@ tray_destroy_cb (GtkObject *object,
  	return TRUE;
 }
 
+static gchar *
+markup_escape (const char *text)
+{
+	return (text == NULL) ? NULL : g_markup_escape_text (text, -1);
+}
 
-void
-rb_shell_notify_playing_entry (RBShell *shell,
-			       RhythmDBEntry *entry,
-			       gboolean requested)
+static void
+rb_shell_construct_notify_titles (RBShell *shell,
+				  RhythmDBEntry *entry)
 {
 	GValue *value;
 	char *stream_title = NULL;
 	char *artist = NULL;
 	char *album = NULL;
 	char *title = NULL;
-	char *sec;
 	GString *secondary;
 
-	if (entry == NULL)
+	if (shell->priv->cached_notify_primary != NULL)
+		g_free (shell->priv->cached_notify_primary);
+	if (shell->priv->cached_notify_secondary != NULL)
+		g_free (shell->priv->cached_notify_secondary);
+
+	if (entry == NULL) {
+		shell->priv->cached_notify_primary = NULL;
+		shell->priv->cached_notify_secondary = NULL;
 		return;
+	}
 
 	secondary = g_string_sized_new (100);
 
@@ -2851,16 +2938,16 @@ rb_shell_notify_playing_entry (RBShell *
 						       entry,
 						       RHYTHMDB_PROP_STREAM_SONG_ARTIST);
 	if (value != NULL) {
-		artist = g_value_dup_string (value);
+		artist = markup_escape (g_value_get_string (value));
 		g_value_unset (value);
 		g_free (value);
 	} else {
-		artist = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
+		artist = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
 	}
 
 	if (artist != NULL && artist[0] != '\0') {
 		/* Translators: by Artist */
-		g_string_append_printf (secondary, _("by %s"), artist);
+		g_string_append_printf (secondary, _("by <b>%s</b>"), artist);
 	}
 	g_free (artist);
 
@@ -2869,11 +2956,11 @@ rb_shell_notify_playing_entry (RBShell *
 						       entry,
 						       RHYTHMDB_PROP_STREAM_SONG_ALBUM);
 	if (value != NULL) {
-		album = g_value_dup_string (value);
+		album = markup_escape (g_value_get_string (value));
 		g_value_unset (value);
 		g_free (value);
 	} else {
-		album = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM);
+		album = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
 	}
 
 	if (album != NULL && album[0] != '\0') {
@@ -2881,7 +2968,7 @@ rb_shell_notify_playing_entry (RBShell *
 			g_string_append_c (secondary, ' ');
 
 		/* Translators: from Album */
-		g_string_append_printf (secondary, _("from %s"), album);
+		g_string_append_printf (secondary, _("from <b>%s</b>"), album);
 	}
 	g_free (album);
 
@@ -2893,13 +2980,13 @@ rb_shell_notify_playing_entry (RBShell *
 						       entry,
 						       RHYTHMDB_PROP_STREAM_SONG_TITLE);
 	if (value != NULL) {
-		title = g_value_dup_string (value);
+		title = markup_escape (g_value_get_string (value));
 		g_value_unset (value);
 		g_free (value);
 
-		stream_title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
+		stream_title = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
 	} else {
-		title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
+		title = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
 	}
 
 	if (stream_title != NULL && stream_title[0] != '\0') {
@@ -2910,15 +2997,31 @@ rb_shell_notify_playing_entry (RBShell *
 	}
 	g_free (stream_title);
 
-	sec = g_string_free (secondary, FALSE);
-	rb_shell_hidden_notify (shell,
-				4000,
-				title,
-				NULL,
-				sec,
-				requested);
-	g_free (sec);
-	g_free (title);
+	if (title != NULL)
+		shell->priv->cached_notify_primary = title;
+	else
+		/* Translators: unknown track title */
+		shell->priv->cached_notify_primary = g_strdup (_("Unknown"));
+
+	shell->priv->cached_notify_secondary = g_string_free (secondary, FALSE);
+}
+
+void
+rb_shell_notify_playing_entry (RBShell *shell,
+			       RhythmDBEntry *entry,
+			       gboolean requested)
+{
+	rb_shell_construct_notify_titles (shell, entry);
+
+	if (entry == NULL)
+		return;
+
+	rb_shell_hidden_notify_markup (shell,
+				       PLAYING_ENTRY_NOTIFY_TIME * 1000,
+				       shell->priv->cached_notify_primary,
+				       shell->priv->cached_art_icon,
+				       shell->priv->cached_notify_secondary,
+				       requested);
 }
 
 void
@@ -2929,6 +3032,24 @@ rb_shell_hidden_notify (RBShell *shell,
 			const char *secondary,
 			gboolean requested)
 {
+	char *primary_markup = g_markup_escape_text (primary, -1);
+	char *secondary_markup = g_markup_escape_text (secondary, -1);
+
+	rb_shell_hidden_notify_markup (shell, timeout, primary_markup, 
+				       icon, secondary_markup, requested);
+
+	g_free (primary_markup);
+	g_free (secondary_markup);
+}
+
+void
+rb_shell_hidden_notify_markup (RBShell *shell,
+			       guint timeout,
+			       const char *primary_markup,
+			       GtkWidget *icon,
+			       const char *secondary_markup,
+			       gboolean requested)
+{
 
 	if (requested == FALSE && rb_shell_get_visibility (shell)) {
 		rb_debug ("shell is visible, not notifying");
@@ -2937,9 +3058,9 @@ rb_shell_hidden_notify (RBShell *shell,
 
 	rb_tray_icon_notify (shell->priv->tray_icon,
 			     timeout,
-			     primary,
+			     primary_markup,
 			     icon,
-			     secondary,
+			     secondary_markup,
 			     requested);
 }
 
--- shell/rb-shell.h	2006/12/30 06:51:37	1.1
+++ shell/rb-shell.h	2006/12/30 13:42:41
@@ -137,6 +137,12 @@ void            rb_shell_hidden_notify  
 					 GtkWidget *icon,
 					 const char *secondary,
 					 gboolean requested);
+void            rb_shell_hidden_notify_markup  (RBShell *shell,
+						guint timeout,
+						const char *primary_markup,
+						GtkWidget *icon,
+						const char *secondary_markup,
+						gboolean requested);
 void		rb_shell_notify_playing_entry (RBShell *shell,
 					       RhythmDBEntry *entry,
 					       gboolean requested);
--- shell/rb-tray-icon.c	2006/12/30 04:37:23	1.1
+++ shell/rb-tray-icon.c	2006/12/30 13:41:55
@@ -30,6 +30,7 @@
 #include <gtk/gtk.h>
 #include <libgnomevfs/gnome-vfs.h>
 #include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <libsexy/sexy-tooltip.h>
 
 #include "rb-tray-icon.h"
 #include "rb-stock-icons.h"
@@ -39,6 +40,8 @@
 #include "rb-shell.h"
 #include "rb-shell-player.h"
 
+#define TRAY_ICON_DEFAULT_TOOLTIP _("Rhythmbox")
+
 static void rb_tray_icon_class_init (RBTrayIconClass *klass);
 static void rb_tray_icon_init (RBTrayIcon *shell_player);
 static GObject *rb_tray_icon_constructor (GType type, guint n_construct_properties,
@@ -59,6 +62,12 @@ static void rb_tray_icon_button_press_ev
 						RBTrayIcon *icon);
 static void rb_tray_icon_scroll_event_cb (GtkWidget *ebox, GdkEvent *event,
 						RBTrayIcon *icon);
+static void rb_tray_icon_enter_notify_event_cb (RBTrayIcon *icon,
+						GdkEvent *event,
+						GtkWidget *widget);
+static void rb_tray_icon_leave_notify_event_cb (RBTrayIcon *icon,
+						GdkEvent *event,
+						GtkWidget *widget);
 static void rb_tray_icon_show_window_changed_cb (GtkAction *action,
 						 RBTrayIcon *icon);
 static void rb_tray_icon_show_notifications_changed_cb (GtkAction *action,
@@ -71,15 +80,25 @@ static void rb_tray_icon_drop_cb (GtkWid
 				  guint info,
 				  guint time,
 				  RBTrayIcon *icon);
+static void rb_tray_icon_suppress_tooltips (RBTrayIcon *icon, guint duration);
+static void rb_tray_icon_construct_tooltip (RBTrayIcon *icon);
 
 struct RBTrayIconPrivate
 {
-	GtkTooltips *tooltips;
 	GtkUIManager *ui_manager;
 	GtkActionGroup *actiongroup;
 
 	GtkWidget *ebox;
 
+	GtkWidget *tooltip;
+	gboolean tooltips_should_show;
+	gboolean tooltips_suppressed;
+	GtkWidget *tooltip_primary;
+	GtkWidget *tooltip_secondary;
+	GtkWidget *tooltip_image_box;
+	guint tooltip_unsuppress_id;
+	guint tooltip_unhide_id;
+ 
 	RBShell *shell;
 	RBShellPlayer *shell_player;
 
@@ -161,10 +180,7 @@ rb_tray_icon_init (RBTrayIcon *icon)
 
 	icon->priv = RB_TRAY_ICON_GET_PRIVATE (icon);
 
-	icon->priv->tooltips = gtk_tooltips_new ();
-
-	gtk_tooltips_set_tip (icon->priv->tooltips, GTK_WIDGET (icon),
-			      _("Not playing"), NULL);
+	rb_tray_icon_construct_tooltip (icon);
 
 	icon->priv->ebox = gtk_event_box_new ();
 	g_signal_connect_object (G_OBJECT (icon->priv->ebox),
@@ -175,6 +191,14 @@ rb_tray_icon_init (RBTrayIcon *icon)
 				 "scroll_event",
 				 G_CALLBACK (rb_tray_icon_scroll_event_cb),
 				 icon, 0);
+	g_signal_connect_object (G_OBJECT (icon->priv->ebox),
+				 "enter-notify-event",
+				 G_CALLBACK (rb_tray_icon_enter_notify_event_cb),
+				 icon, G_CONNECT_SWAPPED);
+	g_signal_connect_object (G_OBJECT (icon->priv->ebox),
+				 "leave-notify-event",
+				 G_CALLBACK (rb_tray_icon_leave_notify_event_cb),
+				 icon, G_CONNECT_SWAPPED);
 	gtk_drag_dest_set (icon->priv->ebox, GTK_DEST_DEFAULT_ALL, target_uri, 1, GDK_ACTION_COPY);
 	g_signal_connect_object (G_OBJECT (icon->priv->ebox), "drag_data_received",
 				 G_CALLBACK (rb_tray_icon_drop_cb), icon, 0);
@@ -239,8 +263,12 @@ rb_tray_icon_finalize (GObject *object)
 		g_object_unref (tray->priv->shell_player);
 	}
 
-	gtk_object_destroy (GTK_OBJECT (tray->priv->tooltips));
-
+	if (tray->priv->tooltip_unsuppress_id > 0)
+		g_source_remove (tray->priv->tooltip_unsuppress_id);
+	if (tray->priv->tooltip_unhide_id > 0)
+		g_source_remove (tray->priv->tooltip_unhide_id);
+	gtk_object_destroy (GTK_OBJECT (tray->priv->tooltip));
+ 
 	G_OBJECT_CLASS (rb_tray_icon_parent_class)->finalize (object);
 }
 
@@ -503,33 +531,96 @@ rb_tray_icon_get_geom (RBTrayIcon *icon,
 	*height = widget->allocation.y;
 }
 
+static void
+rb_tray_icon_update_tooltip_visibility (RBTrayIcon *icon)
+{
+	if (icon->priv->tooltips_should_show && !icon->priv->tooltips_suppressed)
+		gtk_widget_show (icon->priv->tooltip);
+	else
+		gtk_widget_hide (icon->priv->tooltip);
+}
+
+static gboolean
+rb_tray_icon_unhide_cb (RBTrayIcon *icon)
+{
+	gdk_threads_enter ();
+	rb_tray_icon_update_tooltip_visibility (icon);
+	icon->priv->tooltip_unhide_id = 0;
+	gdk_threads_leave ();
+	return FALSE;
+}
+
 void
-rb_tray_icon_set_tooltip (RBTrayIcon *icon, const char *tooltip)
+rb_tray_icon_set_tooltip_primary_markup (RBTrayIcon *icon, 
+					 const char *primary_markup)
 {
-	gtk_tooltips_set_tip (icon->priv->tooltips,
-			      GTK_WIDGET (icon),
-			      tooltip, NULL);
+	/* hide, then reshow in the right position & size */
+	gtk_widget_hide (icon->priv->tooltip);
+
+	if (primary_markup == NULL)
+		primary_markup = TRAY_ICON_DEFAULT_TOOLTIP;
+	gtk_label_set_markup (GTK_LABEL (icon->priv->tooltip_primary), 
+			      primary_markup);
+
+	if (icon->priv->tooltip_unhide_id > 0)
+		g_source_remove (icon->priv->tooltip_unhide_id);
+	icon->priv->tooltip_unhide_id = g_idle_add ((GSourceFunc) rb_tray_icon_unhide_cb, icon);
+}
+
+void 
+rb_tray_icon_set_tooltip_icon (RBTrayIcon *icon, GtkWidget *msgicon)
+{
+	GtkContainer *image_box;
+	GtkWidget *current_image;
+
+	if (msgicon == NULL)
+		msgicon = gtk_image_new_from_icon_name ("gnome-media-player", 
+							GTK_ICON_SIZE_DIALOG);
+	image_box = GTK_CONTAINER (icon->priv->tooltip_image_box);
+	current_image = GTK_WIDGET (g_list_nth_data (gtk_container_get_children (image_box), 0));
+	if (current_image != msgicon) {
+		gtk_container_remove (image_box, current_image);
+		gtk_box_pack_start (GTK_BOX (image_box), msgicon, FALSE, FALSE, 0);
+		gtk_widget_show (msgicon);
+	}
+
 }
 
 void
+rb_tray_icon_set_tooltip_secondary_markup (RBTrayIcon *icon, 
+					   const char *secondary_markup)
+{
+	if (secondary_markup != NULL) {
+		gtk_label_set_markup (GTK_LABEL (icon->priv->tooltip_secondary),
+				      secondary_markup);
+		gtk_widget_show (icon->priv->tooltip_secondary);
+	} else
+		gtk_widget_hide (icon->priv->tooltip_secondary);
+
+}
+	
+void
 rb_tray_icon_notify (RBTrayIcon *icon,
 		     guint timeout,
-		     const char *primary,
+		     const char *primary_markup,
 		     GtkWidget *msgicon,
-		     const char *secondary,
+		     const char *secondary_markup,
 		     gboolean requested)
 {
 	if (!egg_tray_icon_have_manager (EGG_TRAY_ICON (icon))) {
-		rb_debug ("not showing notification: %s", primary);
+		rb_debug ("not showing notification: %s", primary_markup);
 		return;
 	}
 	if ((requested || icon->priv->show_notifications) == FALSE) {
-		rb_debug ("ignoring notification: %s", primary);
+		rb_debug ("ignoring notification: %s", primary_markup);
 		return;
 	}
 
-	rb_debug ("doing notify: %s", primary);
-	egg_tray_icon_notify (EGG_TRAY_ICON (icon), timeout, primary, msgicon, secondary);
+	rb_debug ("doing notify: %s", primary_markup);
+	if (timeout > 0)
+		rb_tray_icon_suppress_tooltips (icon, timeout);
+	egg_tray_icon_notify (EGG_TRAY_ICON (icon), timeout, 
+			      primary_markup, msgicon, secondary_markup);
 }
 
 void
@@ -537,3 +628,111 @@ rb_tray_icon_cancel_notify (RBTrayIcon *
 {
 	egg_tray_icon_cancel_message (EGG_TRAY_ICON (icon), 1);
 }
+
+static void
+rb_tray_icon_enter_notify_event_cb (RBTrayIcon *icon, 
+				    GdkEvent *event,
+				    GtkWidget *widget)
+{
+	icon->priv->tooltips_should_show = TRUE;
+	rb_tray_icon_update_tooltip_visibility (icon);
+}
+
+static void
+rb_tray_icon_leave_notify_event_cb (RBTrayIcon *icon, 
+				    GdkEvent *event,
+				    GtkWidget *widget)
+{
+	icon->priv->tooltips_should_show = FALSE;
+	rb_tray_icon_update_tooltip_visibility (icon);
+}
+
+static void
+rb_tray_icon_tooltip_size_allocate_cb (RBTrayIcon *icon,
+				       GtkAllocation *allocation, 
+				       GtkWidget *tooltip)
+{
+	sexy_tooltip_position_to_widget (SEXY_TOOLTIP (icon->priv->tooltip), 
+					 icon->priv->ebox);
+}
+
+static void
+rb_tray_icon_construct_tooltip (RBTrayIcon *icon)
+{
+	GtkWidget *hbox, *vbox, *image;
+	gint size;
+	PangoFontDescription *font_desc;
+	
+	icon->priv->tooltips_should_show = FALSE;
+	icon->priv->tooltips_suppressed = FALSE;
+	icon->priv->tooltip = sexy_tooltip_new ();
+
+	g_signal_connect_object (icon->priv->tooltip, "size-allocate", 
+				 (GCallback) rb_tray_icon_tooltip_size_allocate_cb, 
+				 icon, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+	icon->priv->tooltip_primary = gtk_label_new (TRAY_ICON_DEFAULT_TOOLTIP);
+	gtk_widget_modify_font (icon->priv->tooltip_primary, NULL);
+	size = pango_font_description_get_size (icon->priv->tooltip_primary->style->font_desc);
+	font_desc = pango_font_description_new ();
+	pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD);
+	pango_font_description_set_size (font_desc, size * PANGO_SCALE_LARGE);
+	gtk_widget_modify_font (icon->priv->tooltip_primary, font_desc);
+	pango_font_description_free (font_desc);
+	gtk_label_set_line_wrap (GTK_LABEL (icon->priv->tooltip_primary),
+				 TRUE);
+	gtk_misc_set_alignment  (GTK_MISC  (icon->priv->tooltip_primary),
+				 0.0, 0.0);
+
+	icon->priv->tooltip_secondary = gtk_label_new (NULL);
+	gtk_widget_set_no_show_all (icon->priv->tooltip_secondary, TRUE);
+	gtk_label_set_line_wrap (GTK_LABEL (icon->priv->tooltip_secondary),
+				 TRUE);
+	gtk_misc_set_alignment  (GTK_MISC  (icon->priv->tooltip_secondary),
+				 0.0, 0.0);
+
+	image = gtk_image_new_from_icon_name ("gnome-media-player",
+					      GTK_ICON_SIZE_DIALOG);
+	icon->priv->tooltip_image_box = gtk_vbox_new (FALSE, 12);
+	gtk_box_pack_start (GTK_BOX (icon->priv->tooltip_image_box), image,
+			    FALSE, FALSE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	vbox = gtk_vbox_new (FALSE, 12);
+
+	gtk_box_pack_start (GTK_BOX (vbox), icon->priv->tooltip_primary,
+			    FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), icon->priv->tooltip_secondary,
+			    TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), icon->priv->tooltip_image_box,
+			    FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox,
+			    TRUE, TRUE, 0);
+	gtk_widget_show_all (hbox);
+
+	gtk_container_add (GTK_CONTAINER (icon->priv->tooltip), hbox);
+}
+
+static gboolean
+rb_tray_icon_unsuppress_cb (RBTrayIcon *icon)
+{
+	gdk_threads_enter ();
+	icon->priv->tooltips_suppressed = FALSE;
+	rb_tray_icon_update_tooltip_visibility (icon);
+	icon->priv->tooltip_unsuppress_id = 0;
+	gdk_threads_leave ();
+	return FALSE;
+}
+
+static void
+rb_tray_icon_suppress_tooltips (RBTrayIcon *icon, guint duration)
+{
+	g_return_if_fail (duration > 0);
+
+	icon->priv->tooltips_suppressed = TRUE;
+	rb_tray_icon_update_tooltip_visibility (icon);
+
+	if (icon->priv->tooltip_unsuppress_id > 0)
+		g_source_remove (icon->priv->tooltip_unsuppress_id);
+	icon->priv->tooltip_unsuppress_id = g_timeout_add (duration, (GSourceFunc) rb_tray_icon_unsuppress_cb, icon);
+}
--- shell/rb-tray-icon.h	2006/12/30 04:37:23	1.1
+++ shell/rb-tray-icon.h	2006/12/30 13:39:00
@@ -59,14 +59,16 @@ void                    rb_tray_icon_get
 
 void                    rb_tray_icon_notify     (RBTrayIcon *icon,
 						 guint timeout,
-						 const char *primary,
+						 const char *primary_markup,
 						 GtkWidget *msgicon,
-						 const char *secondary,
+						 const char *secondary_markup,
 						 gboolean requested);
 
 void                    rb_tray_icon_cancel_notify (RBTrayIcon *icon);
 
-void			rb_tray_icon_set_tooltip(RBTrayIcon *icon, const char *tooltip);
+void rb_tray_icon_set_tooltip_primary_markup (RBTrayIcon *icon, const char *primary_markup);
+void rb_tray_icon_set_tooltip_icon (RBTrayIcon *icon, GtkWidget *msgicon);
+void rb_tray_icon_set_tooltip_secondary_markup (RBTrayIcon *icon, const char *secondary_markup);
 
 G_END_DECLS
 
--- bindings/python/rhythmdb.override	2006/12/30 05:12:16	1.1
+++ bindings/python/rhythmdb.override	2006/12/30 05:28:20
@@ -618,6 +618,7 @@ _wrap_rhythmdb_emit_entry_extra_metadata
 	char *property_name;
 	PyObject *py_metadata;
 	RhythmDBEntry *entry = NULL;
+	GType type;
 	GValue val = {0,};
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OsO:RhythmDB.emit_entry_extra_metadata_notify", kwlist, &py_entry, &property_name, &py_metadata))
@@ -630,7 +631,16 @@ _wrap_rhythmdb_emit_entry_extra_metadata
 		return NULL;
 	}
 
-	pyg_value_from_pyobject (&val, py_metadata);
+	type = pyg_type_from_object((PyObject *) py_metadata->ob_type);
+	if (! type) {
+		PyErr_SetString(PyExc_TypeError, "can't map metadata to GType system");
+		return NULL;
+	}
+	g_value_init (&val, type);
+	if (pyg_value_from_pyobject (&val, py_metadata) == -1) {
+		g_value_unset (&val);
+		return NULL;
+	}
 	rhythmdb_emit_entry_extra_metadata_notify(RHYTHMDB_RHYTHMDB(self->obj), entry, property_name, &val);
 	g_value_unset (&val);
     
--- widgets/eggtrayicon.c	2006/12/30 05:53:39	1.1
+++ widgets/eggtrayicon.c	2006/12/30 13:38:03
@@ -520,15 +520,15 @@ egg_tray_icon_new (const gchar *name)
 guint
 egg_tray_icon_send_message (EggTrayIcon *icon,
 			    gint         timeout,
-			    const gchar *message,
+			    const gchar *message_markup,
 			    gint         len)
 {
   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
   g_return_val_if_fail (timeout >= 0, 0);
-  g_return_val_if_fail (message != NULL, 0);
+  g_return_val_if_fail (message_markup != NULL, 0);
 
 #ifdef HAVE_NOTIFY
-  egg_tray_icon_notify (icon, timeout, _("Notification"), NULL, message);
+  egg_tray_icon_notify (icon, timeout, _("Notification"), NULL, message_markup);
 #endif
 
   return 1;
@@ -564,9 +564,9 @@ egg_tray_icon_get_orientation (EggTrayIc
 void
 egg_tray_icon_notify (EggTrayIcon *icon,
 		      guint timeout,
-		      const char *primary,
+		      const char *primary_markup,
 		      GtkWidget *msgicon,
-		      const char *secondary)
+		      const char *secondary_markup)
 {
 #ifdef HAVE_NOTIFY
 #if (LIBNOTIFY_VERSION_MAJOR > 0 || LIBNOTIFY_VERSION_MINOR >= 3)
@@ -574,8 +574,6 @@ egg_tray_icon_notify (EggTrayIcon *icon,
   GdkPixbuf *pixbuf;
   int x;
   int y;
-  char *esc_primary;
-  char *esc_secondary;
 
   if (!notify_is_initted ())
     if (!notify_init ("rhythmbox"))
@@ -586,33 +584,25 @@ egg_tray_icon_notify (EggTrayIcon *icon,
       notify_notification_close (icon->notify->handle, NULL);
     }
 
-  if (primary == NULL)
+  if (primary_markup == NULL)
     {
-      primary = "";
+      primary_markup = "";
     }
-  if (secondary == NULL)
+  if (secondary_markup == NULL)
     {
-      secondary = "";
+      secondary_markup = "";
     }
 
-#if (LIBNOTIFY_VERSION_MAJOR == 0 && LIBNOTIFY_VERSION_MINOR == 3 && LIBNOTIFY_VERSION_MICRO == 0)
-  esc_primary = strdup (primary);
-#else
-  esc_primary = g_markup_escape_text (primary, strlen (primary));
-#endif
-  esc_secondary = g_markup_escape_text (secondary, strlen (secondary));
-  icon->notify->handle = notify_notification_new (esc_primary,
-                                                  esc_secondary,
+  icon->notify->handle = notify_notification_new (primary_markup,
+                                                  secondary_markup,
                                                   NULL,
                                                   GTK_WIDGET (icon));
-  g_free (esc_primary);
-  g_free (esc_secondary);
 
   notify_notification_set_timeout (icon->notify->handle, timeout);
 
   if (msgicon)
     {
-      pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (msgicon));
+      pixbuf = g_object_ref (gtk_image_get_pixbuf (GTK_IMAGE (msgicon)));
     }
   else
     {
@@ -647,7 +637,7 @@ egg_tray_icon_notify (EggTrayIcon *icon,
 
   if (! notify_notification_show (icon->notify->handle, NULL))
     {
-      g_warning ("failed to send notification (%s)", primary);
+      g_warning ("failed to send notification (%s)", primary_markup);
     }
 
   return;
@@ -657,8 +647,6 @@ egg_tray_icon_notify (EggTrayIcon *icon,
   NotifyIcon *icon_notify = NULL;
   NotifyHints *hints;
   char *fn;
-  char *esc_primary;
-  char *esc_secondary;
 
   if (!notify_is_initted ())
     if (!notify_init ("rhythmbox"))
@@ -690,7 +678,6 @@ egg_tray_icon_notify (EggTrayIcon *icon,
 	    {
 	      icon_notify = NULL;
 	    }
-	  g_free (pix);
 	  g_free (tmp);
 	}
     }
@@ -706,21 +693,17 @@ egg_tray_icon_notify (EggTrayIcon *icon,
       notify_close (icon->notify->handle);
     }
 
-  esc_primary = g_markup_escape_text (primary, strlen (primary));
-  esc_secondary = g_markup_escape_text (secondary, strlen (secondary));
   icon->notify->hints = hints;
   icon->notify->icon = icon_notify;
   icon->notify->handle = notify_send_notification (NULL, "transfer",
  	 	  	  	            	   NOTIFY_URGENCY_LOW,
-			           	    	   primary,
-			    	   	    	   secondary,
+			           	    	   primary_markup,
+			    	   	    	   secondary_markup,
 			    	   	           icon_notify,
 			    	   	           TRUE, timeout/1000,
 			    	   	           hints,
 			    	   	           NULL,
 			    	   	           0);
-  g_free (esc_primary);
-  g_free (esc_secondary);
   return;
 #endif
 #endif
--- widgets/eggtrayicon.h	2006/12/30 06:50:48	1.1
+++ widgets/eggtrayicon.h	2006/12/30 13:38:11
@@ -79,7 +79,7 @@ EggTrayIcon *egg_tray_icon_new          
 
 guint        egg_tray_icon_send_message   (EggTrayIcon *icon,
 					   gint         timeout,
-					   const char  *message,
+					   const char  *message_markup,
 					   gint         len);
 void         egg_tray_icon_cancel_message (EggTrayIcon *icon,
 					   guint        id);
@@ -88,9 +88,9 @@ gboolean     egg_tray_icon_have_manager 
 
 void 	     egg_tray_icon_notify         (EggTrayIcon *icon,
 					   guint timeout,
-					   const char *primary,
+					   const char *primary_markup,
 					   GtkWidget *msgicon,
-					   const char *secondary);
+					   const char *secondary_markup);
 
 GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon);
 
