[libpeas] Split the PeasUIPluginManager into a manager, view and store



commit 209d74c14af821ef88686f4bd5eebab92e8d03f1
Author: Garrett Regier <alias301 gmail com>
Date:   Mon Jul 19 11:49:36 2010 -0700

    Split the PeasUIPluginManager into a manager, view and store
    
    https://bugzilla.gnome.org/show_bug.cgi?id=624309

 libpeasui/Makefile.am                    |   16 +-
 libpeasui/peas-ui-plugin-manager-store.c |  505 ++++++++++++++++
 libpeasui/peas-ui-plugin-manager-store.h |   96 +++
 libpeasui/peas-ui-plugin-manager-view.c  |  877 ++++++++++++++++++++++++++++
 libpeasui/peas-ui-plugin-manager-view.h  |   79 +++
 libpeasui/peas-ui-plugin-manager.c       |  931 +++++++-----------------------
 libpeasui/peas-ui-plugin-manager.h       |    6 +-
 libpeasui/peas-ui.h                      |    1 +
 8 files changed, 1788 insertions(+), 723 deletions(-)
---
diff --git a/libpeasui/Makefile.am b/libpeasui/Makefile.am
index bfbb515..fcbf548 100644
--- a/libpeasui/Makefile.am
+++ b/libpeasui/Makefile.am
@@ -21,13 +21,23 @@ INST_H_FILES = \
 	peas-ui-configurable.h		\
 	peas-ui-plugin-info.h		\
 	peas-ui-plugin-manager.h	\
+	peas-ui-plugin-manager-view.h	\
 	peas-ui.h
 
-libpeasui_1_0_la_SOURCES = \
+NOINST_H_FILES = \
+	peas-ui-plugin-manager-store.h
+
+C_FILES = \
 	peas-ui-configurable.c		\
 	peas-ui-plugin-info.c		\
 	peas-ui-plugin-manager.c	\
-	$(INST_H_FILES)
+	peas-ui-plugin-manager-store.c	\
+	peas-ui-plugin-manager-view.c
+
+libpeasui_1_0_la_SOURCES = \
+	$(INST_H_FILES)			\
+	$(NOINST_H_FILES)		\
+	$(C_FILES)
 
 headerdir = $(prefix)/include/libpeas-1.0/libpeasui
 header_DATA = $(INST_H_FILES)
@@ -41,7 +51,7 @@ if HAVE_INTROSPECTION
 	--warn-all
   INTROSPECTION_COMPILER_ARGS = --includedir=$(top_srcdir)/libpeas
 
-  introspection_sources = $(libpeasui_1_0_la_SOURCES)
+  introspection_sources = $(INST_H_FILES) $(C_FILES)
 
   PeasUI-1.0.gir: libpeasui-1.0.la
   PeasUI_1_0_gir_INCLUDES = GObject-2.0 $(GTK_TYPELIB) Peas-1.0
