diff -u -u -r1.45 Makefile.am
--- Makefile.am	9 Dec 2005 13:57:03 -0000	1.45
+++ Makefile.am	16 Feb 2006 08:34:38 -0000
@@ -5,7 +5,7 @@
 DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install --enable-gtk-doc
 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 shell data po help tests doc
 
 EXTRA_DIST = 			\
 	autogen.sh		\
diff -u -u -r1.208 configure.ac
--- configure.ac	3 Feb 2006 18:18:47 -0000	1.208
+++ configure.ac	16 Feb 2006 08:34:38 -0000
@@ -665,6 +665,13 @@
 dnl Enable gtk-doc
 GTK_DOC_CHECK(1.4)
 
+
+PLUGIN_LIBTOOL_FLAGS="-module -avoid-version"
+AC_SUBST(PLUGIN_LIBTOOL_FLAGS)
+
+AS_AC_EXPAND(PLUGINDIR, ${libdir}/rhythmbox/plugins)
+
+
 AC_OUTPUT([ 
 Makefile
 macros/Makefile
@@ -686,6 +693,8 @@
 data/rhythmbox.pc
 sources/Makefile
 daapsharing/Makefile
+plugins/Makefile
+plugins/sample/Makefile
 help/Makefile
 help/C/Makefile
 help/ja/Makefile
diff -u -u -r1.63 rhythmbox.schemas
--- data/rhythmbox.schemas	3 Feb 2006 18:19:35 -0000	1.63
+++ data/rhythmbox.schemas	16 Feb 2006 08:34:39 -0000
@@ -606,5 +606,17 @@
 	<long>When enabled, Rhythmbox will display notification bubbles when the playing track changes, a podcast is downloaded, a CD finished burning, etc.</long>
         </locale>
       </schema>
+      <schema>
+        <key>/schemas/apps/rhythmbox/plugins/active-plugins</key>
+        <applyto>/apps/rhythmbox/plugins/active-plugins</applyto>
+        <owner>rhythmbox</owner>
+        <type>list</type>
+        <list_type>string</list_type>
+        <default>[]</default>
+        <locale name="C">
+	<short>Plugins that Rhythmbox should activate on startup</short>
+	<long>A list of plugin ids, that will be activated when Rhythmbox starts.</long>
+        </locale>
+      </schema>
     </schemalist>
 </gconfschemafile>
diff -u -u -r1.39 rhythmbox-ui.xml
--- data/ui/rhythmbox-ui.xml	14 Feb 2006 04:37:02 -0000	1.39
+++ data/ui/rhythmbox-ui.xml	16 Feb 2006 08:34:39 -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.26 rb-preferences.h
--- lib/rb-preferences.h	7 Feb 2006 13:19:56 -0000	1.26
+++ lib/rb-preferences.h	16 Feb 2006 08:34:39 -0000
@@ -57,6 +57,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	16 Feb 2006 08:34:45 -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)				\
@@ -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)"\"			\
diff -u -u -r1.387 rb-shell.c
--- shell/rb-shell.c	15 Feb 2006 10:59:11 -0000	1.387
+++ shell/rb-shell.c	16 Feb 2006 08:34:47 -0000
@@ -81,11 +81,13 @@
 #include "rb-iradio-source.h"
 #include "rb-new-podcast-dialog.h"
 #include "rb-shell-preferences.h"
+#include "rb-plugin-manager.h"
 #include "rb-playlist-source.h"
 #include "rb-static-playlist-source.h"
 #include "rb-play-queue-source.h"
 #include "eel-gconf-extensions.h"
 #include "bacon-volume.h"
+#include "rb-plugins-engine.h"
 
 #ifdef WITH_AUDIOSCROBBLER
 #include "rb-audioscrobbler.h"
@@ -176,6 +178,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,
@@ -398,6 +402,7 @@
 	RBSource *selected_source;
 
 	GtkWidget *prefs;
+	GtkWidget *plugins;
 
 	RBTrayIcon *tray_icon;
 	GtkTooltips *tooltips;
