diff -u -u -r1.46 Makefile.am
--- Makefile.am	19 Feb 2006 16:55:59 -0000	1.46
+++ Makefile.am	27 Feb 2006 07:06:02 -0000
@@ -5,7 +5,8 @@
 DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install --enable-gtk-doc --disable-scrollkeeper
 distuninstallcheck_listfiles = find . -type f -print | grep -v '^\./var/scrollkeeper'
 
-SUBDIRS = macros lib metadata player rhythmdb widgets sources iradio podcast remote daapsharing shell data po help tests doc
+SUBDIRS = macros lib metadata player rhythmdb widgets sources iradio podcast remote \
+	daapsharing plugins bindings shell data po help tests doc
 
 EXTRA_DIST = 			\
 	autogen.sh		\
diff -u -u -r1.212 configure.ac
--- configure.ac	22 Feb 2006 20:05:46 -0000	1.212
+++ configure.ac	27 Feb 2006 07:06:04 -0000
@@ -687,6 +687,141 @@
 dnl Enable gnome-doc-utils
 GNOME_DOC_INIT
 
+
+dnl ================================================================
+dnl Plugins
+dnl ================================================================
+
+PLUGIN_LIBTOOL_FLAGS="-module -avoid-version"
+AC_SUBST(PLUGIN_LIBTOOL_FLAGS)
+
+AS_AC_EXPAND(PLUGINDIR, ${libdir}/rhythmbox/plugins)
+
+
+dnl ================================================================
+dnl Python
+dnl ================================================================
+
+AC_MSG_CHECKING([whether Python support is requested])
+AC_ARG_ENABLE([python],
+	AS_HELP_STRING([--enable-python],[Enable python support]),
+	[enable_python=$enableval have_python=$enableval],
+	[enable_python=autodetect have_python=yes])
+AC_MSG_RESULT([$enable_python])
+
+if test "x$have_python" != "xno"; then
+	AM_PATH_PYTHON([2.3],[],[no])
+	if test "x$PYTHON" = "x:"; then
+		have_python=no
+	fi
+fi
+
+if test "x$have_python" != "xno"; then
+	PY_PREFIX=`$PYTHON -c 'import sys ; print sys.prefix'`
+	PY_EXEC_PREFIX=`$PYTHON -c 'import sys ; print sys.exec_prefix'`
+	PYTHON_LIBS="-lpython$PYTHON_VERSION"
+	PYTHON_LIB_LOC="-L$PY_EXEC_PREFIX/lib/python$PYTHON_VERSION/config"
+	PYTHON_CFLAGS="-I$PY_PREFIX/include/python$PYTHON_VERSION"
+	PYTHON_MAKEFILE="$PY_EXEC_PREFIX/lib/python$PYTHON_VERSION/config/Makefile"
+	PYTHON_LOCALMODLIBS=`sed -n -e 's/^LOCALMODLIBS=\(.*\)/\1/p' $PYTHON_MAKEFILE`
+	PYTHON_BASEMODLIBS=`sed -n -e 's/^BASEMODLIBS=\(.*\)/\1/p' $PYTHON_MAKEFILE`
+	PYTHON_OTHER_LIBS=`sed -n -e 's/^LIBS=\(.*\)/\1/p' $PYTHON_MAKEFILE`
+	PYTHON_EXTRA_LIBS="$PYTHON_LOCALMODLIBS $PYTHON_BASEMODLIBS $PYTHON_OTHER_LIBS"
+	AC_SUBST([PYTHON_LIBS])
+	AC_SUBST([PYTHON_LIB_LOC])
+	AC_SUBST([PYTHON_CFLAGS])
+	AC_SUBST([PYTHON_EXTRA_LIBS])
+
+	dnl FIXME: do we really need this test?
+	AC_MSG_CHECKING([whether we can build a shared library depending on libpython])
+	rm -rf testpython
+	mkdir testpython
+	cd testpython
+	cat > testpython.c <<EOF
+#include <Python.h>
+int testpython (void)
+{
+Py_Exit (0);
+}
+EOF
+
+	if /bin/sh ../libtool --mode=compile ${CC} $PYTHON_CFLAGS -c testpython.c >/dev/null 2>&1 && \
+		/bin/sh ../libtool --mode=link ${CC} -o testpython.la -rpath `pwd` -module -avoid-version $PYTHON_LIB_LOC testpython.lo $PYTHON_LIBS $PYTHON_EXTRA_LIBS >/dev/null 2>&1 && \
+		grep 'dlname.*testpython' testpython.la >/dev/null 2>&1; then
+		result=yes
+	else
+		result=no
+		have_python=no
+	fi
+	cd ..
+	rm -rf testpython
+	AC_MSG_RESULT([$result])
+fi
+
+if test "x$have_python" != "xno"; then
+	PYGTK_REQUIRED=2.8.0
+	GNOME_PYTHON_DESKTOP_REQUIRED=2.13.3
+
+	PKG_CHECK_MODULES([PYGTK], [
+		pygtk-2.0 >= $PYGTK_REQUIRED
+		gnome-python-desktop-2.0 >= $GNOME_PYTHON_DESKTOP_REQUIRED],
+		[],[have_python=no])
+
+	AC_SUBST([PYGTK_CFLAGS])
+	AC_SUBST([PYGTK_LIBS])
+fi
+
+if test "x$have_python" != "xno"; then
+	AC_MSG_CHECKING([for pygtk defs])
+	PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
+	AC_MSG_RESULT([$PYGTK_DEFSDIR])
+
+	AC_MSG_CHECKING([for pygtk codegen])
+	PYGTK_CODEGEN="$PYTHON `$PKG_CONFIG --variable=codegendir pygtk-2.0`/codegen.py"
+	AC_MSG_RESULT([$PYGTK_CODEGEN])
+
+	AC_MSG_CHECKING([for pygtk h2def])
+	PYGTK_H2DEF="$PYTHON `$PKG_CONFIG --variable=codegendir pygtk-2.0`/h2def.py"
+	AC_MSG_RESULT([$PYGTK_H2DEF])
+
+	AC_SUBST([PYGTK_DEFSDIR])
+	AC_SUBST([PYGTK_CODEGEN])
+	AC_SUBST([PYGTK_H2DEF])
+
+	dnl Check for -fno-strict-aliasing
+	FLAGS="-fno-strict-aliasing"
+	save_CFLAGS="$CFLAGS"
+	CFLAGS="$CFLAGS $FLAGS"
+	AC_MSG_CHECKING([whether [$]CC understands $FLAGS])
+	AC_TRY_COMPILE([], [], [compiler_has_option=yes], [compiler_has_option=no])
+	CFLAGS="$save_CFLAGS"
+	AC_MSG_RESULT($compiler_has_option)
+	if test $compiler_has_option = yes; then
+		NO_STRICT_ALIASING_CFLAGS="$FLAGS"
+	fi
+	AC_SUBST([NO_STRICT_ALIASING_CFLAGS])
+fi
+
+if test "x$have_python" != "xyes"; then
+	if test "x$enable_python" = "xyes"; then
+		AC_MSG_ERROR([Python support explicity requested, but not found])
+	elif test "x$enable_python" != "xno"; then
+		enable_python=no
+		AC_MSG_WARN([Python not found, disabling python support])
+	fi
+elif test "x$enable_python" != "xno"; then
+	enable_python=yes
+	AC_DEFINE([ENABLE_PYTHON],[1],[Define to compile with python support])
+fi
+
+AM_CONDITIONAL([ENABLE_PYTHON],[test "x$enable_python" = "xyes"])
+
+
+
+dnl ================================================================
+dnl end-game
+dnl ================================================================
+
 AC_OUTPUT([ 
 Makefile
 macros/Makefile
@@ -708,6 +843,12 @@
 data/rhythmbox.pc
 sources/Makefile
 daapsharing/Makefile
+plugins/Makefile
+plugins/sample/Makefile
+plugins/audioscrobbler/Makefile
+plugins/sample-python/Makefile
+bindings/Makefile
+bindings/python/Makefile
 help/Makefile
 po/Makefile.in
 tests/Makefile
@@ -782,6 +923,12 @@
 else
 	AC_MSG_NOTICE([   D-BUS control/activation disabled])
 fi
+if test x"$enable_python" != xno; then
+	AC_MSG_NOTICE([** Python support enabled])
+else
+	AC_MSG_NOTICE([   Python support disabled])
+fi
+
 if test x"$enable_audioscrobbler" != xno; then
 	AC_MSG_NOTICE([** Audioscrobbler support enabled])
 else
diff -u -u -r1.45 rhythmbox-ui.xml
--- data/ui/rhythmbox-ui.xml	22 Feb 2006 13:06:00 -0000	1.45
+++ data/ui/rhythmbox-ui.xml	27 Feb 2006 07:06:05 -0000
@@ -38,6 +38,7 @@
       <menuitem name="EditMovetoTrash" action="EditMovetoTrash"/>
       <separator/>
       <menuitem name="EditPreferencesMenu" action="EditPreferences"/> 
+      <menuitem name="EditPluginsMenu" action="EditPlugins"/> 
     </menu>
 
     <menu name="ViewMenu" action="View">
diff -u -u -r1.42 rb-file-helpers.c
--- lib/rb-file-helpers.c	27 Jan 2006 08:46:06 -0000	1.42
+++ lib/rb-file-helpers.c	27 Feb 2006 07:06:05 -0000
@@ -742,3 +742,16 @@
 	return result;
 }
 
+char *
+rb_uri_get_dir_name (const char *uri)
+{
+	GnomeVFSURI *vfs_uri;
+	char *dirname;
+
+	vfs_uri = gnome_vfs_uri_new (uri);
+	dirname = gnome_vfs_uri_extract_dirname (vfs_uri);
+	gnome_vfs_uri_unref (vfs_uri);
+
+	return dirname;
+}
+
diff -u -u -r1.20 rb-file-helpers.h
--- lib/rb-file-helpers.h	16 Jan 2006 13:45:44 -0000	1.20
+++ lib/rb-file-helpers.h	27 Feb 2006 07:06:05 -0000
@@ -42,6 +42,7 @@
 gboolean	rb_uri_is_readable	(const char *uri);
 gboolean	rb_uri_is_writable	(const char *uri);
 gboolean	rb_uri_is_local		(const char *uri);
+char *		rb_uri_get_dir_name	(const char *uri);
 
 void		rb_uri_handle_recursively(const char *uri,
 					  GFunc func,
diff -u -u -r1.27 rb-preferences.h
--- lib/rb-preferences.h	23 Feb 2006 12:21:02 -0000	1.27
+++ lib/rb-preferences.h	27 Feb 2006 07:06:05 -0000
@@ -58,6 +58,9 @@
 #define CONF_DAAP_SHARE_PASSWORD   CONF_PREFIX "/sharing/share_password"
 #define CONF_DAAP_REQUIRE_PASSWORD CONF_PREFIX "/sharing/require_password"
 
+#define CONF_PLUGINS_PREFIX	CONF_PREFIX "/plugins"
+#define CONF_PLUGIN_ACTIVE_KEY	CONF_PLUGINS_PREFIX "/%s/active"
+
 G_END_DECLS
 
 #endif /* __RB_PREFERENCES_H */
diff -u -u -r1.68 Makefile.am
--- shell/Makefile.am	7 Feb 2006 13:19:57 -0000	1.68
+++ shell/Makefile.am	27 Feb 2006 07:06:06 -0000
@@ -87,6 +87,7 @@
 	$(top_builddir)/metadata/librbmetadata.la	\
 	$(top_builddir)/widgets/librbwidgets.la         \
 	$(top_builddir)/rhythmdb/librhythmdb.la         \
+	$(top_builddir)/plugins/librbplugins.la		\
 	$(top_builddir)/lib/librb.la	                \
 	$(GDA_LIBS)					\
 	$(TOTEM_PLPARSER_LIBS)				\
@@ -122,7 +123,7 @@
 # i hate link order problems
 rhythmbox_LDADD += $(top_builddir)/remote/librbremote.la
 
-rhythmbox_LDFLAGS = -export-dynamic
+rhythmbox_LDFLAGS = -export-dynamic -no-undefined
 
 INCLUDES = 						\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
@@ -138,6 +139,7 @@
 	-I$(top_srcdir)/podcast                    	\
 	-I$(top_srcdir)/remote				\
 	-I$(top_builddir)/remote			\
+	-I$(top_srcdir)/plugins				\
 	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
 	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
 	-DDATADIR=\""$(datadir)"\"			\
@@ -171,6 +173,10 @@
 if USE_NOTIFY
 rhythmbox_LDADD += $(NOTIFY_LIBS)
 INCLUDES += $(NOTIFY_CFLAGS)
+endif
+
+if ENABLE_PYTHON
+rhythmbox_LDADD += $(PYTHON_LIBS) $(top_builddir)/bindings/python/rb.la
 endif
 
 BUILT_SOURCES += $(tab_files)
diff -u -u -r1.11 rb-audioscrobbler.c
--- shell/rb-audioscrobbler.c	13 Feb 2006 10:01:11 -0000	1.11
+++ shell/rb-audioscrobbler.c	27 Feb 2006 07:06:06 -0000
@@ -42,7 +42,6 @@
 #include "rb-file-helpers.h"
 #include "rb-glade-helpers.h"
 #include "rb-preferences.h"
-#include "rb-shell-preferences.h"
 #include "rb-shell.h"
 #include "rb-shell-player.h"
 #include "rb-source.h"
@@ -188,7 +187,6 @@
 {
 	PROP_0,
 	PROP_SHELL_PLAYER,
-	PROP_SHELL_PREFS,
 };
 
 enum
@@ -225,13 +223,6 @@
 							      "RBShellPlayer object",
 							      RB_TYPE_SHELL_PLAYER,
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-	g_object_class_install_property (object_class,
-					 PROP_SHELL_PREFS,
-					 g_param_spec_object ("shell-preferences",
-							      "RBShellPreferences",
-							      "RBShellPreferences object",
-							      RB_TYPE_SHELL_PREFERENCES,
-							      G_PARAM_WRITABLE));
 
 	g_type_class_add_private (klass, sizeof (RBAudioscrobblerPrivate));
 }
@@ -365,7 +356,7 @@
 	G_OBJECT_CLASS (rb_audioscrobbler_parent_class)->finalize (object);
 }
 