diff --git a/libpeasui/peas-ui-plugin-manager-store.c b/libpeasui/peas-ui-plugin-manager-store.c
new file mode 100644
index 0000000..61b268c
--- /dev/null
+++ b/libpeasui/peas-ui-plugin-manager-store.c
@@ -0,0 +1,505 @@
+/*
+ * peas-plugin-manager-store.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2002 Paolo Maggi and James Willcox
+ * Copyright (C) 2003-2006 Paolo Maggi, Paolo Borelli
+ * Copyright (C) 2007-2009 Paolo Maggi, Paolo Borelli, Steve Frécinaux
+ * Copyright (C) 2010 Garrett Regier
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library 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 <libpeas/peas-plugin-info.h>
+
+#include "peas-ui-plugin-manager-store.h"
+#include "peas-ui-plugin-info.h"
+
+static const GType ColumnTypes[] = {
+  G_TYPE_BOOLEAN, /* Enabled */
+  G_TYPE_BOOLEAN, /* Enabled Visible */
+  G_TYPE_STRING,  /* Icon */
+  G_TYPE_BOOLEAN, /* Icon Visible */
+  G_TYPE_STRING,  /* Info */
+  G_TYPE_BOOLEAN, /* Info Visible */
+  G_TYPE_POINTER  /* PeasPluginInfo */
+};
+
+/* G_STATIC_ASSERT is in glib >= 2.20 and we only depend on 2.18.0 */
+#ifdef G_STATIC_ASSERT
+G_STATIC_ASSERT (G_N_ELEMENTS (ColumnTypes) == PEAS_UI_PLUGIN_MANAGER_STORE_N_COLUMNS);
+#endif
+
+struct _PeasUIPluginManagerStorePrivate {
+  PeasEngine *engine;
+};
+
+/* Properties */
+enum {
+  PROP_0,
+  PROP_ENGINE
+};
+
+G_DEFINE_TYPE (PeasUIPluginManagerStore, peas_ui_plugin_manager_store, GTK_TYPE_LIST_STORE);
+
+static void
+update_plugin (PeasUIPluginManagerStore *store,
+               GtkTreeIter              *iter,
+               PeasPluginInfo           *info)
+{
+  gboolean loaded;
+  gboolean available;
+  gboolean builtin;
+  gchar *markup;
+  const gchar *icon_name;
+
+  loaded = peas_plugin_info_is_loaded (info);
+  available = peas_plugin_info_is_available (info);
+  builtin = peas_plugin_info_is_builtin (info);
+
+  markup = g_markup_printf_escaped ("<b>%s</b>\n%s",
+                                    peas_plugin_info_get_name (info),
+                                    peas_plugin_info_get_description (info));
+
+  if (peas_plugin_info_is_available (info))
+    icon_name = peas_ui_plugin_info_get_icon_name (info);
+  else
+    icon_name = GTK_STOCK_DIALOG_ERROR;
+
+  gtk_list_store_set (GTK_LIST_STORE (store), iter,
+    PEAS_UI_PLUGIN_MANAGER_STORE_ENABLED_COLUMN,          loaded,
+    PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,       !builtin && available,
+    PEAS_UI_PLUGIN_MANAGER_STORE_ICON_COLUMN,             icon_name,
+    PEAS_UI_PLUGIN_MANAGER_STORE_ICON_VISIBLE_COLUMN,     !available,
+    PEAS_UI_PLUGIN_MANAGER_STORE_INFO_COLUMN,             markup,
+    PEAS_UI_PLUGIN_MANAGER_STORE_INFO_SENSITIVE_COLUMN,   available && (!builtin || loaded),
+    PEAS_UI_PLUGIN_MANAGER_STORE_PLUGIN_COLUMN,           info,
+    -1);
+
+  g_free (markup);
+}
+
+static void
+plugin_loaded_toggled_cb (PeasEngine               *engine,
+                          PeasPluginInfo           *info,
+                          PeasUIPluginManagerStore *store)
+{
+  GtkTreeIter iter;
+
+  if (peas_ui_plugin_manager_store_get_iter_from_plugin (store, &iter, info))
+    update_plugin (store, &iter, info);
+}
+
+static gint
+model_name_sort_func (PeasUIPluginManagerStore *store,
+                      GtkTreeIter              *iter1,
+                      GtkTreeIter              *iter2,
+                      gpointer                  user_data)
+{
+  PeasPluginInfo *info1;
+  PeasPluginInfo *info2;
+
+  info1 = peas_ui_plugin_manager_store_get_plugin (store, iter1);
+  info2 = peas_ui_plugin_manager_store_get_plugin (store, iter2);
+
+  return g_utf8_collate (peas_plugin_info_get_name (info1),
+                         peas_plugin_info_get_name (info2));
+}
+
+static void
+peas_ui_plugin_manager_store_init (PeasUIPluginManagerStore *store)
+{
+  store->priv = G_TYPE_INSTANCE_GET_PRIVATE (store,
+                                             PEAS_UI_TYPE_PLUGIN_MANAGER_STORE,
+                                             PeasUIPluginManagerStorePrivate);
+
+  gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+                                   PEAS_UI_PLUGIN_MANAGER_STORE_N_COLUMNS,
+                                   (GType *) ColumnTypes);
+
+  /* Sort on the plugin names */
+  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
+                                           (GtkTreeIterCompareFunc) model_name_sort_func,
+                                           NULL, NULL);
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+                                        GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+                                        GTK_SORT_ASCENDING);
+}
+
+static void
+peas_ui_plugin_manager_store_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  PeasUIPluginManagerStore *store = PEAS_UI_PLUGIN_MANAGER_STORE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      store->priv->engine = g_value_get_object (value);
+      g_object_ref (store->priv->engine);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+peas_ui_plugin_manager_store_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  PeasUIPluginManagerStore *store = PEAS_UI_PLUGIN_MANAGER_STORE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      g_value_set_object (value, store->priv->engine);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+peas_ui_plugin_manager_store_constructed (GObject *object)
+{
+  PeasUIPluginManagerStore *store = PEAS_UI_PLUGIN_MANAGER_STORE (object);
+
+  g_signal_connect_after (store->priv->engine,
+                          "load-plugin",
+                          G_CALLBACK (plugin_loaded_toggled_cb),
+                          store);
+  g_signal_connect_after (store->priv->engine,
+                          "unload-plugin",
+                          G_CALLBACK (plugin_loaded_toggled_cb),
+                          store);
+
+  peas_ui_plugin_manager_store_reload (store);
+
+  if (G_OBJECT_CLASS (peas_ui_plugin_manager_store_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (peas_ui_plugin_manager_store_parent_class)->constructed (object);
+}
+
+static void
+peas_ui_plugin_manager_store_dispose (GObject *object)
+{
+  PeasUIPluginManagerStore *store = PEAS_UI_PLUGIN_MANAGER_STORE (object);
+
+  if (store->priv->engine != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (store->priv->engine,
+                                            plugin_loaded_toggled_cb,
+                                            store);
+
+      g_object_unref (store->priv->engine);
+      store->priv->engine = NULL;
+    }
+
+  G_OBJECT_CLASS (peas_ui_plugin_manager_store_parent_class)->dispose (object);
+}
+
+static void
+peas_ui_plugin_manager_store_class_init (PeasUIPluginManagerStoreClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = peas_ui_plugin_manager_store_set_property;
+  object_class->get_property = peas_ui_plugin_manager_store_get_property;
+  object_class->constructed = peas_ui_plugin_manager_store_constructed;
+  object_class->dispose = peas_ui_plugin_manager_store_dispose;
+
+  /*
+   * PeasUIPLuginManagerStore:engine:
+   *
+   * The #PeasEngine this store is attached to.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ENGINE,
+                                   g_param_spec_object ("engine",
+                                                        "engine",
+                                                        "The PeasEngine this store is attached to",
+                                                        PEAS_TYPE_ENGINE,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (object_class, sizeof (PeasUIPluginManagerStorePrivate));
+}
+
+/*
+ * peas_ui_plugin_manager_store_new:
+ * @engine: A #PeasEngine.
+ *
+ * Creates a new plugin manager store for the given #PeasEngine.
+ *
+ * Returns: the new #PeasUIPluginManagerStore.
+ */
+PeasUIPluginManagerStore  *
+peas_ui_plugin_manager_store_new (PeasEngine *engine)
+{
+  g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
+
+  return PEAS_UI_PLUGIN_MANAGER_STORE (g_object_new (PEAS_UI_TYPE_PLUGIN_MANAGER_STORE,
+                                                     "engine", engine,
+                                                     NULL));
+}
+
+/*
+ * peas_ui_plugin_manager_store_reload:
+ * @store: A #PeasUIPluginManagerStore.
+ *
+ * Reloads the list of plugins.
+ */
+void
+peas_ui_plugin_manager_store_reload (PeasUIPluginManagerStore *store)
+{
+  GtkListStore *list_store;
+  const GList *plugins;
+  GtkTreeIter iter;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store));
+
+  list_store = GTK_LIST_STORE (store);
+
+  gtk_list_store_clear (list_store);
+
+  plugins = peas_engine_get_plugin_list (store->priv->engine);
+
+  while (plugins != NULL)
+    {
+      PeasPluginInfo *info;
+
+      info = PEAS_PLUGIN_INFO (plugins->data);
+
+      gtk_list_store_append (list_store, &iter);
+      update_plugin (store, &iter, info);
+
+      plugins = plugins->next;
+    }
+}
+
+/*
+ * peas_ui_plugin_manager_store_set_enabled:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ * @enabled: If the plugin should be enabled.
+ *
+ * Sets if the plugin at @iter should be enabled.
+ */
+void
+peas_ui_plugin_manager_store_set_enabled (PeasUIPluginManagerStore *store,
+                                          GtkTreeIter              *iter,
+                                          gboolean                  enabled)
+{
+  PeasPluginInfo *info;
+  gboolean success = TRUE;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store));
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (peas_ui_plugin_manager_store_can_enable (store, iter));
+
+  info = peas_ui_plugin_manager_store_get_plugin (store, iter);
+  g_return_if_fail (info != NULL);
+
+  if (enabled)
+    {
+      /* load the plugin */
+      if (!peas_engine_load_plugin (store->priv->engine, info))
+        success = FALSE;
+    }
+  else
+    {
+      /* unload the plugin */
+      if (!peas_engine_unload_plugin (store->priv->engine, info))
+        success = FALSE;
+    }
+
+  if (success)
+    update_plugin (store, iter, info);
+}
+
+/*
+ * peas_ui_plugin_manager_store_set_enabled:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * Returns if the plugin at @iter is enabled.
+ *
+ * Returns: if the plugin at @iter is enabled.
+ */
+gboolean
+peas_ui_plugin_manager_store_get_enabled (PeasUIPluginManagerStore *store,
+                                          GtkTreeIter              *iter)
+{
+  gboolean enabled;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store), FALSE);
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+                      PEAS_UI_PLUGIN_MANAGER_STORE_ENABLED_COLUMN, &enabled,
+                      -1);
+
+  return enabled;
+}
+
+/*
+ * peas_ui_plugin_manager_store_set_all_enabled:
+ * @store: A #PeasUIPluginManagerStore.
+ * @enabled: If all the plugins should be enabled.
+ *
+ * Sets if all the plugins should be enabled.
+ */
+void
+peas_ui_plugin_manager_store_set_all_enabled (PeasUIPluginManagerStore *store,
+                                              gboolean                  enabled)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store));
+
+  model = GTK_TREE_MODEL (store);
+
+  if (!gtk_tree_model_get_iter_first (model, &iter))
+    return;
+
+  do
+    {
+      if (peas_ui_plugin_manager_store_can_enable (store, &iter))
+        peas_ui_plugin_manager_store_set_enabled (store, &iter, enabled);
+    }
+  while (gtk_tree_model_iter_next (model, &iter));
+}
+
+/*
+ * peas_ui_plugin_manager_store_toggle_enabled:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * Toggles the if the plugin should should be enabled.
+ */
+void
+peas_ui_plugin_manager_store_toggle_enabled (PeasUIPluginManagerStore *store,
+                                             GtkTreeIter              *iter)
+{
+  gboolean enabled;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store));
+  g_return_if_fail (iter != NULL);
+
+  enabled = peas_ui_plugin_manager_store_get_enabled (store, iter);
+
+  peas_ui_plugin_manager_store_set_enabled (store, iter, !enabled);
+}
+
+/*
+ * peas_ui_plugin_manager_store_can_enabled:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * Returns if the plugin at @iter can be enabled.
+ * Note: that while a plugin may be enableable there are other factors
+ * that can cause it to not be enabled.
+ *
+ * Returns: if the plugin can be enabled.
+ */
+gboolean
+peas_ui_plugin_manager_store_can_enable (PeasUIPluginManagerStore *store,
+                                         GtkTreeIter              *iter)
+{
+  gboolean can_enable;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store), FALSE);
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+                      PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN, &can_enable,
+                      -1);
+
+  return can_enable;
+}
+
+/*
+ * peas_ui_plugin_manager_store_get_plugin:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * Returns the plugin at @iter.
+ *
+ * Returns: the plugin at @iter.
+ */
+PeasPluginInfo *
+peas_ui_plugin_manager_store_get_plugin (PeasUIPluginManagerStore *store,
+                                         GtkTreeIter              *iter)
+{
+  PeasPluginInfo *info;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store), NULL);
+  g_return_val_if_fail (iter != NULL, NULL);
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+                      PEAS_UI_PLUGIN_MANAGER_STORE_PLUGIN_COLUMN, &info,
+                      -1);
+
+  return info;
+}
+
+/*
+ * peas_ui_plugin_manager_store_get_iter_from_plugin:
+ * @store: A #PeasUIPluginManagerStore.
+ * @iter: A #GtkTreeIter.
+ * @info: A #PeasPluginInfo.
+ *
+ * Sets @iter to the @info.
+ *
+ * Returns: if @iter was set.
+ */
+gboolean
+peas_ui_plugin_manager_store_get_iter_from_plugin (PeasUIPluginManagerStore *store,
+                                                   GtkTreeIter              *iter,
+                                                   const PeasPluginInfo     *info)
+{
+  GtkTreeModel *model = GTK_TREE_MODEL (store);
+  gboolean found = FALSE;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_STORE (store), FALSE);
+  g_return_val_if_fail (iter != NULL, FALSE);
+  g_return_val_if_fail (info != NULL, FALSE);
+
+  if (gtk_tree_model_get_iter_first (model, iter))
+    {
+      PeasPluginInfo *current_info;
+
+      do
+        {
+          current_info = peas_ui_plugin_manager_store_get_plugin (store, iter);
+
+          found = (info == current_info);
+        }
+      while (!found && gtk_tree_model_iter_next (model, iter));
+    }
+
+  return found;
+}
diff --git a/libpeasui/peas-ui-plugin-manager-store.h b/libpeasui/peas-ui-plugin-manager-store.h
new file mode 100644
index 0000000..293e919
--- /dev/null
+++ b/libpeasui/peas-ui-plugin-manager-store.h
@@ -0,0 +1,96 @@
+/*
+ * peas-plugin-manager-store.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2002 Paolo Maggi and James Willcox
+ * Copyright (C) 2003-2006 Paolo Maggi, Paolo Borelli
+ * Copyright (C) 2007-2009 Paolo Maggi, Paolo Borelli, Steve Frécinaux
+ * Copyright (C) 2010 Garrett Regier
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library 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 __PEAS_UI_PLUGIN_MANAGER_STORE_H__
+#define __PEAS_UI_PLUGIN_MANAGER_STORE_H__
+
+#include <gtk/gtk.h>
+#include <libpeas/peas-engine.h>
+#include <libpeas/peas-plugin-info.h>
+
+G_BEGIN_DECLS
+
+enum {
+  PEAS_UI_PLUGIN_MANAGER_STORE_ENABLED_COLUMN = 0,
+  PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_ICON_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_ICON_VISIBLE_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_INFO_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_INFO_SENSITIVE_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_PLUGIN_COLUMN,
+  PEAS_UI_PLUGIN_MANAGER_STORE_N_COLUMNS
+} PeasUIPluginManagerStoreColumns;
+
+/*
+ * Type checking and casting macros
+ */
+#define PEAS_UI_TYPE_PLUGIN_MANAGER_STORE             (peas_ui_plugin_manager_store_get_type())
+#define PEAS_UI_PLUGIN_MANAGER_STORE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_STORE, PeasUIPluginManagerStore))
+#define PEAS_UI_PLUGIN_MANAGER_STORE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), PEAS_UI_TYPE_PLUGIN_MANAGER_STORE, PeasUIPluginManagerStoreClass))
+#define PEAS_UI_IS_PLUGIN_MANAGER_STORE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_STORE))
+#define PEAS_UI_IS_PLUGIN_MANAGER_STORE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), PEAS_UI_TYPE_PLUGIN_MANAGER_STORE))
+#define PEAS_UI_PLUGIN_MANAGER_STORE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_STORE, PeasUIPluginManagerStoreClass))
+
+typedef struct _PeasUIPluginManagerStore         PeasUIPluginManagerStore;
+typedef struct _PeasUIPluginManagerStoreClass    PeasUIPluginManagerStoreClass;
+typedef struct _PeasUIPluginManagerStorePrivate  PeasUIPluginManagerStorePrivate;
+
+struct _PeasUIPluginManagerStore {
+  GtkListStore parent;
+
+  /*< private > */
+  PeasUIPluginManagerStorePrivate *priv;
+};
+
+struct _PeasUIPluginManagerStoreClass {
+  GtkListStoreClass parent_class;
+};
+
+GType                     peas_ui_plugin_manager_store_get_type             (void) G_GNUC_CONST;
+PeasUIPluginManagerStore *peas_ui_plugin_manager_store_new                  (PeasEngine               *engine);
+
+void                      peas_ui_plugin_manager_store_reload               (PeasUIPluginManagerStore *store);
+
+void                      peas_ui_plugin_manager_store_set_enabled          (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter,
+                                                                             gboolean                  enabled);
+gboolean                  peas_ui_plugin_manager_store_get_enabled          (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter);
+void                      peas_ui_plugin_manager_store_set_all_enabled      (PeasUIPluginManagerStore *store,
+                                                                             gboolean                  enabled);
+void                      peas_ui_plugin_manager_store_toggle_enabled       (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter);
+
+gboolean                  peas_ui_plugin_manager_store_can_enable           (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter);
+
+PeasPluginInfo           *peas_ui_plugin_manager_store_get_plugin           (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter);
+
+gboolean                  peas_ui_plugin_manager_store_get_iter_from_plugin (PeasUIPluginManagerStore *store,
+                                                                             GtkTreeIter              *iter,
+                                                                             const PeasPluginInfo     *info);
+G_END_DECLS
+
+#endif /* __PEAS_UI_PLUGIN_MANAGER_STORE_H__  */
diff --git a/libpeasui/peas-ui-plugin-manager-view.c b/libpeasui/peas-ui-plugin-manager-view.c
new file mode 100644
index 0000000..e31cbb3
--- /dev/null
+++ b/libpeasui/peas-ui-plugin-manager-view.c
@@ -0,0 +1,877 @@
+/*
+ * peas-plugin-manager-view.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2002 Paolo Maggi and James Willcox
+ * Copyright (C) 2003-2006 Paolo Maggi, Paolo Borelli
+ * Copyright (C) 2007-2009 Paolo Maggi, Paolo Borelli, Steve Frécinaux
+ * Copyright (C) 2010 Garrett Regier
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library 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>
+
+#include <libpeas/peas-i18n.h>
+
+#include "peas-ui-plugin-manager-view.h"
+#include "peas-ui-plugin-manager-store.h"
+#include "peas-ui-configurable.h"
+
+/**
+ * SECTION:peas-ui-plugin-manager-view
+ * @short_description: Management tree view for plugins.
+ *
+ * The #PeasUIPluginManagerView is a tree view that can be used to manage
+ * plugins, i.e. load or unload them, and see some pieces of information.
+ *
+ * The only thing you need to do as an application writer if you wish
+ * to use the view to configure your plugins is to instantiate it using
+ * peas_ui_plugin_manager_view_new() and pack it into another
+ * widget or a window.
+ *
+ * Note: Changing the model of the view is not supported.
+ *
+ **/
+
+struct _PeasUIPluginManagerViewPrivate {
+  PeasEngine *engine;
+
+  PeasUIPluginManagerStore *store;
+
+  GtkWidget *popup_menu;
+
+  guint show_builtin : 1;
+};
+
+/* Properties */
+enum {
+  PROP_0,
+  PROP_ENGINE,
+  PROP_SHOW_BUILTIN
+};
+
+/* Signals */
+enum {
+  POPULATE_POPUP,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (PeasUIPluginManagerView, peas_ui_plugin_manager_view, GTK_TYPE_TREE_VIEW);
+
+static void
+convert_iter_to_child_iter (PeasUIPluginManagerView *view,
+                            GtkTreeIter             *iter)
+{
+  if (!view->priv->show_builtin)
+    {
+      GtkTreeModel *model;
+      GtkTreeIter child_iter;
+
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+      gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+                                                        &child_iter, iter);
+
+      *iter = child_iter;
+    }
+}
+
+static gboolean
+convert_child_iter_to_iter (PeasUIPluginManagerView *view,
+                            GtkTreeIter             *child_iter)
+{
+  gboolean success = TRUE;
+
+  if (!view->priv->show_builtin)
+    {
+      GtkTreeModel *model;
+      GtkTreeIter iter;
+
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+      success = gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (model),
+                                                                  &iter, child_iter);
+
+      if (success)
+        *child_iter = iter;
+    }
+
+  return success;
+}
+
+static void
+plugin_list_changed_cb (PeasEngine              *engine,
+                        GParamSpec              *pspec,
+                        PeasUIPluginManagerView *view)
+{
+  PeasPluginInfo *info;
+
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
+
+  peas_ui_plugin_manager_store_reload (view->priv->store);
+
+  if (info == NULL)
+    {
+      GtkTreeModel *model;
+      GtkTreeIter iter;
+
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+      if (gtk_tree_model_get_iter_first (model, &iter))
+        {
+          PeasUIPluginManagerStore *store;
+
+          store = PEAS_UI_PLUGIN_MANAGER_STORE (view->priv->store);
+
+          convert_iter_to_child_iter (view, &iter);
+          info = peas_ui_plugin_manager_store_get_plugin (store, &iter);
+        }
+    }
+
+  if (info != NULL)
+    peas_ui_plugin_manager_view_set_selected_plugin (view, info);
+}
+
+static gboolean
+filter_builtins_visible (PeasUIPluginManagerStore *store,
+                         GtkTreeIter              *iter,
+                         PeasUIPluginManagerView  *view)
+{
+  PeasPluginInfo *info;
+
+  /* We never filter showing builtins */
+  g_assert (view->priv->show_builtin == FALSE);
+
+  info = peas_ui_plugin_manager_store_get_plugin (store, iter);
+
+  if (info == NULL)
+    return FALSE;
+
+  return !peas_plugin_info_is_builtin (info);
+}
+
+static void
+enabled_toggled_cb (GtkCellRendererToggle   *cell,
+                    gchar                   *path_str,
+                    PeasUIPluginManagerView *view)
+{
+  GtkTreeModel *model;
+  GtkTreePath *path;
+  GtkTreeIter iter;
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  path = gtk_tree_path_new_from_string (path_str);
+
+  if (gtk_tree_model_get_iter (model, &iter, path))
+    {
+      convert_iter_to_child_iter (view, &iter);
+      peas_ui_plugin_manager_store_toggle_enabled (view->priv->store, &iter);
+    }
+
+  gtk_tree_path_free (path);
+}
+
+static void
+row_activated_cb (GtkTreeView             *tree_view,
+                  GtkTreePath             *path,
+                  GtkTreeViewColumn       *column,
+                  PeasUIPluginManagerView *view)
+{
+  GtkTreeIter iter;
+
+  g_return_if_fail (gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view),
+                                             &iter, path));
+
+  convert_iter_to_child_iter (view, &iter);
+
+  if (peas_ui_plugin_manager_store_can_enable (view->priv->store, &iter))
+    peas_ui_plugin_manager_store_toggle_enabled (view->priv->store, &iter);
+}
+
+/* Callback used as the interactive search comparison function */
+static gboolean
+name_search_cb (GtkTreeModel            *model,
+                gint                     column,
+                const gchar             *key,
+                GtkTreeIter             *iter,
+                PeasUIPluginManagerView *view)
+{
+  PeasPluginInfo *info;
+  gchar *normalized_string;
+  gchar *normalized_key;
+  gchar *case_normalized_string;
+  gchar *case_normalized_key;
+  gint key_len;
+  gboolean retval;
+
+  info = peas_ui_plugin_manager_store_get_plugin (view->priv->store, iter);
+
+  if (info == NULL)
+    return FALSE;
+
+  normalized_string = g_utf8_normalize (peas_plugin_info_get_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
+enabled_menu_cb (GtkMenu                 *menu,
+                 PeasUIPluginManagerView *view)
+{
+  GtkTreeIter iter;
+  GtkTreeSelection *selection;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  g_return_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter));
+
+  convert_iter_to_child_iter (view, &iter);
+
+  peas_ui_plugin_manager_store_toggle_enabled (view->priv->store, &iter);
+}
+
+static void
+enable_all_menu_cb (GtkMenu                 *menu,
+                    PeasUIPluginManagerView *view)
+{
+  peas_ui_plugin_manager_store_set_all_enabled (view->priv->store, TRUE);
+}
+
+static void
+disable_all_menu_cb (GtkMenu                 *menu,
+                     PeasUIPluginManagerView *view)
+{
+  peas_ui_plugin_manager_store_set_all_enabled (view->priv->store, FALSE);
+}
+
+static GtkWidget *
+create_popup_menu (PeasUIPluginManagerView *view)
+{
+  PeasPluginInfo *info;
+  GtkWidget *menu;
+  GtkWidget *item;
+
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
+
+  if (info == NULL)
+    return NULL;
+
+  menu = gtk_menu_new ();
+
+  item = gtk_check_menu_item_new_with_mnemonic (_("_Enabled"));
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+                                  peas_plugin_info_is_loaded (info));
+  g_signal_connect (item, "toggled", G_CALLBACK (enabled_menu_cb), view);
+  gtk_widget_set_sensitive (item, peas_plugin_info_is_available (info) &&
+                                  !peas_plugin_info_is_builtin (info));
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_menu_item_new_with_mnemonic (_("E_nable All"));
+  g_signal_connect (item, "activate", G_CALLBACK (enable_all_menu_cb), view);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_menu_item_new_with_mnemonic (_("_Disable All"));
+  g_signal_connect (item, "activate", G_CALLBACK (disable_all_menu_cb), view);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  g_signal_emit (view, signals[POPULATE_POPUP], 0, menu);
+
+  gtk_widget_show_all (menu);
+
+  return menu;
+}
+
+static void
+popup_menu_detach (PeasUIPluginManagerView *view,
+                   GtkMenu                 *menu)
+{
+  view->priv->popup_menu = NULL;
+}
+
+static void
+menu_position_under_tree_view (GtkMenu     *menu,
+                               gint        *x,
+                               gint        *y,
+                               gboolean    *push_in,
+                               GtkTreeView *tree_view)
+{
+  GtkTreeSelection *selection;
+  GtkTreeIter iter;
+  GdkWindow *window;
+
+  selection = gtk_tree_view_get_selection (tree_view);
+
+  window = gtk_widget_get_window (GTK_WIDGET (tree_view));
+  gdk_window_get_origin (window, x, y);
+
+  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      GtkTreeModel *model;
+      GtkTreePath *path;
+      GdkRectangle rect;
+
+      model = gtk_tree_view_get_model (tree_view);
+      path = gtk_tree_model_get_path (model, &iter);
+      gtk_tree_view_get_cell_area (tree_view,
+                                   path,
+                                   gtk_tree_view_get_column (tree_view, 0), /* FIXME 0 for RTL ? */
+                                   &rect);
+      gtk_tree_path_free (path);
+
+      *x += rect.x;
+      *y += rect.y + rect.height;
+
+      if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
+        {
+          GtkRequisition requisition;
+          gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
+          *x += rect.width - requisition.width;
+        }
+    }
+  else
+    {
+      GtkAllocation allocation;
+      gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
+      *x += allocation.x;
+      *y += allocation.y;
+
+      if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
+        {
+          GtkRequisition requisition;
+          gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
+          *x += allocation.width - requisition.width;
+        }
+    }
+
+  *push_in = TRUE;
+}
+
+static void
+show_popup_menu (GtkTreeView             *tree_view,
+                 PeasUIPluginManagerView *view,
+                 GdkEventButton          *event)
+{
+  if (view->priv->popup_menu)
+    gtk_widget_destroy (view->priv->popup_menu);
+
+  view->priv->popup_menu = create_popup_menu (view);
+
+  if (view->priv->popup_menu == NULL)
+    return;
+
+  gtk_menu_attach_to_widget (GTK_MENU (view->priv->popup_menu),
+                             GTK_WIDGET (view),
+                             (GtkMenuDetachFunc) popup_menu_detach);
+
+  if (event != NULL)
+    {
+      gtk_menu_popup (GTK_MENU (view->priv->popup_menu), NULL, NULL,
+                      NULL, NULL, event->button, event->time);
+    }
+  else
+    {
+      gtk_menu_popup (GTK_MENU (view->priv->popup_menu), NULL, NULL,
+                      (GtkMenuPositionFunc) menu_position_under_tree_view,
+                      view, 0, gtk_get_current_event_time ());
+
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (view->priv->popup_menu),
+                                   FALSE);
+    }
+}
+
+static gboolean
+button_press_event_cb (GtkWidget               *tree_view,
+                       GdkEventButton          *event,
+                       PeasUIPluginManagerView *view)
+{
+  GtkWidgetClass *widget_class;
+  gboolean handled;
+
+  if (event->type != GDK_BUTTON_PRESS || event->button != 3)
+    return FALSE;
+
+  widget_class = GTK_WIDGET_CLASS (peas_ui_plugin_manager_view_parent_class);
+
+  /* The selection must by updated */
+  handled = widget_class->button_press_event (tree_view, event);
+
+  if (!handled)
+    return FALSE;
+
+  show_popup_menu (GTK_TREE_VIEW (tree_view), view, event);
+
+  return TRUE;
+}
+
+static gboolean
+popup_menu_cb (GtkTreeView             *tree_view,
+               PeasUIPluginManagerView *view)
+{
+  show_popup_menu (tree_view, view, NULL);
+
+  return TRUE;
+}
+
+static void
+peas_ui_plugin_manager_view_init (PeasUIPluginManagerView *view)
+{
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+
+  view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
+                                            PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW,
+                                            PeasUIPluginManagerViewPrivate);
+
+  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+
+  /* first column */
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Enabled"));
+  gtk_tree_view_column_set_resizable (column, FALSE);
+
+  cell = gtk_cell_renderer_toggle_new ();
+  gtk_tree_view_column_pack_start (column, cell, FALSE);
+  g_object_set (cell, "xpad", 6, NULL);
+  gtk_tree_view_column_set_attributes (column, cell,
+                                       "active", PEAS_UI_PLUGIN_MANAGER_STORE_ENABLED_COLUMN,
+                                       "activatable", PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,
+                                       "sensitive", PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,
+                                       "visible", PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,
+                                       NULL);
+  g_signal_connect (cell,
+                    "toggled",
+                    G_CALLBACK (enabled_toggled_cb),
+                    view);
+
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+  /* second column */
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Plugin"));
+  gtk_tree_view_column_set_resizable (column, FALSE);
+
+  cell = gtk_cell_renderer_pixbuf_new ();
+  gtk_tree_view_column_pack_start (column, cell, FALSE);
+  g_object_set (cell, "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
+  gtk_tree_view_column_set_attributes (column, cell,
+                                       //"sensitive", PEAS_UI_PLUGIN_MANAGER_STORE_CAN_ENABLE_COLUMN,
+                                       "icon-name", PEAS_UI_PLUGIN_MANAGER_STORE_ICON_COLUMN,
+                                       NULL);
+
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, cell, TRUE);
+  g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+  gtk_tree_view_column_set_attributes (column, cell,
+                                       "sensitive", PEAS_UI_PLUGIN_MANAGER_STORE_INFO_SENSITIVE_COLUMN,
+                                       "markup", PEAS_UI_PLUGIN_MANAGER_STORE_INFO_COLUMN,
+                                       NULL);
+
+  gtk_tree_view_column_set_spacing (column, 6);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+  /* Enable search for our non-string column */
+  gtk_tree_view_set_search_column (GTK_TREE_VIEW (view),
+                                   PEAS_UI_PLUGIN_MANAGER_STORE_PLUGIN_COLUMN);
+  gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
+                                       (GtkTreeViewSearchEqualFunc) name_search_cb,
+                                       view,
+                                       NULL);
+
+  gtk_widget_show (GTK_WIDGET (view));
+
+  g_signal_connect (view,
+                    "row-activated",
+                    G_CALLBACK (row_activated_cb),
+                    view);
+  g_signal_connect (view,
+                    "button-press-event",
+                    G_CALLBACK (button_press_event_cb),
+                    view);
+  g_signal_connect (view,
+                    "popup-menu",
+                    G_CALLBACK (popup_menu_cb),
+                    view);
+}
+
+static void
+peas_ui_plugin_manager_view_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  PeasUIPluginManagerView *view = PEAS_UI_PLUGIN_MANAGER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      view->priv->engine = g_value_get_object (value);
+      g_object_ref (view->priv->engine);
+      break;
+    case PROP_SHOW_BUILTIN:
+      peas_ui_plugin_manager_view_set_show_builtin (view,
+                                                    g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+peas_ui_plugin_manager_view_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  PeasUIPluginManagerView *view = PEAS_UI_PLUGIN_MANAGER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      g_value_set_object (value, view->priv->engine);
+      break;
+    case PROP_SHOW_BUILTIN:
+      g_value_set_boolean (value,
+                           peas_ui_plugin_manager_view_get_show_builtin (view));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+peas_ui_plugin_manager_view_constructed (GObject *object)
+{
+  PeasUIPluginManagerView *view = PEAS_UI_PLUGIN_MANAGER_VIEW (object);
+
+  view->priv->store = peas_ui_plugin_manager_store_new (view->priv->engine);
+
+  /* Properly set the model */
+  view->priv->show_builtin = TRUE;
+  peas_ui_plugin_manager_view_set_show_builtin (view, FALSE);
+
+  g_signal_connect (view->priv->engine,
+                    "notify::plugin-list",
+                    G_CALLBACK (plugin_list_changed_cb),
+                    view);
+
+  if (G_OBJECT_CLASS (peas_ui_plugin_manager_view_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (peas_ui_plugin_manager_view_parent_class)->constructed (object);
+}
+
+static void
+peas_ui_plugin_manager_view_dispose (GObject *object)
+{
+  PeasUIPluginManagerView *view = PEAS_UI_PLUGIN_MANAGER_VIEW (object);
+
+  if (view->priv->popup_menu != NULL)
+    {
+      gtk_widget_destroy (view->priv->popup_menu);
+      view->priv->popup_menu = NULL;
+    }
+
+  if (view->priv->store != NULL)
+    {
+      g_object_unref (view->priv->store);
+      view->priv->store = NULL;
+    }
+
+  if (view->priv->engine != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (view->priv->engine,
+                                            plugin_list_changed_cb,
+                                            view);
+
+      g_object_unref (view->priv->engine);
+      view->priv->engine = NULL;
+    }
+
+  G_OBJECT_CLASS (peas_ui_plugin_manager_view_parent_class)->dispose (object);
+}
+
+static void
+peas_ui_plugin_manager_view_class_init (PeasUIPluginManagerViewClass *klass)
+{
+  GType the_type = G_TYPE_FROM_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = peas_ui_plugin_manager_view_set_property;
+  object_class->get_property = peas_ui_plugin_manager_view_get_property;
+  object_class->constructed = peas_ui_plugin_manager_view_constructed;
+  object_class->dispose = peas_ui_plugin_manager_view_dispose;
+
+  /**
+   * PeasUIPLuginManagerView:engine:
+   *
+   * The #PeasEngine this view is attached to.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ENGINE,
+                                   g_param_spec_object ("engine",
+                                                        "engine",
+                                                        "The PeasEngine this view is attached to",
+                                                        PEAS_TYPE_ENGINE,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * PeasUIPLuginManagerView:show-builtin:
+   *
+   * If builtin plugins should be shown.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ENGINE,
+                                   g_param_spec_boolean ("show-builtin",
+                                                         "show-builtin",
+                                                         "If builtin plugins should be shown",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * PeasUIPluginManagerView::populate-popup:
+   * @view: A #PeasUIPluginManagerView.
+   * @menu: A #GtkMenu.
+   *
+   * The populate-popup signal is emitted before showing the context
+   * menu of the view. If you need to add items to the context menu,
+   * connect to this signal and add your menuitems to the @menu.
+   */
+  signals[POPULATE_POPUP] =
+    g_signal_new ("populate-popup",
+                  the_type,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (PeasUIPluginManagerViewClass, populate_popup),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE,
+                  1,
+                  GTK_TYPE_MENU);
+
+  g_type_class_add_private (object_class, sizeof (PeasUIPluginManagerViewPrivate));
+}
+
+/**
+ * peas_ui_plugin_manager_view_new:
+ * @engine: A #PeasEngine.
+ *
+ * Creates a new plugin manager view for the given #PeasEngine.
+ *
+ * Returns: the new #PeasUIPluginManagerView.
+ */
+GtkWidget *
+peas_ui_plugin_manager_view_new (PeasEngine *engine)
+{
+  g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
+
+  return GTK_WIDGET (g_object_new (PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW,
+                                   "engine", engine,
+                                   NULL));
+}
+
+/**
+ * peas_ui_plugin_manager_view_set_show_builtin:
+ * @view: A #PeasUIPluginManagerView.
+ * @show_builtin: If builtin plugins should be shown.
+ *
+ * Sets if builtin plugins should be shown.
+ */
+void
+peas_ui_plugin_manager_view_set_show_builtin (PeasUIPluginManagerView *view,
+                                              gboolean                 show_builtin)
+{
+  GtkTreeIter iter;
+  gboolean iter_set;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view));
+
+  show_builtin = (show_builtin != FALSE);
+
+  if (view->priv->show_builtin == show_builtin)
+    return;
+
+  /* We must get the selected iter before setting if builtin
+     plugins should be shown so the proper model is set */
+  iter_set = peas_ui_plugin_manager_view_get_selected_iter (view, &iter);
+
+  view->priv->show_builtin = show_builtin;
+
+  if (show_builtin == TRUE)
+    {
+      gtk_tree_view_set_model (GTK_TREE_VIEW (view),
+                               GTK_TREE_MODEL (view->priv->store));
+    }
+  else
+    {
+      GtkTreeModel *model;
+
+      model = gtk_tree_model_filter_new (GTK_TREE_MODEL (view->priv->store), NULL);
+      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
+                                              (GtkTreeModelFilterVisibleFunc) filter_builtins_visible,
+                                              view,
+                                              NULL);
+
+      gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
+
+      g_object_unref (model);
+    }
+
+  if (iter_set)
+    peas_ui_plugin_manager_view_set_selected_iter (view, &iter);
+}
+
+/**
+ * peas_ui_plugin_manager_view_get_show_builtin:
+ * @view: A #PeasUIPluginManagerView.
+ *
+ * Returns if builtin plugins should be shown.
+ *
+ * Returns: if builtin plugins should be shown.
+ */
+gboolean
+peas_ui_plugin_manager_view_get_show_builtin (PeasUIPluginManagerView *view)
+{
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view), FALSE);
+
+  return view->priv->show_builtin;
+}
+
+/**
+ * peas_ui_plugin_manager_view_set_selected_iter:
+ * @view: A #PeasUIPluginManagerView.
+ * @iter: A #GtkTreeIter.
+ *
+ * Selects @iter.
+ */
+void
+peas_ui_plugin_manager_view_set_selected_iter (PeasUIPluginManagerView *view,
+                                               GtkTreeIter             *iter)
+{
+  GtkTreeSelection *selection;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view));
+  g_return_if_fail (iter != NULL);
+
+  if (!convert_child_iter_to_iter (view, iter))
+    return;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_select_iter (selection, iter);
+}
+
+/**
+ * peas_ui_plugin_manager_view_get_selected_iter:
+ * @view: A #PeasUIPluginManagerView.
+ * @iter: A #GtkTreeIter.
+ *
+ * Returns if @iter was set to the selected plugin.
+ *
+ * Returns: if @iter was set.
+ */
+gboolean
+peas_ui_plugin_manager_view_get_selected_iter (PeasUIPluginManagerView *view,
+                                               GtkTreeIter             *iter)
+{
+  GtkTreeSelection *selection;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view), FALSE);
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  if (!gtk_tree_selection_get_selected (selection, NULL, iter))
+    return FALSE;
+
+  convert_iter_to_child_iter (view, iter);
+
+  return TRUE;
+}
+
+/**
+ * peas_ui_plugin_manager_view_set_selected_plugin:
+ * @view: A #PeasUIPluginManagerView.
+ * @info: A #PeasPluginInfo.
+ *
+ * Selects the given plugin.
+ */
+void
+peas_ui_plugin_manager_view_set_selected_plugin (PeasUIPluginManagerView *view,
+                                                 PeasPluginInfo          *info)
+{
+  GtkTreeIter iter;
+  GtkTreeSelection *selection;
+
+  g_return_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view));
+  g_return_if_fail (info != NULL);
+
+  g_return_if_fail (peas_ui_plugin_manager_store_get_iter_from_plugin (view->priv->store,
+                                                                       &iter, info));
+
+  if (!convert_child_iter_to_iter (view, &iter))
+    return;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_select_iter (selection, &iter);
+}
+
+/**
+ * peas_ui_plugin_manager_view_get_selected_plugin:
+ * @view: A #PeasUIPluginManagerView.
+ *
+ * Returns the currently selected plugin, or %NULL if a plugin is not selected.
+ *
+ * Returns: the selected plugin.
+ */
+PeasPluginInfo *
+peas_ui_plugin_manager_view_get_selected_plugin (PeasUIPluginManagerView *view)
+{
+  GtkTreeIter iter;
+  PeasPluginInfo *info = NULL;
+
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER_VIEW (view), NULL);
+
+  if (peas_ui_plugin_manager_view_get_selected_iter (view, &iter))
+    info = peas_ui_plugin_manager_store_get_plugin (view->priv->store, &iter);
+
+  return info;
+}
diff --git a/libpeasui/peas-ui-plugin-manager-view.h b/libpeasui/peas-ui-plugin-manager-view.h
new file mode 100644
index 0000000..414e2a4
--- /dev/null
+++ b/libpeasui/peas-ui-plugin-manager-view.h
@@ -0,0 +1,79 @@
+/*
+ * peas-plugin-manager-view.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2002 Paolo Maggi and James Willcox
+ * Copyright (C) 2003-2006 Paolo Maggi, Paolo Borelli
+ * Copyright (C) 2007-2009 Paolo Maggi, Paolo Borelli, Steve Frécinaux
+ * Copyright (C) 2010 Garrett Regier
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library 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 __PEAS_UI_PLUGIN_MANAGER_VIEW_H__
+#define __PEAS_UI_PLUGIN_MANAGER_VIEW_H__
+
+#include <gtk/gtk.h>
+#include <libpeas/peas-engine.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW             (peas_ui_plugin_manager_view_get_type())
+#define PEAS_UI_PLUGIN_MANAGER_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW, PeasUIPluginManagerView))
+#define PEAS_UI_PLUGIN_MANAGER_VIEW_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW, PeasUIPluginManagerViewClass))
+#define PEAS_UI_IS_PLUGIN_MANAGER_VIEW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW))
+#define PEAS_UI_IS_PLUGIN_MANAGER_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW))
+#define PEAS_UI_PLUGIN_MANAGER_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), PEAS_UI_TYPE_PLUGIN_MANAGER_VIEW, PeasUIPluginManagerViewClass))
+
+typedef struct _PeasUIPluginManagerView         PeasUIPluginManagerView;
+typedef struct _PeasUIPluginManagerViewClass    PeasUIPluginManagerViewClass;
+typedef struct _PeasUIPluginManagerViewPrivate  PeasUIPluginManagerViewPrivate;
+
+struct _PeasUIPluginManagerView {
+  GtkTreeView parent;
+
+  /*< private > */
+  PeasUIPluginManagerViewPrivate *priv;
+};
+
+struct _PeasUIPluginManagerViewClass {
+  GtkTreeViewClass parent_class;
+
+  void  (*populate_popup)  (PeasUIPluginManagerView *view,
+                            GtkMenu                 *menu);
+};
+
+GType           peas_ui_plugin_manager_view_get_type            (void) G_GNUC_CONST;
+GtkWidget      *peas_ui_plugin_manager_view_new                 (PeasEngine              *engine);
+
+void            peas_ui_plugin_manager_view_set_show_builtin    (PeasUIPluginManagerView *view,
+                                                                 gboolean                 show_builtin);
+gboolean        peas_ui_plugin_manager_view_get_show_builtin    (PeasUIPluginManagerView *view);
+
+void            peas_ui_plugin_manager_view_set_selected_iter   (PeasUIPluginManagerView *view,
+                                                                 GtkTreeIter             *iter);
+gboolean        peas_ui_plugin_manager_view_get_selected_iter   (PeasUIPluginManagerView *view,
+                                                                 GtkTreeIter             *iter);
+
+void            peas_ui_plugin_manager_view_set_selected_plugin (PeasUIPluginManagerView *view,
+                                                                 PeasPluginInfo          *info);
+PeasPluginInfo *peas_ui_plugin_manager_view_get_selected_plugin (PeasUIPluginManagerView *view);
+
+G_END_DECLS
+
+#endif /* __PEAS_UI_PLUGIN_MANAGER_VIEW_H__  */
diff --git a/libpeasui/peas-ui-plugin-manager.c b/libpeasui/peas-ui-plugin-manager.c
index be787a9..eb4ba16 100644
--- a/libpeasui/peas-ui-plugin-manager.c
+++ b/libpeasui/peas-ui-plugin-manager.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2002 Paolo Maggi and James Willcox
  * Copyright (C) 2003-2006 Paolo Maggi, Paolo Borelli
  * Copyright (C) 2007-2009 Paolo Maggi, Paolo Borelli, Steve Frécinaux
