diff -u -u -r1.223 configure.ac
--- configure.ac	30 Mar 2006 04:08:22 -0000	1.223
+++ configure.ac	30 Mar 2006 05:01:28 -0000
@@ -864,6 +864,7 @@
 plugins/audioscrobbler/Makefile
 plugins/sample-python/Makefile
 plugins/pythonconsole/Makefile
+plugins/artdisplay/Makefile
 bindings/Makefile
 bindings/python/Makefile
 help/Makefile
diff -u -u -r1.4 rb.defs
--- bindings/python/rb.defs	30 Mar 2006 04:08:23 -0000	1.4
+++ bindings/python/rb.defs	30 Mar 2006 05:01:29 -0000
@@ -42,6 +42,17 @@
   )
 )
 
+(define-enum ShellUILocation
+  (in-module "RB")
+  (c-name "RBShellUILocation")
+  (gtype-id "RB_TYPE_SHELL_UI_LOCATION")
+  (values
+    '("sidebar" "RB_SHELL_UI_LOCATION_SIDEBAR")
+    '("main-top" "RB_SHELL_UI_LOCATION_MAIN_TOP")
+    '("main-bottom" "RB_SHELL_UI_LOCATION_MAIN_BOTTOM")
+  )
+)
+
 ;; From ../../plugins/rb-plugin.h
 
 (define-function rb_plugin_get_type
@@ -244,6 +255,27 @@
   (parameters
     '("RBSource*" "source")
     '("RBSource*" "parent")
+  )
+)
+
+
+(define-method add_widget
+  (of-object "RBShell")
+  (c-name "rb_shell_add_widget")
+  (return-type "none")
+  (parameters
+    '("GtkWidget*" "widget")
+    '("RBShellUILocation" "location")
+  )
+)
+
+(define-method remove_widget
+  (of-object "RBShell")
+  (c-name "rb_shell_remove_widget")
+  (return-type "none")
+  (parameters
+    '("GtkWidget*" "widget")
+    '("RBShellUILocation" "location")
   )
 )
 
diff -u -u -r1.2 Makefile.am
--- plugins/Makefile.am	19 Mar 2006 22:39:40 -0000	1.2
+++ plugins/Makefile.am	30 Mar 2006 05:01:29 -0000
@@ -7,7 +7,7 @@
 endif
 
 if ENABLE_PYTHON
-SUBDIRS += pythonconsole sample-python
+SUBDIRS += pythonconsole sample-python artdisplay
 endif
 
 noinst_LTLIBRARIES = librbplugins.la
diff -u -u -r1.3 rb-python-module.c
--- plugins/rb-python-module.c	29 Mar 2006 10:12:28 -0000	1.3
+++ plugins/rb-python-module.c	30 Mar 2006 05:01:29 -0000
@@ -161,7 +161,7 @@
 	mdict = PyModule_GetDict (rb);
 
 	pyrb_register_classes (mdict);
-	/*pyrb_add_constants (rb, "RB_");*/
+	pyrb_add_constants (rb, "RB_");
 
 	/* Retreive the Python type for rb.Plugin */
 	PyRBPlugin_Type = (PyTypeObject *) PyDict_GetItemString (mdict, "Plugin"); 
diff -u -u -r1.418 rb-shell.c
--- shell/rb-shell.c	30 Mar 2006 04:08:23 -0000	1.418
+++ shell/rb-shell.c	30 Mar 2006 05:01:31 -0000
@@ -351,6 +351,10 @@
 	GtkWidget *queue_paned;
 	GtkWidget *queue_sidebar;
 
+	GtkBox *sidebar_container;
+	GtkBox *top_container;
+	GtkBox *bottom_container;
+
 	GList *sources;
 	GHashTable *sources_hash;
 
@@ -1111,6 +1115,11 @@
 	gtk_widget_show_all (shell->priv->queue_sidebar);
 	gtk_widget_set_no_show_all (shell->priv->queue_sidebar, TRUE);
 
+	/* places for plugins to put UI */
+	shell->priv->top_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
+	shell->priv->bottom_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
+	shell->priv->sidebar_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
+
 	/* set up sidebars */
 	shell->priv->paned = gtk_hpaned_new ();
 	{
@@ -1133,8 +1142,9 @@
 					    TRUE, TRUE, 0);
 		}
 
+		gtk_box_pack_start_defaults (shell->priv->sidebar_container, shell->priv->queue_paned);
 		gtk_paned_pack1 (GTK_PANED (shell->priv->paned),
-				 shell->priv->queue_paned,
+				 GTK_WIDGET (shell->priv->sidebar_container),
 				 FALSE, TRUE);
 		gtk_paned_pack2 (GTK_PANED (shell->priv->paned),
 				 vbox,
@@ -1152,9 +1162,11 @@
  	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (shell->priv->player_shell), FALSE, TRUE, 6);	
 	gtk_widget_show (GTK_WIDGET (shell->priv->player_shell));
 
+	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (shell->priv->top_container), FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox), shell->priv->paned, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (shell->priv->bottom_container), FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (shell->priv->statusbar), FALSE, TRUE, 0);
-	gtk_widget_show (vbox);
+	gtk_widget_show_all (vbox);
 
 	gtk_container_add (GTK_CONTAINER (win), vbox);
 