@@ -468,9 +473,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) },
@@ -1289,6 +1297,7 @@
 
 	rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
 
+	rb_plugins_engine_init (shell);
 	
 	/* GO GO GO! */
 	rb_debug ("loading database");
@@ -2128,7 +2137,58 @@
 					      GTK_WINDOW (shell->priv->window));
 	}
 
-	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
@@ -2257,6 +2317,8 @@
 rb_shell_quit (RBShell *shell)
 {
 	rb_debug ("Quitting");
+
+	rb_plugins_engine_shutdown ();
 
 	rb_podcast_source_shutdown (shell->priv->podcast_source);	
 	
--- /dev/null	2006-02-16 13:08:05.305390480 +1100
+++ plugins/rb-module.c	2006-02-16 15:12:12.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-16 13:08:05.305390480 +1100
+++ plugins/rb-module.h	2006-02-16 15:12:12.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-16 13:08:05.305390480 +1100
+++ plugins/rb-plugin.c	2006-02-16 15:12:12.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-16 13:08:05.305390480 +1100
+++ plugins/rb-plugin.h	2006-02-16 15:12:12.000000000 +1100
@@ -0,0 +1,161 @@
+/*
+ * 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 _RBPlugin RBPlugin;
+
+struct _RBPlugin 
+{
+	GObject parent;
+};
+
+/*
+ * 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);
+
+	/* Padding for future expansion */
+	void		(*_gedit_reserved1)	(void);
+	void		(*_gedit_reserved2)	(void);
+	void		(*_gedit_reserved3)	(void);
+	void		(*_gedit_reserved4)	(void);
+} 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-16 13:08:05.305390480 +1100
+++ plugins/rb-plugin-manager.h	2006-02-16 15:12:12.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-16 13:08:05.305390480 +1100
+++ plugins/rb-plugin-manager.c	2006-02-16 17:07:13.000000000 +1100
@@ -0,0 +1,539 @@
+/*
+ * 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"
+
+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
+{
+	GtkWidget	*tree;
+
+	GtkWidget	*about_button;
+	GtkWidget	*configure_button;
+
+	const GList	*plugins;
+
+	GtkWidget 	*about;
+};
+
+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
+about_button_cb (GtkWidget          *button,
+		 RBPluginManager *pm)
+{
+	RBPluginInfo *info;
+	GdkPixbuf *pixbuf = NULL;
+
+	info = plugin_manager_get_selected_plugin (pm);
+
+	g_return_if_fail (info != NULL);
+
+	/*pixbuf = gdk_pixbuf_new_from_file (GNOME_ICONDIR "/gedit-plugin-manager.png", NULL);*/
+
+	/* if there is another about dialog already open destroy it */
+	if (pm->priv->about)
+		gtk_widget_destroy (pm->priv->about);
+
+	pm->priv->about = g_object_new (GTK_TYPE_ABOUT_DIALOG,
+		"name", rb_plugins_engine_get_plugin_name (info),
+		"copyright", rb_plugins_engine_get_plugin_copyright (info),
+		"authors", rb_plugins_engine_get_plugin_authors (info),
+		"comments", rb_plugins_engine_get_plugin_description (info),
+		"website", rb_plugins_engine_get_plugin_website (info),
+		"logo", pixbuf,
+		NULL);
+
+	gtk_window_set_destroy_with_parent (GTK_WINDOW (pm->priv->about),
+					    TRUE);
+
+	g_signal_connect (pm->priv->about,
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy),
+			  NULL);
+	g_signal_connect (pm->priv->about,
+			  "destroy",
+			  G_CALLBACK (gtk_widget_destroyed),
+			  &pm->priv->about);
+
+	gtk_window_set_transient_for (GTK_WINDOW (pm->priv->about),
+				      GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm))));
+	gtk_widget_show (pm->priv->about);
+
+	if (pixbuf != NULL)
+		g_object_unref (pixbuf);
+}
+
+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 (GtkTreeView *view,
+		   gpointer     data)
+{
+	RBPluginManager *pm = data;
+	RBPluginInfo *info;
+
+	info = plugin_manager_get_selected_plugin (pm);
+
+	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->about_button),
+				  info != NULL);
+	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);
+
+		gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button),
+					  rb_plugins_engine_plugin_is_configurable (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 (pm->priv->tree,
+			  "cursor_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)
+{
+	GtkWidget *viewport;
+	GtkWidget *hbuttonbox;
+
+	pm->priv = RB_PLUGIN_MANAGER_GET_PRIVATE (pm);
+
+	gtk_box_set_spacing (GTK_BOX (pm), 6);
+
+	viewport = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (viewport),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (viewport), 
+					     GTK_SHADOW_IN);
+
+	gtk_box_pack_start (GTK_BOX (pm), viewport, TRUE, TRUE, 0);
+
+	pm->priv->tree = gtk_tree_view_new ();
+	gtk_container_add (GTK_CONTAINER (viewport), pm->priv->tree);
+
+	hbuttonbox = gtk_hbutton_box_new ();
+	gtk_box_pack_start (GTK_BOX (pm), hbuttonbox, FALSE, FALSE, 0);
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
+	gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8);
+
+	pm->priv->about_button = gtk_button_new_with_mnemonic (_("_About Plugin"));
+	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->about_button);
+
+	pm->priv->configure_button = gtk_button_new_with_mnemonic (_("C_onfigure Plugin"));
+	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->configure_button);
+
+	/* setup a window of a sane size. */
+	gtk_widget_set_size_request (GTK_WIDGET (viewport), 270, 100);
+
+	g_signal_connect (pm->priv->about_button,
+			  "clicked",
+			  G_CALLBACK (about_button_cb),
+			  pm);
+	g_signal_connect (pm->priv->configure_button,
+			  "clicked",
+			  G_CALLBACK (configure_button_cb),
+			  pm);
+
+	plugin_manager_construct_tree (pm);
+
+	/* get the list of available plugins (or installed) */
+	pm->priv->plugins = rb_plugins_engine_get_plugins_list ();
+
+	if (pm->priv->plugins != NULL)
+	{
+		plugin_manager_populate_lists (pm);
+	}
+	else
+	{
+		gtk_widget_set_sensitive (pm->priv->about_button, FALSE);
+		gtk_widget_set_sensitive (pm->priv->configure_button, FALSE);		
+	}
+}
+
+GtkWidget *rb_plugin_manager_new (void)
+{
+	return g_object_new (RB_TYPE_PLUGIN_MANAGER,0);
+}
--- /dev/null	2006-02-16 13:08:05.305390480 +1100
+++ plugins/rb-plugins-engine.c	2006-02-16 19:32:24.000000000 +1100
@@ -0,0 +1,639 @@
+/*
+ * 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 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->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);
+
+		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-16 13:08:05.305390480 +1100
+++ plugins/rb-plugins-engine.h	2006-02-16 17:56:07.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-16 13:08:05.305390480 +1100
+++ plugins/Makefile.am	2006-02-16 15:12:12.000000000 +1100
@@ -0,0 +1,38 @@
+## arch-tag: Automake rules for Rhythmbox's plugin system
+
+SUBDIRS = sample
+
+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)\"		\
+	-D_XOPEN_SOURCE -D_BSD_SOURCE
+
+librbplugins_la_LDFLAGS = -export-dynamic
--- /dev/null	2006-02-16 13:08:05.305390480 +1100
+++ plugins/sample/Makefile.am	2006-02-16 15:12:12.000000000 +1100
@@ -0,0 +1,45 @@
+plugindir = $(PLUGINDIR)
+plugin_LTLIBRARIES = libsample.la
+
+libsample_la_SOURCES = \
+	rb-sample-plugin.h	\
+	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-16 13:08:05.305390480 +1100
+++ plugins/sample/rb-sample-plugin.c	2006-02-16 15:12:12.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-16 13:08:05.305390480 +1100
+++ plugins/sample/sample.rb-plugin.desktop.in	2006-02-16 17:00:12.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