+ * Copyright (C) 2010 Garrett Regier
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Library General Public License as published by
@@ -28,9 +29,11 @@
 #include <string.h>
 
 #include <libpeas/peas-engine.h>
+#include <libpeas/peas-plugin-info.h>
 #include <libpeas/peas-i18n.h>
 
 #include "peas-ui-plugin-manager.h"
+#include "peas-ui-plugin-manager-view.h"
 #include "peas-ui-plugin-info.h"
 #include "peas-ui-configurable.h"
 
@@ -44,32 +47,25 @@
  * <inlinegraphic fileref="peas-ui-plugin-manager.png" format="PNG" />
  *
  * The only thing you need to do as an application writer if you wish to use
- * the manager to configure * your plugins is to instantiate it using
+ * the manager to configure your plugins is to instantiate it using
  * peas_ui_plugin_manager_new() and pack it into another widget or a window
  * (as in the screenshot above).
  *
  **/
 
-#define PLUGIN_MANAGER_NAME_TITLE _("Plugin")
-#define PLUGIN_MANAGER_ACTIVE_TITLE _("Enabled")
-
-enum {
-  ACTIVE_COLUMN,
-  AVAILABLE_COLUMN,
-  INFO_COLUMN,
-  N_COLUMNS
-};
-
 struct _PeasUIPluginManagerPrivate {
   PeasEngine *engine;
 
-  GtkWidget *tree;
+  GtkWidget *sw;
+  GtkWidget *view;
+
+  GtkWidget *about;
+
   GtkWidget *about_button;
   GtkWidget *configure_button;
-  GtkWidget *about;
-  GtkWidget *popup_menu;
 };
 