-RBAudioscrobbler *
+RBAudioscrobbler*
 rb_audioscrobbler_new (RBShellPlayer *shell_player)
 {
 	return g_object_new (RB_TYPE_AUDIOSCROBBLER,
@@ -389,16 +380,6 @@
 						 G_CALLBACK (rb_audioscrobbler_song_changed_cb),
 						 audioscrobbler, 0);
 			break;
-		case PROP_SHELL_PREFS:
-		{
-			RBShellPreferences *prefs = g_value_get_object (value);
-			GtkWidget *config_widget = rb_audioscrobbler_get_config_widget (audioscrobbler);
-
-			rb_shell_preferences_append_page (prefs,
-				       			  _("Audioscrobbler"),
-							  config_widget);
-			break;
-		}
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 	}
@@ -929,15 +910,19 @@
 	if (strcmp (entry->key, CONF_AUDIOSCROBBLER_USERNAME) == 0) {
 		g_free (audioscrobbler->priv->username);
 		audioscrobbler->priv->username = g_strdup (gconf_value_get_string (entry->value));
-		gtk_entry_set_text (GTK_ENTRY (audioscrobbler->priv->username_entry),
-				    gconf_value_get_string (entry->value));
+
+		if (audioscrobbler->priv->username_entry)
+			gtk_entry_set_text (GTK_ENTRY (audioscrobbler->priv->username_entry),
+					    gconf_value_get_string (entry->value));
 
 		audioscrobbler->priv->handshake = FALSE;
 	} else if (strcmp (entry->key, CONF_AUDIOSCROBBLER_PASSWORD) == 0) {
 		g_free (audioscrobbler->priv->password);
 		audioscrobbler->priv->password = g_strdup (gconf_value_get_string (entry->value));
-		gtk_entry_set_text (GTK_ENTRY (audioscrobbler->priv->password_entry),
-				    gconf_value_get_string (entry->value));
+	
+		if (audioscrobbler->priv->password_entry)
+			gtk_entry_set_text (GTK_ENTRY (audioscrobbler->priv->password_entry),
+					    gconf_value_get_string (entry->value));
 	} else if (strcmp (entry->key, CONF_AUDIOSCROBBLER_ENABLED) == 0) {
 		audioscrobbler->priv->enabled = gconf_value_get_bool (entry->value);
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audioscrobbler->priv->enabled_check),
diff -u -u -r1.401 rb-shell.c
--- shell/rb-shell.c	27 Feb 2006 00:44:34 -0000	1.401
+++ shell/rb-shell.c	27 Feb 2006 07:06:09 -0000
@@ -86,6 +86,8 @@
 #include "bacon-volume.h"
 #include "rb-missing-files-source.h"
 #include "rb-import-errors-source.h"
+#include "rb-plugins-engine.h"
+#include "rb-plugin-manager.h"
 
 #ifdef WITH_AUDIOSCROBBLER
 #include "rb-audioscrobbler.h"
@@ -171,6 +173,8 @@
 			       RBShell *shell);
 static void rb_shell_cmd_preferences (GtkAction *action,
 		                      RBShell *shell);
+static void rb_shell_cmd_plugins (GtkAction *action,
+		                  RBShell *shell);
 static void rb_shell_cmd_add_folder_to_library (GtkAction *action,
 						RBShell *shell);
 static void rb_shell_cmd_add_file_to_library (GtkAction *action,
@@ -392,6 +396,7 @@
 	RBSource *selected_source;
 
 	GtkWidget *prefs;
+	GtkWidget *plugins;
 
 	RBTrayIcon *tray_icon;
 	GtkTooltips *tooltips;
@@ -457,9 +462,12 @@
 	{ "MusicQuit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q",
 	  N_("Quit the music player"),
 	  G_CALLBACK (rb_shell_cmd_quit) },
-	{ "EditPreferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces"), NULL,
+	{ "EditPreferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces..."), NULL,
 	  N_("Edit music player preferences"),
 	  G_CALLBACK (rb_shell_cmd_preferences) },
+	{ "EditPlugins", NULL, N_("P_lugins..."), NULL,
+	  N_("Change and configure plugins"),
+	  G_CALLBACK (rb_shell_cmd_plugins) },
 	{ "ViewAll", NULL, N_("Show _All"), "<control>Y",
 	  N_("Show all items in this music source"),
 	  G_CALLBACK (rb_shell_cmd_view_all) },
@@ -1296,6 +1304,7 @@
 
 	rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
 
+	rb_plugins_engine_init (shell);
 	
 	/* GO GO GO! */
 	rb_debug ("loading database");
@@ -2132,17 +2141,64 @@
 {
 	if (shell->priv->prefs == NULL) {
 		shell->priv->prefs = rb_shell_preferences_new (shell->priv->sources);
-#ifdef WITH_AUDIOSCROBBLER
-		g_object_set (G_OBJECT (shell->priv->audioscrobbler),
-			      "shell_preferences", shell->priv->prefs,
-			      NULL);
-#endif
 
 		gtk_window_set_transient_for (GTK_WINDOW (shell->priv->prefs),
 					      GTK_WINDOW (shell->priv->window));
+		gtk_widget_show_all (shell->priv->prefs);
 	}
 
-	gtk_widget_show_all (shell->priv->prefs);
+	gtk_window_present (GTK_WINDOW (shell->priv->prefs));
+}
+
+static gboolean
+rb_shell_plugins_window_delete_cb (GtkWidget *window,
+				   GdkEventAny *event,
+				   gpointer data)
+{
+	gtk_widget_hide (window);
+
+	return TRUE;
+}
+
+static void
+rb_shell_plugins_response_cb (GtkDialog *dialog,
+			      int response_id,
+			      gpointer data)
+{
+	if (response_id == GTK_RESPONSE_CLOSE)
+		gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+
+static void
+rb_shell_cmd_plugins (GtkAction *action,
+		      RBShell *shell)
+{
+	if (shell->priv->plugins == NULL) {
+		GtkWidget *manager;
+		
+		shell->priv->plugins = gtk_dialog_new_with_buttons (_("Configure Plugins"),
+								    GTK_WINDOW (shell->priv->window),
+								    GTK_DIALOG_DESTROY_WITH_PARENT,
+								    GTK_STOCK_CLOSE,
+								    GTK_RESPONSE_CLOSE,
+								    NULL);
+		g_signal_connect_object (G_OBJECT (shell->priv->plugins),
+					 "delete_event",
+					 G_CALLBACK (rb_shell_plugins_window_delete_cb),
+					 NULL, 0);
+		g_signal_connect_object (G_OBJECT (shell->priv->plugins),
+					 "response",
+					 G_CALLBACK (rb_shell_plugins_response_cb),
+					 NULL, 0);
+		
+		manager = rb_plugin_manager_new ();
+		gtk_widget_show_all (GTK_WIDGET (manager));
+		gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell->priv->plugins)->vbox),
+				   manager);
+	}
+	
+	gtk_window_present (GTK_WINDOW (shell->priv->plugins));
 }
 
 static void