@@ -3462,5 +3474,69 @@
 {
 	gtk_widget_show_all (GTK_WIDGET (shell->priv->window));
 	gtk_widget_destroy (GTK_WIDGET (druid));
+}
+
+static GtkBox*
+rb_shell_get_box_for_ui_location (RBShell *shell, RBShellUILocation location)
+{
+	GtkBox *box = NULL;
+
+	switch (location) {
+	case RB_SHELL_UI_LOCATION_SIDEBAR:
+		box = shell->priv->sidebar_container;
+		break;
+	case RB_SHELL_UI_LOCATION_MAIN_TOP:
+		box = shell->priv->top_container;
+		break;
+	case RB_SHELL_UI_LOCATION_MAIN_BOTTOM:
+		box = shell->priv->bottom_container;
+		break;
+	}
+
+	return box;
+}
+
+void
+rb_shell_add_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
+{
+	GtkBox *box;
+
+	box = rb_shell_get_box_for_ui_location (shell, location);
+	g_return_if_fail (box != NULL);
+
+	gtk_box_pack_start (box, widget, FALSE, TRUE, 0);
+}
+
+void
+rb_shell_remove_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
+{
+	GtkBox *box;
+
+	box = rb_shell_get_box_for_ui_location (shell, location);
+	g_return_if_fail (box != NULL);
+
+	gtk_container_remove (GTK_CONTAINER (box), widget);
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+rb_shell_ui_location_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0)	{
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (RB_SHELL_UI_LOCATION_SIDEBAR, "Sidebar"),
+			ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_TOP, "Main Top"),
+			ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_BOTTOM, "Main Bottom"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static ("RBShellUILocation", values);
+	}
+
+	return etype;
 }
 
diff -u -u -r1.24 rb-shell.h
--- shell/rb-shell.h	11 Mar 2006 05:02:22 -0000	1.24
+++ shell/rb-shell.h	30 Mar 2006 05:01:31 -0000
@@ -47,6 +47,18 @@
 
 GQuark rb_shell_error_quark (void);
 
+
+typedef enum
+{
+	RB_SHELL_UI_LOCATION_SIDEBAR,
+	RB_SHELL_UI_LOCATION_MAIN_TOP,
+	RB_SHELL_UI_LOCATION_MAIN_BOTTOM,
+} RBShellUILocation;
+
+GType rb_shell_ui_location_get_type (void);
+#define RB_TYPE_SHELL_UI_LOCATION	(rb_shell_ui_location_get_type())
+
+
 typedef struct RBShellPrivate RBShellPrivate;
 
 typedef struct
@@ -126,6 +138,9 @@
 gboolean        rb_shell_get_party_mode (RBShell *shell);
 
 void rb_shell_append_source (RBShell *shell, RBSource *source, RBSource *parent);
+
+void rb_shell_add_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location);
+void rb_shell_remove_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location);
 
 G_END_DECLS
 
--- /dev/null	2006-03-28 06:29:48.000000000 +1100
+++ plugins/artdisplay/Makefile.am	2006-03-30 03:50:27.000000000 +1100
@@ -0,0 +1,13 @@
+# art display plugin
+plugindir = $(PLUGINDIR)
+plugin_PYTHON = artdisplay.py
+
+plugin_in_files = artdisplay.rb-plugin.desktop.in
+%.rb-plugin: %.rb-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.rb-plugin.desktop.in=.rb-plugin)
+
+EXTRA_DIST = $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
--- /dev/null	2006-03-28 06:29:48.000000000 +1100
+++ plugins/artdisplay/artdisplay.rb-plugin.desktop.in	2006-03-30 03:50:01.000000000 +1100
@@ -0,0 +1,9 @@
+[RB Plugin]
+Loader=python
+Module=artdisplay
+IAge=1
+_Name=Art Display
+_Description=Displays art for the playing track
+Authors=James Livingston <jrl@ids.org.au>
+Copyright=Copyright © 2006 james Livingston
+Website=http://www.rhythmbox.org
--- /dev/null	2006-03-28 06:29:48.000000000 +1100
+++ plugins/artdisplay/artdisplay.py	2006-03-30 04:25:28.000000000 +1100
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# artdisplay.py
+#
+# Copyright (C) 2006 - James Livingston
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from gettext import gettext as _
+import rhythmdb, rb
+import gtk, gnomevfs
+
+class ArtDisplay(rb.Plugin):
+
+	def __init__(self):
+		rb.Plugin.__init__(self)
+			
+	def activate (self, shell):
+		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)
+
+		self.art_widget = gtk.Image ()
+		shell.add_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR)
+	
+	def deactivate (self, shell):
+		sp = shell.get_player ()
+		sp.disconnect (self.pec_id)
+		shell.remove_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR)
+		self.art_widget = None
+
+	def playing_entry_changed (self, sp, entry):
+		location = entry.get_string (rhythmdb.PROP_LOCATION)
+		pixbuf = None
+
+		art_location = location[:location.rfind('/')] + '/folder.jpg'
+		if gnomevfs.exists (art_location):
+			art_path = gnomevfs.get_local_path_from_uri (art_location)
+			if art_path is not None:
+				pixbuf = gtk.gdk.pixbuf_new_from_file (art_path)
+
+		if pixbuf is not None:
+			self.art_widget.set_from_pixbuf (pixbuf)
+			self.art_widget.show ()
+		else:
+			self.art_widget.clear ()
+			self.art_widget.hide ()
+
+	def playing_changed (self, sp, playing):
+		if playing:
+			entry = sp.get_playing_entry ()
+			self.playing_entry_changed (sp, entry)
+		else:
+			self.art_widget.hide()
+