+/* Properties */
 enum {
   PROP_0,
   PROP_ENGINE
@@ -77,90 +73,37 @@ enum {
 
 G_DEFINE_TYPE (PeasUIPluginManager, peas_ui_plugin_manager, GTK_TYPE_VBOX);
 
-static PeasPluginInfo  *plugin_manager_get_selected_plugin  (PeasUIPluginManager *pm);
-static void             plugin_manager_toggle_active        (PeasUIPluginManager *pm,
-                                                             GtkTreeIter         *iter,
-                                                             GtkTreeModel        *model);
-static void             peas_ui_plugin_manager_constructed  (GObject             *object);
-static void             peas_ui_plugin_manager_finalize     (GObject             *object);
-
 static gboolean
 plugin_is_configurable (PeasUIPluginManager *pm,
                         PeasPluginInfo      *info)
 {
+  if (info == NULL || !peas_plugin_info_is_loaded (info))
+    return FALSE;
+
   return peas_engine_provides_extension (pm->priv->engine,
                                          info,
                                          PEAS_UI_TYPE_CONFIGURABLE);
 }
 
 static void
-peas_ui_plugin_manager_set_property (GObject      *object,
-                                     guint         prop_id,
-                                     const GValue *value,
-                                     GParamSpec   *pspec)
+update_button_sensitivity (PeasUIPluginManager *pm,
+                           PeasPluginInfo      *info)
 {
-  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
-
-  switch (prop_id)
-    {
-    case PROP_ENGINE:
-      pm->priv->engine = g_value_get_object (value);
-      g_object_ref (pm->priv->engine);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  gtk_widget_set_sensitive (pm->priv->about_button, info != NULL);
+  gtk_widget_set_sensitive (pm->priv->configure_button,
+                            plugin_is_configurable (pm, info));
 }
 
 static void
-peas_ui_plugin_manager_get_property (GObject    *object,
-                                     guint       prop_id,
-                                     GValue     *value,
-                                     GParamSpec *pspec)
-{
-  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
-
-  switch (prop_id)
-    {
-    case PROP_ENGINE:
-      g_value_set_object (value, pm->priv->engine);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-peas_ui_plugin_manager_class_init (PeasUIPluginManagerClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->set_property = peas_ui_plugin_manager_set_property;
-  object_class->get_property = peas_ui_plugin_manager_get_property;
-  object_class->constructed = peas_ui_plugin_manager_constructed;
-  object_class->finalize = peas_ui_plugin_manager_finalize;
-
-  g_object_class_install_property (object_class, PROP_ENGINE,
-                                   g_param_spec_object ("engine",
-                                                        "engine",
-                                                        "The PeasEngine this manager is attached to",
-                                                        PEAS_TYPE_ENGINE,
-                                                        G_PARAM_READWRITE |
-                                                        G_PARAM_CONSTRUCT_ONLY |
-                                                        G_PARAM_STATIC_STRINGS));
-
-  g_type_class_add_private (object_class, sizeof (PeasUIPluginManagerPrivate));
-}
-
-static void
-about_button_cb (GtkWidget           *button,
-                 PeasUIPluginManager *pm)
+show_about_cb (GtkWidget           *widget,
+               PeasUIPluginManager *pm)
 {
+  PeasUIPluginManagerView *view;
   PeasPluginInfo *info;
 
-  info = plugin_manager_get_selected_plugin (pm);
+  view = PEAS_UI_PLUGIN_MANAGER_VIEW (pm->priv->view);
+
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
   g_return_if_fail (info != NULL);
 
   /* if there is another about dialog already open destroy it */
@@ -194,9 +137,10 @@ about_button_cb (GtkWidget           *button,
 }
 
 static void
-configure_button_cb (GtkWidget           *button,
-                     PeasUIPluginManager *pm)
+show_configure_cb (GtkWidget           *widget,
+                   PeasUIPluginManager *pm)
 {
+  PeasUIPluginManagerView *view = PEAS_UI_PLUGIN_MANAGER_VIEW (pm->priv->view);
   PeasPluginInfo *info;
   PeasExtension *exten;
   GtkWindow *toplevel;
@@ -205,15 +149,15 @@ configure_button_cb (GtkWidget           *button,
   GtkWidget *vbox;
   GtkWindowGroup *wg;
 
-  info = plugin_manager_get_selected_plugin (pm);
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
   g_return_if_fail (info != NULL);
 
-  exten = peas_engine_get_extension (pm->priv->engine, info, PEAS_UI_TYPE_CONFIGURABLE); 
-  g_return_if_fail (exten != NULL);
+  exten = peas_engine_get_extension (pm->priv->engine, info, PEAS_UI_TYPE_CONFIGURABLE);
+  g_return_if_fail (PEAS_IS_EXTENSION (exten));
 
   peas_extension_call (exten, "create_configure_widget", &conf_widget);
-
   g_object_unref (exten);
+
   g_return_if_fail (GTK_IS_WIDGET (conf_widget));
   g_return_if_fail (!gtk_widget_is_toplevel (conf_widget));
 
@@ -225,7 +169,6 @@ configure_button_cb (GtkWidget           *button,
                                           GTK_STOCK_CLOSE,
                                           GTK_RESPONSE_CLOSE,
                                           NULL);
-  g_signal_connect (conf_dlg, "response", G_CALLBACK (gtk_widget_destroy), NULL);
 
   vbox = gtk_dialog_get_content_area (GTK_DIALOG (conf_dlg));
   gtk_box_pack_start (GTK_BOX (vbox), conf_widget, TRUE, TRUE, 0);
@@ -245,669 +188,114 @@ configure_button_cb (GtkWidget           *button,
   gtk_window_set_transient_for (GTK_WINDOW (conf_dlg), toplevel);
   gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE);
   gtk_widget_show_all (conf_dlg);
-}
-
-static void
-plugin_manager_view_info_cell_cb (GtkTreeViewColumn *tree_column,
-                                  GtkCellRenderer   *cell,
-                                  GtkTreeModel      *tree_model,
-                                  GtkTreeIter       *iter,
-                                  gpointer           data)
-{
-  PeasPluginInfo *info;
-  gchar *text;
-
-  g_return_if_fail (tree_model != NULL);
-  g_return_if_fail (tree_column != NULL);
-
-  gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1);
-
-  if (info == NULL)
-    return;
-
-  text = g_markup_printf_escaped ("<b>%s</b>\n%s",
-                                  peas_plugin_info_get_name (info),
-                                  peas_plugin_info_get_description (info));
-  g_object_set (G_OBJECT (cell),
-                "markup", text,
-                "sensitive", peas_plugin_info_is_available (info),
-                NULL);
-
-  g_free (text);
-}
-
-static void
-plugin_manager_view_icon_cell_cb (GtkTreeViewColumn *tree_column,
-                                  GtkCellRenderer   *cell,
-                                  GtkTreeModel      *tree_model,
-                                  GtkTreeIter       *iter,
-                                  gpointer           data)
-{
-  PeasPluginInfo *info;
-
-  g_return_if_fail (tree_model != NULL);
-  g_return_if_fail (tree_column != NULL);
 
-  gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1);
-
-  if (info == NULL)
-    return;
-
-  g_object_set (G_OBJECT (cell),
-                "icon-name", peas_ui_plugin_info_get_icon_name (info),
-                "sensitive", peas_plugin_info_is_available (info),
-                NULL);
+  g_signal_connect (conf_dlg,
+                    "response",
+                    G_CALLBACK (gtk_widget_destroy),
+                    NULL);
 }
 
-
 static void
-active_toggled_cb (GtkCellRendererToggle *cell,
-                   gchar                 *path_str,
-                   PeasUIPluginManager   *pm)
+plugin_loaded_toggled_cb (PeasEngine          *engine,
+                          PeasPluginInfo      *info,
+                          PeasUIPluginManager *pm)
 {
-  GtkTreeIter iter;
-  GtkTreePath *path;
-  GtkTreeModel *model;
-
-  path = gtk_tree_path_new_from_string (path_str);
+  PeasUIPluginManagerView *view;
+  PeasPluginInfo *selected;
 
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
-  g_return_if_fail (model != NULL);
+  view = PEAS_UI_PLUGIN_MANAGER_VIEW (pm->priv->view);
+  selected = peas_ui_plugin_manager_view_get_selected_plugin (view);
 
-  if (gtk_tree_model_get_iter (model, &iter, path))
-    plugin_manager_toggle_active (pm, &iter, model);
-
-  gtk_tree_path_free (path);
+  if (selected == info)
+    update_button_sensitivity (pm, info);
 }
 
 static void
-cursor_changed_cb (GtkTreeView         *view,
-                   PeasUIPluginManager *pm)
+selection_changed_cb (GtkTreeSelection    *selection,
+                      PeasUIPluginManager *pm)
 {
+  PeasUIPluginManagerView *view;
   PeasPluginInfo *info;
 
-  info = plugin_manager_get_selected_plugin (pm);
+  view = PEAS_UI_PLUGIN_MANAGER_VIEW (pm->priv->view);
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
 
-  gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->about_button),
-                            info != NULL);
-  gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button),
-                            info != NULL && plugin_is_configurable (pm, info));
+  update_button_sensitivity (pm, info);
 }
 
 static void
-row_activated_cb (GtkTreeView         *tree_view,
-                  GtkTreePath         *path,
-                  GtkTreeViewColumn   *column,
-                  PeasUIPluginManager *pm)
-{
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
-  g_return_if_fail (model != NULL);
-
-  if (gtk_tree_model_get_iter (model, &iter, path))
-    plugin_manager_toggle_active (pm, &iter, model);
-}
-
-static void
-plugin_manager_populate_lists (PeasUIPluginManager *pm)
-{
-  const GList *plugins;
-  GtkListStore *model;
-  GtkTreeIter iter;
-
-  plugins = peas_engine_get_plugin_list (pm->priv->engine);
-
-  model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)));
-
-  while (plugins)
-    {
-      PeasPluginInfo *info;
-      info = (PeasPluginInfo *) plugins->data;
-
-      if (!peas_plugin_info_is_builtin (info))
-        {
-          gtk_list_store_append (model, &iter);
-          gtk_list_store_set (model, &iter,
-                              ACTIVE_COLUMN, peas_plugin_info_is_loaded (info),
-                              AVAILABLE_COLUMN, peas_plugin_info_is_available (info),
-                              INFO_COLUMN, info,
-                              -1);
-        }
-
-      plugins = plugins->next;
-    }
-
-  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
-    {
-      GtkTreeSelection *selection;
-      PeasPluginInfo *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,
-                          INFO_COLUMN, &info, -1);
-
-      gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button),
-                                plugin_is_configurable (pm, info));
-    }
-}
-
-static gboolean
-plugin_manager_set_active (PeasUIPluginManager *pm,
-                           GtkTreeIter         *iter,
-                           GtkTreeModel        *model,
-                           gboolean             active)
+cursor_changed_cb (GtkTreeView         *tree_view,
+                   PeasUIPluginManager *pm)
 {
+  PeasUIPluginManagerView *view;
   PeasPluginInfo *info;
-  gboolean res = TRUE;
 
-  gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1);
-  g_return_val_if_fail (info != NULL, FALSE);
+  view = PEAS_UI_PLUGIN_MANAGER_VIEW (pm->priv->view);
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
 