@@ -2253,6 +2309,8 @@
 rb_shell_quit (RBShell *shell)
 {
 	rb_debug ("Quitting");
+
+	rb_plugins_engine_shutdown ();
 
 	rb_podcast_source_shutdown (shell->priv->podcast_source);	
 	
diff -u -u -r1.23 rb-shell.h
--- shell/rb-shell.h	17 Feb 2006 15:05:48 -0000	1.23
+++ shell/rb-shell.h	27 Feb 2006 07:06:09 -0000
@@ -35,13 +35,13 @@
 #define RB_IS_SHELL_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_SHELL))
 #define RB_SHELL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_SHELL, RBShellClass))
 
-typedef enum
+enum
 {
 	RB_SHELL_ERROR_NO_SUCH_URI,
 	RB_SHELL_ERROR_NO_SUCH_PROPERTY,
 	RB_SHELL_ERROR_IMMUTABLE_PROPERTY,
 	RB_SHELL_ERROR_INVALID_PROPERTY_TYPE
-} RBShellError;
+};
 
 #define RB_SHELL_ERROR rb_shell_error_quark ()
 
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ data/glade/plugins.glade	2006-02-27 18:03:14.000000000 +1100
@@ -0,0 +1,588 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="edit_plugins">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Rhythmbox Plugins</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="plugins_vbox">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="closebutton1">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-close</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-7</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox3">
+	  <property name="border_width">6</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	      <child>
+		<widget class="GtkViewport" id="viewport1">
+		  <property name="visible">True</property>
+		  <property name="shadow_type">GTK_SHADOW_IN</property>
+
+		  <child>
+		    <widget class="GtkVBox" id="plugins_tree_vbox">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">0</property>
+
+		      <child>
+			<placeholder/>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVSeparator" id="vseparator1">
+	      <property name="visible">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">3</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox1">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">6</property>
+
+	      <child>
+		<widget class="GtkTable" id="table1">
+		  <property name="visible">True</property>
+		  <property name="n_rows">5</property>
+		  <property name="n_columns">2</property>
+		  <property name="homogeneous">False</property>
+		  <property name="row_spacing">6</property>
+		  <property name="column_spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="site_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Site:</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">4</property>
+		      <property name="bottom_attach">5</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="copyright_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Copyright:</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">3</property>
+		      <property name="bottom_attach">4</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="author_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Author:</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">2</property>
+		      <property name="bottom_attach">3</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="description_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Description:</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImage" id="plugin_icon">
+		      <property name="visible">True</property>
+		      <property name="stock">gtk-about</property>
+		      <property name="icon_size">6</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options">fill</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="site_text">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">True</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">4</property>
+		      <property name="bottom_attach">5</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="copyright_text">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">True</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">3</property>
+		      <property name="bottom_attach">4</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="author_text">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">True</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">2</property>
+		      <property name="bottom_attach">3</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="description_text">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">True</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="plugin_title">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="filler_3">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes"></property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox1">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="filler_1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkButton" id="configure_button">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+
+		      <child>
+			<widget class="GtkAlignment" id="alignment1">
+			  <property name="visible">True</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xscale">0</property>
+			  <property name="yscale">0</property>
+			  <property name="top_padding">0</property>
+			  <property name="bottom_padding">0</property>
+			  <property name="left_padding">0</property>
+			  <property name="right_padding">0</property>
+
+			  <child>
+			    <widget class="GtkHBox" id="hbox2">
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">2</property>
+
+			      <child>
+				<widget class="GtkImage" id="image1">
+				  <property name="visible">True</property>
+				  <property name="stock">gtk-preferences</property>
+				  <property name="icon_size">4</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkLabel" id="label2">
+				  <property name="visible">True</property>
+				  <property name="label" translatable="yes">Configure...</property>
+				  <property name="use_underline">True</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_LEFT</property>
+				  <property name="wrap">False</property>
+				  <property name="selectable">False</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+				  <property name="width_chars">-1</property>
+				  <property name="single_line_mode">False</property>
+				  <property name="angle">0</property>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="filler_2">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ bindings/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,5 @@
+SUBDIRS = 
+
+if ENABLE_PYTHON
+SUBDIRS += python
+endif
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ bindings/python/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,65 @@
+## Process this file with automake to produce Makefile.in
+
+noinst_LTLIBRARIES = rb.la
+
+nodist_rb_la_SOURCES = rb.c
+
+rb_la_LDFLAGS = \
+	-module -avoid-version
+
+rb_la_LIBADD = \
+	$(PYTHON_LIBS)		\
+	$(PYTHON_EXTRA_LIBS)	\
+	$(PYGTK_LIBS)
+
+rb_la_CFLAGS = \
+	-I$(top_srcdir)			\
+	-I$(top_srcdir)/lib             \
+	-I$(top_srcdir)/metadata                       	\
+	-I$(top_srcdir)/player                       	\
+	-I$(top_srcdir)/rhythmdb                       	\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/sources                    	\
+	-I$(top_srcdir)/iradio                    	\
+	-I$(top_srcdir)/podcast                    	\
+	-I$(top_srcdir)/remote				\
+	-I$(top_builddir)/remote			\
+	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/shell		\
+	$(RHYTHMBOX_CFLAGS)		\
+	$(NO_STRICT_ALIASING_CFLAGS)	\
+	$(PYGTK_CFLAGS)			\
+	$(PYTHON_CFLAGS)		\
+	$(AM_CFLAGS)
+	
+rb.c: rb.defs rb.override
+	( cd $(srcdir) && $(PYGTK_CODEGEN) \
+		--register $(PYGTK_DEFSDIR)/pango-types.defs \
+		--register $(PYGTK_DEFSDIR)/gdk-types.defs \
+		--register $(PYGTK_DEFSDIR)/gtk-types.defs \
+		--register $(PYGTK_DEFSDIR)/gtksourceview.defs \
+		--override $*.override \
+		--prefix py$* $(<F) ) > $@ 
+
+BINDING_HEADERS_SRCDIR_IN = \
+	plugins/rb-plugin.h			\
+	shell/rb-shell.h
+
+BINDING_HEADERS_BUILDDIR_IN = 
+
+BINDING_HEADERS_SRCDIR		:= $(addprefix $(top_srcdir)/,$(BINDING_HEADERS_SRCDIR_IN))
+BINDING_HEADERS_BUILDDIR	:= $(addprefix $(top_builddir)/,$(BINDING_HEADERS_BUILDDIR_IN))
+
+regenerate-python-binding:
+	$(PYGTK_H2DEF) $(sort $(BINDING_HEADERS_SRCDIR) $(BINDING_HEADERS_BUILDDIR)) > rb.defs.new
+
+BUILT_SOURCES = rb.c
+
+EXTRA_DIST = \
+	rb.override	\
+	rb.defs
+
+CLEANFILES = $(BUILT_SOURCES)
+
+dist-hook:
+	cd $(distdir); rm -f $(BUILT_SOURCES)
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ bindings/python/rb.defs	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,240 @@
+;; -*- scheme -*-
+; object definitions ...
+
+(define-object Plugin
+  (in-module "RB")
+  (parent "GObject")
+  (c-name "RBPlugin")
+  (gtype-id "RB_TYPE_PLUGIN")
+)
+
+(define-object Shell
+  (in-module "RB")
+  (parent "GObject")
+  (c-name "RBShell")
+  (gtype-id "RB_TYPE_SHELL")
+)
+
+;; Enumerations and flags ...
+
+
+;; From ../../plugins/rb-plugin.h
+
+(define-function rb_plugin_get_type
+  (c-name "rb_plugin_get_type")
+  (return-type "GType")
+)
+
+(define-method activate
+  (of-object "RBPlugin")
+  (c-name "rb_plugin_activate")
+  (return-type "none")
+  (parameters
+    '("RBShell*" "shell")
+  )
+)
+
+(define-method deactivate
+  (of-object "RBPlugin")
+  (c-name "rb_plugin_deactivate")
+  (return-type "none")
+  (parameters
+    '("RBShell*" "shell")
+  )
+)
+
+(define-method is_configurable
+  (of-object "RBPlugin")
+  (c-name "rb_plugin_is_configurable")
+  (return-type "gboolean")
+)
+
+(define-method create_configure_dialog
+  (of-object "RBPlugin")
+  (c-name "rb_plugin_create_configure_dialog")
+  (return-type "GtkWidget*")
+)
+
+
+
+;; From ../../shell/rb-shell.h
+
+(define-function rb_shell_error_quark
+  (c-name "rb_shell_error_quark")
+  (return-type "GQuark")
+)
+
+(define-function rb_shell_get_type
+  (c-name "rb_shell_get_type")
+  (return-type "GType")
+)
+
+(define-function rb_shell_new
+  (c-name "rb_shell_new")
+  (is-constructor-of "RBShell")
+  (return-type "RBShell*")
+  (parameters
+    '("int" "argc")
+    '("char**" "argv")
+    '("gboolean" "no_registration")
+    '("gboolean" "no_update")
+    '("gboolean" "dry_run")
+    '("char*" "rhythmdb")
+  )
+)
+
+(define-method present
+  (of-object "RBShell")
+  (c-name "rb_shell_present")
+  (return-type "gboolean")
+  (parameters
+    '("guint32" "timestamp")
+    '("GError**" "error")
+  )
+)
+
+(define-method guess_type_for_uri
+  (of-object "RBShell")
+  (c-name "rb_shell_guess_type_for_uri")
+  (return-type "gint")
+  (parameters
+    '("const-char*" "uri")
+  )
+)
+
+(define-method add_uri
+  (of-object "RBShell")
+  (c-name "rb_shell_add_uri")
+  (return-type "gboolean")
+  (parameters
+    '("gint" "entry_type")
+    '("const-char*" "uri")
+    '("const-char*" "title")
+    '("const-char*" "genre")
+    '("GError**" "error")
+  )
+)
+
+(define-method load_uri
+  (of-object "RBShell")
+  (c-name "rb_shell_load_uri")
+  (return-type "gboolean")
+  (parameters
+    '("const-char*" "uri")
+    '("gboolean" "play")
+    '("GError**" "error")
+  )
+)
+
+(define-method get_player
+  (of-object "RBShell")
+  (c-name "rb_shell_get_player")
+  (return-type "GObject*")
+)
+
+(define-method get_player_path
+  (of-object "RBShell")
+  (c-name "rb_shell_get_player_path")
+  (return-type "const-char*")
+)
+
+(define-method get_playlist_manager
+  (of-object "RBShell")
+  (c-name "rb_shell_get_playlist_manager")
+  (return-type "GObject*")
+)
+
+(define-method get_playlist_manager_path
+  (of-object "RBShell")
+  (c-name "rb_shell_get_playlist_manager_path")
+  (return-type "const-char*")
+)
+
+(define-method toggle_visibility
+  (of-object "RBShell")
+  (c-name "rb_shell_toggle_visibility")
+  (return-type "none")
+)
+
+(define-method get_song_properties
+  (of-object "RBShell")
+  (c-name "rb_shell_get_song_properties")
+  (return-type "gboolean")
+  (parameters
+    '("const-char*" "uri")
+    '("GHashTable**" "properties")
+    '("GError**" "error")
+  )
+)
+
+(define-method set_song_property
+  (of-object "RBShell")
+  (c-name "rb_shell_set_song_property")
+  (return-type "gboolean")
+  (parameters
+    '("const-char*" "uri")
+    '("const-char*" "propname")
+    '("const-GValue*" "value")
+    '("GError**" "error")
+  )
+)
+
+(define-method add_to_queue
+  (of-object "RBShell")
+  (c-name "rb_shell_add_to_queue")
+  (return-type "gboolean")
+  (parameters
+    '("const-gchar*" "uri")
+    '("GError**" "error")
+  )
+)
+
+(define-method remove_from_queue
+  (of-object "RBShell")
+  (c-name "rb_shell_remove_from_queue")
+  (return-type "gboolean")
+  (parameters
+    '("const-gchar*" "uri")
+    '("GError**" "error")
+  )
+)
+
+(define-method hidden_notify
+  (of-object "RBShell")
+  (c-name "rb_shell_hidden_notify")
+  (return-type "none")
+  (parameters
+    '("guint" "timeout")
+    '("const-char*" "primary")
+    '("GtkWidget*" "icon")
+    '("const-char*" "secondary")
+  )
+)
+
+(define-method register_entry_type_for_source
+  (of-object "RBShell")
+  (c-name "rb_shell_register_entry_type_for_source")
+  (return-type "none")
+  (parameters
+    '("RBSource*" "source")
+    '("RhythmDBEntryType" "type")
+  )
+)
+
+(define-method get_party_mode
+  (of-object "RBShell")
+  (c-name "rb_shell_get_party_mode")
+  (return-type "gboolean")
+)
+
+(define-method append_source
+  (of-object "RBShell")
+  (c-name "rb_shell_append_source")
+  (return-type "none")
+  (parameters
+    '("RBSource*" "source")
+    '("RBSource*" "parent")
+  )
+)
+
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ bindings/python/rb.override	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,24 @@
+%%
+headers
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include "rb-plugin.h"
+#include "rb-shell.h"
+
+void pyrb_register_classes (PyObject *d); 
+/*void pyrb_add_constants (PyObject *module, const gchar *strip_prefix);*/
+
+%%
+modulename rb 
+%%
+import gtk.Widget as PyGtkWidget_Type
+import gobject.GObject as PyGObject_Type 
+import gtk.VBox as PyGtkVBox_Type
+import gtk.Window as PyGtkWindow_Type
+%%
+ignore-glob
+  *_get_type
+  *_quark
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,60 @@
+## arch-tag: Automake rules for Rhythmbox's plugin system
+
+SUBDIRS = sample
+
+if WITH_AUDIOSCROBBLER
+SUBDIRS += audioscrobbler
+endif
+
+if ENABLE_PYTHON
+SUBDIRS += sample-python
+endif
+
+noinst_LTLIBRARIES = librbplugins.la
+
+librbplugins_la_SOURCES =				\
+	rb-plugin.h					\
+	rb-plugin.c					\
+	rb-module.c					\
+	rb-module.h					\
+	rb-plugin-manager.h				\
+	rb-plugin-manager.c				\
+	rb-plugins-engine.h				\
+	rb-plugins-engine.c
+
+INCLUDES =						\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_builddir)/lib 				\
+	-I$(top_srcdir)/player				\
+	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/library				\
+	-I$(top_srcdir)/iradio				\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/remote				\
+	-I$(top_builddir)/remote			\
+	$(WARN_CFLAGS)					\
+	$(RHYTHMBOX_CFLAGS)				\
+	-DRB_PLUGIN_DIR=\"$(PLUGINDIR)\"
+
+if ENABLE_PYTHON
+librbplugins_la_SOURCES += \
+	rb-python-module.c		\
+	rb-python-module.h		\
+	rb-python-plugin.c		\
+	rb-python-plugin.h	
+
+INCLUDES += \
+	$(NO_STRICT_ALIASING_CFLAGS)	\
+	$(PYGTK_CFLAGS)			\
+	$(PYTHON_CFLAGS)
+endif
+
+
+librbplugins_la_LDFLAGS = -export-dynamic
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-module.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,195 @@
+/*
+ * gedit-module.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2005 - Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+ 
+/* This is a modified version of ephy-module.c from Epiphany source code.
+ * Here the original copyright assignment:
+ *
+ *  Copyright (C) 2003 Marco Pesenti Gritti
+ *  Copyright (C) 2003, 2004 Christian Persch
+ *
+ */
+
+/*
+ * Modified by the gedit Team, 2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ * See the ChangeLog files for a list of changes. 
+ *
+ * $Id: gedit-module.c,v 1.2 2005/12/12 17:27:55 pborelli Exp $
+ */
+
+#include "config.h"
+
+#include "rb-module.h"
+#include "rb-debug.h"
+
+#include <gmodule.h>
+
+typedef struct _RBModuleClass RBModuleClass;
+
+struct _RBModuleClass
+{
+	GTypeModuleClass parent_class;
+};
+
+struct _RBModule
+{
+	GTypeModule parent_instance;
+
+	GModule *library;
+
+	gchar *path;
+	GType type;
+};
+
+typedef GType (*RBModuleRegisterFunc) (GTypeModule *);
+
+static void rb_module_init		(RBModule *action);
+static void rb_module_class_init	(RBModuleClass *class);
+
+static GObjectClass *parent_class = NULL;
+
+G_DEFINE_TYPE (RBModule, rb_module, G_TYPE_TYPE_MODULE)
+
+
+static gboolean
+rb_module_load (GTypeModule *gmodule)
+{
+	RBModule *module = RB_MODULE (gmodule);
+	RBModuleRegisterFunc register_func;
+
+	rb_debug ("Loading %s", module->path);
+
+	module->library = g_module_open (module->path, 0);
+
+	if (module->library == NULL)
+	{
+		g_warning (g_module_error());
+
+		return FALSE;
+	}
+
+	/* extract symbols from the lib */
+	if (!g_module_symbol (module->library, "register_rb_plugin",
+			      (void *) &register_func))
+	{
+		g_warning (g_module_error());
+		g_module_close (module->library);
+
+		return FALSE;
+	}
+
+	g_assert (register_func);
+
+	module->type = register_func (gmodule);
+
+	if (module->type == 0)
+	{
+		g_warning ("Invalid rb plugin contained by module %s", module->path);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+rb_module_unload (GTypeModule *gmodule)
+{
+	RBModule *module = RB_MODULE (gmodule);
+
+	rb_debug ("Unloading %s", module->path);
+
+	g_module_close (module->library);
+
+	module->library = NULL;
+	module->type = 0;
+}
+
+const gchar *
+rb_module_get_path (RBModule *module)
+{
+	g_return_val_if_fail (RB_IS_MODULE (module), NULL);
+
+	return module->path;
+}
+
+GObject *
+rb_module_new_object (RBModule *module)
+{
+	rb_debug ("Creating object of type %s", g_type_name (module->type));
+
+	if (module->type == 0)
+	{
+		return NULL;
+	}
+
+	return g_object_new (module->type, NULL);
+}
+
+static void
+rb_module_init (RBModule *module)
+{
+	rb_debug ("RBModule %p initialising", module);
+}
+
+static void
+rb_module_finalize (GObject *object)
+{
+	RBModule *module = RB_MODULE (object);
+
+	rb_debug ("GeditModule %p finalising", module);
+
+	g_free (module->path);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+rb_module_class_init (RBModuleClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
+
+	parent_class = (GObjectClass *) g_type_class_peek_parent (class);
+
+	object_class->finalize = rb_module_finalize;
+
+	module_class->load = rb_module_load;
+	module_class->unload = rb_module_unload;
+}
+
+RBModule *
+rb_module_new (const gchar *path)
+{
+	RBModule *result;
+
+	if (path == NULL || path[0] == '\0')
+	{
+		return NULL;
+	}
+
+	result = g_object_new (RB_TYPE_MODULE, NULL);
+
+	g_type_module_set_name (G_TYPE_MODULE (result), path);
+	result->path = g_strdup (path);
+
+	return result;
+}
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-module.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,62 @@
+/*
+ * gedit-module.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2005 - Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+ 
+/* This is a modified version of gedit-module.h from Epiphany source code.
+ * Here the original copyright assignment:
+ *
+ *  Copyright (C) 2003 Marco Pesenti Gritti
+ *  Copyright (C) 2003, 2004 Christian Persch
+ *
+ */
+
+/*
+ * Modified by the gedit Team, 2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+ 
+#ifndef GEDIT_MODULE_H
+#define GEDIT_MODULE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_MODULE		(rb_module_get_type ())
+#define RB_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_MODULE, RBModule))
+#define RB_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), RB_TYPE_MODULE, RBModuleClass))
+#define RB_IS_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_MODULE))
+#define RB_IS_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((obj), RB_TYPE_MODULE))
+#define RB_MODULE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), RB_TYPE_MODULE, RBModuleClass))
+
+typedef struct _RBModule	RBModule;
+
+GType		 rb_module_get_type		(void) G_GNUC_CONST;;
+
+RBModule	*rb_module_new		(const gchar *path);
+
+const gchar	*rb_module_get_path		(RBModule *module);
+
+GObject		*rb_module_new_object	(RBModule *module);
+
+G_END_DECLS
+
+#endif
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugin.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,105 @@
+/*
+ * gedit-plugin.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+ 
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "rb-plugin.h"
+
+G_DEFINE_TYPE (RBPlugin, rb_plugin, G_TYPE_OBJECT)
+
+static void
+dummy (RBPlugin *plugin, RBShell *shell)
+{
+	/* Empty */
+}
+
+static GtkWidget *
+create_configure_dialog	(RBPlugin *plugin)
+{
+	return NULL;
+}
+
+static gboolean
+is_configurable (RBPlugin *plugin)
+{
+	return (RB_PLUGIN_GET_CLASS (plugin)->create_configure_dialog !=
+		create_configure_dialog);
+}
+
+static void 
+rb_plugin_class_init (RBPluginClass *klass)
+{
+	klass->activate = dummy;
+	klass->deactivate = dummy;
+	
+	klass->create_configure_dialog = create_configure_dialog;
+	klass->is_configurable = is_configurable;
+}
+
+static void
+rb_plugin_init (RBPlugin *plugin)
+{
+	/* Empty */
+}
+
+void
+rb_plugin_activate (RBPlugin *plugin,
+		    RBShell *shell)
+{
+	g_return_if_fail (RB_IS_PLUGIN (plugin));
+	g_return_if_fail (RB_IS_SHELL (shell));
+	
+	RB_PLUGIN_GET_CLASS (plugin)->activate (plugin, shell);
+}
+
+void
+rb_plugin_deactivate	(RBPlugin *plugin,
+			 RBShell *shell)
+{
+	g_return_if_fail (RB_IS_PLUGIN (plugin));
+	g_return_if_fail (RB_IS_SHELL (shell));
+
+	RB_PLUGIN_GET_CLASS (plugin)->deactivate (plugin, shell);
+}
+				 
+gboolean
+rb_plugin_is_configurable (RBPlugin *plugin)
+{
+	g_return_val_if_fail (RB_IS_PLUGIN (plugin), FALSE);
+
+	return RB_PLUGIN_GET_CLASS (plugin)->is_configurable (plugin);
+}
+
+GtkWidget *
+rb_plugin_create_configure_dialog (RBPlugin *plugin)
+{
+	g_return_val_if_fail (RB_IS_PLUGIN (plugin), NULL);
+	
+	return RB_PLUGIN_GET_CLASS (plugin)->create_configure_dialog (plugin);
+}
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugin.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,153 @@
+/*
+ * gedit-plugin.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 - Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+ 
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifndef __RB_PLUGIN_H__
+#define __RB_PLUGIN_H__
+
+#include <glib-object.h>
+
+#include "rb-shell.h"
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define RB_TYPE_PLUGIN              (rb_plugin_get_type())
+#define RB_PLUGIN(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), RB_TYPE_PLUGIN, RBPlugin))
+#define RB_PLUGIN_CONST(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), RB_TYPE_PLUGIN, RBPlugin const))
+#define RB_PLUGIN_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), RB_TYPE_PLUGIN, RBPluginClass))
+#define RB_IS_PLUGIN(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), RB_TYPE_PLUGIN))
+#define RB_IS_PLUGIN_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), RB_TYPE_PLUGIN))
+#define RB_PLUGIN_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), RB_TYPE_PLUGIN, RBPluginClass))
+
+/*
+ * Main object structure
+ */
+typedef struct
+{
+	GObject parent;
+} RBPlugin;
+
+/*
+ * Class definition
+ */
+typedef struct
+{
+	GObjectClass parent_class;
+
+	/* Virtual public methods */
+	
+	void 		(*activate)		(RBPlugin *plugin,
+						 RBShell *shell);
+	void 		(*deactivate)		(RBPlugin *plugin,
+						 RBShell *shell);
+
+	GtkWidget*	(*create_configure_dialog)
+						(RBPlugin *plugin);
+
+	/* Plugins should not override this, it's handled automatically by
+	   the RbPluginClass */
+	gboolean 	(*is_configurable)
+						(RBPlugin *plugin);
+} RBPluginClass;
+
+/*
+ * Public methods
+ */
+GType 		 rb_plugin_get_type 		(void) G_GNUC_CONST;
+
+void 		 rb_plugin_activate		(RBPlugin *plugin,
+						 RBShell *shell);
+void 		 rb_plugin_deactivate		(RBPlugin *plugin,
+						 RBShell *shell);
+				 
+gboolean	 rb_plugin_is_configurable	(RBPlugin *plugin);
+GtkWidget	*rb_plugin_create_configure_dialog		
+						(RBPlugin *plugin);
+
+/*
+ * Utility macro used to register plugins
+ *
+ * use: RBT_PLUGIN_REGISTER(RBSamplePlugin, rb_sample_plugin)
+ */
+
+#define RB_PLUGIN_REGISTER(PluginName, plugin_name)				\
+										\
+static GType plugin_name##_type = 0;						\
+										\
+GType										\
+plugin_name##_get_type (void)							\
+{										\
+	return plugin_name##_type;						\
+}										\
+										\
+static void     plugin_name##_init              (PluginName        *self);	\
+static void     plugin_name##_class_init        (PluginName##Class *klass);	\
+static gpointer plugin_name##_parent_class = NULL;				\
+static void     plugin_name##_class_intern_init (gpointer klass)		\
+{										\
+	plugin_name##_parent_class = g_type_class_peek_parent (klass);		\
+	plugin_name##_class_init ((PluginName##Class *) klass);			\
+}										\
+										\
+G_MODULE_EXPORT GType								\
+register_rb_plugin (GTypeModule *module)					\
+{										\
+	static const GTypeInfo our_info =					\
+	{									\
+		sizeof (PluginName##Class),					\
+		NULL, /* base_init */						\
+		NULL, /* base_finalize */					\
+		(GClassInitFunc) plugin_name##_class_intern_init,		\
+		NULL,								\
+		NULL, /* class_data */						\
+		sizeof (PluginName),						\
+		0, /* n_preallocs */						\
+		(GInstanceInitFunc) plugin_name##_init				\
+	};									\
+										\
+	rb_debug ("Registering plugin %s", #PluginName);			\
+										\
+	/* Initialise the i18n stuff */						\
+	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);			\
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");			\
+										\
+	plugin_name##_type = g_type_module_register_type (module,		\
+					    RB_TYPE_PLUGIN,			\
+					    #PluginName,			\
+					    &our_info,				\
+					    0);					\
+	return plugin_name##_type;						\
+}
+
+
+G_END_DECLS
+
+#endif  /* __GEDIT_PLUGIN_H__ */
+
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugin-manager.c	2006-02-27 17:56:52.000000000 +1100
@@ -0,0 +1,496 @@
+/*
+ * gedit-plugin-manager.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi and James Willcox
+ * Copyright (C) 2003-2005 Paolo Maggi
+ *
+ * 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 of the License, 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. 
+ */
+
+/*
+ * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <glade/glade-xml.h>
+#include <libgnome/gnome-url.h>
+
+#include "rb-plugin-manager.h"
+#include "rb-plugins-engine.h"
+#include "rb-plugin.h"
+#include "rb-debug.h"
+#include "rb-glade-helpers.h"
+
+enum
+{
+	ACTIVE_COLUMN,
+	NAME_COLUMN,
+	N_COLUMNS
+};
+
+#define PLUGIN_MANAGER_NAME_TITLE _("Plugin")
+#define PLUGIN_MANAGER_ACTIVE_TITLE _("Enabled")
+
+#define RB_PLUGIN_MANAGER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), RB_TYPE_PLUGIN_MANAGER, RBPluginManagerPrivate))
+
+struct _RBPluginManagerPrivate
+{
+	const GList	*plugins;
+	GtkWidget	*tree;
+
+	GtkWidget	*configure_button;
+	GtkWidget	*site_label;
+	GtkWidget	*copyright_label;
+	GtkWidget	*author_label;
+	GtkWidget	*description_label;
+	GtkWidget	*plugin_icon;
+	GtkWidget	*site_text;
+	GtkWidget	*copyright_text;
+	GtkWidget	*author_text;
+	GtkWidget	*description_text;
+	GtkWidget	*plugin_title;
+};
+
+G_DEFINE_TYPE(RBPluginManager, rb_plugin_manager, GTK_TYPE_VBOX)
+
+static RBPluginInfo *plugin_manager_get_selected_plugin (RBPluginManager *pm); 
+static void plugin_manager_toggle_active (GtkTreeIter *iter, GtkTreeModel *model); 
+static void plugin_manager_toggle_all (RBPluginManager *pm); 
+
+static void 
+rb_plugin_manager_class_init (RBPluginManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (RBPluginManagerPrivate));
+}
+
+static void
+configure_button_cb (GtkWidget          *button,
+		     RBPluginManager *pm)
+{
+	RBPluginInfo *info;
+	GtkWindow *toplevel;
+
+	info = plugin_manager_get_selected_plugin (pm);
+
+	g_return_if_fail (info != NULL);
+
+	rb_debug ("Configuring: %s", rb_plugins_engine_get_plugin_name (info));
+
+	toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pm)));
+
+	rb_plugins_engine_configure_plugin (info, toplevel);
+
+	rb_debug ("Done configuring plugin");	
+}
+
+static void
+plugin_manager_view_cell_cb (GtkTreeViewColumn *tree_column,
+			     GtkCellRenderer   *cell,
+			     GtkTreeModel      *tree_model,
+			     GtkTreeIter       *iter,
+			     gpointer           data)
+{
+	RBPluginInfo *info;
+	
+	g_return_if_fail (tree_model != NULL);
+	g_return_if_fail (tree_column != NULL);
+
+	gtk_tree_model_get (tree_model, iter, NAME_COLUMN, &info, -1);
+
+	if (info == NULL)
+		return;
+
+	g_object_set (G_OBJECT (cell), 
+		      "text", 
+		      rb_plugins_engine_get_plugin_name (info), 
+		      NULL);
+}
+
+static void
+active_toggled_cb (GtkCellRendererToggle *cell,
+		   gchar                 *path_str,
+		   RBPluginManager    *pm)
+{
+	GtkTreeIter iter;
+	GtkTreePath *path;
+	GtkTreeModel *model;
+
+	path = gtk_tree_path_new_from_string (path_str);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
+	g_return_if_fail (model != NULL);
+
+	gtk_tree_model_get_iter (model, &iter, path);
+
+	if (&iter != NULL)
+		plugin_manager_toggle_active (&iter, model);
+
+	gtk_tree_path_free (path);
+}
+
+static void
+cursor_changed_cb (GtkTreeSelection *selection,
+		   gpointer     data)
+{
+	RBPluginManager *pm = data;
+	GtkTreeView *view;
+	RBPluginInfo *info;
+
+	view = gtk_tree_selection_get_tree_view (selection);
+	info = plugin_manager_get_selected_plugin (pm);
+
+	/* update info widgets */
+	gtk_label_set_text (GTK_LABEL (pm->priv->plugin_title),
+			    rb_plugins_engine_get_plugin_name (info));
+	gtk_label_set_text (GTK_LABEL (pm->priv->description_text),
+			    rb_plugins_engine_get_plugin_description (info));
+	/* TODO plugin_icon;*/
+	gtk_label_set_text (GTK_LABEL (pm->priv->copyright_text),
+			    rb_plugins_engine_get_plugin_copyright (info));
+	gtk_label_set_text (GTK_LABEL (pm->priv->site_text),
+			    rb_plugins_engine_get_plugin_website (info));
+	/* TODO authors list*/
+
+	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button),
+				  (info != NULL) && 
+				   rb_plugins_engine_plugin_is_configurable (info));
+}
+
+static void
+row_activated_cb (GtkTreeView       *tree_view,
+		  GtkTreePath       *path,
+		  GtkTreeViewColumn *column,
+		  gpointer           data)
+{
+	RBPluginManager *pm = data;
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
+
+	g_return_if_fail (model != NULL);
+
+	gtk_tree_model_get_iter (model, &iter, path);
+
+	g_return_if_fail (&iter != NULL);
+
+	plugin_manager_toggle_active (&iter, model);
+}
+
+static void
+column_clicked_cb (GtkTreeViewColumn *tree_column,
+		   gpointer           data)
+{
+	RBPluginManager *pm = data;
+
+	g_return_if_fail (pm != NULL);
+
+	plugin_manager_toggle_all (pm);
+}
+
+static void
+plugin_manager_populate_lists (RBPluginManager *pm)
+{
+	const GList *plugins;
+	GtkListStore *model;
+	GtkTreeIter iter;
+
+	plugins = pm->priv->plugins;
+
+	model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)));
+
+	while (plugins)	{
+		RBPluginInfo *info;
+		info = (RBPluginInfo *)plugins->data;
+
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    ACTIVE_COLUMN, rb_plugins_engine_plugin_is_active (info),
+				    NAME_COLUMN, info,
+				    -1);
+
+		plugins = plugins->next;
+	}
+
+	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) {
+		GtkTreeSelection *selection;
+		RBPluginInfo* info;
+
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree));
+		g_return_if_fail (selection != NULL);
+		
+		gtk_tree_selection_select_iter (selection, &iter);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
+				    NAME_COLUMN, &info, -1);
+		/*cursor_changed_cb (selection, info);*/
+	}
+}
+
+static void
+plugin_manager_set_active (GtkTreeIter  *iter,
+			   GtkTreeModel *model,
+			   gboolean      active)
+{
+	RBPluginInfo *info;
+	
+	gtk_tree_model_get (model, iter, NAME_COLUMN, &info, -1);
+
+	g_return_if_fail (info != NULL);
+
+	if (active) {
+		/* activate the plugin */
+		if (!rb_plugins_engine_activate_plugin (info)) {
+			rb_debug ("Could not activate %s.", rb_plugins_engine_get_plugin_name (info));
+			active ^= 1;
+		}
+	} else {
+		/* deactivate the plugin */
+		if (!rb_plugins_engine_deactivate_plugin (info)) {
+			rb_debug ("Could not deactivate %s.", rb_plugins_engine_get_plugin_name (info));
+			active ^= 1;
+		}
+	}
+  
+	/* set new value */
+	gtk_list_store_set (GTK_LIST_STORE (model), 
+			    iter, 
+			    ACTIVE_COLUMN,
+			    rb_plugins_engine_plugin_is_active (info), 
+			    -1);
+}
+
+static void
+plugin_manager_toggle_active (GtkTreeIter  *iter,
+			      GtkTreeModel *model)
+{
+	gboolean active;
+	
+	gtk_tree_model_get (model, iter, ACTIVE_COLUMN, &active, -1);
+
+	active ^= 1;
+	plugin_manager_set_active (iter, model, active);
+}
+
+static RBPluginInfo *
+plugin_manager_get_selected_plugin (RBPluginManager *pm)
+{
+	RBPluginInfo *info = NULL;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkTreeSelection *selection;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
+	g_return_val_if_fail (model != NULL, NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree));
+	g_return_val_if_fail (selection != NULL, NULL);
+
+	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+		gtk_tree_model_get (model, &iter, NAME_COLUMN, &info, -1);
+	}
+	
+	return info;
+}
+
+static void
+plugin_manager_toggle_all (RBPluginManager *pm)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	static gboolean active;
+
+	active ^= 1;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
+
+	g_return_if_fail (model != NULL);
+
+	gtk_tree_model_get_iter_first (model, &iter);
+
+	do {
+		plugin_manager_set_active (&iter, model, active);		
+	}
+	while (gtk_tree_model_iter_next (model, &iter));
+}
+
+/* Callback used as the interactive search comparison function */
+static gboolean
+name_search_cb (GtkTreeModel *model,
+		gint          column,
+		const gchar  *key,
+		GtkTreeIter  *iter,
+		gpointer      data)
+{
+	RBPluginInfo *info;
+	gchar *normalized_string;
+	gchar *normalized_key;
+	gchar *case_normalized_string;
+	gchar *case_normalized_key;
+	gint key_len;
+	gboolean retval;
+
+	gtk_tree_model_get (model, iter, NAME_COLUMN, &info, -1);
+	if (!info)
+		return FALSE;
+
+	normalized_string = g_utf8_normalize (rb_plugins_engine_get_plugin_name (info), -1, G_NORMALIZE_ALL);
+	normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
+	case_normalized_string = g_utf8_casefold (normalized_string, -1);
+	case_normalized_key = g_utf8_casefold (normalized_key, -1);
+
+	key_len = strlen (case_normalized_key);
+
+	/* Oddly enough, this callback must return whether to stop the search
+	 * because we found a match, not whether we actually matched.
+	 */
+	retval = (strncmp (case_normalized_key, case_normalized_string, key_len) != 0);
+
+	g_free (normalized_key);
+	g_free (normalized_string);
+	g_free (case_normalized_key);
+	g_free (case_normalized_string);
+
+	return retval;
+}
+
+static void
+plugin_manager_construct_tree (RBPluginManager *pm)
+{
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *cell;
+	GtkListStore *model;
+
+	model = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+
+	gtk_tree_view_set_model (GTK_TREE_VIEW (pm->priv->tree), GTK_TREE_MODEL (model));
+	g_object_unref (model);
+
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (pm->priv->tree), TRUE);
+	gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (pm->priv->tree), TRUE);
+
+	/* first column */
+	cell = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (cell,
+			  "toggled",
+			  G_CALLBACK (active_toggled_cb),
+			  pm);
+	column = gtk_tree_view_column_new_with_attributes (PLUGIN_MANAGER_ACTIVE_TITLE,
+							   cell,
+							  "active",
+							   ACTIVE_COLUMN,
+							   NULL);
+	gtk_tree_view_column_set_clickable (column, TRUE);
+	gtk_tree_view_column_set_resizable (column, TRUE);
+	g_signal_connect (column, "clicked", G_CALLBACK (column_clicked_cb), pm);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column);
+
+	/* second column */
+	cell = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (PLUGIN_MANAGER_NAME_TITLE, cell, NULL);
+	gtk_tree_view_column_set_resizable (column, TRUE);
+	gtk_tree_view_column_set_cell_data_func (column, cell, plugin_manager_view_cell_cb,
+						 pm, NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column);
+
+	/* Enable search for our non-string column */
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (pm->priv->tree), NAME_COLUMN);
+	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (pm->priv->tree),
+					     name_search_cb,
+					     NULL,
+					     NULL);
+
+	g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)),
+			  "changed",
+			  G_CALLBACK (cursor_changed_cb),
+			  pm);
+	g_signal_connect (pm->priv->tree,
+			  "row_activated",
+			  G_CALLBACK (row_activated_cb),
+			  pm);
+
+	gtk_widget_show (pm->priv->tree);
+}
+
+static void 
+rb_plugin_manager_init (RBPluginManager *pm)
+{
+	GladeXML *xml;
+	GtkWidget *plugins_tree_vbox;
+	char *string;
+
+	pm->priv = RB_PLUGIN_MANAGER_GET_PRIVATE (pm);
+
+	xml = rb_glade_xml_new ("plugins.glade",
+				"plugins_vbox",
+				pm);
+	gtk_container_add (GTK_CONTAINER (pm), glade_xml_get_widget (xml, "plugins_vbox"));
+
+	gtk_box_set_spacing (GTK_BOX (pm), 6);
+
+	pm->priv->tree = gtk_tree_view_new ();
+	plugins_tree_vbox = glade_xml_get_widget (xml, "plugins_tree_vbox");
+	gtk_container_add (GTK_CONTAINER (plugins_tree_vbox), pm->priv->tree);
+	
+	pm->priv->configure_button = glade_xml_get_widget (xml, "configure_button");
+	g_signal_connect (pm->priv->configure_button,
+			  "clicked",
+			  G_CALLBACK (configure_button_cb),
+			  pm);
+
+	pm->priv->plugin_title = glade_xml_get_widget (xml, "plugin_title");
+	string = g_strdup_printf ("<span size=\"x-large\">%s</span>",
+				  gtk_label_get_label (GTK_LABEL (pm->priv->plugin_title)));
+	gtk_label_set_markup_with_mnemonic (GTK_LABEL (pm->priv->plugin_title), string);
+	g_free (string);
+	
+	pm->priv->site_label = glade_xml_get_widget (xml, "site_label");
+	rb_glade_boldify_label (xml, "site_label");
+	pm->priv->copyright_label = glade_xml_get_widget (xml, "copyright_label");
+	rb_glade_boldify_label (xml, "copyright_label");
+	pm->priv->author_label = glade_xml_get_widget (xml, "author_label");
+	rb_glade_boldify_label (xml, "author_label");
+	pm->priv->description_label = glade_xml_get_widget (xml, "description_label");
+	rb_glade_boldify_label (xml, "description_label");
+
+	pm->priv->plugin_icon = glade_xml_get_widget (xml, "plugin_icon");
+	pm->priv->site_text = glade_xml_get_widget (xml, "site_text");
+	pm->priv->copyright_text = glade_xml_get_widget (xml, "copyright_text");
+	pm->priv->author_text = glade_xml_get_widget (xml, "author_text");
+	pm->priv->description_text = glade_xml_get_widget (xml, "description_text");
+
+	plugin_manager_construct_tree (pm);
+
+	/* get the list of available plugins (or installed) */
+	pm->priv->plugins = rb_plugins_engine_get_plugins_list ();
+
+	plugin_manager_populate_lists (pm);
+}
+
+GtkWidget *rb_plugin_manager_new (void)
+{
+	return g_object_new (RB_TYPE_PLUGIN_MANAGER, 0);
+}
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugin-manager.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,77 @@
+/*
+ * gedit-plugin-manager.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * 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 of the License, 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. 
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifndef __RB_PLUGIN_MANAGER_H__
+#define __RB_PLUGIN_MANAGER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define RB_TYPE_PLUGIN_MANAGER              (rb_plugin_manager_get_type())
+#define RB_PLUGIN_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), RB_TYPE_PLUGIN_MANAGER, RBPluginManager))
+#define RB_PLUGIN_MANAGER_CONST(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), RB_TYPE_PLUGIN_MANAGER, RBPluginManager const))
+#define RB_PLUGIN_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), RB_TYPE_PLUGIN_MANAGER, RBPluginManagerClass))
+#define RB_IS_PLUGIN_MANAGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), RB_TYPE_PLUGIN_MANAGER))
+#define RB_IS_PLUGIN_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), RB_TYPE_PLUGIN_MANAGER))
+#define RB_PLUGIN_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), RB_TYPE_PLUGIN_MANAGER, RBPluginManagerClass))
+
+/* Private structure type */
+typedef struct _RBPluginManagerPrivate RBPluginManagerPrivate;
+
+/*
+ * Main object structure
+ */
+typedef struct
+{
+	GtkVBox vbox;
+
+	/*< private > */
+	RBPluginManagerPrivate *priv;
+} RBPluginManager;
+
+/*
+ * Class definition
+ */
+typedef struct
+{
+	GtkVBoxClass parent_class;
+} RBPluginManagerClass;
+
+/*
+ * Public methods
+ */
+GType		 rb_plugin_manager_get_type		(void) G_GNUC_CONST;
+
+GtkWidget	*rb_plugin_manager_new		(void);
+   
+G_END_DECLS
+
+#endif  /* __RB_PLUGIN_MANAGER_H__  */
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugins-engine.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,647 @@
+/*
+ * gedit-plugins-engine.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <glib/gkeyfile.h>
+#include <libgnome/gnome-util.h>
+
+#include "eel-gconf-extensions.h"
+#include "rb-file-helpers.h"
+#include "rb-preferences.h"
+#include "rb-util.h"
+#include "rb-plugin.h"
+#include "rb-debug.h"
+#include "rb-dialog.h"
+
+#include "rb-module.h"
+#ifdef ENABLE_PYTHON
+#include "rb-python-module.h"
+#endif
+
+#include "rb-plugins-engine.h"
+
+#define USER_RB_PLUGINS_LOCATION "rhythmbox/plugins/"
+#define UNINSTALLED_PLUGINS_LOCATION "plugins"
+
+#define RB_PLUGINS_ENGINE_BASE_KEY CONF_PREFIX "/plugins"
+#define RB_PLUGINS_ENGINE_KEY RB_PLUGINS_ENGINE_BASE_KEY "/active-plugins"
+
+#define PLUGIN_EXT	".rb-plugin"
+
+typedef enum
+{
+	RB_PLUGIN_LOADER_C,
+	RB_PLUGIN_LOADER_PY,
+} RBPluginLang;
+
+struct _RBPluginInfo
+{
+	gchar        *file;
+	
+	gchar        *location;
+	RBPluginLang lang;
+	GTypeModule  *module;
+
+	gchar        *name;
+	gchar        *desc;
+	gchar       **authors;
+	gchar        *copyright;
+	gchar        *website;
+	 
+	RBPlugin  *plugin;
+	
+	gboolean     active;
+	guint        notification_id;
+};
+
+static void rb_plugin_info_free (RBPluginInfo *info);
+static void rb_plugins_engine_plugin_active_cb (GConfClient *client,
+						guint cnxn_id,
+						GConfEntry *entry,
+						RBPluginInfo *info);
+static gboolean rb_plugins_engine_activate_plugin_real (RBPluginInfo *info,
+							RBShell *shell);
+static void rb_plugins_engine_deactivate_plugin_real (RBPluginInfo *info,
+						      RBShell *shell);
+
+
+static GHashTable *rb_plugins = NULL;
+RBShell *rb_plugins_shell;
+
+static RBPluginInfo *
+rb_plugins_engine_load (const gchar *file)
+{
+	RBPluginInfo *info;
+	GKeyFile *plugin_file = NULL;
+	gchar *str;
+
+	g_return_val_if_fail (file != NULL, NULL);
+
+	rb_debug ("Loading plugin: %s", file);
+
+	info = g_new0 (RBPluginInfo, 1);
+	info->file = g_strdup (file);
+
+	plugin_file = g_key_file_new ();
+	if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL))
+	{
+		g_warning ("Bad plugin file: %s", file);
+		goto error;
+	}
+
+	if (!g_key_file_has_key (plugin_file,
+			   	 "RB Plugin",
+				 "IAge",
+				 NULL))
+	{
+		rb_debug ("IAge key does not exist in file: %s", file);
+		goto error;
+	}
+	
+	/* Check IAge=1 */
+	if (g_key_file_get_integer (plugin_file,
+				    "RB Plugin",
+				    "IAge",
+				    NULL) != 1)
+	{
+		rb_debug ("Wrong IAge in file: %s", file);
+		goto error;
+	}
+				    
+	/* Get Location */
+	str = g_key_file_get_string (plugin_file,
+				     "RB Plugin",
+				     "Module",
+				     NULL);
+	if (str)
+	{
+		info->location = str;
+	}
+	else
+	{
+		g_warning ("Could not find 'Module' in %s", file);
+		goto error;
+	}
+	
+	/* Get the loader for this plugin */
+	str = g_key_file_get_string (plugin_file,
+				     "RB Plugin",
+				     "Loader",
+				     NULL);
+	if (str && strcmp(str, "python") == 0)
+	{
+		info->lang = RB_PLUGIN_LOADER_PY;
+#ifndef ENABLE_PYTHON
+		g_warning ("Cannot load python extension '%s', Rhythmbox was not "
+					"compiled with python support", file);
+		goto error;
+#endif
+	}
+	else
+	{
+		info->lang = RB_PLUGIN_LOADER_C;
+	}
+	g_free (str);
+
+	/* Get Name */
+	str = g_key_file_get_locale_string (plugin_file,
+					    "RB Plugin",
+					    "Name",
+					    NULL, NULL);
+	if (str)
+		info->name = str;
+	else
+	{
+		g_warning ("Could not find 'Name' in %s", file);
+		goto error;
+	}
+
+	/* Get Description */
+	str = g_key_file_get_locale_string (plugin_file,
+					    "RB Plugin",
+					    "Description",
+					    NULL, NULL);
+	if (str)
+		info->desc = str;
+	else
+		rb_debug ("Could not find 'Description' in %s", file);
+
+
+	/* Get Authors */
+	info->authors = g_key_file_get_string_list (plugin_file,
+						    "RB Plugin",
+						    "Authors",
+						    NULL,
+						    NULL);
+	if (info->authors == NULL)
+		rb_debug ("Could not find 'Authors' in %s", file);
+
+
+	/* Get Copyright */
+	str = g_key_file_get_string (plugin_file,
+				     "RB Plugin",
+				     "Copyright",
+				     NULL);
+	if (str)
+		info->copyright = str;
+	else
+		rb_debug ("Could not find 'Copyright' in %s", file);
+
+	/* Get Copyright */
+	str = g_key_file_get_string (plugin_file,
+				     "RB Plugin",
+				     "Website",
+				     NULL);
+	if (str)
+		info->website = str;
+	else
+		rb_debug ("Could not find 'Website' in %s", file);
+		
+	g_key_file_free (plugin_file);
+	
+	return info;
+
+error:
+	g_free (info->file);
+	g_free (info->location);
+	g_free (info->name);
+	g_free (info);
+	g_key_file_free (plugin_file);
+
+	return NULL;
+}
+
+static void
+rb_plugins_engine_load_cb (const char *uri, gpointer userdata)
+{
+	gchar *plugin_file;
+	RBPluginInfo *info;
+	char *key_name;
+	gboolean activate;
+			
+	if (!g_str_has_suffix (uri, PLUGIN_EXT))
+		return;
+
+	plugin_file = gnome_vfs_get_local_path_from_uri (uri);
+	info = rb_plugins_engine_load (plugin_file);
+	g_free (plugin_file);
+
+	if (info == NULL)
+		return;
+
+	if (g_hash_table_lookup (rb_plugins, info->location)) {
+		rb_plugin_info_free (info);
+		return;
+	}
+
+	g_hash_table_insert (rb_plugins, info->location, info);
+	rb_debug ("Plugin %s loaded", info->name);
+	
+	key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
+	info->notification_id = eel_gconf_notification_add (key_name,
+							    (GConfClientNotifyFunc)rb_plugins_engine_plugin_active_cb,
+							    info);
+	activate = eel_gconf_get_boolean (key_name);
+	g_free (key_name);
+
+	if (activate)
+		rb_plugins_engine_activate_plugin (info);
+}
+
+static void
+rb_plugins_engine_load_dir (const gchar *path)
+{
+	char *uri;
+
+	uri = rb_uri_resolve_relative (path);
+	rb_uri_handle_recursively (uri, (GFunc)rb_plugins_engine_load_cb, NULL, NULL);
+	g_free (uri);
+}
+
+static void
+rb_plugins_engine_load_all (void)
+{
+	gchar *pdir;
+
+	/* load user's plugins */
+	pdir = gnome_util_home_file (USER_RB_PLUGINS_LOCATION);
+	rb_plugins_engine_load_dir (pdir);
+	g_free (pdir);
+
+#ifdef SHARE_UNINSTALLED_DIR
+	/* load plugins when running  uninstalled */
+	rb_plugins_engine_load_dir (UNINSTALLED_PLUGINS_LOCATION);
+#endif
+	
+	/* load system-wide plugins */
+	rb_plugins_engine_load_dir (RB_PLUGIN_DIR);
+}
+
+gboolean
+rb_plugins_engine_init (RBShell *shell)
+{
+	g_return_val_if_fail (rb_plugins == NULL, FALSE);
+	
+	if (!g_module_supported ())
+	{
+		g_warning ("rb is not able to initialize the plugins engine.");
+		return FALSE;
+	}
+
+	rb_plugins = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)rb_plugin_info_free);
+
+	rb_plugins_shell = shell;
+	g_object_ref (G_OBJECT (rb_plugins_shell));
+
+	rb_plugins_engine_load_all ();
+
+	return TRUE;
+}
+
+void
+rb_plugins_engine_garbage_collect (void)
+{
+#ifdef ENABLE_PYTHON
+	rb_python_garbage_collect ();
+#endif
+}
+
+static void
+rb_plugin_info_free (RBPluginInfo *info)
+{
+	if (info->active)
+		rb_plugins_engine_deactivate_plugin_real (info, rb_plugins_shell);
+	
+	if (info->plugin != NULL) {
+	       	rb_debug ("Unref plugin %s", info->name);
+
+		g_object_unref (info->plugin);
+			
+		/* info->module must not be unref since it is not possible to finalize 
+		 * a type module */
+	}
+
+	eel_gconf_notification_remove (info->notification_id);
+
+	g_free (info->file);
+	g_free (info->location);
+	g_free (info->name);
+	g_free (info->desc);
+	g_free (info->website);
+	g_free (info->copyright);
+	g_strfreev (info->authors);
+
+	g_free (info);
+}
+
+void
+rb_plugins_engine_shutdown (void)
+{
+	g_hash_table_destroy (rb_plugins);
+	rb_plugins = NULL;
+
+	g_object_unref (rb_plugins_shell);
+	rb_plugins_shell = NULL;
+
+#ifdef ENABLE_PYTHON
+	rb_python_shutdown ();
+#endif
+}
+
+GList *
+rb_plugins_engine_get_plugins_list (void)
+{
+	return rb_collate_hash_table_values (rb_plugins);
+}
+
+static gboolean
+load_plugin_module (RBPluginInfo *info)
+{
+	gchar *path;
+	gchar *dirname;
+
+	g_return_val_if_fail (info != NULL, FALSE);
+	g_return_val_if_fail (info->file != NULL, FALSE);
+	g_return_val_if_fail (info->location != NULL, FALSE);
+	g_return_val_if_fail (info->plugin == NULL, FALSE);
+	
+	switch (info->lang)
+	{
+		case RB_PLUGIN_LOADER_C:
+			dirname = g_path_get_dirname (info->file);	
+			g_return_val_if_fail (dirname != NULL, FALSE);
+
+			path = g_module_build_path (dirname, info->location);
+#ifdef SHARE_UNINSTALLED_DIR
+			if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+				char *temp;
+
+				g_free (path);
+				temp = g_build_filename (dirname, ".libs", NULL);
+				
+				path = g_module_build_path (temp, info->location);
+				g_free (temp);
+			}
+#endif
+			
+			g_free (dirname);
+			g_return_val_if_fail (path != NULL, FALSE);
+	
+			info->module = G_TYPE_MODULE (rb_module_new (path));
+			g_free (path);
+			break;
+		case RB_PLUGIN_LOADER_PY:
+		{
+#ifdef ENABLE_PYTHON
+			gchar *dir = g_path_get_dirname (info->file);
+			
+			info->module = G_TYPE_MODULE (
+					rb_python_module_new (dir, info->location));
+					
+			g_free (dir);
+#endif
+			break;
+		}
+	}
+
+	
+	if (g_type_module_use (info->module) == FALSE)
+	{
+		switch (info->lang)
+		{
+			case RB_PLUGIN_LOADER_C:
+				g_warning ("Could not load plugin file at %s\n",
+				   rb_module_get_path (RB_MODULE (info->module)));
+				break;
+			case RB_PLUGIN_LOADER_PY:
+				g_warning ("Could not load python module %s\n", info->location);
+				break;
+		}
+			   
+		g_object_unref (G_OBJECT (info->module));
+		info->module = NULL;
+		
+		return FALSE;
+	}
+	
+	switch (info->lang)
+	{
+		case RB_PLUGIN_LOADER_C:
+			info->plugin = RB_PLUGIN (rb_module_new_object (RB_MODULE (info->module)));
+			break;
+		case RB_PLUGIN_LOADER_PY:
+#ifdef ENABLE_PYTHON
+			info->plugin = RB_PLUGIN (rb_python_module_new_object (RB_PYTHON_MODULE (info->module)));
+#endif
+			break;
+	}
+	
+	g_type_module_unuse (info->module);
+
+	rb_debug ("End");
+	
+	return TRUE;
+}
+
+static gboolean 	 
+rb_plugins_engine_activate_plugin_real (RBPluginInfo *info, RBShell *shell)
+{
+	gboolean res = TRUE;
+
+	if (info->plugin == NULL)
+		res = load_plugin_module (info);
+
+	if (res)
+		rb_plugin_activate (info->plugin, shell);
+	else
+		g_warning ("Error, impossible to activate plugin '%s'", info->name);
+
+	return res;
+}
+
+gboolean 	 
+rb_plugins_engine_activate_plugin (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, FALSE);
+
+	if (info->active)
+		return TRUE;
+
+	if (rb_plugins_engine_activate_plugin_real (info, rb_plugins_shell)) {
+		char *key_name;
+
+		key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
+		eel_gconf_set_boolean (key_name, TRUE);
+		g_free (key_name);
+	
+		info->active = TRUE;
+
+		return TRUE;
+	}
+
+	rb_error_dialog (NULL, _("Plugin Error"), _("Unable to activate plugin %s"), info->name);
+
+	return FALSE;
+}
+
+static void
+rb_plugins_engine_deactivate_plugin_real (RBPluginInfo *info, RBShell *shell)
+{
+	rb_plugin_deactivate (info->plugin, rb_plugins_shell);
+}
+
+gboolean
+rb_plugins_engine_deactivate_plugin (RBPluginInfo *info)
+{
+	char *key_name;
+	
+	g_return_val_if_fail (info != NULL, FALSE);
+
+	if (!info->active)
+		return TRUE;
+
+	rb_plugins_engine_deactivate_plugin_real (info, rb_plugins_shell);
+
+	/* Update plugin state */
+	info->active = FALSE;
+
+	key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
+	eel_gconf_set_boolean (key_name, FALSE);
+	g_free (key_name);
+
+	return TRUE;
+}
+
+gboolean
+rb_plugins_engine_plugin_is_active (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, FALSE);
+	
+	return info->active;
+}
+
+
+gboolean
+rb_plugins_engine_plugin_is_configurable (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, FALSE);
+
+	if ((info->plugin == NULL) || !info->active)
+		return FALSE;
+	
+	return rb_plugin_is_configurable (info->plugin);
+}
+
+void 	 
+rb_plugins_engine_configure_plugin (RBPluginInfo *info, 
+				       GtkWindow       *parent)
+{
+	GtkWidget *conf_dlg;
+	
+	GtkWindowGroup *wg;
+	
+	g_return_if_fail (info != NULL);
+
+	conf_dlg = rb_plugin_create_configure_dialog (info->plugin);
+	g_return_if_fail (conf_dlg != NULL);
+	gtk_window_set_transient_for (GTK_WINDOW (conf_dlg),
+				      parent);
+
+	wg = parent->group;		      
+	if (wg == NULL)
+	{
+		wg = gtk_window_group_new ();
+		gtk_window_group_add_window (wg, parent);
+	}
+			
+	gtk_window_group_add_window (wg,
+				     GTK_WINDOW (conf_dlg));
+		
+	gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE);		     
+	gtk_widget_show (conf_dlg);
+}
+
+static void
+rb_plugins_engine_plugin_active_cb (GConfClient *client,
+				    guint cnxn_id,
+				    GConfEntry *entry,
+				    RBPluginInfo *info)
+{
+	if (gconf_value_get_bool (entry->value)) {
+		rb_plugins_engine_activate_plugin (info);
+	} else {
+		rb_plugins_engine_deactivate_plugin (info);
+	}
+}
+
+const gchar *
+rb_plugins_engine_get_plugin_name (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, NULL);
+	
+	return info->name;
+}
+
+const gchar *
+rb_plugins_engine_get_plugin_description (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, NULL);
+	
+	return info->desc;
+}
+
+const gchar **
+rb_plugins_engine_get_plugin_authors (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, (const gchar **)NULL);
+	
+	return (const gchar **)info->authors;
+}
+
+const gchar *
+rb_plugins_engine_get_plugin_website (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, NULL);
+	
+	return info->website;
+}
+
+const gchar *
+rb_plugins_engine_get_plugin_copyright (RBPluginInfo *info)
+{
+	g_return_val_if_fail (info != NULL, NULL);
+	
+	return info->copyright;
+}
+
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-plugins-engine.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,65 @@
+/*
+ * gedit-plugins-engine.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 - Paolo Maggi 
+ *
+ * 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 of the License, 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. 
+ */
+ 
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a 
+ * list of people on the gedit Team.  
+ */
+
+#ifndef __RB_PLUGINS_ENGINE_H__
+#define __RB_PLUGINS_ENGINE_H__
+
+#include <glib.h>
+#include <rb-shell.h>
+
+typedef struct _RBPluginInfo RBPluginInfo;
+
+gboolean	 rb_plugins_engine_init 		(RBShell *shell);
+void		 rb_plugins_engine_shutdown 		(void);
+
+void		 rb_plugins_engine_garbage_collect	(void);
+
+GList		*rb_plugins_engine_get_plugins_list 	(void);
+
+gboolean 	 rb_plugins_engine_activate_plugin 	(RBPluginInfo *info);
+gboolean 	 rb_plugins_engine_deactivate_plugin	(RBPluginInfo *info);
+gboolean 	 rb_plugins_engine_plugin_is_active 	(RBPluginInfo *info);
+
+gboolean	 rb_plugins_engine_plugin_is_configurable 
+							(RBPluginInfo *info);
+void	 	 rb_plugins_engine_configure_plugin	(RBPluginInfo *info, 
+							 GtkWindow       *parent);
+
+const gchar	*rb_plugins_engine_get_plugin_name	(RBPluginInfo *info);
+const gchar	*rb_plugins_engine_get_plugin_description
+							(RBPluginInfo *info);
+
+const gchar    **rb_plugins_engine_get_plugin_authors
+							(RBPluginInfo *info);
+const gchar	*rb_plugins_engine_get_plugin_website
+							(RBPluginInfo *info);
+const gchar	*rb_plugins_engine_get_plugin_copyright
+							(RBPluginInfo *info);
+
+#endif  /* __RB_PLUGINS_ENGINE_H__ */
+
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-python-module.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,399 @@
+/*
+ * rb-python-module.c
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * 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 of the License, 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. 
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include <signal.h>
+
+#include <gmodule.h>
+
+#include "rb-python-module.h"
+#include "rb-python-plugin.h"
+#include "rb-debug.h"
+
+#define RB_PYTHON_MODULE_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
+						 RB_TYPE_PYTHON_MODULE, \
+						 RBPythonModulePrivate))
+
+typedef struct
+{
+	gchar *module;
+	gchar *path;
+	GType type;
+} RBPythonModulePrivate;
+
+enum
+{
+	PROP_0,
+	PROP_PATH,
+	PROP_MODULE
+};
+
+/* Exported by pyrb module */
+void pyrb_register_classes (PyObject *d);
+/*void pyrb_add_constants (PyObject *module, const gchar *strip_prefix);*/
+extern PyMethodDef pyrb_functions[];
+
+/* We retreive this to check for correct class hierarchy */
+static PyTypeObject *PyRBPlugin_Type;
+
+G_DEFINE_TYPE (RBPythonModule, rb_python_module, G_TYPE_TYPE_MODULE);
+
+static void
+rb_python_module_init_python ()
+{
+	PyObject *pygtk, *mdict, *require;
+	PyObject *rb, *gtk, *pygtk_version, *pygtk_required_version;
+	PyObject *gettext, *install, *gettext_args;
+	struct sigaction old_sigint;
+	gint res;
+	char *argv[] = { "rb", NULL };
+	
+	if (Py_IsInitialized ())
+	{
+		g_warning ("Python Should only be initialized once, since it's in class_init");
+		g_return_if_reached ();
+	}
+
+	/* Hack to make python not overwrite SIGINT: this is needed to avoid
+	 * the crash reported on gedit bug #326191 */
+	
+	/* Save old handler */
+	res = sigaction (SIGINT, NULL, &old_sigint);  
+	if (res != 0)
+	{
+		g_warning ("Error initializing Python interpreter: cannot get "
+		           "handler to SIGINT signal (%s)",
+		           strerror (errno));
+
+		return;
+	}
+
+	/* Python initialization */
+	Py_Initialize ();
+
+	/* Restore old handler */
+	res = sigaction (SIGINT, &old_sigint, NULL);
+	if (res != 0)
+	{
+		g_warning ("Error initializing Python interpreter: cannot restore "
+		           "handler to SIGINT signal (%s)",
+		           strerror (errno));
+		return;
+	}
+
+	PySys_SetArgv (1, argv);
+
+	/* pygtk.require("2.0") */
+	pygtk = PyImport_ImportModule ("pygtk");
+	if (pygtk == NULL)
+	{
+		g_warning ("Could not import pygtk");
+		return;
+	}
+
+	mdict = PyModule_GetDict (pygtk);
+	require = PyDict_GetItemString (mdict, "require");
+	PyObject_CallObject (require, Py_BuildValue ("(S)", PyString_FromString ("2.0")));
+
+	/* import gobject */
+	init_pygobject ();
+
+	/* import gtk */
+	init_pygtk ();
+
+	/* gtk.pygtk_version < (2, 4, 0) */
+	gtk = PyImport_ImportModule ("gtk");
+	if (gtk == NULL)
+	{
+		g_warning ("Could not import gtk");
+		return;
+	}
+
+	mdict = PyModule_GetDict (gtk);
+	pygtk_version = PyDict_GetItemString (mdict, "pygtk_version");
+	pygtk_required_version = Py_BuildValue ("(iii)", 2, 4, 0);
+	if (PyObject_Compare (pygtk_version, pygtk_required_version) == -1)
+	{
+		g_warning("PyGTK %s required, but %s found.",
+				  PyString_AsString (PyObject_Repr (pygtk_required_version)),
+				  PyString_AsString (PyObject_Repr (pygtk_version)));
+		Py_DECREF (pygtk_required_version);
+		return;
+	}
+	Py_DECREF (pygtk_required_version);
+
+	/* import rb */
+	rb = Py_InitModule ("rb", pyrb_functions);
+	mdict = PyModule_GetDict (rb);
+
+	pyrb_register_classes (mdict);
+	/*pyrb_add_constants (rb, "RB_");*/
+
+	/* Retreive the Python type for rb.Plugin */
+	PyRBPlugin_Type = (PyTypeObject *) PyDict_GetItemString (mdict, "Plugin"); 
+	if (PyRBPlugin_Type == NULL)
+	{
+		PyErr_Print ();
+		return;
+	}
+
+	/* i18n support */
+	gettext = PyImport_ImportModule ("gettext");
+	if (gettext == NULL)
+	{
+		g_warning ("Could not import gettext");
+		return;
+	}
+
+	mdict = PyModule_GetDict (gettext);
+	install = PyDict_GetItemString (mdict, "install");
+	gettext_args = Py_BuildValue ("ss", GETTEXT_PACKAGE, GNOMELOCALEDIR);
+	PyObject_CallObject (install, gettext_args);
+	Py_DECREF (gettext_args);
+}
+
+static gboolean
+rb_python_module_load (GTypeModule *gmodule)
+{
+	RBPythonModulePrivate *priv = RB_PYTHON_MODULE_GET_PRIVATE (gmodule);
+	PyObject *main_module, *main_locals, *locals, *key, *value;
+	PyObject *module;
+	int pos = 0;
+
+	main_module = PyImport_AddModule ("__main__");
+	if (main_module == NULL)
+	{
+		g_warning ("Could not get __main__.");
+		return FALSE;
+	}
+
+	/* If we have a special path, we register it */
+	if (priv->path != NULL)
+	{
+		PyObject *sys_path = PySys_GetObject ("path");
+		PyObject *path = PyString_FromString (priv->path);
+
+		if (PySequence_Contains(sys_path, path) == 0)
+			PyList_Insert (sys_path, 0, path);
+
+		Py_DECREF(path);
+	}
+	
+	main_locals = PyModule_GetDict (main_module);
+	module = PyImport_ImportModuleEx (priv->module, main_locals, main_locals, NULL);
+	if (!module)
+	{
+		PyErr_Print ();
+		return FALSE;
+	}
+
+	locals = PyModule_GetDict (module);
+	while (PyDict_Next (locals, &pos, &key, &value))
+	{
+		if (!PyType_Check(value))
+			continue;
+
+		if (PyObject_IsSubclass (value, (PyObject*) PyRBPlugin_Type))
+		{
+			priv->type = rb_python_object_get_type (gmodule, value);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+rb_python_module_unload (GTypeModule *module)
+{
+	RBPythonModulePrivate *priv = RB_PYTHON_MODULE_GET_PRIVATE (module);
+	rb_debug ("Unloading python module");
+	
+	priv->type = 0;
+}
+
+GObject *
+rb_python_module_new_object (RBPythonModule *module)
+{
+	RBPythonModulePrivate *priv = RB_PYTHON_MODULE_GET_PRIVATE (module);
+	rb_debug ("Creating object of type %s", g_type_name (priv->type));
+
+	if (priv->type == 0)
+		return NULL;
+
+	return g_object_new (priv->type, NULL);
+}
+
+static void
+rb_python_module_init (RBPythonModule *module)
+{
+	rb_debug ("Init of python module");
+}
+
+static void
+rb_python_module_finalize (GObject *object)
+{
+	RBPythonModulePrivate *priv = RB_PYTHON_MODULE_GET_PRIVATE (object);
+	rb_debug ("Finalizing python module %s", g_type_name (priv->type));
+
+	g_free (priv->module);
+	g_free (priv->path);
+
+	G_OBJECT_CLASS (rb_python_module_parent_class)->finalize (object);
+}
+
+static void
+rb_python_module_get_property (GObject    *object,
+				  guint       prop_id,
+				  GValue     *value,
+				  GParamSpec *pspec)
+{
+	/* no readable properties */
+	g_return_if_reached ();
+}
+
+static void
+rb_python_module_set_property (GObject      *object,
+				  guint         prop_id,
+				  const GValue *value,
+				  GParamSpec   *pspec)
+{
+	RBPythonModule *mod = RB_PYTHON_MODULE (object);
+
+	switch (prop_id)
+	{
+		case PROP_MODULE:
+			RB_PYTHON_MODULE_GET_PRIVATE (mod)->module = g_value_dup_string (value);
+			break;
+		case PROP_PATH:
+			RB_PYTHON_MODULE_GET_PRIVATE (mod)->path = g_value_dup_string (value);
+			break;
+		default:
+			g_return_if_reached ();
+	}
+}
+
+static void
+rb_python_module_class_init (RBPythonModuleClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
+
+	object_class->finalize = rb_python_module_finalize;
+	object_class->get_property = rb_python_module_get_property;
+	object_class->set_property = rb_python_module_set_property;
+
+	g_object_class_install_property
+			(object_class,
+			 PROP_MODULE,
+			 g_param_spec_string ("module",
+					      "Module Name",
+					      "The python module to load for this plugin",
+					      NULL,
+					      G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+					      
+	g_object_class_install_property
+			(object_class,
+			 PROP_PATH,
+			 g_param_spec_string ("path",
+					      "Path",
+					      "The python path to use when loading this module",
+					      NULL,
+					      G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (object_class, sizeof (RBPythonModulePrivate));
+	
+	module_class->load = rb_python_module_load;
+	module_class->unload = rb_python_module_unload;
+
+	/* Init python subsystem, this should happen only once
+	 * in the process lifetime, and doing it here is ok since
+	 * class_init is called once */
+	rb_python_module_init_python ();
+}
+
+RBPythonModule *
+rb_python_module_new (const gchar *path,
+			 const gchar *module)
+{
+	RBPythonModule *result;
+
+	if (module == NULL || module[0] == '\0')
+		return NULL;
+
+	result = g_object_new (RB_TYPE_PYTHON_MODULE,
+			       "module", module,
+			       "path", path,
+			       NULL);
+
+	g_type_module_set_name (G_TYPE_MODULE (result), module);
+
+	return result;
+}
+
+
+/* --- these are not module methods, they are here out of convenience --- */
+
+static gint idle_garbage_collect_id = 0;
+
+static gboolean
+run_gc (gpointer data)
+{
+	while (PyGC_Collect ())
+		;	
+
+	idle_garbage_collect_id = 0;
+	return FALSE;
+}
+
+void
+rb_python_garbage_collect (void)
+{
+	if (Py_IsInitialized() && idle_garbage_collect_id == 0)
+	{
+		idle_garbage_collect_id = g_idle_add (run_gc, NULL);
+	}
+}
+
+void
+rb_python_shutdown (void)
+{
+	if (Py_IsInitialized ())
+	{
+		if (idle_garbage_collect_id != 0)
+		{
+			g_source_remove (idle_garbage_collect_id);
+			idle_garbage_collect_id = 0;
+		}
+
+		while (PyGC_Collect ())
+			;	
+
+		Py_Finalize ();
+	}
+}
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-python-module.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,62 @@
+/*
+ * rb-python-module.h
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * 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 of the License, 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. 
+ */
+
+#ifndef RB_PYTHON_MODULE_H
+#define RB_PYTHON_MODULE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_PYTHON_MODULE		(rb_python_module_get_type ())
+#define RB_PYTHON_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_PYTHON_MODULE, RBPythonModule))
+#define RB_PYTHON_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), RB_TYPE_PYTHON_MODULE, RBPythonModuleClass))
+#define RB_IS_PYTHON_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_PYTHON_MODULE))
+#define RB_IS_PYTHON_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((obj), RB_TYPE_PYTHON_MODULE))
+#define RB_PYTHON_MODULE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), RB_TYPE_PYTHON_MODULE, RBPythonModuleClass))
+
+typedef struct
+{
+	GTypeModuleClass parent_class;
+} RBPythonModuleClass;
+
+typedef struct
+{
+	GTypeModule parent_instance;
+} RBPythonModule;
+
+GType			 rb_python_module_get_type		(void);
+
+RBPythonModule	*rb_python_module_new		(const gchar* path,
+								 const gchar *module);
+
+GObject			*rb_python_module_new_object		(RBPythonModule *module);
+
+
+/* --- python utils --- */
+
+void			rb_python_garbage_collect		(void);
+
+void			rb_python_shutdown			(void);
+
+G_END_DECLS
+
+#endif
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-python-plugin.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,257 @@
+/*
+ * rb-python-plugin.c
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * 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 of the License, 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. 
+ */
+
+#include <config.h>
+
+#include "rb-python-plugin.h"
+#include "rb-plugin.h"
+#include "rb-debug.h"
+
+#include <pygobject.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+static PyObject *
+call_python_method (RBPythonObject *object,
+		    RBShell       *shell,
+		    gchar             *method)
+{
+	PyObject *py_ret = NULL;
+
+	g_return_val_if_fail (PyObject_HasAttrString (object->instance, method), NULL);
+
+	if (shell == NULL)
+	{
+		py_ret = PyObject_CallMethod (object->instance,
+					      method,
+					      NULL);
+	}
+	else
+	{
+		py_ret = PyObject_CallMethod (object->instance,
+					      method,
+					      "(N)",
+					      pygobject_new (G_OBJECT (shell)));
+	}
+	
+	if (!py_ret)
+		PyErr_Print ();
+
+	return py_ret;
+}
+
+static gboolean
+check_py_object_is_gtk_widget (PyObject *py_obj)
+{
+	static PyTypeObject *_PyGtkWidget_Type = NULL;
+
+	if (_PyGtkWidget_Type == NULL)
+	{
+		PyObject *module;
+
+	    	if ((module = PyImport_ImportModule ("gtk")))
+	    	{
+			PyObject *moddict = PyModule_GetDict (module);
+			_PyGtkWidget_Type = (PyTypeObject *) PyDict_GetItemString (moddict, "Widget");
+	    	}
+
+		if (_PyGtkWidget_Type == NULL)
+		{
+			PyErr_SetString(PyExc_TypeError, "could not find python gtk widget type");
+			PyErr_Print();
+
+			return FALSE;
+		}
+	}
+
+	return PyObject_TypeCheck (py_obj, _PyGtkWidget_Type) ? TRUE : FALSE;
+}
+
+static void
+impl_deactivate (RBPlugin *plugin,
+		 RBShell *shell)
+{
+	PyGILState_STATE state = pyg_gil_state_ensure ();
+	RBPythonObject *object = (RBPythonObject *)plugin;
+	
+	if (PyObject_HasAttrString (object->instance, "deactivate"))
+	{		
+		PyObject *py_ret = call_python_method (object, shell, "deactivate");
+		
+		if (py_ret)
+		{
+			Py_XDECREF (py_ret);
+		}
+	}
+	else
+		RB_PLUGIN_CLASS (parent_class)->deactivate (plugin, shell);
+
+	pyg_gil_state_release (state);
+}
+
+static void
+impl_activate (RBPlugin *plugin,
+	       RBShell *shell)
+{
+	PyGILState_STATE state = pyg_gil_state_ensure ();
+	RBPythonObject *object = (RBPythonObject *)plugin;
+	
+	if (PyObject_HasAttrString (object->instance, "activate"))
+	{
+		PyObject *py_ret = call_python_method (object, shell, "activate");
+
+		if (py_ret)
+		{
+			Py_XDECREF (py_ret);
+		}
+	}
+	else
+		RB_PLUGIN_CLASS (parent_class)->activate (plugin, shell);
+	
+	pyg_gil_state_release (state);
+}
+
+static GtkWidget *
+impl_create_configure_dialog (RBPlugin *plugin)
+{
+	PyGILState_STATE state = pyg_gil_state_ensure ();
+	RBPythonObject *object = (RBPythonObject *)plugin;
+	GtkWidget *ret = NULL;
+	
+	if (PyObject_HasAttrString (object->instance, "create_configure_dialog"))
+	{
+		PyObject *py_ret = call_python_method (object, NULL, "create_configure_dialog");
+	
+		if (py_ret)
+		{
+			if (check_py_object_is_gtk_widget (py_ret))
+			{
+				ret = GTK_WIDGET (pygobject_get (py_ret));
+				g_object_ref (ret);
+			}
+			else
+			{
+				PyErr_SetString(PyExc_TypeError, "return value for create_configure_dialog is not a GtkWidget");
+				PyErr_Print();
+			}
+			
+			Py_DECREF (py_ret);
+		}
+	}
+	else
+		ret = RB_PLUGIN_CLASS (parent_class)->create_configure_dialog (plugin);
+ 
+	pyg_gil_state_release (state);
+	return ret;
+}
+
+static gboolean
+impl_is_configurable (RBPlugin *plugin)
+{
+	PyGILState_STATE state = pyg_gil_state_ensure ();
+	RBPythonObject *object = (RBPythonObject *) plugin;
+	PyObject *dict = object->instance->ob_type->tp_dict;	
+	gboolean result;
+	
+	if (dict == NULL)
+		result = FALSE;
+	else if (!PyDict_Check(dict))
+		result = FALSE;
+	else 
+		result = PyDict_GetItemString(dict, "create_configure_dialog") != NULL;
+
+	pyg_gil_state_release (state);
+	
+	return result;
+}
+						
+static void 
+rb_python_object_init (RBPythonObject *object)
+{
+	RBPythonObjectClass *class;
+
+	rb_debug ("Creating python plugin instance");
+
+	class = (RBPythonObjectClass*) (((GTypeInstance*) object)->g_class);
+
+	object->instance = PyObject_CallObject (class->type, NULL);
+	if (object->instance == NULL)
+		PyErr_Print();
+}
+
+static void
+rb_python_object_finalize (GObject *object)
+{
+	rb_debug ("Finalizing python plugin instance");
+
+	Py_DECREF (((RBPythonObject *) object)->instance);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+rb_python_object_class_init (RBPythonObjectClass *klass,
+			     gpointer                class_data)
+{
+	RBPluginClass *plugin_class = RB_PLUGIN_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	klass->type = (PyObject*) class_data;
+
+	G_OBJECT_CLASS (klass)->finalize = rb_python_object_finalize;
+
+	plugin_class->activate = impl_activate;
+	plugin_class->deactivate = impl_deactivate;
+	plugin_class->create_configure_dialog = impl_create_configure_dialog;
+	plugin_class->is_configurable = impl_is_configurable;
+}
+
+GType
+rb_python_object_get_type (GTypeModule *module, 
+			      PyObject    *type)
+{
+	GTypeInfo *info;
+	GType gtype;
+	gchar *type_name;
+
+	info = g_new0 (GTypeInfo, 1);
+
+	info->class_size = sizeof (RBPythonObjectClass);
+	info->class_init = (GClassInitFunc) rb_python_object_class_init;
+	info->instance_size = sizeof (RBPythonObject);
+	info->instance_init = (GInstanceInitFunc) rb_python_object_init;
+	info->class_data = type;
+	Py_INCREF (type);
+
+	type_name = g_strdup_printf ("%s+RBPythonPlugin",
+				     PyString_AsString (PyObject_GetAttrString (type, "__name__")));
+
+	rb_debug ("Registering python plugin instance: %s", type_name);
+	gtype = g_type_module_register_type (module, 
+					     RB_TYPE_PLUGIN,
+					     type_name,
+					     info, 0);
+	g_free (type_name);
+
+	return gtype;
+}
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/rb-python-plugin.h	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,46 @@
+/*
+ * rb-python-plugin.h
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * 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 of the License, 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. 
+ */
+#ifndef RB_PYTHON_OBJECT_H
+#define RB_PYTHON_OBJECT_H
+
+#include <Python.h>
+#include <glib-object.h>
+#include "rb-plugin.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+	RBPlugin parent_slot;
+	PyObject *instance;
+} RBPythonObject;
+
+typedef struct
+{
+	RBPluginClass parent_slot;
+	PyObject *type;
+} RBPythonObjectClass;
+
+GType rb_python_object_get_type (GTypeModule *module, PyObject *type);
+
+G_END_DECLS
+
+#endif
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/audioscrobbler/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,43 @@
+plugindir = $(PLUGINDIR)
+plugin_LTLIBRARIES = libaudioscrobbler.la
+
+libaudioscrobbler_la_SOURCES = \
+	rb-audioscrobbler-plugin.c
+	
+libaudioscrobbler_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libaudioscrobbler_la_LD_ADD = $(SOUP_LIBS)
+
+INCLUDES = 						\
+        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib                        	\
+	-I$(top_srcdir)/metadata                       	\
+	-I$(top_srcdir)/player                       	\
+	-I$(top_srcdir)/rhythmdb                       	\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/sources                    	\
+	-I$(top_srcdir)/iradio                    	\
+	-I$(top_srcdir)/podcast                    	\
+	-I$(top_srcdir)/remote				\
+	-I$(top_builddir)/remote			\
+	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/shell				\
+	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
+	-DDATADIR=\""$(datadir)"\"			\
+	$(SOUP_CFLAGS)					\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(WARN_CFLAGS)					\
+	-D_XOPEN_SOURCE -D_BSD_SOURCE
+
+plugin_in_files = audioscrobbler.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-02-27 10:05:56.364381512 +1100
+++ plugins/audioscrobbler/audioscrobbler.rb-plugin.desktop.in	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,8 @@
+[RB Plugin]
+Module=audioscrobbler
+IAge=1
+_Name=Last.fm support
+_Description=Submits songs to last.fm
+Authors=author
+Copyright=copyright
+Website=http://www.gnome.org/projects/rhythmbox
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/audioscrobbler/rb-audioscrobbler-plugin.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,172 @@
+/*
+ * rb-audioscrobbler-plugin.c
+ * 
+ * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h> /* For strlen */
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "rb-audioscrobbler.h"
+#include "rb-plugin.h"
+#include "rb-debug.h"
+#include "rb-shell.h"
+#include "rb-dialog.h"
+
+
+#define RB_TYPE_AUDIOSCROBBLER_PLUGIN		(rb_audioscrobbler_plugin_get_type ())
+#define RB_AUDIOSCROBBLER_PLUGIN(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_AUDIOSCROBBLER_PLUGIN, RBAudioscrobblerPlugin))
+#define RB_AUDIOSCROBBLER_PLUGIN_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_AUDIOSCROBBLER_PLUGIN, RBAudioscrobblerPluginClass))
+#define RB_IS_AUDIOSCROBBLER_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_AUDIOSCROBBLER_PLUGIN))
+#define RB_IS_AUDIOSCROBBLER_PLUGIN_CLASS(k)		(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_AUDIOSCROBBLER_PLUGIN))
+#define RB_AUDIOSCROBBLER_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_AUDIOSCROBBLER_PLUGIN, RBAudioscrobblerPluginClass))
+
+typedef struct
+{
+	RBAudioscrobbler *audioscrobbler;
+	GtkWidget *preferences;
+} RBAudioscrobblerPluginPrivate;
+
+typedef struct
+{
+	RBPlugin parent;
+	RBAudioscrobblerPluginPrivate *priv;
+} RBAudioscrobblerPlugin;
+
+typedef struct
+{
+	RBPluginClass parent_class;
+} RBAudioscrobblerPluginClass;
+
+
+G_MODULE_EXPORT GType register_rb_plugin (GTypeModule *module);
+GType	rb_audioscrobbler_plugin_get_type		(void) G_GNUC_CONST;
+
+
+
+static void rb_audioscrobbler_plugin_init (RBAudioscrobblerPlugin *plugin);
+static void rb_audioscrobbler_plugin_finalize (GObject *object);
+static void impl_activate (RBPlugin *plugin, RBShell *shell);
+static void impl_deactivate (RBPlugin *plugin, RBShell *shell);
+static GtkWidget* impl_create_configure_dialog (RBPlugin *plugin);
+
+RB_PLUGIN_REGISTER(RBAudioscrobblerPlugin, rb_audioscrobbler_plugin)
+#define RB_AUDIOSCROBBLER_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), RB_TYPE_AUDIOSCROBBLER_PLUGIN, RBAudioscrobblerPluginPrivate))
+
+
+static void
+rb_audioscrobbler_plugin_class_init (RBAudioscrobblerPluginClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBPluginClass *plugin_class = RB_PLUGIN_CLASS (klass);
+
+	object_class->finalize = rb_audioscrobbler_plugin_finalize;
+
+	plugin_class->activate = impl_activate;
+	plugin_class->deactivate = impl_deactivate;
+	plugin_class->create_configure_dialog = impl_create_configure_dialog;
+	
+	g_type_class_add_private (object_class, sizeof (RBAudioscrobblerPluginPrivate));
+}
+
+static void
+rb_audioscrobbler_plugin_init (RBAudioscrobblerPlugin *plugin)
+{
+	plugin->priv = RB_AUDIOSCROBBLER_PLUGIN_GET_PRIVATE (plugin);
+
+	rb_debug ("RBAudioscrobblerPlugin initialising");
+}
+
+static void
+rb_audioscrobbler_plugin_finalize (GObject *object)
+{
+	RBAudioscrobblerPluginPrivate *priv = RB_AUDIOSCROBBLER_PLUGIN (object)->priv;
+
+	rb_debug ("RBAudioscrobblerPlugin finalising");
+
+	g_assert (priv->audioscrobbler == NULL);
+
+	if (priv->preferences)
+		gtk_widget_destroy (priv->preferences);
+
+	G_OBJECT_CLASS (rb_audioscrobbler_plugin_parent_class)->finalize (object);
+}
+
+
+
+static void
+impl_activate (RBPlugin *plugin,
+	       RBShell *shell)
+{
+	RBAudioscrobblerPluginPrivate *priv = RB_AUDIOSCROBBLER_PLUGIN (plugin)->priv;
+
+	g_assert (priv->audioscrobbler == NULL);
+	priv->audioscrobbler = rb_audioscrobbler_new (RB_SHELL_PLAYER (rb_shell_get_player (shell)));
+}
+
+static void
+impl_deactivate	(RBPlugin *plugin,
+		 RBShell *shell)
+{
+	RBAudioscrobblerPluginPrivate *priv = RB_AUDIOSCROBBLER_PLUGIN (plugin)->priv;
+
+	g_assert (priv->audioscrobbler != NULL);
+	g_object_unref (priv->audioscrobbler);
+	priv->audioscrobbler = NULL;
+}
+
+static void
+preferences_response_cb (GtkWidget *dialog, gint response, RBPlugin *plugin)
+{
+	gtk_widget_hide (dialog);
+}
+
+static GtkWidget*
+impl_create_configure_dialog (RBPlugin *plugin)
+{
+	RBAudioscrobblerPluginPrivate *priv = RB_AUDIOSCROBBLER_PLUGIN (plugin)->priv;
+
+	if (priv->preferences == NULL) {
+		GtkWidget *widget;
+
+		widget =  rb_audioscrobbler_get_config_widget (priv->audioscrobbler);
+
+		priv->preferences = gtk_dialog_new_with_buttons (_("Audioscrobbler preferences"),
+								 NULL,
+								 GTK_DIALOG_DESTROY_WITH_PARENT,
+								 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+								 NULL);
+		g_signal_connect (G_OBJECT (priv->preferences),
+				  "response",
+				  G_CALLBACK (preferences_response_cb),
+				  plugin);
+		gtk_widget_hide_on_delete (priv->preferences);
+		
+		gtk_container_add (GTK_CONTAINER (GTK_DIALOG (priv->preferences)->vbox), widget);
+	}
+	
+	gtk_widget_show_all (priv->preferences);
+	return priv->preferences;
+}
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/sample/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,44 @@
+plugindir = $(PLUGINDIR)
+plugin_LTLIBRARIES = libsample.la
+
+libsample_la_SOURCES = \
+	rb-sample-plugin.c
+	
+libsample_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+
+INCLUDES = 						\
+        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib                        	\
+	-I$(top_srcdir)/metadata                       	\
+	-I$(top_srcdir)/player                       	\
+	-I$(top_srcdir)/rhythmdb                       	\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/sources                    	\
+	-I$(top_srcdir)/iradio                    	\
+	-I$(top_srcdir)/podcast                    	\
+	-I$(top_srcdir)/remote				\
+	-I$(top_builddir)/remote			\
+	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/shell				\
+	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
+	-DDATADIR=\""$(datadir)"\"			\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(WARN_CFLAGS)					\
+	-D_XOPEN_SOURCE -D_BSD_SOURCE
+
+gladedir = $(datadir)/rhythmbox/glade
+glade_DATA =
+
+plugin_in_files = sample.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 = $(glade_DATA) $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/sample/sample.rb-plugin.desktop.in	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,8 @@
+[RB Plugin]
+Module=sample
+IAge=1
+_Name=Sample Plugin
+_Description=Does a whole lot of not much
+Authors=Paolo Maggi <paolo@gnome.org>
+Copyright=Copyright © 2002-2005 Paolo Maggi
+Website=http://www.gnome.org/projects/rhythmbox
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/sample/rb-sample-plugin.c	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,125 @@
+/*
+ * rb-sample-plugin.h
+ * 
+ * Copyright (C) 2002-2005 - Paolo Maggi
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h> /* For strlen */
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "rb-plugin.h"
+#include "rb-debug.h"
+#include "rb-shell.h"
+#include "rb-dialog.h"
+
+
+#define RB_TYPE_SAMPLE_PLUGIN		(rb_sample_plugin_get_type ())
+#define RB_SAMPLE_PLUGIN(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_SAMPLE_PLUGIN, RBSamplePlugin))
+#define RB_SAMPLE_PLUGIN_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_SAMPLE_PLUGIN, RBSamplePluginClass))
+#define RB_IS_SAMPLE_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_SAMPLE_PLUGIN))
+#define RB_IS_SAMPLE_PLUGIN_CLASS(k)		(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_SAMPLE_PLUGIN))
+#define RB_SAMPLE_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_SAMPLE_PLUGIN, RBSamplePluginClass))
+
+typedef struct
+{
+	gpointer dummy;
+} RBSamplePluginPrivate;
+
+typedef struct
+{
+	RBPlugin parent;
+	RBSamplePluginPrivate *priv;
+} RBSamplePlugin;
+
+typedef struct
+{
+	RBPluginClass parent_class;
+} RBSamplePluginClass;
+
+
+G_MODULE_EXPORT GType register_rb_plugin (GTypeModule *module);
+GType	rb_sample_plugin_get_type		(void) G_GNUC_CONST;
+
+
+
+static void rb_sample_plugin_init (RBSamplePlugin *plugin);
+static void rb_sample_plugin_finalize (GObject *object);
+static void impl_activate (RBPlugin *plugin, RBShell *shell);
+static void impl_deactivate (RBPlugin *plugin, RBShell *shell);
+
+RB_PLUGIN_REGISTER(RBSamplePlugin, rb_sample_plugin)
+#define RB_SAMPLE_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), RB_TYPE_SAMPLE_PLUGIN, RBSamplePluginPrivate))
+
+
+static void
+rb_sample_plugin_class_init (RBSamplePluginClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBPluginClass *plugin_class = RB_PLUGIN_CLASS (klass);
+
+	object_class->finalize = rb_sample_plugin_finalize;
+
+	plugin_class->activate = impl_activate;
+	plugin_class->deactivate = impl_deactivate;
+	
+	g_type_class_add_private (object_class, sizeof (RBSamplePluginPrivate));
+}
+
+static void
+rb_sample_plugin_init (RBSamplePlugin *plugin)
+{
+	plugin->priv = RB_SAMPLE_PLUGIN_GET_PRIVATE (plugin);
+
+	rb_debug ("RBSamplePlugin initialising");
+}
+
+static void
+rb_sample_plugin_finalize (GObject *object)
+{
+/*
+	RBSamplePlugin *plugin = RB_SAMPLE_PLUGIN (object);
+*/
+	rb_debug ("RBSamplePlugin finalising");
+
+	G_OBJECT_CLASS (rb_sample_plugin_parent_class)->finalize (object);
+}
+
+
+
+static void
+impl_activate (RBPlugin *plugin,
+	       RBShell *shell)
+{
+	rb_error_dialog (NULL, _("Sample Plugin"), "Sample plugin activated, with shell %p", shell);
+}
+
+static void
+impl_deactivate	(RBPlugin *plugin,
+		 RBShell *shell)
+{
+	rb_error_dialog (NULL, _("Sample Plugin"), "Sample plugin deactivated");
+}
+
+
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/sample-python/Makefile.am	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,14 @@
+# sample python plugin
+plugindir = $(PLUGINDIR)
+plugin_PYTHON = \
+	sample-python.py
+
+plugin_in_files = sample-python.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-02-27 10:05:56.364381512 +1100
+++ plugins/sample-python/sample-python.py	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,12 @@
+import rb
+
+class SamplePython(rb.Plugin):
+
+	def __init__(self):
+		rb.Plugin.__init__(self)
+			
+	def activate(self, shell):
+		print "activating sample python plugin"
+	
+	def deactivate(self, shell):
+		print "deactivating sample python plugin"
--- /dev/null	2006-02-27 10:05:56.364381512 +1100
+++ plugins/sample-python/sample-python.rb-plugin.desktop.in	2006-02-27 15:16:18.000000000 +1100
@@ -0,0 +1,9 @@
+[RB Plugin]
+Loader=python
+Module=sample-python
+IAge=1
+_Name=Python Sample
+_Description= doesn't do much
+Authors=James Livingston <jrl@ids.org.au>
+Copyright=Copyright © 2006 james Livingston
+Website=http://www.rhythmbox.org