-  if (active)
-    {
-      /* load the plugin */
-      if (!peas_engine_load_plugin (pm->priv->engine, info))
-        res = FALSE;
-    }
-  else
-    {
-      /* unload the plugin */
-      if (!peas_engine_unload_plugin (pm->priv->engine, info))
-        res = FALSE;
-    }
-
-  /* cause the configure button sensitivity to be updated */
-  cursor_changed_cb (GTK_TREE_VIEW (pm->priv->tree), pm);
-
-  return res;
+  update_button_sensitivity (pm, info);
 }
 
 static void
-plugin_manager_toggle_active (PeasUIPluginManager *pm,
-                              GtkTreeIter         *iter,
-                              GtkTreeModel        *model)
-{
-  gboolean active;
-
-  gtk_tree_model_get (model, iter, ACTIVE_COLUMN, &active, -1);
-
-  active ^= 1;
-
-  plugin_manager_set_active (pm, iter, model, active);
-}
-
-static PeasPluginInfo *
-plugin_manager_get_selected_plugin (PeasUIPluginManager *pm)
-{
-  PeasPluginInfo *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, INFO_COLUMN, &info, -1);
-
-  return info;
-}
-
-static void
-plugin_manager_set_active_all (PeasUIPluginManager *pm,
-                               gboolean             active)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
-  g_return_if_fail (model != NULL);
-
-  if (!gtk_tree_model_get_iter_first (model, &iter))
-    return;
-
-  do
-    plugin_manager_set_active (pm, &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)
+populate_popup_cb (PeasUIPluginManagerView *view,
+                   GtkMenu                 *menu,
+                   PeasUIPluginManager     *pm)
 {
   PeasPluginInfo *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, INFO_COLUMN, &info, -1);
-  if (!info)
-    return FALSE;
-
-  normalized_string = g_utf8_normalize (peas_plugin_info_get_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
-enable_plugin_menu_cb (GtkMenu             *menu,
-                       PeasUIPluginManager *pm)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  GtkTreeSelection *selection;
-
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree));
-  g_return_if_fail (model != NULL);
-
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree));
-  g_return_if_fail (selection != NULL);
-
-  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
-    plugin_manager_toggle_active (pm, &iter, model);
-}
-
-static void
-enable_all_menu_cb (GtkMenu             *menu,
-                    PeasUIPluginManager *pm)
-{
-  plugin_manager_set_active_all (pm, TRUE);
-}
-
-static void
-disable_all_menu_cb (GtkMenu             *menu,
-                     PeasUIPluginManager *pm)
-{
-  plugin_manager_set_active_all (pm, FALSE);
-}
-
-static GtkWidget *
-create_tree_popup_menu (PeasUIPluginManager *pm)
-{
-  GtkWidget *menu;
   GtkWidget *item;
   GtkWidget *image;
-  PeasPluginInfo *info;
 
-  info = plugin_manager_get_selected_plugin (pm);
+  info = peas_ui_plugin_manager_view_get_selected_plugin (view);
 
   if (info == NULL)
-    return NULL;
-
-  menu = gtk_menu_new ();
-
-  item = gtk_image_menu_item_new_with_mnemonic (_("_About"));
-  image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU);
-  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
-  g_signal_connect (item, "activate", G_CALLBACK (about_button_cb), pm);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+    return;
 
   item = gtk_image_menu_item_new_with_mnemonic (_("C_onfigure"));
   image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES,
                                     GTK_ICON_SIZE_MENU);
   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
-  g_signal_connect (item, "activate", G_CALLBACK (configure_button_cb), pm);
+  g_signal_connect (item, "activate", G_CALLBACK (show_configure_cb), pm);
   gtk_widget_set_sensitive (item, plugin_is_configurable (pm, info));
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
-  item = gtk_check_menu_item_new_with_mnemonic (_("A_ctivate"));
-  gtk_widget_set_sensitive (item, peas_plugin_info_is_available (info));
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
-                                  peas_plugin_info_is_loaded (info));
-  g_signal_connect (item, "toggled", G_CALLBACK (enable_plugin_menu_cb), pm);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
-  item = gtk_separator_menu_item_new ();
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
-  item = gtk_menu_item_new_with_mnemonic (_("Ac_tivate All"));
-  g_signal_connect (item, "activate", G_CALLBACK (enable_all_menu_cb), pm);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
-  item = gtk_menu_item_new_with_mnemonic (_("_Deactivate All"));
-  g_signal_connect (item, "activate", G_CALLBACK (disable_all_menu_cb), pm);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
-  gtk_widget_show_all (menu);
-
-  return menu;
-}
-
-static void
-tree_popup_menu_detach (PeasUIPluginManager *pm,
-                        GtkMenu             *menu)
-{
-  pm->priv->popup_menu = NULL;
-}
-
-static void
-menu_position_under_tree_view (GtkMenu  *menu,
-                               gint     *x,
-                               gint     *y,
-                               gboolean *push_in,
-                               gpointer  user_data)
-{
-  GtkTreeView *tree = GTK_TREE_VIEW (user_data);
-  GtkTreeModel *model;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-  GdkWindow *window;
+  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 
-  model = gtk_tree_view_get_model (tree);
-  g_return_if_fail (model != NULL);
-
-  selection = gtk_tree_view_get_selection (tree);
-  g_return_if_fail (selection != NULL);
-
-  window = gtk_widget_get_window (GTK_WIDGET (tree));
-  gdk_window_get_origin (window, x, y);
-
-  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
-    {
-      GtkTreePath *path;
-      GdkRectangle rect;
-
-      path = gtk_tree_model_get_path (model, &iter);
-      gtk_tree_view_get_cell_area (tree,
-                                   path,
-                                   gtk_tree_view_get_column (tree, 0), /* FIXME 0 for RTL ? */
-                                   &rect);
-      gtk_tree_path_free (path);
-
-      *x += rect.x;
-      *y += rect.y + rect.height;
-
-      if (gtk_widget_get_direction (GTK_WIDGET (tree)) == GTK_TEXT_DIR_RTL)
-        {
-          GtkRequisition requisition;
-          gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
-          *x += rect.width - requisition.width;
-        }
-    }
-  else
-    {
-      GtkAllocation allocation;
-      gtk_widget_get_allocation (GTK_WIDGET (tree), &allocation);
-      *x += allocation.x;
-      *y += allocation.y;
-
-      if (gtk_widget_get_direction (GTK_WIDGET (tree)) == GTK_TEXT_DIR_RTL)
-        {
-          GtkRequisition requisition;
-          gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
-          *x += allocation.width - requisition.width;
-        }
-    }
-
-  *push_in = TRUE;
-}
-
-static void
-show_tree_popup_menu (GtkTreeView         *tree,
-                      PeasUIPluginManager *pm,
-                      GdkEventButton      *event)
-{
-  if (pm->priv->popup_menu)
-    gtk_widget_destroy (pm->priv->popup_menu);
-
-  pm->priv->popup_menu = create_tree_popup_menu (pm);
-
-  if (pm->priv->popup_menu == NULL)
-    return;
-
-  gtk_menu_attach_to_widget (GTK_MENU (pm->priv->popup_menu),
-                             GTK_WIDGET (pm),
-                             (GtkMenuDetachFunc) tree_popup_menu_detach);
-
-  if (event != NULL)
-    {
-      gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL,
-                      NULL, NULL, event->button, event->time);
-    }
-  else
-    {
-      gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL,
-                      menu_position_under_tree_view, tree,
-                      0, gtk_get_current_event_time ());
-
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (pm->priv->popup_menu),
-                                   FALSE);
-    }
-}
-
-static gboolean
-button_press_event_cb (GtkWidget           *tree,
-                       GdkEventButton      *event,
-                       PeasUIPluginManager *pm)
-{
-  /* We want the treeview selection to be updated before showing the menu.
-   * This code is evil, thanks to Federico Mena Quintero's black magic.
-   * See: http://mail.gnome.org/archives/gtk-devel-list/2006-February/msg00168.html
-   * FIXME: Let's remove it asap.
-   */
-
-  static gboolean in_press = FALSE;
-  gboolean handled;
-
-  if (in_press)
-    return FALSE;               /* we re-entered */
-
-  if (GDK_BUTTON_PRESS != event->type || 3 != event->button)
-    return FALSE;               /* let the normal handler run */
-
-  in_press = TRUE;
-  handled = gtk_widget_event (tree, (GdkEvent *) event);
-  in_press = FALSE;
-
-  if (!handled)
-    return FALSE;
-
-  /* The selection is fully updated by now */
-  show_tree_popup_menu (GTK_TREE_VIEW (tree), pm, event);
-  return TRUE;
-}
-
-static gboolean
-popup_menu_cb (GtkTreeView         *tree,
-               PeasUIPluginManager *pm)
-{
-  show_tree_popup_menu (tree, pm, NULL);
-  return TRUE;
-}
-
-static gint
-model_name_sort_func (GtkTreeModel *model,
-                      GtkTreeIter  *iter1,
-                      GtkTreeIter  *iter2,
-                      gpointer      user_data)
-{
-  PeasPluginInfo *info1, *info2;
-
-  gtk_tree_model_get (model, iter1, INFO_COLUMN, &info1, -1);
-  gtk_tree_model_get (model, iter2, INFO_COLUMN, &info2, -1);
-
-  return g_utf8_collate (peas_plugin_info_get_name (info1),
-                         peas_plugin_info_get_name (info2));
-}
-
-static void
-plugin_manager_construct_tree (PeasUIPluginManager *pm)
-{
-  GtkTreeViewColumn *column;
-  GtkCellRenderer *cell;
-  GtkListStore *model;
-
-  model = gtk_list_store_new (N_COLUMNS,
-                              G_TYPE_BOOLEAN, 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_visible (GTK_TREE_VIEW (pm->priv->tree), FALSE);
-
-  /* first column */
-  cell = gtk_cell_renderer_toggle_new ();
-  g_object_set (cell, "xpad", 6, NULL);
-  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,
-                                                     "activatable", AVAILABLE_COLUMN,
-                                                     "sensitive", AVAILABLE_COLUMN,
-                                                     NULL);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column);
-
-  /* second column */
-  column = gtk_tree_view_column_new ();
-  gtk_tree_view_column_set_title (column, PLUGIN_MANAGER_NAME_TITLE);
-  gtk_tree_view_column_set_resizable (column, TRUE);
-
-  cell = gtk_cell_renderer_pixbuf_new ();
-  gtk_tree_view_column_pack_start (column, cell, FALSE);
-  g_object_set (cell, "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
-  gtk_tree_view_column_set_cell_data_func (column, cell,
-                                           plugin_manager_view_icon_cell_cb,
-                                           pm, NULL);
-
-  cell = gtk_cell_renderer_text_new ();
-  gtk_tree_view_column_pack_start (column, cell, TRUE);
-  g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-  gtk_tree_view_column_set_cell_data_func (column, cell,
-                                           plugin_manager_view_info_cell_cb,
-                                           pm, NULL);
-
-
-  gtk_tree_view_column_set_spacing (column, 6);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column);
-
-  /* Sort on the plugin names */
-  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model),
-                                           model_name_sort_func, NULL, NULL);
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
-                                        GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
-                                        GTK_SORT_ASCENDING);
-
-  /* Enable search for our non-string column */
-  gtk_tree_view_set_search_column (GTK_TREE_VIEW (pm->priv->tree),
-                                   INFO_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);
-  g_signal_connect (pm->priv->tree, "button-press-event", G_CALLBACK (button_press_event_cb), pm);
-  g_signal_connect (pm->priv->tree, "popup-menu", G_CALLBACK (popup_menu_cb), pm);
-  gtk_widget_show (pm->priv->tree);
-}
-
-static void
-plugin_toggled_cb (PeasEngine          *engine,
-                   PeasPluginInfo      *info,
-                   PeasUIPluginManager *pm)
-{
-  GtkTreeSelection *selection;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  gboolean info_found = FALSE;
-
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree));
-
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
-    {
-      /* There is an item selected: it's probably the one we want! */
-      PeasPluginInfo *tinfo;
-      gtk_tree_model_get (model, &iter, INFO_COLUMN, &tinfo, -1);
-      info_found = info == tinfo;
-    }
-
-  if (!info_found && gtk_tree_model_get_iter_first (model, &iter))
-    {
-      do
-        {
-          PeasPluginInfo *tinfo;
-          gtk_tree_model_get (model, &iter, INFO_COLUMN, &tinfo, -1);
-          info_found = info == tinfo;
-        }
-      while (!info_found && gtk_tree_model_iter_next (model, &iter));
-    }
-
-  if (!info_found)
-    {
-      g_warning ("PeasUIPluginManager: plugin '%s' not found in the tree model",
-                 peas_plugin_info_get_name (info));
-      return;
-    }
-
-  gtk_list_store_set (GTK_LIST_STORE (model), &iter, ACTIVE_COLUMN,
-                      peas_plugin_info_is_loaded (info), -1);
+  item = gtk_image_menu_item_new_with_mnemonic (_("_About"));
+  image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU);
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+  g_signal_connect (item, "activate", G_CALLBACK (show_about_cb), pm);
+  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 }
 
 static void
 peas_ui_plugin_manager_init (PeasUIPluginManager *pm)
 {
   GtkWidget *label;
-  GtkWidget *viewport;
   GtkWidget *hbuttonbox;
 
-  pm->priv = G_TYPE_INSTANCE_GET_PRIVATE (pm, PEAS_UI_TYPE_PLUGIN_MANAGER, PeasUIPluginManagerPrivate);
+  pm->priv = G_TYPE_INSTANCE_GET_PRIVATE (pm,
+                                          PEAS_UI_TYPE_PLUGIN_MANAGER,
+                                          PeasUIPluginManagerPrivate);
 
   gtk_box_set_spacing (GTK_BOX (pm), 6);
 
-  label = gtk_label_new_with_mnemonic (_("Active _Plugins:"));
+  gtk_widget_push_composite_child ();
+
+  label = gtk_label_new_with_mnemonic (_("_Plugins:"));
   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-
   gtk_box_pack_start (GTK_BOX (pm), label, FALSE, TRUE, 0);
 
-  viewport = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (viewport),
+  pm->priv->sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pm->priv->sw),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (viewport),
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (pm->priv->sw),
                                        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);
-
-  gtk_label_set_mnemonic_widget (GTK_LABEL (label), pm->priv->tree);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), pm->priv->sw);
+  gtk_box_pack_start (GTK_BOX (pm), pm->priv->sw, TRUE, TRUE, 0);
 
   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); /* should use the default or HIG */
+  gtk_box_set_spacing (GTK_BOX (hbuttonbox), 6);
+  gtk_box_pack_start (GTK_BOX (pm), hbuttonbox, FALSE, FALSE, 0);
 
   pm->priv->about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
   gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->about_button);
@@ -915,61 +303,152 @@ peas_ui_plugin_manager_init (PeasUIPluginManager *pm)
   pm->priv->configure_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
   gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->configure_button);
 
+  gtk_widget_pop_composite_child ();
+
   /* setup a window of a sane size. */
-  gtk_widget_set_size_request (GTK_WIDGET (viewport), 270, 100);
+  gtk_widget_set_size_request (GTK_WIDGET (pm->priv->sw), 270, 100);
+
+  g_signal_connect (pm->priv->about_button,
+                    "clicked",
+                    G_CALLBACK (show_about_cb),
+                    pm);
+  g_signal_connect (pm->priv->configure_button,
+                    "clicked",
+                    G_CALLBACK (show_configure_cb),
+                    pm);
+}
+
+static void
+peas_ui_plugin_manager_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
 
-  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);
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      pm->priv->engine = g_value_get_object (value);
+      g_object_ref (pm->priv->engine);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 
-  plugin_manager_construct_tree (pm);
+static void
+peas_ui_plugin_manager_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENGINE:
+      g_value_set_object (value, pm->priv->engine);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
 }
 
 static void
 peas_ui_plugin_manager_constructed (GObject *object)
 {
   PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
+  GtkTreeSelection *selection;
 
-  /* When we create the manager, we always rescan the plugins directory */
+  /* When we create the manager, we always rescan the plugins directory
+     Must come after the view has connected to notify::plugin-list */
   peas_engine_rescan_plugins (pm->priv->engine);
 
-  /* populate the treeview */
+  pm->priv->view = peas_ui_plugin_manager_view_new (pm->priv->engine);
+  gtk_container_add (GTK_CONTAINER (pm->priv->sw), pm->priv->view);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->view));
+
   g_signal_connect_after (pm->priv->engine,
                           "load-plugin",
-                          G_CALLBACK (plugin_toggled_cb), pm);
+                          G_CALLBACK (plugin_loaded_toggled_cb),
+                          pm);
   g_signal_connect_after (pm->priv->engine,
                           "unload-plugin",
-                          G_CALLBACK (plugin_toggled_cb), pm);
+                          G_CALLBACK (plugin_loaded_toggled_cb),
+                          pm);
+  g_signal_connect (selection,
+                    "changed",
+                    G_CALLBACK (selection_changed_cb),
+                    pm);
+  g_signal_connect (pm->priv->view,
+                    "cursor-changed",
+                    G_CALLBACK (cursor_changed_cb),
+                    pm);
+  g_signal_connect (pm->priv->view,
+                    "populate-popup",
+                    G_CALLBACK (populate_popup_cb),
+                    pm);
+
+  /* Update the button sensitivity */
+  selection_changed_cb (selection, pm);
+
+  if (G_OBJECT_CLASS (peas_ui_plugin_manager_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (peas_ui_plugin_manager_parent_class)->constructed (object);
+}
 
-  if (peas_engine_get_plugin_list (pm->priv->engine) != NULL)
-    {
-      plugin_manager_populate_lists (pm);
-    }
-  else
+static void
+peas_ui_plugin_manager_dispose (GObject *object)
+{
+  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
+
+  if (pm->priv->engine != NULL)
     {
-      gtk_widget_set_sensitive (pm->priv->about_button, FALSE);
-      gtk_widget_set_sensitive (pm->priv->configure_button, FALSE);
+      g_signal_handlers_disconnect_by_func (pm->priv->engine,
+                                            plugin_loaded_toggled_cb,
+                                            pm);
+
+      g_object_unref (pm->priv->engine);
+      pm->priv->engine = NULL;
     }
+
+  G_OBJECT_CLASS (peas_ui_plugin_manager_parent_class)->dispose (object);
 }
 
 static void
-peas_ui_plugin_manager_finalize (GObject *object)
+peas_ui_plugin_manager_class_init (PeasUIPluginManagerClass *klass)
 {
-  PeasUIPluginManager *pm = PEAS_UI_PLUGIN_MANAGER (object);
-
-  g_signal_handlers_disconnect_by_func (pm->priv->engine,
-                                        plugin_toggled_cb, pm);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  if (pm->priv->popup_menu)
-    gtk_widget_destroy (pm->priv->popup_menu);
+  object_class->set_property = peas_ui_plugin_manager_set_property;
+  object_class->get_property = peas_ui_plugin_manager_get_property;
+  object_class->constructed = peas_ui_plugin_manager_constructed;
+  object_class->dispose = peas_ui_plugin_manager_dispose;
 
-  g_object_unref (pm->priv->engine);
+  /**
+   * PeasUIPLuginManager:engine:
+   *
+   * The #PeasEngine this manager is attached to.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ENGINE,
+                                   g_param_spec_object ("engine",
+                                                        "engine",
+                                                        "The PeasEngine this manager is attached to",
+                                                        PEAS_TYPE_ENGINE,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
 
-  G_OBJECT_CLASS (peas_ui_plugin_manager_parent_class)->finalize (object);
+  g_type_class_add_private (object_class, sizeof (PeasUIPluginManagerPrivate));
 }
 
 /**
  * peas_ui_plugin_manager_new:
- * @engine: a #PeasEngine.
+ * @engine: A #PeasEngine.
  *
  * Creates a new plugin manager for the given #PeasEngine.
  *
@@ -984,3 +463,19 @@ peas_ui_plugin_manager_new (PeasEngine *engine)
                                    "engine", engine,
                                    NULL));
 }
+
+/**
+ * peas_ui_plugin_manager_get_view:
+ * @pm: A @PeasUIPluginManager.
+ *
+ * Returns the #PeasUIPluginManagerView of @pm.
+ *
+ * Returns: the view of @pm.
+ */
+GtkWidget *
+peas_ui_plugin_manager_get_view (PeasUIPluginManager *pm)
+{
+  g_return_val_if_fail (PEAS_UI_IS_PLUGIN_MANAGER (pm), NULL);
+
+  return pm->priv->view;
+}
diff --git a/libpeasui/peas-ui-plugin-manager.h b/libpeasui/peas-ui-plugin-manager.h
index 91e41c9..280a3e2 100644
--- a/libpeasui/peas-ui-plugin-manager.h
+++ b/libpeasui/peas-ui-plugin-manager.h
@@ -54,8 +54,10 @@ struct _PeasUIPluginManagerClass
   GtkVBoxClass parent_class;
 };
 
-GType       peas_ui_plugin_manager_get_type  (void)  G_GNUC_CONST;
-GtkWidget  *peas_ui_plugin_manager_new       (PeasEngine *engine);
+GType      peas_ui_plugin_manager_get_type  (void)  G_GNUC_CONST;
+GtkWidget  *peas_ui_plugin_manager_new      (PeasEngine          *engine);
+
+GtkWidget  *peas_ui_plugin_manager_get_view (PeasUIPluginManager *pm);
 
 G_END_DECLS
 
diff --git a/libpeasui/peas-ui.h b/libpeasui/peas-ui.h
index 9192a9d..f2e7c6e 100644
--- a/libpeasui/peas-ui.h
+++ b/libpeasui/peas-ui.h
@@ -23,5 +23,6 @@
 #include "peas-ui-configurable.h"
 #include "peas-ui-plugin-info.h"
 #include "peas-ui-plugin-manager.h"
+#include "peas-ui-plugin-manager-view.h"
 
 #endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]