[gnome-disk-utility/new-ui] Start reworking the UI



commit 1dd2a25631274a3d976522d4763eb0d91397ec75
Author: David Zeuthen <davidz redhat com>
Date:   Wed Sep 2 12:37:44 2009 -0400

    Start reworking the UI

 src/gdu-gtk/Makefile.am                     |    6 +
 src/gdu-gtk/gdu-ata-smart-dialog.c          |  663 +++++---------
 src/gdu-gtk/gdu-create-linux-md-dialog.c    |    7 +-
 src/gdu-gtk/gdu-details-element.c           |  556 +++++++++++
 src/gdu-gtk/gdu-details-element.h           |   99 ++
 src/gdu-gtk/gdu-details-table.c             |  477 ++++++++++
 src/gdu-gtk/gdu-details-table.h             |   66 ++
 src/gdu-gtk/gdu-gtk-types.h                 |   19 +-
 src/gdu-gtk/gdu-gtk.h                       |    3 +
 src/gdu-gtk/gdu-pool-tree-model.c           |   69 ++-
 src/gdu-gtk/gdu-pool-tree-model.h           |    1 +
 src/gdu-gtk/gdu-size-widget.c               |    4 +-
 src/gdu-gtk/gdu-volume-grid.c               | 1321 +++++++++++++++++++++++++++
 src/gdu-gtk/gdu-volume-grid.h               |   67 ++
 src/gdu/gdu-drive.c                         |    4 +-
 src/gdu/gdu-linux-md-drive.c                |    2 +-
 src/gdu/gdu-presentable.c                   |   61 ++
 src/gdu/gdu-presentable.h                   |    5 +
 src/gdu/gdu-util.c                          |   58 +-
 src/gdu/gdu-util.h                          |    5 +-
 src/gdu/gdu-volume-hole.c                   |    2 +-
 src/gdu/gdu-volume.c                        |    2 +-
 src/palimpsest/Makefile.am                  |    2 +
 src/palimpsest/gdu-section-drive.c          |  605 ++++++++++++
 src/palimpsest/gdu-section-drive.h          |   58 ++
 src/palimpsest/gdu-section-linux-md-drive.c |    9 +-
 src/palimpsest/gdu-section-volumes.c        |  601 ++++++++++++
 src/palimpsest/gdu-section-volumes.h        |   58 ++
 src/palimpsest/gdu-section.c                |    6 +-
 src/palimpsest/gdu-section.h                |   12 +-
 src/palimpsest/gdu-shell.c                  |   66 ++-
 31 files changed, 4390 insertions(+), 524 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 3eba71e..9da383f 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -37,6 +37,9 @@ libgdu_gtkinclude_HEADERS =              				\
 	gdu-create-linux-md-dialog.h					\
 	gdu-ata-smart-dialog.h						\
 	gdu-spinner.h							\
+	gdu-volume-grid.h						\
+	gdu-details-table.h						\
+	gdu-details-element.h						\
 	$(NULL)
 
 libgdu_gtk_la_SOURCES =                 	               				\
@@ -51,6 +54,9 @@ libgdu_gtk_la_SOURCES =                 	               				\
 	gdu-create-linux-md-dialog.h		gdu-create-linux-md-dialog.c		\
 	gdu-ata-smart-dialog.h			gdu-ata-smart-dialog.c			\
 	gdu-spinner.h				gdu-spinner.c				\
+	gdu-volume-grid.h			gdu-volume-grid.c			\
+	gdu-details-table.h			gdu-details-table.c			\
+	gdu-details-element.h			gdu-details-element.c			\
 	$(NULL)
 
 libgdu_gtk_la_CPPFLAGS = 				\
diff --git a/src/gdu-gtk/gdu-ata-smart-dialog.c b/src/gdu-gtk/gdu-ata-smart-dialog.c
index 164faa2..94d4ac2 100644
--- a/src/gdu-gtk/gdu-ata-smart-dialog.c
+++ b/src/gdu-gtk/gdu-ata-smart-dialog.c
@@ -32,6 +32,8 @@
 #include "gdu-ata-smart-dialog.h"
 #include "gdu-spinner.h"
 #include "gdu-pool-tree-model.h"
+#include "gdu-details-table.h"
+#include "gdu-details-element.h"
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -47,25 +49,17 @@ struct GduAtaSmartDialogPrivate
         GduPoolTreeModel *pool_tree_model;
         GtkWidget *drive_combo_box;
 
-        GtkWidget *updated_label;
-        GtkWidget *updating_spinner;
-        GtkWidget *updating_label;
-        GtkWidget *update_link_label;
-
-        GtkWidget *self_test_result_label;
-        GtkWidget *self_test_progress_bar;
-        GtkWidget *self_test_run_link_label;
-        GtkWidget *self_test_cancel_link_label;
-
-        GtkWidget *model_label;
-        GtkWidget *firmware_label;
-        GtkWidget *serial_label;
-        GtkWidget *power_on_hours_label;
-        GtkWidget *temperature_label;
-        GtkWidget *sectors_label;
-        GtkWidget *self_assessment_label;
-        GtkWidget *overall_assessment_image;
-        GtkWidget *overall_assessment_label;
+        GduDetailsElement *updated_element;
+        GduDetailsElement *self_test_element;
+        GduDetailsElement *model_element;
+        GduDetailsElement *firmware_element;
+        GduDetailsElement *serial_element;
+        GduDetailsElement *powered_on_element;
+        GduDetailsElement *temperature_element;
+        GduDetailsElement *bad_sectors_element;
+        GduDetailsElement *self_assessment_element;
+        GduDetailsElement *overall_assessment_element;
+
         GtkWidget *no_warn_check_button;
 
         GtkWidget *tree_view;
@@ -82,6 +76,9 @@ enum
         PROP_DRIVE,
 };
 
+static gboolean is_self_test_running (GduDevice *device,
+                                      SkSmartSelfTest *out_test_type);
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 #define _GDU_TYPE_SK_ATTR (_gdu_type_sk_attr_get_type ())
@@ -1129,9 +1126,8 @@ refresh_cb (GduDevice  *device,
 
 
 static void
-on_activate_link_update_smart_data (GtkLabel    *label,
-                                    const gchar *uri,
-                                    gpointer     user_data)
+on_updated_element_activated (GduDetailsElement    *element,
+                              gpointer     user_data)
 {
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
 
@@ -1139,8 +1135,6 @@ on_activate_link_update_smart_data (GtkLabel    *label,
                                                  refresh_cb,
                                                  dialog);
 
-        g_signal_stop_emission_by_name (label, "activate-link");
-
         dialog->priv->is_updating = TRUE;
         update_dialog (dialog);
 }
@@ -1157,23 +1151,6 @@ cancel_self_test_cb (GduDevice  *device,
                 g_error_free (error);
 }
 
-
-static void
-on_activate_link_cancel_self_test (GtkLabel    *link_label,
-                                   const gchar *uri,
-                                   gpointer     user_data)
-{
-        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
-
-        g_signal_stop_emission_by_name (link_label, "activate-link");
-
-        gdu_device_op_cancel_job (dialog->priv->device,
-                                  cancel_self_test_cb,
-                                  dialog);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
 static void
 run_self_test_cb (GduDevice  *device,
                   GError     *error,
@@ -1186,9 +1163,8 @@ run_self_test_cb (GduDevice  *device,
 
 
 static void
-on_activate_link_run_self_test (GtkLabel    *link_label,
-                                const gchar *uri,
-                                gpointer     user_data)
+on_self_tests_element_activated (GduDetailsElement *element,
+                                 gpointer           user_data)
 {
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
         GtkWidget *test_dialog;
@@ -1203,7 +1179,12 @@ on_activate_link_run_self_test (GtkLabel    *link_label,
         gint response;
         const gchar *test;
 
-        g_signal_stop_emission_by_name (link_label, "activate-link");
+        if (is_self_test_running (dialog->priv->device, NULL)) {
+                gdu_device_op_cancel_job (dialog->priv->device,
+                                          cancel_self_test_cb,
+                                          dialog);
+                goto out;
+        }
 
         test_dialog = gtk_dialog_new_with_buttons (NULL,
                                                    GTK_WINDOW (dialog),
@@ -1479,14 +1460,10 @@ gdu_ata_smart_dialog_constructed (GObject *object)
         GtkWidget *align;
         GtkWidget *vbox;
         GtkWidget *vbox2;
-        GtkWidget *hbox;
-        GtkWidget *image;
         GtkWidget *table;
         GtkWidget *label;
         GtkWidget *tree_view;
         GtkWidget *scrolled_window;
-        GtkWidget *spinner;
-        GtkWidget *progress_bar;
         GtkWidget *check_button;
         GtkCellRenderer *renderer;
         GtkTreeViewColumn *column;
@@ -1498,6 +1475,8 @@ gdu_ata_smart_dialog_constructed (GObject *object)
         GtkTreeIter iter = {0};
         const gchar *tooltip_markup;
         gboolean rtl;
+        GPtrArray *elements;
+        GduDetailsElement *element;
 
         rtl = (gtk_widget_get_direction (GTK_WIDGET (dialog)) == GTK_TEXT_DIR_RTL);
 
@@ -1518,6 +1497,7 @@ gdu_ata_smart_dialog_constructed (GObject *object)
 
         pool = gdu_device_get_pool (dialog->priv->device);
         dialog->priv->pool_tree_model = gdu_pool_tree_model_new (pool,
+                                                                 NULL,
                                                                  GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES);
         g_object_unref (pool);
 
@@ -1593,342 +1573,102 @@ gdu_ata_smart_dialog_constructed (GObject *object)
         vbox2 = gtk_vbox_new (FALSE, 6);
         gtk_container_add (GTK_CONTAINER (align), vbox2);
 
-        table = gtk_table_new (4, 2, FALSE);
-        gtk_table_set_col_spacings (GTK_TABLE (table), 12);
-        gtk_table_set_row_spacings (GTK_TABLE (table), 6);
-        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
-
-        row = 0;
-
-        /* ------------------------------ */
-        /* updated */
-
-        /* Translators: Tooltip for the Updated item in the status table */
-        tooltip_markup = _("Time since SMART data was last read â?? SMART data is updated every 30 minutes unless "
-                           "the disk is sleeping");
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Updated:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        hbox = gtk_hbox_new (FALSE, 0);
-
-        /* Translators: Used in the status table when data is currently being updated */
-        label = gtk_label_new (_("Updating..."));
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-        dialog->priv->updating_label = label;
-
-        spinner = gdu_spinner_new ();
-        gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 6);
-        dialog->priv->updating_spinner = spinner;
-
-        label = gdu_time_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->updated_label = label;
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-
-
-        label = gtk_label_new (NULL);
-        s = g_strdup_printf (rtl ? "<a href=\"update-now\" title=\"%s\">%s</a> â?? " :
-                                   " â?? <a href=\"update-now\" title=\"%s\">%s</a>",
-                             /* Translators: Tooltip for the "Update Now" hyperlink */
-                             _("Reads SMART data from the disk, waking it up if necessary"),
-                            /* Translators: Text used in the hyperlink in the status table to update the SMART status */
-                             _("Update now"));
-        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
-        gtk_label_set_markup (GTK_LABEL (label), s);
-        g_free (s);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->update_link_label = label;
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-        g_signal_connect (label,
-                          "activate-link",
-                          G_CALLBACK (on_activate_link_update_smart_data),
+        element = gdu_details_element_new (_("Updated:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Updated' item in the status table */
+                                           _("Time since SMART data was last read â?? SMART data is updated every "
+                                             "30 minutes unless the disk is sleeping"));
+        g_signal_connect (element,
+                          "activated",
+                          G_CALLBACK (on_updated_element_activated),
                           dialog);
+        g_ptr_array_add (elements, element);
+        dialog->priv->updated_element = element;
 
-        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-        row++;
-
-        /* control visibility (see update_dialog()) */
-        gtk_widget_set_no_show_all (dialog->priv->updated_label, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->updating_label, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->updating_spinner, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->update_link_label, TRUE);
-
-        /* ------------------------------ */
-        /* self-tests */
-
-        /* Translators: Tooltip for the Self-tests item in the status table */
-        tooltip_markup = _("The result of the last self-test that ran on the disk");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Self-tests:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        hbox = gtk_hbox_new (FALSE, 0);
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-        dialog->priv->self_test_result_label = label;
-
-
-        progress_bar = gtk_progress_bar_new ();
-        gtk_box_pack_start (GTK_BOX (hbox), progress_bar, FALSE, FALSE, 0);
-        dialog->priv->self_test_progress_bar = progress_bar;
-
-        label = gtk_label_new (NULL);
-        s = g_strdup_printf (rtl ? "<a href=\"run-self-test\" title=\"%s\">%s</a> â?? " :
-                                   " â?? <a href=\"run-self-test\" title=\"%s\">%s</a>",
-                             /* Translators: Tooltip for the "Run self-test" hyperlink */
-                             _("Initiates a self-test on the drive"),
-                             /* Translators: Text used in the hyperlink in the status table to run a self-test */
-                             _("Run self-test"));
-        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
-        gtk_label_set_markup (GTK_LABEL (label), s);
-        g_free (s);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-        dialog->priv->self_test_run_link_label = label;
-        g_signal_connect (label,
-                          "activate-link",
-                          G_CALLBACK (on_activate_link_run_self_test),
-                          dialog);
-
-        label = gtk_label_new (NULL);
-        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
-        s = g_strdup_printf (rtl ? "<a href=\"cancel-self-test\" title=\"%s\">%s</a> â?? " :
-                                   " â?? <a href=\"cancel-self-test\" title=\"%s\">%s</a>",
-                             /* Translators: Tooptip for the "Cancel" hyperlink */
-                             _("Cancels the currently running test"),
-                             /* Translators: Text used in the hyperlink in the status table to cancel a self-test */
-                             _("Cancel"));
-        gtk_label_set_markup (GTK_LABEL (label), s);
-        g_free (s);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-        dialog->priv->self_test_cancel_link_label = label;
-        g_signal_connect (label,
-                          "activate-link",
-                          G_CALLBACK (on_activate_link_cancel_self_test),
+        element = gdu_details_element_new (_("Self-tests:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Self-tests' item in the status table */
+                                           _("The result of the last self-test that ran on the disk"));
+        g_signal_connect (element,
+                          "activated",
+                          G_CALLBACK (on_self_tests_element_activated),
                           dialog);
+        g_ptr_array_add (elements, element);
+        dialog->priv->self_test_element = element;
 
-        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* control visibility (see update_dialog()) */
-        gtk_widget_set_no_show_all (dialog->priv->self_test_result_label, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->self_test_progress_bar, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->self_test_run_link_label, TRUE);
-        gtk_widget_set_no_show_all (dialog->priv->self_test_cancel_link_label, TRUE);
-
-        /* ------------------------------ */
-        /* model */
-
-        /* Translators: Tooltip for the "Model Name:" item in the status table */
-        tooltip_markup = _("The name of the model of the disk");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Model Name:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
+        element = gdu_details_element_new (_("Model:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Model' item in the status table */
+                                           _("The name of the model of the disk"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->model_element = element;
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->model_label = label;
-
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* ------------------------------ */
-        /* firmware */
-
-        /* Translators: Tooltip for the "Firmware Version:" item in the status table */
-        tooltip_markup = _("The firmware version of the disk");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Firmware Version:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
+        element = gdu_details_element_new (_("Firmware Version:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Firmware Version' item in the
+                                            * status table */
+                                           _("The firmware version of the disk"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->firmware_element = element;
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->firmware_label = label;
-
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* ------------------------------ */
-        /* serial */
-
-        /* Translators: Tooltip for the "Serial:" item in the status table */
-        tooltip_markup = _("The serial number of the disk");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Serial Number:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->serial_label = label;
-
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* ------------------------------ */
-        /* power on hours */
-
-        /* Translators: Tooltip for the "Powered On:" item in the status table */
-        tooltip_markup = _("The amount of elapsed time the disk has been in a powered-up state");
+        element = gdu_details_element_new (_("Serial Number:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Serial Number' item in the status table */
+                                           _("The serial number of the disk"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->serial_element = element;
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Powered On:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->power_on_hours_label = label;
-
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* ------------------------------ */
-        /* temperature */
-
-        /* Translators: Tooltip for the "Temperature:" item in the status table */
-        tooltip_markup = _("The temperature of the disk");
+        element = gdu_details_element_new (_("Powered On:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Powered On' item in the status table */
+                                           _("The amount of elapsed time the disk has been in a powered-up state"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->powered_on_element = element;
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Temperature:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->temperature_label = label;
+        element = gdu_details_element_new (_("Temperature:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Temperature' item in the status table */
+                                           _("The temperature of the disk"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->temperature_element = element;
 
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        row++;
-
-        /* ------------------------------ */
-        /* bad sectors */
-
-        /* Translators: Tooltip for the "Bad Sectors" item in the status table */
-        tooltip_markup = _("The sum of pending and reallocated bad sectors");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Bad Sectors:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->sectors_label = label;
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-        row++;
+        element = gdu_details_element_new (_("Bad Sectors:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Bad Sectors' item in the status table */
+                                           _("The sum of pending and reallocated bad sectors"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->bad_sectors_element = element;
 
-        /* ------------------------------ */
-        /* self assessment */
-
-        /* Translators: Tooltip for the "Self Assessment" item in the status table */
-        tooltip_markup = _("The assessment from the disk itself whether it is about to fail");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Self Assessment:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
+        element = gdu_details_element_new (_("Self Assessment:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Self Assesment' item in the status table */
+                                           _("The assessment from the disk itself whether it is about to fail"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->self_assessment_element = element;
 
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->self_assessment_label = label;
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-        row++;
-
-        /* ------------------------------ */
-        /* overall assessment */
-
-        /* Translators: Tooltip for the "Overall Assessment" in the status table */
-        tooltip_markup = _("An overall assessment of the health of the disk");
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
         /* Translators: Item name in the status table */
-        gtk_label_set_markup (GTK_LABEL (label), _("Overall Assessment:"));
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-
-        hbox = gtk_hbox_new (FALSE, 2);
-        image = gtk_image_new_from_icon_name ("gdu-smart-unknown",
-                                              GTK_ICON_SIZE_MENU);
-        gtk_widget_set_tooltip_markup (image, tooltip_markup);
-        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
-        dialog->priv->overall_assessment_image = image;
-
-        label = gtk_label_new (NULL);
-        gtk_widget_set_tooltip_markup (label, tooltip_markup);
-        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
-        dialog->priv->overall_assessment_label = label;
-        gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
-
-        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
-                          GTK_FILL, GTK_FILL, 0, 0);
-        row++;
+        element = gdu_details_element_new (_("Overall Assessment:"),
+                                           NULL,
+                                           /* Translators: Tooltip for the 'Overall Assessment' item in the
+                                            * status table */
+                                           _("An overall assessment of the health of the disk"));
+        g_ptr_array_add (elements, element);
+        dialog->priv->overall_assessment_element = element;
+
+        table = gdu_details_table_new (1, elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
 
         /* ------------------------------ */
 
@@ -2425,7 +2165,7 @@ update_dialog (GduAtaSmartDialog *dialog)
         gchar *selftest_text;
         gchar *action_text;
         gboolean highlight;
-        GTimeVal updated;
+        guint64 updated;
         gconstpointer blob;
         gsize blob_size;
         GIcon *status_icon;
@@ -2450,8 +2190,7 @@ update_dialog (GduAtaSmartDialog *dialog)
         temperature_text = NULL;
         selftest_text = NULL;
         action_text = NULL;
-        updated.tv_sec = 0;
-        dialog->priv->last_updated = 0;
+        updated = 0;
         status_icon = NULL;
         sk_disk = NULL;
 
@@ -2494,8 +2233,7 @@ update_dialog (GduAtaSmartDialog *dialog)
                 goto has_data;
         }
 
-        dialog->priv->last_updated = updated.tv_sec = gdu_device_drive_ata_smart_get_time_collected (dialog->priv->device);
-        updated.tv_usec = 0;
+        dialog->priv->last_updated = updated = gdu_device_drive_ata_smart_get_time_collected (dialog->priv->device);
 
         s = gdu_util_ata_smart_status_to_desc (gdu_device_drive_ata_smart_get_status (dialog->priv->device),
                                                &highlight,
@@ -2644,25 +2382,110 @@ update_dialog (GduAtaSmartDialog *dialog)
 
         if (status_icon == NULL)
                 status_icon = g_themed_icon_new ("gdu-smart-unknown");
-        gtk_image_set_from_gicon (GTK_IMAGE (dialog->priv->overall_assessment_image),
-                                  status_icon,
-                                  GTK_ICON_SIZE_MENU);
-
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->self_assessment_label), self_assessment_text != NULL ? self_assessment_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->overall_assessment_label), overall_assessment_text != NULL ? overall_assessment_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->sectors_label), bad_sectors_text != NULL ? bad_sectors_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->power_on_hours_label), powered_on_text != NULL ? powered_on_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->temperature_label), temperature_text != NULL ? temperature_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->model_label), model_text != NULL ? model_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->firmware_label), firmware_text != NULL ? firmware_text : "-");
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->serial_label), serial_text != NULL ? serial_text : "-");
-        if (updated.tv_sec == 0) {
-                gdu_time_label_set_time (GDU_TIME_LABEL (dialog->priv->updated_label), NULL);
-                gtk_label_set_markup (GTK_LABEL (dialog->priv->updated_label), "-");
+        gdu_details_element_set_icon (dialog->priv->overall_assessment_element, status_icon);
+
+        gdu_details_element_set_text (dialog->priv->self_assessment_element,
+                                      self_assessment_text != NULL ? self_assessment_text : "-");
+        gdu_details_element_set_text (dialog->priv->overall_assessment_element,
+                                      overall_assessment_text != NULL ? overall_assessment_text : "-");
+        gdu_details_element_set_text (dialog->priv->bad_sectors_element,
+                                      bad_sectors_text != NULL ? bad_sectors_text : "-");
+        gdu_details_element_set_text (dialog->priv->powered_on_element,
+                                      powered_on_text != NULL ? powered_on_text : "-");
+        gdu_details_element_set_text (dialog->priv->temperature_element,
+                                      temperature_text != NULL ? temperature_text : "-");
+        gdu_details_element_set_text (dialog->priv->model_element,
+                                      model_text != NULL ? model_text : "-");
+        gdu_details_element_set_text (dialog->priv->firmware_element,
+                                      firmware_text != NULL ? firmware_text : "-");
+        gdu_details_element_set_text (dialog->priv->serial_element,
+                                      serial_text != NULL ? serial_text : "-");
+        if (dialog->priv->is_updating) {
+                gdu_details_element_set_is_spinning (dialog->priv->updated_element, TRUE);
+                gdu_details_element_set_text (dialog->priv->updated_element, "Updating");
+                gdu_details_element_set_time (dialog->priv->updated_element, 0);
+                gdu_details_element_set_action_text (dialog->priv->updated_element, NULL);
+                gdu_details_element_set_action_tooltip (dialog->priv->updated_element, NULL);
+        } else {
+                gdu_details_element_set_is_spinning (dialog->priv->updated_element, FALSE);
+                if (updated > 0) {
+                        gdu_details_element_set_time (dialog->priv->updated_element, updated);
+                        gdu_details_element_set_text (dialog->priv->updated_element, NULL);
+                } else {
+                        gdu_details_element_set_time (dialog->priv->updated_element, 0);
+                        gdu_details_element_set_text (dialog->priv->updated_element, "-");
+                }
+                gdu_details_element_set_action_text (dialog->priv->updated_element,
+                                                     /* Translators: Text used in the hyperlink in the status table
+                                                      * to update the SMART status */
+                                                     _("Update Now"));
+                gdu_details_element_set_action_tooltip (dialog->priv->updated_element,
+                                                        /* Translators: Tooltip for the "Update Now" hyperlink */
+                                                        _("Reads SMART data from the disk, waking it up if necessary"));
+        }
+
+        if (dialog->priv->device != NULL && is_self_test_running (dialog->priv->device, &test_type)) {
+                gdouble fraction;
+                const gchar *test_type_str;
+
+                switch (test_type) {
+                case SK_SMART_SELF_TEST_SHORT:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Short self-test in progress: ");
+                        break;
+                case SK_SMART_SELF_TEST_EXTENDED:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Extended self-test in progress: ");
+                        break;
+                case SK_SMART_SELF_TEST_CONVEYANCE:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Conveyance self-test in progress: ");
+                        break;
+                default:
+                        g_assert_not_reached ();
+                        break;
+                }
+
+                if (gdu_device_job_is_cancellable (dialog->priv->device)) {
+                        gdu_details_element_set_action_text (dialog->priv->self_test_element,
+                                                             /* Translators: Text used in the hyperlink in the status
+                                                              * table to cancel a self-test */
+                                                             _("Cancel Test"));
+                        gdu_details_element_set_action_tooltip (dialog->priv->self_test_element,
+                                                                /* Translators: Tooptip for the "Cancel" hyperlink */
+                                                                _("Cancels the currently running test"));
+                } else {
+                        gdu_details_element_set_action_text (dialog->priv->self_test_element, NULL);
+                        gdu_details_element_set_action_tooltip (dialog->priv->self_test_element, NULL);
+                }
+                gdu_details_element_set_text (dialog->priv->self_test_element, test_type_str);
+
+                fraction = gdu_device_job_get_percentage (dialog->priv->device) / 100.0;
+                if (fraction < 0.0)
+                        fraction = 0.0;
+                if (fraction > 1.0)
+                        fraction = 1.0;
+
+                gdu_details_element_set_progress (dialog->priv->self_test_element, fraction);
         } else {
-                gdu_time_label_set_time (GDU_TIME_LABEL (dialog->priv->updated_label), &updated);
+                gdu_details_element_set_progress (dialog->priv->self_test_element, -1.0);
+                gdu_details_element_set_text (dialog->priv->self_test_element,
+                                              selftest_text != NULL ? selftest_text : "-");
+                if (dialog->priv->device != NULL) {
+                        /* TODO: check if self-tests are available at all */
+                        gdu_details_element_set_action_text (dialog->priv->self_test_element,
+                                                             /* Translators: Text used in the hyperlink in the
+                                                              * status table to run a self-test */
+                                                             _("Run self-test"));
+                        gdu_details_element_set_action_tooltip (dialog->priv->self_test_element,
+                                                                /* Translators: Tooltip for the "Run self-test"
+                                                                 * hyperlink */
+                                                                _("Initiates a self-test on the drive"));
+                } else {
+                        gdu_details_element_set_action_text (dialog->priv->self_test_element, NULL);
+                        gdu_details_element_set_action_tooltip (dialog->priv->self_test_element, NULL);
+                }
         }
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->self_test_result_label), selftest_text != NULL ? selftest_text : "-");
 
         if (sk_disk == NULL) {
                 gtk_list_store_clear (dialog->priv->attr_list_store);
@@ -2703,76 +2526,6 @@ update_dialog (GduAtaSmartDialog *dialog)
                 no_warn = get_ata_smart_no_warn (dialog->priv->device);
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->no_warn_check_button), no_warn);
 
-        /* control visility of update widgets */
-        if (dialog->priv->is_updating) {
-                gtk_widget_hide (dialog->priv->updated_label);
-                gtk_widget_hide (dialog->priv->update_link_label);
-                gtk_widget_show (dialog->priv->updating_spinner);
-                gtk_widget_show (dialog->priv->updating_label);
-                gdu_spinner_start (GDU_SPINNER (dialog->priv->updating_spinner));
-        } else {
-                gtk_widget_hide (dialog->priv->updating_spinner);
-                gdu_spinner_stop (GDU_SPINNER (dialog->priv->updating_spinner));
-                gtk_widget_hide (dialog->priv->updating_label);
-                gtk_widget_show (dialog->priv->updated_label);
-                if (dialog->priv->device == NULL) {
-                        gtk_widget_hide (dialog->priv->update_link_label);
-                } else {
-                        gtk_widget_show (dialog->priv->update_link_label);
-                }
-        }
-
-        /* control visibility of self-test widgets */
-        if (dialog->priv->device != NULL && is_self_test_running (dialog->priv->device, &test_type)) {
-                gdouble fraction;
-                const gchar *test_type_str;
-
-                fraction = gdu_device_job_get_percentage (dialog->priv->device) / 100.0;
-                if (fraction < 0.0)
-                        fraction = 0.0;
-                if (fraction > 1.0)
-                        fraction = 1.0;
-
-                gtk_widget_show (dialog->priv->self_test_result_label);
-                gtk_widget_hide (dialog->priv->self_test_run_link_label);
-                gtk_widget_show (dialog->priv->self_test_progress_bar);
-                if (gdu_device_job_is_cancellable (dialog->priv->device))
-                        gtk_widget_show (dialog->priv->self_test_cancel_link_label);
-                else
-                        gtk_widget_hide (dialog->priv->self_test_cancel_link_label);
-                gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->priv->self_test_progress_bar),
-                                               fraction);
-
-                switch (test_type) {
-                case SK_SMART_SELF_TEST_SHORT:
-                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
-                        test_type_str = _("Short self-test in progress: ");
-                        break;
-                case SK_SMART_SELF_TEST_EXTENDED:
-                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
-                        test_type_str = _("Extended self-test in progress: ");
-                        break;
-                case SK_SMART_SELF_TEST_CONVEYANCE:
-                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
-                        test_type_str = _("Conveyance self-test in progress: ");
-                        break;
-                default:
-                        g_assert_not_reached ();
-                        break;
-                }
-                gtk_label_set_markup (GTK_LABEL (dialog->priv->self_test_result_label),
-                                      test_type_str);
-        } else {
-                gtk_widget_hide (dialog->priv->self_test_progress_bar);
-                gtk_widget_hide (dialog->priv->self_test_cancel_link_label);
-                gtk_widget_show (dialog->priv->self_test_result_label);
-                if (dialog->priv->device == NULL) {
-                        gtk_widget_hide (dialog->priv->self_test_run_link_label);
-                } else {
-                        gtk_widget_show (dialog->priv->self_test_run_link_label);
-                }
-        }
-
         if (sk_disk != NULL)
                 sk_disk_free (sk_disk);
 
diff --git a/src/gdu-gtk/gdu-create-linux-md-dialog.c b/src/gdu-gtk/gdu-create-linux-md-dialog.c
index ea90378..0f5342d 100644
--- a/src/gdu-gtk/gdu-create-linux-md-dialog.c
+++ b/src/gdu-gtk/gdu-create-linux-md-dialog.c
@@ -731,7 +731,7 @@ notes_data_func (GtkCellLayout   *cell_layout,
 
                         if (array_size > 1000 * 1000) {
                                 gchar *strsize;
-                                strsize = gdu_util_get_size_for_display (component_size, FALSE);
+                                strsize = gdu_util_get_size_for_display (component_size, FALSE, FALSE);
 
                                 if (whole_disk_is_uninitialized) {
                                         /* Translators: This is shown in the Details column.
@@ -762,7 +762,7 @@ notes_data_func (GtkCellLayout   *cell_layout,
                 } else {
                         gchar *strsize;
 
-                        strsize = gdu_util_get_size_for_display (largest_segment, FALSE);
+                        strsize = gdu_util_get_size_for_display (largest_segment, FALSE, FALSE);
 
                         if (whole_disk_is_uninitialized) {
                                 /* Translators: This is shown in the Details column.
@@ -1064,6 +1064,7 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         GtkTreeViewColumn *column;
 
         dialog->priv->model = gdu_pool_tree_model_new (dialog->priv->pool,
+                                                       NULL,
                                                        GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES |
                                                        GDU_POOL_TREE_MODEL_FLAGS_NO_UNALLOCATABLE_DRIVES);
 
@@ -1498,7 +1499,7 @@ update (GduCreateLinuxMdDialog *dialog)
                 if (tip_text == NULL) {
                         gchar *strsize;
 
-                        strsize = gdu_util_get_size_for_display (array_size, FALSE);
+                        strsize = gdu_util_get_size_for_display (array_size, FALSE, FALSE);
                         /* Translators: This is for the tip text shown in the dialog.
                          * First %s is the size e.g. '42 GB'.
                          * Second %s is the short localized name for the RAID level, e.g. "RAID-1".
diff --git a/src/gdu-gtk/gdu-details-element.c b/src/gdu-gtk/gdu-details-element.c
new file mode 100644
index 0000000..9393149
--- /dev/null
+++ b/src/gdu-gtk/gdu-details-element.c
@@ -0,0 +1,556 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <math.h>
+
+#include "gdu-size-widget.h"
+
+struct GduDetailsElementPrivate
+{
+        gchar *heading;
+        gchar *text;
+        guint64 time;
+        gdouble progress;
+        gchar *tooltip;
+        GIcon *icon;
+        gchar *action_text;
+        gchar *action_uri;
+        gchar *action_tooltip;
+        gboolean is_spinning;
+};
+
+enum
+{
+        PROP_0,
+        PROP_HEADING,
+        PROP_TEXT,
+        PROP_TIME,
+        PROP_PROGRESS,
+        PROP_TOOLTIP,
+        PROP_ICON,
+        PROP_ACTION_TEXT,
+        PROP_ACTION_URI,
+        PROP_ACTION_TOOLTIP,
+        PROP_IS_SPINNING
+};
+
+enum
+{
+        CHANGED_SIGNAL,
+        ACTIVATED_SIGNAL,
+        LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0,};
+
+G_DEFINE_TYPE (GduDetailsElement, gdu_details_element, G_TYPE_OBJECT)
+
+static void
+gdu_details_element_finalize (GObject *object)
+{
+        GduDetailsElement *element = GDU_DETAILS_ELEMENT (object);
+
+        g_free (element->priv->heading);
+        g_free (element->priv->text);
+        g_free (element->priv->tooltip);
+        if (element->priv->icon != NULL)
+                g_object_unref (element->priv->icon);
+        g_free (element->priv->action_text);
+        g_free (element->priv->action_uri);
+        g_free (element->priv->action_tooltip);
+
+        if (G_OBJECT_CLASS (gdu_details_element_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_details_element_parent_class)->finalize (object);
+}
+
+static void
+gdu_details_element_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+        GduDetailsElement *element = GDU_DETAILS_ELEMENT (object);
+
+        switch (property_id) {
+        case PROP_HEADING:
+                g_value_set_string (value, gdu_details_element_get_heading (element));
+                break;
+
+        case PROP_TEXT:
+                g_value_set_string (value, gdu_details_element_get_text (element));
+                break;
+
+        case PROP_TIME:
+                g_value_set_uint64 (value, gdu_details_element_get_time (element));
+                break;
+
+        case PROP_PROGRESS:
+                g_value_set_double (value, gdu_details_element_get_progress (element));
+                break;
+
+        case PROP_TOOLTIP:
+                g_value_set_string (value, gdu_details_element_get_tooltip (element));
+                break;
+
+        case PROP_ICON:
+                g_value_take_object (value, gdu_details_element_get_icon (element));
+                break;
+
+        case PROP_ACTION_TEXT:
+                g_value_set_string (value, gdu_details_element_get_action_text (element));
+                break;
+
+        case PROP_ACTION_URI:
+                g_value_set_string (value, gdu_details_element_get_action_uri (element));
+                break;
+
+        case PROP_ACTION_TOOLTIP:
+                g_value_set_string (value, gdu_details_element_get_action_tooltip (element));
+                break;
+
+        case PROP_IS_SPINNING:
+                g_value_set_boolean (value, gdu_details_element_get_is_spinning (element));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_details_element_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+        GduDetailsElement *element = GDU_DETAILS_ELEMENT (object);
+
+        switch (property_id) {
+
+        case PROP_HEADING:
+                gdu_details_element_set_heading (element, g_value_get_string (value));
+                break;
+
+        case PROP_TEXT:
+                gdu_details_element_set_text (element, g_value_get_string (value));
+                break;
+
+        case PROP_TIME:
+                gdu_details_element_set_time (element, g_value_get_uint64 (value));
+                break;
+
+        case PROP_PROGRESS:
+                gdu_details_element_set_progress (element, g_value_get_double (value));
+                break;
+
+        case PROP_TOOLTIP:
+                gdu_details_element_set_tooltip (element, g_value_get_string (value));
+                break;
+
+        case PROP_ICON:
+                gdu_details_element_set_icon (element, g_value_get_object (value));
+                break;
+
+        case PROP_ACTION_TEXT:
+                gdu_details_element_set_action_text (element, g_value_get_string (value));
+                break;
+
+        case PROP_ACTION_URI:
+                gdu_details_element_set_action_uri (element, g_value_get_string (value));
+                break;
+
+        case PROP_ACTION_TOOLTIP:
+                gdu_details_element_set_action_tooltip (element, g_value_get_string (value));
+                break;
+
+        case PROP_IS_SPINNING:
+                gdu_details_element_set_is_spinning (element, g_value_get_boolean (value));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_details_element_init (GduDetailsElement *element)
+{
+        element->priv = G_TYPE_INSTANCE_GET_PRIVATE (element,
+                                                     GDU_TYPE_DETAILS_ELEMENT,
+                                                     GduDetailsElementPrivate);
+
+}
+
+static void
+gdu_details_element_class_init (GduDetailsElementClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduDetailsElementPrivate));
+
+        gobject_class->get_property        = gdu_details_element_get_property;
+        gobject_class->set_property        = gdu_details_element_set_property;
+        gobject_class->finalize            = gdu_details_element_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_HEADING,
+                                         g_param_spec_string ("heading",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_TEXT,
+                                         g_param_spec_string ("text",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_TIME,
+                                         g_param_spec_uint64 ("time",
+                                                              NULL,
+                                                              NULL,
+                                                              0,
+                                                              G_MAXUINT64,
+                                                              0,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_PROGRESS,
+                                         g_param_spec_double ("progress",
+                                                              NULL,
+                                                              NULL,
+                                                              -G_MAXDOUBLE,
+                                                              G_MAXDOUBLE,
+                                                              -1.0,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_TOOLTIP,
+                                         g_param_spec_string ("tooltip",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_ICON,
+                                         g_param_spec_object ("icon",
+                                                              NULL,
+                                                              NULL,
+                                                              G_TYPE_ICON,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_ACTION_TEXT,
+                                         g_param_spec_string ("action-text",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_ACTION_URI,
+                                         g_param_spec_string ("action-uri",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_ACTION_TOOLTIP,
+                                         g_param_spec_string ("action-tooltip",
+                                                             NULL,
+                                                             NULL,
+                                                             NULL,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_IS_SPINNING,
+                                         g_param_spec_boolean ("is-spinning",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READABLE |
+                                                               G_PARAM_WRITABLE |
+                                                               G_PARAM_CONSTRUCT));
+
+        signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                                GDU_TYPE_DETAILS_ELEMENT,
+                                                G_SIGNAL_RUN_LAST,
+                                                G_STRUCT_OFFSET (GduDetailsElementClass, changed),
+                                                NULL,
+                                                NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE,
+                                                0);
+
+        signals[ACTIVATED_SIGNAL] = g_signal_new ("activated",
+                                                  GDU_TYPE_DETAILS_ELEMENT,
+                                                  G_SIGNAL_RUN_LAST,
+                                                  G_STRUCT_OFFSET (GduDetailsElementClass, activated),
+                                                  NULL,
+                                                  NULL,
+                                                  g_cclosure_marshal_VOID__VOID,
+                                                  G_TYPE_NONE,
+                                                  0);
+}
+
+GduDetailsElement *
+gdu_details_element_new (const gchar       *heading,
+                         const gchar       *text,
+                         const gchar       *tooltip)
+{
+        return GDU_DETAILS_ELEMENT (g_object_new (GDU_TYPE_DETAILS_ELEMENT,
+                                                  "heading", heading,
+                                                  "text", text,
+                                                  "tooltip", tooltip,
+                                                  NULL));
+}
+
+const gchar *
+gdu_details_element_get_heading (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->heading;
+}
+
+const gchar *
+gdu_details_element_get_text (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->text;
+}
+
+guint64
+gdu_details_element_get_time (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), 0);
+        return element->priv->time;
+}
+
+gdouble
+gdu_details_element_get_progress (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), -1);
+        return element->priv->progress;
+}
+
+const gchar *
+gdu_details_element_get_tooltip (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->tooltip;
+}
+
+GIcon *
+gdu_details_element_get_icon (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->icon != NULL ? g_object_ref (element->priv->icon) : NULL;
+}
+
+const gchar *
+gdu_details_element_get_action_text (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->action_text;
+}
+
+const gchar *
+gdu_details_element_get_action_uri (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->action_uri;
+}
+
+const gchar *
+gdu_details_element_get_action_tooltip (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), NULL);
+        return element->priv->action_tooltip;
+}
+
+gboolean
+gdu_details_element_get_is_spinning (GduDetailsElement *element)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_ELEMENT (element), FALSE);
+        return element->priv->is_spinning;
+}
+
+void
+gdu_details_element_set_heading (GduDetailsElement *element,
+                                 const gchar       *heading)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->heading, heading) != 0) {
+                g_free (element->priv->heading);
+                element->priv->heading = g_strdup (heading);
+                g_object_notify (G_OBJECT (element), "heading");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_text (GduDetailsElement *element,
+                              const gchar       *text)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->text, text) != 0) {
+                g_free (element->priv->text);
+                element->priv->text = g_strdup (text);
+                g_object_notify (G_OBJECT (element), "text");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_time (GduDetailsElement *element,
+                              guint64            time)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (element->priv->time != time) {
+                element->priv->time = time;
+                g_object_notify (G_OBJECT (element), "time");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_progress (GduDetailsElement *element,
+                                  gdouble            progress)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (element->priv->progress != progress) {
+                element->priv->progress = progress;
+                g_object_notify (G_OBJECT (element), "progress");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_tooltip (GduDetailsElement *element,
+                                 const gchar       *tooltip)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->tooltip, tooltip) != 0) {
+                g_free (element->priv->tooltip);
+                element->priv->tooltip = g_strdup (tooltip);
+                g_object_notify (G_OBJECT (element), "tooltip");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_icon (GduDetailsElement *element,
+                              GIcon             *icon)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (!g_icon_equal (element->priv->icon, icon)) {
+                if (element->priv->icon != NULL)
+                        g_object_unref (element->priv->icon);
+                element->priv->icon = g_object_ref (icon);
+                g_object_notify (G_OBJECT (element), "icon");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_action_text (GduDetailsElement *element,
+                                     const gchar       *action_text)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->action_text, action_text) != 0) {
+                g_free (element->priv->action_text);
+                element->priv->action_text = g_strdup (action_text);
+                g_object_notify (G_OBJECT (element), "action-text");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_action_uri (GduDetailsElement *element,
+                                    const gchar       *action_uri)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->action_uri, action_uri) != 0) {
+                g_free (element->priv->action_uri);
+                element->priv->action_uri = g_strdup (action_uri);
+                g_object_notify (G_OBJECT (element), "action-uri");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_action_tooltip (GduDetailsElement *element,
+                                        const gchar       *action_tooltip)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (g_strcmp0 (element->priv->action_tooltip, action_tooltip) != 0) {
+                g_free (element->priv->action_tooltip);
+                element->priv->action_tooltip = g_strdup (action_tooltip);
+                g_object_notify (G_OBJECT (element), "action-tooltip");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
+void
+gdu_details_element_set_is_spinning (GduDetailsElement *element,
+                                     gboolean           is_spinning)
+{
+        g_return_if_fail (GDU_IS_DETAILS_ELEMENT (element));
+        if (element->priv->is_spinning != is_spinning) {
+                element->priv->is_spinning = is_spinning;
+                g_object_notify (G_OBJECT (element), "is-spinning");
+                g_signal_emit (element, signals[CHANGED_SIGNAL], 0);
+        }
+}
+
diff --git a/src/gdu-gtk/gdu-details-element.h b/src/gdu-gtk/gdu-details-element.h
new file mode 100644
index 0000000..d7ebc43
--- /dev/null
+++ b/src/gdu-gtk/gdu-details-element.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_DETAILS_ELEMENT_H
+#define __GDU_DETAILS_ELEMENT_H
+
+#include <gdu-gtk/gdu-gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_DETAILS_ELEMENT         (gdu_details_element_get_type())
+#define GDU_DETAILS_ELEMENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_DETAILS_ELEMENT, GduDetailsElement))
+#define GDU_DETAILS_ELEMENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_DETAILS_ELEMENT, GduDetailsElementClass))
+#define GDU_IS_DETAILS_ELEMENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_DETAILS_ELEMENT))
+#define GDU_IS_DETAILS_ELEMENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_DETAILS_ELEMENT))
+#define GDU_DETAILS_ELEMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_DETAILS_ELEMENT, GduDetailsElementClass))
+
+typedef struct GduDetailsElementClass   GduDetailsElementClass;
+typedef struct GduDetailsElementPrivate GduDetailsElementPrivate;
+
+struct GduDetailsElement
+{
+        GObject parent;
+
+        /*< private >*/
+        GduDetailsElementPrivate *priv;
+};
+
+struct GduDetailsElementClass
+{
+        GObjectClass parent_class;
+
+        /* signals */
+        void (*changed) (GduDetailsElement *element);
+
+        /* signals */
+        void (*activated) (GduDetailsElement *element);
+};
+
+GType               gdu_details_element_get_type            (void) G_GNUC_CONST;
+GduDetailsElement*  gdu_details_element_new                 (const gchar       *heading,
+                                                             const gchar       *text,
+                                                             const gchar       *tooltip);
+
+const gchar        *gdu_details_element_get_heading        (GduDetailsElement *element);
+GIcon              *gdu_details_element_get_icon           (GduDetailsElement *element);
+const gchar        *gdu_details_element_get_text           (GduDetailsElement *element);
+guint64             gdu_details_element_get_time           (GduDetailsElement *element);
+gdouble             gdu_details_element_get_progress       (GduDetailsElement *element);
+const gchar        *gdu_details_element_get_tooltip        (GduDetailsElement *element);
+const gchar        *gdu_details_element_get_action_text    (GduDetailsElement *element);
+const gchar        *gdu_details_element_get_action_uri     (GduDetailsElement *element);
+const gchar        *gdu_details_element_get_action_tooltip (GduDetailsElement *element);
+gboolean            gdu_details_element_get_is_spinning    (GduDetailsElement *element);
+
+void                gdu_details_element_set_heading        (GduDetailsElement *element,
+                                                            const gchar       *heading);
+void                gdu_details_element_set_icon           (GduDetailsElement *element,
+                                                            GIcon             *icon);
+void                gdu_details_element_set_text           (GduDetailsElement *element,
+                                                            const gchar       *text);
+void                gdu_details_element_set_time           (GduDetailsElement *element,
+                                                            guint64            time);
+void                gdu_details_element_set_progress       (GduDetailsElement *element,
+                                                            gdouble            progress);
+void                gdu_details_element_set_tooltip        (GduDetailsElement *element,
+                                                            const gchar       *tooltip);
+void                gdu_details_element_set_action_text    (GduDetailsElement *element,
+                                                            const gchar       *action_text);
+void                gdu_details_element_set_action_uri     (GduDetailsElement *element,
+                                                            const gchar       *action_uri);
+void                gdu_details_element_set_action_tooltip (GduDetailsElement *element,
+                                                            const gchar       *action_tooltip);
+void                gdu_details_element_set_is_spinning    (GduDetailsElement *element,
+                                                            gboolean           is_spinning);
+
+G_END_DECLS
+
+#endif  /* __GDU_SIZE_WIDGET_H */
+
diff --git a/src/gdu-gtk/gdu-details-table.c b/src/gdu-gtk/gdu-details-table.c
new file mode 100644
index 0000000..2784a0e
--- /dev/null
+++ b/src/gdu-gtk/gdu-details-table.c
@@ -0,0 +1,477 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <math.h>
+
+#include "gdu-size-widget.h"
+
+struct GduDetailsTablePrivate
+{
+        guint      num_columns;
+        GPtrArray *elements;
+
+        GPtrArray *column_tables;
+
+        GPtrArray *element_data_array;
+};
+
+enum
+{
+        PROP_0,
+        PROP_NUM_COLUMNS,
+        PROP_ELEMENTS,
+};
+
+static void do_relayout (GduDetailsTable *table);
+
+G_DEFINE_TYPE (GduDetailsTable, gdu_details_table, GTK_TYPE_HBOX)
+
+static void
+gdu_details_table_finalize (GObject *object)
+{
+        GduDetailsTable *table = GDU_DETAILS_TABLE (object);
+
+        if (table->priv->element_data_array != NULL)
+                g_ptr_array_unref (table->priv->element_data_array);
+
+        if (table->priv->column_tables != NULL)
+                g_ptr_array_unref (table->priv->column_tables);
+
+        if (table->priv->elements != NULL)
+                g_ptr_array_unref (table->priv->elements);
+
+        if (G_OBJECT_CLASS (gdu_details_table_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_details_table_parent_class)->finalize (object);
+}
+
+static void
+gdu_details_table_get_property (GObject    *object,
+                                guint       property_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+        GduDetailsTable *table = GDU_DETAILS_TABLE (object);
+
+        switch (property_id) {
+        case PROP_NUM_COLUMNS:
+                g_value_set_uint (value, gdu_details_table_get_num_columns (table));
+                break;
+
+        case PROP_ELEMENTS:
+                g_value_take_boxed (value, gdu_details_table_get_elements (table));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_details_table_set_property (GObject      *object,
+                                guint         property_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+        GduDetailsTable *table = GDU_DETAILS_TABLE (object);
+
+        switch (property_id) {
+        case PROP_NUM_COLUMNS:
+                gdu_details_table_set_num_columns (table, g_value_get_uint (value));
+                break;
+
+        case PROP_ELEMENTS:
+                gdu_details_table_set_elements (table, g_value_get_boxed (value));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_details_table_init (GduDetailsTable *table)
+{
+        table->priv = G_TYPE_INSTANCE_GET_PRIVATE (table,
+                                                   GDU_TYPE_DETAILS_TABLE,
+                                                   GduDetailsTablePrivate);
+
+}
+
+static void
+gdu_details_table_constructed (GObject *object)
+{
+        GduDetailsTable *table = GDU_DETAILS_TABLE (object);
+
+        gtk_box_set_homogeneous (GTK_BOX (table), TRUE);
+        gtk_box_set_spacing (GTK_BOX (table), 12);
+
+        if (G_OBJECT_CLASS (gdu_details_table_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_details_table_parent_class)->constructed (object);
+}
+
+static void
+gdu_details_table_class_init (GduDetailsTableClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduDetailsTablePrivate));
+
+        gobject_class->get_property        = gdu_details_table_get_property;
+        gobject_class->set_property        = gdu_details_table_set_property;
+        gobject_class->constructed         = gdu_details_table_constructed;
+        gobject_class->finalize            = gdu_details_table_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_NUM_COLUMNS,
+                                         g_param_spec_uint ("num-columns",
+                                                            NULL,
+                                                            NULL,
+                                                            1,
+                                                            G_MAXUINT,
+                                                            2,
+                                                            G_PARAM_READABLE |
+                                                            G_PARAM_WRITABLE |
+                                                            G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_ELEMENTS,
+                                         g_param_spec_boxed ("elements",
+                                                             NULL,
+                                                             NULL,
+                                                             G_TYPE_PTR_ARRAY,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+}
+
+GtkWidget *
+gdu_details_table_new (guint      num_columns,
+                       GPtrArray *elements)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_DETAILS_TABLE,
+                                         "num-columns", num_columns,
+                                         "elements", elements,
+                                         NULL));
+}
+
+guint
+gdu_details_table_get_num_columns (GduDetailsTable *table)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_TABLE (table), 0);
+        return table->priv->num_columns;
+}
+
+GPtrArray *
+gdu_details_table_get_elements (GduDetailsTable *table)
+{
+        g_return_val_if_fail (GDU_IS_DETAILS_TABLE (table), NULL);
+        return table->priv->elements != NULL ? g_ptr_array_ref (table->priv->elements) : NULL;
+}
+
+void
+gdu_details_table_set_num_columns (GduDetailsTable *table,
+                                   guint            num_columns)
+{
+        g_return_if_fail (GDU_IS_DETAILS_TABLE (table));
+        if (table->priv->num_columns != num_columns) {
+                table->priv->num_columns = num_columns;
+                do_relayout (table);
+                g_object_notify (G_OBJECT (table), "num-columns");
+        }
+}
+
+void
+gdu_details_table_set_elements (GduDetailsTable *table,
+                                GPtrArray       *elements)
+{
+        g_return_if_fail (GDU_IS_DETAILS_TABLE (table));
+        if (table->priv->elements != NULL)
+                g_ptr_array_unref (table->priv->elements);
+        table->priv->elements = elements != NULL ? g_ptr_array_ref (elements) : NULL;
+        do_relayout (table);
+        g_object_notify (G_OBJECT (table), "elements");
+}
+
+typedef struct {
+        GduDetailsElement *element;
+
+        GtkWidget *heading_label;
+        GtkWidget *spinner;
+        GtkWidget *image;
+        GtkWidget *time_label;
+        GtkWidget *label;
+        GtkWidget *action_hyphen_label;
+        GtkWidget *action_label;
+        GtkWidget *progress_bar;
+} ElementData;
+
+static void on_details_element_changed (GduDetailsElement *element,
+                                        ElementData       *data);
+
+static void on_activate_link (GtkLabel    *label,
+                              const gchar *uri,
+                              gpointer     user_data);
+
+static void
+element_data_free (ElementData *data)
+{
+        g_signal_handlers_disconnect_by_func (data->element,
+                                              G_CALLBACK (on_details_element_changed),
+                                              data);
+        g_object_unref (data->element);
+        g_signal_handlers_disconnect_by_func (data->action_label,
+                                              G_CALLBACK (on_activate_link),
+                                              data);
+        g_object_unref (data->action_label);
+        g_free (data);
+}
+
+static void
+on_details_element_changed (GduDetailsElement *element,
+                            ElementData       *data)
+{
+        gchar *s;
+        GIcon *icon;
+        const gchar *text;
+        const gchar *action_text;
+        const gchar *action_uri;
+        const gchar *action_tooltip;
+        guint64 time;
+        gdouble progress;
+
+        s = g_strdup_printf ("<span fgcolor='#404040'>%s</span>",
+                             gdu_details_element_get_heading (element));
+        gtk_label_set_markup (GTK_LABEL (data->heading_label), s);
+        g_free (s);
+
+        text = gdu_details_element_get_text (element);
+        if (text != NULL) {
+                gtk_label_set_markup (GTK_LABEL (data->label), text);
+                gtk_widget_set_no_show_all (data->label, FALSE);
+                gtk_widget_show (data->label);
+        } else {
+                gtk_widget_set_no_show_all (data->label, TRUE);
+                gtk_widget_hide (data->label);
+        }
+
+        time = gdu_details_element_get_time (element);
+        if (time > 0) {
+                GTimeVal time_val;
+                time_val.tv_sec = time;
+                time_val.tv_usec = 0;
+                gdu_time_label_set_time (GDU_TIME_LABEL (data->time_label), &time_val);
+                gtk_widget_set_no_show_all (data->time_label, FALSE);
+                gtk_widget_show (data->time_label);
+        } else {
+                gtk_widget_set_no_show_all (data->time_label, TRUE);
+                gtk_widget_hide (data->time_label);
+        }
+
+        icon = gdu_details_element_get_icon (element);
+        if (icon != NULL) {
+                gtk_image_set_from_gicon (GTK_IMAGE (data->image),
+                                          icon,
+                                          GTK_ICON_SIZE_MENU);
+                gtk_widget_set_no_show_all (data->image, FALSE);
+                gtk_widget_show (data->image);
+                g_object_unref (icon);
+        } else {
+                gtk_widget_set_no_show_all (data->image, TRUE);
+                gtk_widget_hide (data->image);
+        }
+
+        action_text = gdu_details_element_get_action_text (element);
+        action_uri = gdu_details_element_get_action_uri (element);
+        action_tooltip = gdu_details_element_get_action_tooltip (element);
+
+        if (action_text != NULL) {
+                s = g_strdup_printf ("<a href=\"%s\" title=\"%s\">%s</a>",
+                                     action_uri != NULL ? action_uri : "",
+                                     action_tooltip != NULL ? action_tooltip : "",
+                                     action_text);
+                gtk_label_set_markup (GTK_LABEL (data->action_label), s);
+                g_free (s);
+                gtk_widget_set_no_show_all (data->action_label, FALSE);
+                gtk_widget_set_no_show_all (data->action_hyphen_label, FALSE);
+                gtk_widget_show (data->action_label);
+                gtk_widget_show (data->action_hyphen_label);
+        } else {
+                gtk_widget_set_no_show_all (data->action_label, TRUE);
+                gtk_widget_set_no_show_all (data->action_hyphen_label, TRUE);
+                gtk_widget_hide (data->action_label);
+                gtk_widget_hide (data->action_hyphen_label);
+        }
+
+        if (gdu_details_element_get_is_spinning (element)) {
+                gdu_spinner_start (GDU_SPINNER (data->spinner));
+                gtk_widget_set_no_show_all (data->spinner, FALSE);
+                gtk_widget_show (data->spinner);
+        } else {
+                gdu_spinner_stop (GDU_SPINNER (data->spinner));
+                gtk_widget_set_no_show_all (data->spinner, TRUE);
+                gtk_widget_hide (data->spinner);
+        }
+
+        progress = gdu_details_element_get_progress (element);
+        if (progress >= 0.0) {
+                gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->progress_bar),
+                                               progress);
+                gtk_widget_set_no_show_all (data->progress_bar, FALSE);
+                gtk_widget_show (data->progress_bar);
+        } else {
+                gtk_widget_set_no_show_all (data->progress_bar, TRUE);
+                gtk_widget_hide (data->progress_bar);
+        }
+}
+
+static void
+on_activate_link (GtkLabel    *label,
+                  const gchar *uri,
+                  gpointer     user_data)
+{
+        ElementData *data = user_data;
+
+        g_signal_emit_by_name (data->element, "activated");
+        g_signal_stop_emission_by_name (label, "activate-link");
+}
+
+static void
+do_relayout (GduDetailsTable *table)
+{
+        GList *children;
+        GList *l;
+        guint n;
+        guint row;
+
+        children = gtk_container_get_children (GTK_CONTAINER (table));
+        for (l = children; l != NULL; l = l->next) {
+                gtk_container_remove (GTK_CONTAINER (table), GTK_WIDGET (l->data));
+        }
+        g_list_free (children);
+
+        if (table->priv->column_tables != NULL)
+                g_ptr_array_unref (table->priv->column_tables);
+
+        if (table->priv->element_data_array != NULL)
+                g_ptr_array_unref (table->priv->element_data_array);
+
+        if (table->priv->num_columns == 0 || table->priv->elements == NULL)
+                goto out;
+
+        table->priv->column_tables = g_ptr_array_new_with_free_func (g_object_unref);
+        table->priv->element_data_array = g_ptr_array_new_with_free_func ((GDestroyNotify) element_data_free);
+
+        for (n = 0; n < table->priv->num_columns; n++) {
+                GtkWidget *column_table;
+
+                column_table = gtk_table_new (1, 2, FALSE);
+                gtk_table_set_col_spacings (GTK_TABLE (column_table), 12);
+                gtk_table_set_row_spacings (GTK_TABLE (column_table), 6);
+                gtk_box_pack_start (GTK_BOX (table),
+                                    column_table,
+                                    TRUE,
+                                    TRUE,
+                                    0);
+
+                g_ptr_array_add (table->priv->column_tables, g_object_ref (column_table));
+        }
+
+        row = -1;
+        for (n = 0; n < table->priv->elements->len; n++) {
+                GduDetailsElement *element = GDU_DETAILS_ELEMENT (table->priv->elements->pdata[n]);
+                guint column_table_number;
+                GtkWidget *column_table;
+                ElementData *data;
+                GtkWidget *hbox;
+
+                column_table_number = n % table->priv->num_columns;
+                if (column_table_number == 0)
+                        row++;
+                column_table = table->priv->column_tables->pdata[column_table_number];
+
+                data = g_new0 (ElementData, 1);
+                data->element = g_object_ref (element);
+
+                data->heading_label = gtk_label_new (NULL);
+                gtk_misc_set_alignment (GTK_MISC (data->heading_label), 0.0, 0.5);
+                gtk_table_attach (GTK_TABLE (column_table),
+                                  data->heading_label,
+                                  0, 1, row, row + 1,
+                                  GTK_FILL, GTK_FILL, 0, 0);
+
+                hbox = gtk_hbox_new (FALSE, 2);
+
+                data->image = gtk_image_new_from_stock ("gtk-missing-image",
+                                                        GTK_ICON_SIZE_MENU);
+                gtk_box_pack_start (GTK_BOX (hbox), data->image, FALSE, FALSE, 0);
+
+                data->spinner = gdu_spinner_new ();
+                gtk_box_pack_start (GTK_BOX (hbox), data->spinner, FALSE, FALSE, 0);
+
+                data->label = gtk_label_new (NULL);
+                gtk_misc_set_alignment (GTK_MISC (data->label), 0.0, 0.5);
+                gtk_box_pack_start (GTK_BOX (hbox), data->label, FALSE, FALSE, 0);
+
+                data->time_label = gdu_time_label_new (NULL);
+                gtk_misc_set_alignment (GTK_MISC (data->time_label), 0.0, 0.5);
+                gtk_box_pack_start (GTK_BOX (hbox), data->time_label, FALSE, FALSE, 0);
+
+                data->progress_bar = gtk_progress_bar_new ();
+                gtk_box_pack_start (GTK_BOX (hbox), data->progress_bar, FALSE, FALSE, 0);
+
+                data->action_hyphen_label = gtk_label_new (" â?? ");
+                gtk_misc_set_alignment (GTK_MISC (data->action_hyphen_label), 0.0, 0.5);
+                gtk_box_pack_start (GTK_BOX (hbox), data->action_hyphen_label, FALSE, FALSE, 0);
+
+                data->action_label = g_object_ref (gtk_label_new (NULL));
+                gtk_misc_set_alignment (GTK_MISC (data->action_label), 0.0, 0.5);
+                gtk_box_pack_start (GTK_BOX (hbox), data->action_label, FALSE, FALSE, 0);
+
+                gtk_table_attach (GTK_TABLE (column_table),
+                                  hbox,
+                                  1, 2, row, row + 1,
+                                  GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+                g_ptr_array_add (table->priv->element_data_array, data);
+
+                g_signal_connect (element,
+                                  "changed",
+                                  G_CALLBACK (on_details_element_changed),
+                                  data);
+                g_signal_connect (data->action_label,
+                                  "activate-link",
+                                  G_CALLBACK (on_activate_link),
+                                  data);
+                on_details_element_changed (element, data);
+        }
+        gtk_widget_show_all (GTK_WIDGET (table));
+
+ out:
+        ;
+}
diff --git a/src/gdu-gtk/gdu-details-table.h b/src/gdu-gtk/gdu-details-table.h
new file mode 100644
index 0000000..45fe3ce
--- /dev/null
+++ b/src/gdu-gtk/gdu-details-table.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_DETAILS_TABLE_H
+#define __GDU_DETAILS_TABLE_H
+
+#include <gdu-gtk/gdu-gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_DETAILS_TABLE         (gdu_details_table_get_type())
+#define GDU_DETAILS_TABLE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_DETAILS_TABLE, GduDetailsTable))
+#define GDU_DETAILS_TABLE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_DETAILS_TABLE, GduDetailsTableClass))
+#define GDU_IS_DETAILS_TABLE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_DETAILS_TABLE))
+#define GDU_IS_DETAILS_TABLE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_DETAILS_TABLE))
+#define GDU_DETAILS_TABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_DETAILS_TABLE, GduDetailsTableClass))
+
+typedef struct GduDetailsTableClass   GduDetailsTableClass;
+typedef struct GduDetailsTablePrivate GduDetailsTablePrivate;
+
+struct GduDetailsTable
+{
+        GtkHBox parent;
+
+        /*< private >*/
+        GduDetailsTablePrivate *priv;
+};
+
+struct GduDetailsTableClass
+{
+        GtkHBoxClass parent_class;
+};
+
+GType       gdu_details_table_get_type        (void) G_GNUC_CONST;
+GtkWidget*  gdu_details_table_new             (guint            num_columns,
+                                               GPtrArray       *elements);
+guint       gdu_details_table_get_num_columns (GduDetailsTable *table);
+GPtrArray  *gdu_details_table_get_elements    (GduDetailsTable *table);
+void        gdu_details_table_set_num_columns (GduDetailsTable *table,
+                                               guint            num_columns);
+void        gdu_details_table_set_elements    (GduDetailsTable *table,
+                                               GPtrArray       *elements);
+
+G_END_DECLS
+
+#endif  /* __GDU_DETAILS_TABLE_H */
+
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index fca5641..cb6c740 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -40,18 +40,13 @@ typedef struct GduGraph                    GduGraph;
 typedef struct GduTimeLabel                GduTimeLabel;
 typedef struct GduAtaSmartDialog           GduAtaSmartDialog;
 typedef struct GduSpinner                  GduSpinner;
-
-struct GduPoolTreeModel;
-typedef struct GduPoolTreeModel         GduPoolTreeModel;
-
-struct GduPoolTreeView;
-typedef struct GduPoolTreeView          GduPoolTreeView;
-
-struct GduCreateLinuxMdDialog;
-typedef struct GduCreateLinuxMdDialog   GduCreateLinuxMdDialog;
-
-struct GduSizeWidget;
-typedef struct GduSizeWidget            GduSizeWidget;
+typedef struct GduPoolTreeModel            GduPoolTreeModel;
+typedef struct GduPoolTreeView             GduPoolTreeView;
+typedef struct GduCreateLinuxMdDialog      GduCreateLinuxMdDialog;
+typedef struct GduSizeWidget               GduSizeWidget;
+typedef struct GduVolumeGrid               GduVolumeGrid;
+typedef struct GduDetailsTable             GduDetailsTable;
+typedef struct GduDetailsElement           GduDetailsElement;
 
 G_END_DECLS
 
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index dccb785..89bce01 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -36,6 +36,9 @@
 #include <gdu-gtk/gdu-create-linux-md-dialog.h>
 #include <gdu-gtk/gdu-ata-smart-dialog.h>
 #include <gdu-gtk/gdu-spinner.h>
+#include <gdu-gtk/gdu-volume-grid.h>
+#include <gdu-gtk/gdu-details-table.h>
+#include <gdu-gtk/gdu-details-element.h>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
 G_BEGIN_DECLS
diff --git a/src/gdu-gtk/gdu-pool-tree-model.c b/src/gdu-gtk/gdu-pool-tree-model.c
index a53d0e4..cc554b8 100644
--- a/src/gdu-gtk/gdu-pool-tree-model.c
+++ b/src/gdu-gtk/gdu-pool-tree-model.c
@@ -30,6 +30,7 @@
 struct GduPoolTreeModelPrivate
 {
         GduPool *pool;
+        GduPresentable *root;
         GduPoolTreeModelFlags flags;
 };
 
@@ -38,6 +39,7 @@ G_DEFINE_TYPE (GduPoolTreeModel, gdu_pool_tree_model, GTK_TYPE_TREE_STORE)
 enum
 {
         PROP_0,
+        PROP_ROOT,
         PROP_POOL,
         PROP_FLAGS,
 };
@@ -72,6 +74,11 @@ gdu_pool_tree_model_set_property (GObject      *object,
                 model->priv->pool = g_value_dup_object (value);
                 break;
 
+        case PROP_ROOT:
+                if (g_value_get_object (value) != NULL)
+                        model->priv->root = g_value_dup_object (value);
+                break;
+
         case PROP_FLAGS:
                 model->priv->flags = g_value_get_flags (value);
                 break;
@@ -95,6 +102,10 @@ gdu_pool_tree_model_get_property (GObject     *object,
                 g_value_set_object (value, model->priv->pool);
                 break;
 
+        case PROP_ROOT:
+                g_value_set_object (value, model->priv->root);
+                break;
+
         case PROP_FLAGS:
                 g_value_set_flags (value, model->priv->flags);
                 break;
@@ -235,6 +246,22 @@ gdu_pool_tree_model_class_init (GduPoolTreeModelClass *klass)
                                                               G_PARAM_CONSTRUCT_ONLY));
 
         /**
+         * GduPoolTreeModel:root:
+         *
+         * %NULL to include all #GduPresentable objects in #GduPoolTreeModel:pool, otherwise only
+         * include presentables that are descendents of this #GduPresentable.
+         */
+        g_object_class_install_property (gobject_class,
+                                         PROP_ROOT,
+                                         g_param_spec_object ("root",
+                                                              NULL,
+                                                              NULL,
+                                                              GDU_TYPE_PRESENTABLE,
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+
+        /**
          * GduPoolTreeModel:flags:
          *
          * The flags for the model.
@@ -259,10 +286,12 @@ gdu_pool_tree_model_init (GduPoolTreeModel *model)
 
 GduPoolTreeModel *
 gdu_pool_tree_model_new (GduPool               *pool,
+                         GduPresentable        *root,
                          GduPoolTreeModelFlags  flags)
 {
         return GDU_POOL_TREE_MODEL (g_object_new (GDU_TYPE_POOL_TREE_MODEL,
                                                   "pool", pool,
+                                                  "root", root,
                                                   "flags", flags,
                                                   NULL));
 }
@@ -381,6 +410,34 @@ should_include_presentable (GduPoolTreeModel *model,
             (GDU_IS_VOLUME (presentable) || GDU_IS_VOLUME_HOLE (presentable)))
                 goto out;
 
+        if (model->priv->root != NULL) {
+                gboolean is_enclosed_by_root;
+                GduPresentable *p_iter;
+
+                is_enclosed_by_root = FALSE;
+                p_iter = g_object_ref (presentable);
+                do {
+                        GduPresentable *p;
+
+                        p = gdu_presentable_get_enclosing_presentable (p_iter);
+                        g_object_unref (p_iter);
+                        if (p == NULL)
+                                break;
+
+                        if (p == model->priv->root) {
+                                is_enclosed_by_root = TRUE;
+                                g_object_unref (p);
+                                break;
+                        }
+
+                        p_iter = p;
+
+                } while (TRUE);
+
+                if (!is_enclosed_by_root)
+                        goto out;
+        }
+
         if (GDU_IS_DRIVE (presentable)) {
                 if ((model->priv->flags & GDU_POOL_TREE_MODEL_FLAGS_NO_UNALLOCATABLE_DRIVES) &&
                     (!gdu_drive_has_unallocated_space (GDU_DRIVE (presentable), NULL, NULL, NULL)))
@@ -418,10 +475,14 @@ add_presentable (GduPoolTreeModel *model,
                 if (gdu_pool_tree_model_get_iter_for_presentable (model, enclosing_presentable, &iter2)) {
                         parent_iter = &iter2;
                 } else {
-                        /* add parent if it's not already added */
-                        g_warning ("No parent for %s", gdu_presentable_get_id (enclosing_presentable));
-                        add_presentable (model, enclosing_presentable, &iter2);
-                        parent_iter = &iter2;
+                        if (should_include_presentable (model, enclosing_presentable)) {
+                                /* add parent if it's not already added */
+                                g_warning ("No parent for %s", gdu_presentable_get_id (enclosing_presentable));
+                                add_presentable (model, enclosing_presentable, &iter2);
+                                parent_iter = &iter2;
+                        } else {
+                                /* parent explicitly excluded */
+                        }
                 }
                 g_object_unref (enclosing_presentable);
         }
diff --git a/src/gdu-gtk/gdu-pool-tree-model.h b/src/gdu-gtk/gdu-pool-tree-model.h
index 970fa5c..81a016c 100644
--- a/src/gdu-gtk/gdu-pool-tree-model.h
+++ b/src/gdu-gtk/gdu-pool-tree-model.h
@@ -55,6 +55,7 @@ struct GduPoolTreeModelClass
 
 GType             gdu_pool_tree_model_get_type                 (void) G_GNUC_CONST;
 GduPoolTreeModel *gdu_pool_tree_model_new                      (GduPool               *pool,
+                                                                GduPresentable        *root,
                                                                 GduPoolTreeModelFlags  flags);
 gboolean          gdu_pool_tree_model_get_iter_for_presentable (GduPoolTreeModel      *model,
                                                                 GduPresentable        *presentable,
diff --git a/src/gdu-gtk/gdu-size-widget.c b/src/gdu-gtk/gdu-size-widget.c
index 6ed770a..20bced3 100644
--- a/src/gdu-gtk/gdu-size-widget.c
+++ b/src/gdu-gtk/gdu-size-widget.c
@@ -127,7 +127,7 @@ on_hscale_format_value (GtkScale *scale,
 {
         gchar *ret;
 
-        ret = gdu_util_get_size_for_display ((guint64 ) value, FALSE);
+        ret = gdu_util_get_size_for_display ((guint64 ) value, FALSE, FALSE);
 
         return ret;
 }
@@ -175,7 +175,7 @@ on_query_tooltip (GtkWidget  *w,
         gchar *s;
         gchar *s1;
 
-        s1 = gdu_util_get_size_for_display (widget->priv->size, TRUE);
+        s1 = gdu_util_get_size_for_display (widget->priv->size, FALSE, TRUE);
         /* TODO: handle this use-case
         s = g_strdup_printf ("<b>%s</b>\n"
                              "\n"
diff --git a/src/gdu-gtk/gdu-volume-grid.c b/src/gdu-gtk/gdu-volume-grid.c
new file mode 100644
index 0000000..4a72223
--- /dev/null
+++ b/src/gdu-gtk/gdu-volume-grid.c
@@ -0,0 +1,1321 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-volume-grid.c
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <X11/XKBlib.h>
+
+#include <gdu-gtk/gdu-gtk.h>
+
+#include "gdu-volume-grid.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef enum
+{
+        GRID_EDGE_NONE    = 0,
+        GRID_EDGE_TOP    = (1<<0),
+        GRID_EDGE_BOTTOM = (1<<1),
+        GRID_EDGE_LEFT   = (1<<2),
+        GRID_EDGE_RIGHT  = (1<<3)
+} GridEdgeFlags;
+
+typedef struct GridElement GridElement;
+
+struct GridElement
+{
+        /* these values are set in recompute_grid() */
+        gdouble size_ratio;
+        GduPresentable *presentable; /* if NULL, it means no media is available */
+        GList *embedded_elements;
+        GridElement *parent;
+        GridElement *prev;
+        GridElement *next;
+
+        /* these values are set in recompute_size() */
+        gint x;
+        gint y;
+        gint width;
+        gint height;
+        GridEdgeFlags edge_flags;
+};
+
+static void
+grid_element_free (GridElement *element)
+{
+        if (element->presentable != NULL)
+                g_object_unref (element->presentable);
+
+        g_list_foreach (element->embedded_elements, (GFunc) grid_element_free, NULL);
+        g_list_free (element->embedded_elements);
+
+        g_free (element);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct GduVolumeGridPrivate
+{
+        GduPool *pool;
+        GduDrive *drive;
+        GduDevice *device;
+
+        GList *elements;
+
+        GridElement *selected;
+        GridElement *focused;
+};
+
+enum
+{
+        PROP_0,
+        PROP_DRIVE,
+};
+
+enum
+{
+        CHANGED_SIGNAL,
+        LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (GduVolumeGrid, gdu_volume_grid, GTK_TYPE_DRAWING_AREA)
+
+static void recompute_grid (GduVolumeGrid *grid);
+
+static void recompute_size (GduVolumeGrid *grid,
+                            gint           width,
+                            gint           height);
+
+static GridElement *find_element_for_presentable (GduVolumeGrid *grid,
+                                                  GduPresentable *presentable);
+
+static GridElement *find_element_for_position (GduVolumeGrid *grid,
+                                               gint x,
+                                               gint y);
+
+static gboolean gdu_volume_grid_expose_event (GtkWidget           *widget,
+                                              GdkEventExpose      *event);
+
+static void on_presentable_added        (GduPool        *pool,
+                                         GduPresentable *p,
+                                         gpointer        user_data);
+static void on_presentable_removed      (GduPool        *pool,
+                                         GduPresentable *p,
+                                         gpointer        user_data);
+static void on_presentable_changed      (GduPool        *pool,
+                                         GduPresentable *p,
+                                         gpointer        user_data);
+static void on_presentable_job_changed (GduPool        *pool,
+                                        GduPresentable *p,
+                                        gpointer        user_data);
+
+static void
+gdu_volume_grid_finalize (GObject *object)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (object);
+
+        g_list_foreach (grid->priv->elements, (GFunc) grid_element_free, NULL);
+        g_list_free (grid->priv->elements);
+
+        g_object_unref (grid->priv->drive);
+        if (grid->priv->device != NULL)
+                g_object_unref (grid->priv->device);
+
+        g_signal_handlers_disconnect_by_func (grid->priv->pool,
+                                              on_presentable_added,
+                                              grid);
+        g_signal_handlers_disconnect_by_func (grid->priv->pool,
+                                              on_presentable_removed,
+                                              grid);
+        g_signal_handlers_disconnect_by_func (grid->priv->pool,
+                                              on_presentable_changed,
+                                              grid);
+        g_signal_handlers_disconnect_by_func (grid->priv->pool,
+                                              on_presentable_job_changed,
+                                              grid);
+        g_object_unref (grid->priv->pool);
+
+        if (G_OBJECT_CLASS (gdu_volume_grid_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_volume_grid_parent_class)->finalize (object);
+}
+
+static void
+gdu_volume_grid_get_property (GObject    *object,
+                              guint       property_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (object);
+
+        switch (property_id) {
+        case PROP_DRIVE:
+                g_value_set_object (value, grid->priv->drive);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_volume_grid_set_property (GObject      *object,
+                              guint         property_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (object);
+
+        switch (property_id) {
+        case PROP_DRIVE:
+                grid->priv->drive = GDU_DRIVE (g_value_dup_object (value));
+                grid->priv->pool = gdu_presentable_get_pool (GDU_PRESENTABLE (grid->priv->drive));
+                grid->priv->device = gdu_presentable_get_device (GDU_PRESENTABLE (grid->priv->drive));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_volume_grid_constructed (GObject *object)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (object);
+
+        gtk_widget_set_size_request (GTK_WIDGET (grid),
+                                     -1,
+                                     100);
+
+        g_signal_connect (grid->priv->pool,
+                          "presentable-added",
+                          G_CALLBACK (on_presentable_added),
+                          grid);
+        g_signal_connect (grid->priv->pool,
+                          "presentable-removed",
+                          G_CALLBACK (on_presentable_removed),
+                          grid);
+        g_signal_connect (grid->priv->pool,
+                          "presentable-changed",
+                          G_CALLBACK (on_presentable_changed),
+                          grid);
+        g_signal_connect (grid->priv->pool,
+                          "presentable-job-changed",
+                          G_CALLBACK (on_presentable_job_changed),
+                          grid);
+
+        recompute_grid (grid);
+
+        /* select the first element */
+        if (grid->priv->elements != NULL) {
+                GridElement *element = grid->priv->elements->data;
+                grid->priv->selected = element;
+                grid->priv->focused = element;
+        }
+
+        if (G_OBJECT_CLASS (gdu_volume_grid_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_volume_grid_parent_class)->constructed (object);
+}
+
+static gboolean
+is_ctrl_pressed (void)
+{
+        gboolean ret;
+        XkbStateRec state;
+        Bool status;
+
+        ret = FALSE;
+
+        gdk_error_trap_push ();
+        status = XkbGetState (GDK_DISPLAY (), XkbUseCoreKbd, &state);
+        gdk_error_trap_pop ();
+
+        if (status == Success) {
+                ret = ((state.mods & ControlMask) != 0);
+        }
+
+        return ret;
+}
+
+static gboolean
+gdu_volume_grid_key_press_event (GtkWidget      *widget,
+                                 GdkEventKey    *event)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
+        gboolean handled;
+        GridElement *target;
+
+        handled = FALSE;
+
+        if (event->type != GDK_KEY_PRESS)
+                goto out;
+
+        switch (event->keyval) {
+        case GDK_Left:
+        case GDK_Right:
+        case GDK_Up:
+        case GDK_Down:
+                target = NULL;
+
+                if (grid->priv->focused == NULL) {
+                        g_warning ("TODO: handle nothing being selected/focused");
+                } else {
+                        GridElement *element;
+
+                        element = grid->priv->focused;
+                        if (element != NULL) {
+                                if (event->keyval == GDK_Left) {
+                                        if (element->prev != NULL) {
+                                                target = element->prev;
+                                        } else {
+                                                if (element->parent && element->parent->prev != NULL)
+                                                        target = element->parent->prev;
+                                        }
+                                } else if (event->keyval == GDK_Right) {
+                                        if (element->next != NULL) {
+                                                target = element->next;
+                                        } else {
+                                                if (element->parent && element->parent->next != NULL)
+                                                        target = element->parent->next;
+                                        }
+                                } else if (event->keyval == GDK_Up) {
+                                        if (element->parent != NULL) {
+                                                target = element->parent;
+                                        }
+                                } else if (event->keyval == GDK_Down) {
+                                        if (element->embedded_elements != NULL) {
+                                                target = (GridElement *) element->embedded_elements->data;
+                                        }
+                                }
+                        }
+                }
+
+                if (target != NULL) {
+                        if (is_ctrl_pressed ()) {
+                                grid->priv->focused = target;
+                        } else {
+                                grid->priv->selected = target;
+                                grid->priv->focused = target;
+                                g_signal_emit (grid,
+                                               signals[CHANGED_SIGNAL],
+                                               0);
+                        }
+                        gtk_widget_queue_draw (GTK_WIDGET (grid));
+                }
+                handled = TRUE;
+                break;
+
+        case GDK_Return:
+        case GDK_space:
+                if (grid->priv->focused != grid->priv->selected &&
+                    grid->priv->focused != NULL) {
+                        grid->priv->selected = grid->priv->focused;
+                        g_signal_emit (grid,
+                                       signals[CHANGED_SIGNAL],
+                                       0);
+                        gtk_widget_queue_draw (GTK_WIDGET (grid));
+                }
+
+                handled = TRUE;
+                break;
+
+        default:
+                break;
+        }
+
+ out:
+        return handled;
+}
+
+static gboolean
+gdu_volume_grid_button_press_event (GtkWidget      *widget,
+                                    GdkEventButton *event)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
+        gboolean handled;
+
+        handled = FALSE;
+
+        if (event->type != GDK_BUTTON_PRESS)
+                goto out;
+
+        if (event->button == 1) {
+                GridElement *element;
+
+                element = find_element_for_position (grid, event->x, event->y);
+                if (element != NULL) {
+                        grid->priv->selected = element;
+                        grid->priv->focused = element;
+
+                        g_signal_emit (grid,
+                                       signals[CHANGED_SIGNAL],
+                                       0);
+
+                        gtk_widget_grab_focus (GTK_WIDGET (grid));
+                        gtk_widget_queue_draw (GTK_WIDGET (grid));
+                }
+
+                handled = TRUE;
+        }
+
+ out:
+        return handled;
+}
+
+static void
+gdu_volume_grid_realize (GtkWidget *widget)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
+        GdkWindowAttr attributes;
+        gint attributes_mask;
+
+        GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+        attributes.x = widget->allocation.x;
+        attributes.y = widget->allocation.y;
+        attributes.width = widget->allocation.width;
+        attributes.height = widget->allocation.height;
+        attributes.wclass = GDK_INPUT_OUTPUT;
+        attributes.window_type = GDK_WINDOW_CHILD;
+        attributes.event_mask = gtk_widget_get_events (widget) |
+                GDK_KEY_PRESS_MASK |
+                GDK_EXPOSURE_MASK |
+                GDK_BUTTON_PRESS_MASK |
+                GDK_BUTTON_RELEASE_MASK |
+                GDK_ENTER_NOTIFY_MASK |
+                GDK_LEAVE_NOTIFY_MASK;
+        attributes.visual = gtk_widget_get_visual (widget);
+        attributes.colormap = gtk_widget_get_colormap (widget);
+
+        attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+        widget->window = gtk_widget_get_parent_window (widget);
+        g_object_ref (widget->window);
+
+        widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                         &attributes,
+                                         attributes_mask);
+        gdk_window_set_user_data (widget->window, grid);
+
+        widget->style = gtk_style_attach (widget->style, widget->window);
+
+        gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+
+static void
+gdu_volume_grid_class_init (GduVolumeGridClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduVolumeGridPrivate));
+
+        object_class->get_property = gdu_volume_grid_get_property;
+        object_class->set_property = gdu_volume_grid_set_property;
+        object_class->constructed  = gdu_volume_grid_constructed;
+        object_class->finalize     = gdu_volume_grid_finalize;
+
+        widget_class->realize            = gdu_volume_grid_realize;
+        widget_class->key_press_event    = gdu_volume_grid_key_press_event;
+        widget_class->button_press_event = gdu_volume_grid_button_press_event;
+        widget_class->expose_event       = gdu_volume_grid_expose_event;
+
+        g_object_class_install_property (object_class,
+                                         PROP_DRIVE,
+                                         g_param_spec_object ("drive",
+                                                              _("Drive"),
+                                                              _("Drive to show volumes for"),
+                                                              GDU_TYPE_DRIVE,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+
+        signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                                GDU_TYPE_VOLUME_GRID,
+                                                G_SIGNAL_RUN_LAST,
+                                                G_STRUCT_OFFSET (GduVolumeGridClass, changed),
+                                                NULL,
+                                                NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE,
+                                                0);
+}
+
+static void
+gdu_volume_grid_init (GduVolumeGrid *grid)
+{
+        grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GDU_TYPE_VOLUME_GRID, GduVolumeGridPrivate);
+
+        GTK_WIDGET_SET_FLAGS (grid, GTK_CAN_FOCUS);
+}
+
+GtkWidget *
+gdu_volume_grid_new (GduDrive *drive)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_VOLUME_GRID,
+                                         "drive", drive,
+                                         NULL));
+}
+
+static gint
+presentable_sort_offset (GduPresentable *a, GduPresentable *b)
+{
+        guint64 oa, ob;
+
+        oa = gdu_presentable_get_offset (a);
+        ob = gdu_presentable_get_offset (b);
+
+        if (oa < ob)
+                return -1;
+        else if (oa > ob)
+                return 1;
+        else
+                return 0;
+}
+
+static void
+recompute_size (GduVolumeGrid *grid,
+                gint           width,
+                gint           height)
+{
+        GList *l;
+        gint x;
+        gint pixels_left;
+
+        x = 0;
+        pixels_left = width;
+        for (l = grid->priv->elements; l != NULL; l = l->next) {
+                GridElement *element = l->data;
+                gint element_width;
+                gboolean is_first;
+                gboolean is_last;
+
+                is_first = (l == grid->priv->elements);
+                is_last  = (l->next == NULL);
+
+                if (is_last) {
+                        element_width = pixels_left;
+                        pixels_left = 0;
+                } else {
+                        element_width = element->size_ratio * width;
+                        if (element_width > pixels_left)
+                                element_width = pixels_left;
+                        pixels_left -= element_width;
+                }
+
+                element->x = x;
+                element->y = 0;
+                element->width = element_width;
+                element->height = height;
+                element->edge_flags = GRID_EDGE_TOP | GRID_EDGE_BOTTOM;
+                if (is_first)
+                        element->edge_flags |= GRID_EDGE_LEFT;
+                if (is_last)
+                        element->edge_flags |= GRID_EDGE_RIGHT;
+
+                x += element_width;
+
+                /* for now we don't recurse - we only handle embedded element for toplevel elements */
+                if (element->embedded_elements != NULL) {
+                        gint e_x;
+                        gint e_width;
+                        gint e_pixels_left;
+                        GList *ll;
+
+                        element->height = height/3;
+                        element->edge_flags &= ~(GRID_EDGE_BOTTOM);
+
+                        e_x = element->x;
+                        e_width = element->width;
+                        e_pixels_left = e_width;
+                        for (ll = element->embedded_elements; ll != NULL; ll = ll->next) {
+                                GridElement *e_element = ll->data;
+                                gint e_element_width;
+                                gboolean e_is_first;
+                                gboolean e_is_last;
+
+                                e_is_first = (ll == element->embedded_elements);
+                                e_is_last  = (ll->next == NULL);
+
+                                if (e_is_last) {
+                                        e_element_width = e_pixels_left;
+                                        e_pixels_left = 0;
+                                } else {
+                                        e_element_width = e_element->size_ratio * e_width;
+                                        if (e_element_width > e_pixels_left)
+                                                element_width = e_pixels_left;
+                                        e_pixels_left -= e_element_width;
+                                }
+
+                                e_element->x = e_x;
+                                e_element->y = element->height;
+                                e_element->width = e_element_width;
+                                e_element->height = height - e_element->y;
+                                e_element->edge_flags = GRID_EDGE_BOTTOM;
+                                if (is_first && e_is_first)
+                                        e_element->edge_flags |= GRID_EDGE_LEFT;
+                                if (is_last && e_is_last)
+                                        e_element->edge_flags |= GRID_EDGE_RIGHT;
+
+                                e_x += e_element_width;
+
+                        }
+                }
+        }
+}
+
+static void
+recompute_grid (GduVolumeGrid *grid)
+{
+        GduPresentable *cur_selected_presentable;
+        GduPresentable *cur_focused_presentable;
+        GList *enclosed_partitions;
+        GList *l;
+        guint64 size;
+        GridElement *element;
+        GridElement *prev_element;
+
+        cur_selected_presentable = NULL;
+        cur_focused_presentable = NULL;
+
+        if (grid->priv->selected != NULL && grid->priv->selected->presentable != NULL)
+                cur_selected_presentable = g_object_ref (grid->priv->selected->presentable);
+
+        if (grid->priv->focused != NULL && grid->priv->focused->presentable != NULL)
+                cur_focused_presentable = g_object_ref (grid->priv->focused->presentable);
+
+        /* first delete old elements */
+        g_list_foreach (grid->priv->elements, (GFunc) grid_element_free, NULL);
+        g_list_free (grid->priv->elements);
+        grid->priv->elements = NULL;
+
+        /* then add new elements */
+        size = gdu_presentable_get_size (GDU_PRESENTABLE (grid->priv->drive));
+        enclosed_partitions = gdu_pool_get_enclosed_presentables (grid->priv->pool,
+                                                                  GDU_PRESENTABLE (grid->priv->drive));
+        enclosed_partitions = g_list_sort (enclosed_partitions, (GCompareFunc) presentable_sort_offset);
+        prev_element = NULL;
+        for (l = enclosed_partitions; l != NULL; l = l->next) {
+                GduPresentable *ep = GDU_PRESENTABLE (l->data);
+                GduDevice *ed;
+                guint64 psize;
+
+                ed = gdu_presentable_get_device (ep);
+
+                psize = gdu_presentable_get_size (ep);
+
+                element = g_new0 (GridElement, 1);
+                element->size_ratio = ((gdouble) psize) / size;
+                element->presentable = g_object_ref (ep);
+                element->prev = prev_element;
+                if (prev_element != NULL)
+                        prev_element->next = element;
+                prev_element = element;
+
+                grid->priv->elements = g_list_append (grid->priv->elements, element);
+
+                if (ed != NULL && gdu_device_is_partition (ed) &&
+                    (g_strcmp0 (gdu_device_partition_get_type (ed), "0x05") == 0 ||
+                     g_strcmp0 (gdu_device_partition_get_type (ed), "0x0f") == 0 ||
+                     g_strcmp0 (gdu_device_partition_get_type (ed), "0x85") == 0)) {
+                        GList *enclosed_logical_partitions;
+                        GList *ll;
+                        GridElement *logical_element;
+                        GridElement *logical_prev_element;
+
+                        enclosed_logical_partitions = gdu_pool_get_enclosed_presentables (grid->priv->pool, ep);
+                        logical_prev_element = NULL;
+                        for (ll = enclosed_logical_partitions; ll != NULL; ll = ll->next) {
+                                GduPresentable *logical_ep = GDU_PRESENTABLE (ll->data);
+                                guint64 lsize;
+
+                                lsize = gdu_presentable_get_size (logical_ep);
+
+                                logical_element = g_new0 (GridElement, 1);
+                                logical_element->size_ratio = ((gdouble) lsize) / psize;
+                                logical_element->presentable = g_object_ref (logical_ep);
+                                logical_element->parent = element;
+                                logical_element->prev = logical_prev_element;
+                                if (logical_prev_element != NULL)
+                                        logical_prev_element->next = logical_element;
+                                logical_prev_element = logical_element;
+
+                                element->embedded_elements = g_list_append (element->embedded_elements,
+                                                                            logical_element);
+                        }
+                        g_list_foreach (enclosed_logical_partitions, (GFunc) g_object_unref, NULL);
+                        g_list_free (enclosed_logical_partitions);
+                        }
+                if (ed != NULL)
+                        g_object_unref (ed);
+
+        }
+        g_list_foreach (enclosed_partitions, (GFunc) g_object_unref, NULL);
+        g_list_free (enclosed_partitions);
+
+        /* If we have no elements, then make up an element with a NULL presentable - this is to handle
+         * the "No Media Inserted" case.
+         */
+        if (grid->priv->elements == NULL) {
+                element = g_new0 (GridElement, 1);
+                element->size_ratio = 1.0;
+                grid->priv->elements = g_list_append (grid->priv->elements, element);
+        }
+
+        /* reselect focused and selected elements */
+        grid->priv->focused = NULL;
+        if (cur_focused_presentable != NULL) {
+                grid->priv->focused = find_element_for_presentable (grid, cur_focused_presentable);
+                g_object_unref (cur_focused_presentable);
+        }
+        grid->priv->selected = NULL;
+        if (cur_selected_presentable != NULL) {
+                grid->priv->selected = find_element_for_presentable (grid, cur_selected_presentable);
+                g_object_unref (cur_selected_presentable);
+        }
+
+        /* ensure something is always focused/selected */
+        if ( grid->priv->focused == NULL) {
+                grid->priv->focused = grid->priv->elements->data;
+        }
+        if (grid->priv->selected == NULL) {
+                grid->priv->selected = grid->priv->focused;
+        }
+
+        /* queue a redraw */
+        gtk_widget_queue_draw (GTK_WIDGET (grid));
+}
+
+static void
+round_rect (cairo_t *cr,
+            gdouble x, gdouble y,
+            gdouble w, gdouble h,
+            gdouble r,
+            GridEdgeFlags edge_flags)
+{
+        gboolean top_left_round;
+        gboolean top_right_round;
+        gboolean bottom_right_round;
+        gboolean bottom_left_round;
+
+        top_left_round     = ((edge_flags & GRID_EDGE_TOP)    && (edge_flags & GRID_EDGE_LEFT));
+        top_right_round    = ((edge_flags & GRID_EDGE_TOP)    && (edge_flags & GRID_EDGE_RIGHT));
+        bottom_right_round = ((edge_flags & GRID_EDGE_BOTTOM) && (edge_flags & GRID_EDGE_RIGHT));
+        bottom_left_round  = ((edge_flags & GRID_EDGE_BOTTOM) && (edge_flags & GRID_EDGE_LEFT));
+
+        if (top_left_round) {
+                cairo_move_to  (cr,
+                                x + r, y);
+        } else {
+                cairo_move_to  (cr,
+                                x, y);
+        }
+
+        if (top_right_round) {
+                cairo_line_to  (cr,
+                                x + w - r, y);
+                cairo_curve_to (cr,
+                                x + w, y,
+                                x + w, y,
+                                x + w, y + r);
+        } else {
+                cairo_line_to  (cr,
+                                x + w, y);
+        }
+
+        if (bottom_right_round) {
+                cairo_line_to  (cr,
+                                x + w, y + h - r);
+                cairo_curve_to (cr,
+                                x + w, y + h,
+                                x + w, y + h,
+                                x + w - r, y + h);
+        } else {
+                cairo_line_to  (cr,
+                                x + w, y + h);
+        }
+
+        if (bottom_left_round) {
+                cairo_line_to  (cr,
+                                x + r, y + h);
+                cairo_curve_to (cr,
+                                x, y + h,
+                                x, y + h,
+                                x, y + h - r);
+        } else {
+                cairo_line_to  (cr,
+                                x, y + h);
+        }
+
+        if (top_left_round) {
+                cairo_line_to  (cr,
+                                x, y + r);
+                cairo_curve_to (cr,
+                                x, y,
+                                x, y,
+                                x + r, y);
+        } else {
+                cairo_line_to  (cr,
+                                x, y);
+        }
+}
+
+static void
+render_element (GduVolumeGrid *grid,
+                cairo_t       *cr,
+                GridElement   *element,
+                gboolean       is_selected,
+                gboolean       is_focused,
+                gboolean       is_grid_focused)
+{
+        gdouble fill_red;
+        gdouble fill_green;
+        gdouble fill_blue;
+        gdouble fill_selected_red;
+        gdouble fill_selected_green;
+        gdouble fill_selected_blue;
+        gdouble fill_selected_not_focused_red;
+        gdouble fill_selected_not_focused_green;
+        gdouble fill_selected_not_focused_blue;
+        gdouble focus_rect_red;
+        gdouble focus_rect_green;
+        gdouble focus_rect_blue;
+        gdouble focus_rect_selected_red;
+        gdouble focus_rect_selected_green;
+        gdouble focus_rect_selected_blue;
+        gdouble focus_rect_selected_not_focused_red;
+        gdouble focus_rect_selected_not_focused_green;
+        gdouble focus_rect_selected_not_focused_blue;
+        gdouble stroke_red;
+        gdouble stroke_green;
+        gdouble stroke_blue;
+        gdouble stroke_selected_red;
+        gdouble stroke_selected_green;
+        gdouble stroke_selected_blue;
+        gdouble stroke_selected_not_focused_red;
+        gdouble stroke_selected_not_focused_green;
+        gdouble stroke_selected_not_focused_blue;
+        gdouble text_red;
+        gdouble text_green;
+        gdouble text_blue;
+        gdouble text_selected_red;
+        gdouble text_selected_green;
+        gdouble text_selected_blue;
+        gdouble text_selected_not_focused_red;
+        gdouble text_selected_not_focused_green;
+        gdouble text_selected_not_focused_blue;
+
+        fill_red     = 1;
+        fill_green   = 1;
+        fill_blue    = 1;
+        fill_selected_red     = 0.40;
+        fill_selected_green   = 0.60;
+        fill_selected_blue    = 0.80;
+        fill_selected_not_focused_red     = 0.60;
+        fill_selected_not_focused_green   = 0.60;
+        fill_selected_not_focused_blue    = 0.60;
+        focus_rect_red     = 0.75;
+        focus_rect_green   = 0.75;
+        focus_rect_blue    = 0.75;
+        focus_rect_selected_red     = 0.70;
+        focus_rect_selected_green   = 0.70;
+        focus_rect_selected_blue    = 0.80;
+        focus_rect_selected_not_focused_red     = 0.70;
+        focus_rect_selected_not_focused_green   = 0.70;
+        focus_rect_selected_not_focused_blue    = 0.70;
+        stroke_red   = 0.75;
+        stroke_green = 0.75;
+        stroke_blue  = 0.75;
+        stroke_selected_red   = 0.3;
+        stroke_selected_green = 0.45;
+        stroke_selected_blue  = 0.6;
+        stroke_selected_not_focused_red   = 0.45;
+        stroke_selected_not_focused_green = 0.45;
+        stroke_selected_not_focused_blue  = 0.45;
+        text_red     = 0;
+        text_green   = 0;
+        text_blue    = 0;
+        text_selected_red     = 1;
+        text_selected_green   = 1;
+        text_selected_blue    = 1;
+        text_selected_not_focused_red     = 1;
+        text_selected_not_focused_green   = 1;
+        text_selected_not_focused_blue    = 1;
+
+#if 0
+        g_debug ("rendering element: x=%d w=%d",
+                 element->x,
+                 element->width);
+#endif
+
+        cairo_save (cr);
+        cairo_rectangle (cr,
+                         element->x + 0.5,
+                         element->y + 0.5,
+                         element->width,
+                         element->height);
+        cairo_clip (cr);
+
+        round_rect (cr,
+                    element->x + 0.5,
+                    element->y + 0.5,
+                    element->width,
+                    element->height,
+                    10,
+                    element->edge_flags);
+
+        if (is_selected) {
+                cairo_pattern_t *gradient;
+                gradient = cairo_pattern_create_radial (element->x + element->width / 2,
+                                                        element->y + element->height / 2,
+                                                        0.0,
+                                                        element->x + element->width / 2,
+                                                        element->y + element->height / 2,
+                                                        element->width/2.0);
+                if (is_grid_focused) {
+                        cairo_pattern_add_color_stop_rgb (gradient,
+                                                          0.0,
+                                                          1.0 * fill_selected_red,
+                                                          1.0 * fill_selected_green,
+                                                          1.0 * fill_selected_blue);
+                        cairo_pattern_add_color_stop_rgb (gradient,
+                                                          1.0,
+                                                          0.8 * fill_selected_red,
+                                                          0.8 * fill_selected_green,
+                                                          0.8 * fill_selected_blue);
+                } else {
+                        cairo_pattern_add_color_stop_rgb (gradient,
+                                                          0.0,
+                                                          1.0 * fill_selected_not_focused_red,
+                                                          1.0 * fill_selected_not_focused_green,
+                                                          1.0 * fill_selected_not_focused_blue);
+                        cairo_pattern_add_color_stop_rgb (gradient,
+                                                          1.0,
+                                                          0.8 * fill_selected_not_focused_red,
+                                                          0.8 * fill_selected_not_focused_green,
+                                                          0.8 * fill_selected_not_focused_blue);
+                }
+                cairo_set_source (cr, gradient);
+                cairo_pattern_destroy (gradient);
+        } else {
+                cairo_set_source_rgb (cr,
+                                      fill_red,
+                                      fill_green,
+                                      fill_blue);
+        }
+        cairo_fill_preserve (cr);
+        if (is_selected) {
+                if (is_grid_focused) {
+                        cairo_set_source_rgb (cr,
+                                              stroke_selected_red,
+                                              stroke_selected_green,
+                                              stroke_selected_blue);
+                } else {
+                        cairo_set_source_rgb (cr,
+                                              stroke_selected_not_focused_red,
+                                              stroke_selected_not_focused_green,
+                                              stroke_selected_not_focused_blue);
+                }
+        } else {
+                cairo_set_source_rgb (cr,
+                                      stroke_red,
+                                      stroke_green,
+                                      stroke_blue);
+        }
+        cairo_set_dash (cr, NULL, 0, 0.0);
+        cairo_set_line_width (cr, 1.0);
+        cairo_stroke (cr);
+
+        /* focus indicator */
+        if (is_focused && is_grid_focused) {
+                gdouble dashes[] = {2.0};
+                round_rect (cr,
+                            element->x + 0.5 + 3,
+                            element->y + 0.5 + 3,
+                            element->width - 3 * 2,
+                            element->height - 3 * 2,
+                            20,
+                            element->edge_flags);
+                cairo_set_source_rgb (cr, focus_rect_red, focus_rect_green, focus_rect_blue);
+                cairo_set_dash (cr, dashes, 1, 0.0);
+                cairo_set_line_width (cr, 1.0);
+                cairo_stroke (cr);
+        }
+
+        if (element->presentable == NULL) { /* no media available */
+                cairo_text_extents_t te;
+                const gchar *text;
+
+                text = _("No Media Detected");
+
+                if (is_selected) {
+                        if (is_grid_focused) {
+                                cairo_set_source_rgb (cr,
+                                                      text_selected_red,
+                                                      text_selected_green,
+                                                      text_selected_blue);
+                        } else {
+                                cairo_set_source_rgb (cr,
+                                                      text_selected_not_focused_red,
+                                                      text_selected_not_focused_green,
+                                                      text_selected_not_focused_blue);
+                        }
+                } else {
+                        cairo_set_source_rgb (cr, text_red, text_green, text_blue);
+                }
+                cairo_select_font_face (cr, "sans",
+                                        CAIRO_FONT_SLANT_NORMAL,
+                                        CAIRO_FONT_WEIGHT_NORMAL);
+                cairo_set_font_size (cr, 8.0);
+
+                cairo_text_extents (cr, text, &te);
+                cairo_move_to (cr,
+                               ceil (element->x + element->width / 2 - te.width/2  - te.x_bearing),
+                               ceil (element->y + element->height / 2 - 2 - te.height/2 - te.y_bearing));
+                cairo_show_text (cr, text);
+
+        } else { /* render descriptive text for the presentable */
+                gchar *s;
+                gchar *s1;
+                cairo_text_extents_t te;
+                cairo_text_extents_t te1;
+                GduDevice *d;
+                gdouble text_height;
+
+                d = gdu_presentable_get_device (element->presentable);
+
+                s = NULL;
+                s1 = NULL;
+                if (d != NULL && g_strcmp0 (gdu_device_id_get_usage (d), "filesystem") == 0) {
+                        gchar *fstype_str;
+                        gchar *size_str;
+
+                        s = g_strdup (gdu_device_id_get_label (d));
+                        fstype_str = gdu_util_get_fstype_for_display (gdu_device_id_get_type (d),
+                                                                      gdu_device_id_get_version (d),
+                                                                      FALSE);
+                        size_str = gdu_util_get_size_for_display (gdu_device_get_size (d),
+                                                                  FALSE,
+                                                                  FALSE);
+                        s1 = g_strdup_printf ("%s %s", size_str, fstype_str);
+                        g_free (fstype_str);
+                        g_free (size_str);
+                } else if (d != NULL && gdu_device_is_partition (d) &&
+                           (g_strcmp0 (gdu_device_partition_get_type (d), "0x05") == 0 ||
+                            g_strcmp0 (gdu_device_partition_get_type (d), "0x0f") == 0 ||
+                            g_strcmp0 (gdu_device_partition_get_type (d), "0x85") == 0)) {
+                        s = g_strdup (_("Extended"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
+                                                            FALSE,
+                                                            FALSE);
+                } else if (d != NULL && g_strcmp0 (gdu_device_id_get_usage (d), "crypto") == 0) {
+                        s = g_strdup (_("Encrypted"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
+                                                            FALSE,
+                                                            FALSE);
+                } else if (!gdu_presentable_is_allocated (element->presentable)) {
+                        s = g_strdup (_("Free"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
+                                                            FALSE,
+                                                            FALSE);
+                } else if (!gdu_presentable_is_recognized (element->presentable)) {
+                        s = g_strdup (_("Unknown"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
+                                                            FALSE,
+                                                            FALSE);
+                }
+
+                if (s == NULL)
+                        s = gdu_presentable_get_name (element->presentable);
+                if (s1 == NULL)
+                        s1 = g_strdup ("");
+
+                if (is_selected) {
+                        if (is_grid_focused) {
+                                cairo_set_source_rgb (cr,
+                                                      text_selected_red,
+                                                      text_selected_green,
+                                                      text_selected_blue);
+                        } else {
+                                cairo_set_source_rgb (cr,
+                                                      text_selected_not_focused_red,
+                                                      text_selected_not_focused_green,
+                                                      text_selected_not_focused_blue);
+                        }
+                } else {
+                        cairo_set_source_rgb (cr, text_red, text_green, text_blue);
+                }
+                cairo_select_font_face (cr, "sans",
+                                        CAIRO_FONT_SLANT_NORMAL,
+                                        CAIRO_FONT_WEIGHT_NORMAL);
+                cairo_set_font_size (cr, 8.0);
+
+                cairo_text_extents (cr, s, &te);
+                cairo_text_extents (cr, s1, &te1);
+
+                text_height = te.height + te1.height;
+
+                cairo_move_to (cr,
+                               ceil (element->x + element->width / 2 - te.width/2  - te.x_bearing),
+                               ceil (element->y + element->height / 2 - 2 - text_height/2 - te.y_bearing));
+                cairo_show_text (cr, s);
+                cairo_move_to (cr,
+                               ceil (element->x + element->width / 2 - te1.width/2  - te1.x_bearing),
+                               ceil (element->y + element->height / 2 + 2 - te1.y_bearing));
+                cairo_show_text (cr, s1);
+                g_free (s);
+                g_free (s1);
+
+                if (d != NULL)
+                        g_object_unref (d);
+        }
+
+        cairo_restore (cr);
+}
+
+static gboolean
+gdu_volume_grid_expose_event (GtkWidget           *widget,
+                              GdkEventExpose      *event)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
+        GList *l;
+        cairo_t *cr;
+        gdouble width;
+        gdouble height;
+
+        width = widget->allocation.width;
+        height = widget->allocation.height;
+
+        recompute_size (grid,
+                        width - 1,
+                        height -1);
+
+        cr = gdk_cairo_create (widget->window);
+        cairo_rectangle (cr,
+                         event->area.x, event->area.y,
+                         event->area.width, event->area.height);
+        cairo_clip (cr);
+
+        for (l = grid->priv->elements; l != NULL; l = l->next) {
+                GridElement *element = l->data;
+                gboolean is_selected;
+                gboolean is_focused;
+                gboolean is_grid_focused;
+                GList *ll;
+
+                is_selected = FALSE;
+                is_focused = FALSE;
+                is_grid_focused = GTK_WIDGET_HAS_FOCUS (grid);
+
+                if (element == grid->priv->selected)
+                        is_selected = TRUE;
+
+                if (element == grid->priv->focused) {
+                        if (grid->priv->focused != grid->priv->selected && is_grid_focused)
+                                is_focused = TRUE;
+                }
+
+                render_element (grid,
+                                cr,
+                                element,
+                                is_selected,
+                                is_focused,
+                                is_grid_focused);
+
+                for (ll = element->embedded_elements; ll != NULL; ll = ll->next) {
+                        GridElement *element = ll->data;
+
+                        is_selected = FALSE;
+                        is_focused = FALSE;
+                        is_grid_focused = GTK_WIDGET_HAS_FOCUS (grid);
+
+                        if (element == grid->priv->selected)
+                                is_selected = TRUE;
+
+                        if (element == grid->priv->focused) {
+                                if (grid->priv->focused != grid->priv->selected && is_grid_focused)
+                                        is_focused = TRUE;
+                        }
+
+                        render_element (grid,
+                                        cr,
+                                        element,
+                                        is_selected,
+                                        is_focused,
+                                        is_grid_focused);
+                }
+        }
+
+        cairo_destroy (cr);
+
+        return FALSE;
+}
+
+static GridElement *
+do_find_element_for_presentable (GList *elements,
+                                 GduPresentable *presentable)
+{
+        GList *l;
+        GridElement *ret;
+
+        ret = NULL;
+
+        for (l = elements; l != NULL; l = l->next) {
+                GridElement *e = l->data;
+                if (e->presentable == presentable) {
+                        ret = e;
+                        goto out;
+                }
+
+                ret = do_find_element_for_presentable (e->embedded_elements, presentable);
+                if (ret != NULL)
+                        goto out;
+
+        }
+
+ out:
+        return ret;
+}
+
+static GridElement *
+find_element_for_presentable (GduVolumeGrid *grid,
+                              GduPresentable *presentable)
+{
+        return do_find_element_for_presentable (grid->priv->elements, presentable);
+}
+
+static GridElement *
+do_find_element_for_position (GList *elements,
+                              gint   x,
+                              gint   y)
+{
+        GList *l;
+        GridElement *ret;
+
+        ret = NULL;
+
+        for (l = elements; l != NULL; l = l->next) {
+                GridElement *e = l->data;
+
+                if ((x >= e->x) &&
+                    (x  < e->x + e->width) &&
+                    (y >= e->y) &&
+                    (y  < e->y + e->height)) {
+                        ret = e;
+                        goto out;
+                }
+
+                ret = do_find_element_for_position (e->embedded_elements, x, y);
+                if (ret != NULL)
+                        goto out;
+
+        }
+
+ out:
+        return ret;
+}
+
+static GridElement *
+find_element_for_position (GduVolumeGrid *grid,
+                           gint x,
+                           gint y)
+{
+        return do_find_element_for_position (grid->priv->elements, x, y);
+}
+
+GduPresentable *
+gdu_volume_grid_get_selected (GduVolumeGrid *grid)
+{
+        GduPresentable *ret;
+
+        g_return_val_if_fail (GDU_IS_VOLUME_GRID (grid), NULL);
+
+                ret = NULL;
+        if (grid->priv->selected != NULL) {
+                if (grid->priv->selected->presentable != NULL)
+                        ret = g_object_ref (grid->priv->selected->presentable);
+        }
+
+        return ret;
+}
+
+static void
+maybe_recompute (GduVolumeGrid  *grid,
+                 GduPresentable *p)
+{
+        gboolean recompute;
+
+        recompute = FALSE;
+        if (p == GDU_PRESENTABLE (grid->priv->drive) ||
+            gdu_presentable_encloses (GDU_PRESENTABLE (grid->priv->drive), p) ||
+            find_element_for_presentable (grid, p) != NULL) {
+                recompute = TRUE;
+        }
+
+        if (recompute) {
+                recompute_grid (grid);
+                g_signal_emit (grid,
+                               signals[CHANGED_SIGNAL],
+                               0);
+        }
+}
+
+static void
+on_presentable_added (GduPool        *pool,
+                      GduPresentable *p,
+                      gpointer        user_data)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (user_data);
+        maybe_recompute (grid, p);
+}
+
+static void
+on_presentable_removed (GduPool        *pool,
+                        GduPresentable *p,
+                        gpointer        user_data)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (user_data);
+        maybe_recompute (grid, p);
+}
+
+static void
+on_presentable_changed (GduPool        *pool,
+                        GduPresentable *p,
+                        gpointer        user_data)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (user_data);
+        maybe_recompute (grid, p);
+}
+
+static void
+on_presentable_job_changed (GduPool        *pool,
+                            GduPresentable *p,
+                            gpointer        user_data)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (user_data);
+        maybe_recompute (grid, p);
+}
diff --git a/src/gdu-gtk/gdu-volume-grid.h b/src/gdu-gtk/gdu-volume-grid.h
new file mode 100644
index 0000000..a3f6777
--- /dev/null
+++ b/src/gdu-gtk/gdu-volume-grid.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-grid-element.h
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#if !defined (__GDU_GTK_INSIDE_GDU_GTK_H) && !defined (GDU_GTK_COMPILATION)
+#error "Only <gdu-gtk/gdu-gtk.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __GDU_VOLUME_GRID_H
+#define __GDU_VOLUME_GRID_H
+
+#include <gdu-gtk/gdu-gtk-types.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_VOLUME_GRID         gdu_volume_grid_get_type()
+#define GDU_VOLUME_GRID(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_VOLUME_GRID, GduVolumeGrid))
+#define GDU_VOLUME_GRID_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_VOLUME_GRID, GduVolumeGridClass))
+#define GDU_IS_VOLUME_GRID(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_VOLUME_GRID))
+#define GDU_IS_VOLUME_GRID_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_VOLUME_GRID))
+#define GDU_VOLUME_GRID_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_VOLUME_GRID, GduVolumeGridClass))
+
+typedef struct GduVolumeGridClass   GduVolumeGridClass;
+typedef struct GduVolumeGridPrivate GduVolumeGridPrivate;
+
+struct GduVolumeGrid
+{
+        GtkDrawingArea parent;
+
+        /*< private >*/
+        GduVolumeGridPrivate *priv;
+};
+
+struct GduVolumeGridClass
+{
+        GtkDrawingAreaClass parent_class;
+
+        /* signals */
+        void (*changed) (GduVolumeGrid *grid);
+};
+
+GType           gdu_volume_grid_get_type         (void) G_GNUC_CONST;
+GtkWidget*      gdu_volume_grid_new              (GduDrive            *drive);
+GduPresentable *gdu_volume_grid_get_selected     (GduVolumeGrid       *grid);
+
+G_END_DECLS
+
+
+
+#endif /* __GDU_VOLUME_GRID_H */
diff --git a/src/gdu/gdu-drive.c b/src/gdu/gdu-drive.c
index 2858776..4d1cced 100644
--- a/src/gdu/gdu-drive.c
+++ b/src/gdu/gdu-drive.c
@@ -559,7 +559,7 @@ gdu_drive_get_name (GduPresentable *presentable)
         is_rotational = gdu_device_drive_get_is_rotational (drive->priv->device);
 
         if (has_media && size > 0) {
-                strsize = gdu_util_get_size_for_display (size, FALSE);
+                strsize = gdu_util_get_size_for_display (size, FALSE, FALSE);
         }
 
         if (is_removable) {
@@ -678,7 +678,7 @@ gdu_drive_get_description (GduPresentable *presentable)
         if (is_removable) {
                 if (has_media && size > 0) {
                         gchar *strsize;
-                        strsize = gdu_util_get_size_for_display (size, FALSE);
+                        strsize = gdu_util_get_size_for_display (size, FALSE, FALSE);
                         /* Translators: This string is the description of a drive. The first %s is the
                          * size of the inserted media, for example '45 GB'.
                          */
diff --git a/src/gdu/gdu-linux-md-drive.c b/src/gdu/gdu-linux-md-drive.c
index 981582c..cc73a5c 100644
--- a/src/gdu/gdu-linux-md-drive.c
+++ b/src/gdu/gdu-linux-md-drive.c
@@ -494,7 +494,7 @@ get_names_and_desc (GduPresentable  *presentable,
         if (drive->priv->device != NULL) {
                 guint64 size;
                 size = gdu_device_get_size (drive->priv->device);
-                strsize = gdu_util_get_size_for_display (size, FALSE);
+                strsize = gdu_util_get_size_for_display (size, FALSE, FALSE);
         }
 
         if (drive->priv->slaves != NULL) {
diff --git a/src/gdu/gdu-presentable.c b/src/gdu/gdu-presentable.c
index 6f38998..d0915f4 100644
--- a/src/gdu/gdu-presentable.c
+++ b/src/gdu/gdu-presentable.c
@@ -25,6 +25,7 @@
 #include <dbus/dbus-glib.h>
 
 #include "gdu-presentable.h"
+#include "gdu-pool.h"
 
 /**
  * SECTION:gdu-presentable
@@ -596,3 +597,63 @@ gdu_presentable_compare (GduPresentable *a,
 
         return ret;
 }
+
+GList *
+gdu_presentable_get_enclosed (GduPresentable *presentable)
+{
+        GList *l;
+        GList *presentables;
+        GList *ret;
+        GduPool *pool;
+
+        pool = gdu_presentable_get_pool (presentable);
+        presentables = gdu_pool_get_presentables (pool);
+
+        ret = NULL;
+        for (l = presentables; l != NULL; l = l->next) {
+                GduPresentable *p = l->data;
+                GduPresentable *e;
+
+                e = gdu_presentable_get_enclosing_presentable (p);
+                if (e != NULL) {
+                        if (gdu_presentable_equals (e, presentable)) {
+                                GList *enclosed_by_p;
+
+                                ret = g_list_prepend (ret, g_object_ref (p));
+
+                                enclosed_by_p = gdu_presentable_get_enclosed (p);
+                                ret = g_list_concat (ret, enclosed_by_p);
+                        }
+                        g_object_unref (e);
+                }
+        }
+
+        g_list_foreach (presentables, (GFunc) g_object_unref, NULL);
+        g_list_free (presentables);
+        g_object_unref (pool);
+        return ret;
+}
+
+gboolean
+gdu_presentable_encloses (GduPresentable *a,
+                          GduPresentable *b)
+{
+        GList *enclosed_by_a;
+        GList *l;
+        gboolean ret;
+
+        ret = FALSE;
+        enclosed_by_a = gdu_presentable_get_enclosed (a);
+        for (l = enclosed_by_a; l != NULL; l = l->next) {
+                GduPresentable *p = GDU_PRESENTABLE (l->data);
+                if (gdu_presentable_equals (b, p)) {
+                        ret = TRUE;
+                        break;
+                }
+        }
+        g_list_foreach (enclosed_by_a, (GFunc) g_object_unref, NULL);
+        g_list_free (enclosed_by_a);
+
+        return ret;
+}
+
diff --git a/src/gdu/gdu-presentable.h b/src/gdu/gdu-presentable.h
index 13b5453..b4bc1d4 100644
--- a/src/gdu/gdu-presentable.h
+++ b/src/gdu/gdu-presentable.h
@@ -103,6 +103,11 @@ gboolean        gdu_presentable_equals                    (GduPresentable *a,
 gint            gdu_presentable_compare                   (GduPresentable *a,
                                                            GduPresentable *b);
 
+GList          *gdu_presentable_get_enclosed              (GduPresentable *presentable);
+gboolean        gdu_presentable_encloses                  (GduPresentable *a,
+                                                           GduPresentable *b);
+
+
 G_END_DECLS
 
 #endif /* __GDU_PRESENTABLE_H */
diff --git a/src/gdu/gdu-util.c b/src/gdu/gdu-util.c
index 7bf22d5..8172d7c 100644
--- a/src/gdu/gdu-util.c
+++ b/src/gdu/gdu-util.c
@@ -33,10 +33,12 @@
 #define KILOBYTE_FACTOR 1000.0
 #define MEGABYTE_FACTOR (1000.0 * 1000.0)
 #define GIGABYTE_FACTOR (1000.0 * 1000.0 * 1000.0)
+#define TERABYTE_FACTOR (1000.0 * 1000.0 * 1000.0 * 1000.0)
 
 #define KIBIBYTE_FACTOR 1024.0
 #define MEBIBYTE_FACTOR (1024.0 * 1024.0)
 #define GIBIBYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
+#define TEBIBYTE_FACTOR (1024.0 * 1024.0 * 1024.0 * 10242.0)
 
 static char *
 get_pow2_size (guint64 size)
@@ -52,9 +54,12 @@ get_pow2_size (guint64 size)
         } else if (size < GIBIBYTE_FACTOR) {
                 displayed_size = (double) size / MEBIBYTE_FACTOR;
                 unit = "MiB";
-        } else {
+        } else if (size < TEBIBYTE_FACTOR) {
                 displayed_size = (double) size / GIBIBYTE_FACTOR;
                 unit = "GiB";
+        } else {
+                displayed_size = (double) size / TEBIBYTE_FACTOR;
+                unit = "TiB";
         }
 
         if (displayed_size < 10.0)
@@ -81,9 +86,12 @@ get_pow10_size (guint64 size)
         } else if (size < GIGABYTE_FACTOR) {
                 displayed_size = (double) size / MEGABYTE_FACTOR;
                 unit = "MB";
-        } else {
+        } else if (size < TERABYTE_FACTOR) {
                 displayed_size = (double) size / GIGABYTE_FACTOR;
                 unit = "GB";
+        } else {
+                displayed_size = (double) size / TERABYTE_FACTOR;
+                unit = "TB";
         }
 
         if (displayed_size < 10.0)
@@ -97,31 +105,45 @@ get_pow10_size (guint64 size)
 }
 
 
-char *
-gdu_util_get_size_for_display (guint64 size, gboolean long_string)
+gchar *
+gdu_util_get_size_for_display (guint64 size,
+                               gboolean use_pow2,
+                               gboolean long_string)
 {
-        char *str;
+        gchar *str;
 
         if (long_string) {
-                char *pow2_str;
-                char *pow10_str;
-                char *size_str;
+                gchar *size_str;
 
-                pow2_str = get_pow2_size (size);
-                pow10_str = get_pow10_size (size);
                 size_str = g_strdup_printf ("%'" G_GINT64_FORMAT, size);
 
-                /* Translators: The first %s is the size in power-of-10 units, e.g. 100 KB
-                 * the second %s is the size in power-of-2 units, e.g. 20 MiB
-                 * the third %s is the size as a number
-                 */
-                str = g_strdup_printf (_("%s / %s / %s bytes"), pow10_str, pow2_str, size_str);
+                if (use_pow2) {
+                        gchar *pow2_str;
+                        pow2_str = get_pow2_size (size);
+
+                        /* Translators: The first %s is the size in power-of-2 units, e.g. '64 KiB'
+                         * the second %s is the size as a number e.g. '65,536 bytes'
+                         */
+                        str = g_strdup_printf (_("%s (%s bytes)"), pow2_str, size_str);
+                        g_free (pow2_str);
+                } else {
+                        gchar *pow10_str;
+                        pow10_str = get_pow10_size (size);
+
+                        /* Translators: The first %s is the size in power-of-10 units, e.g. '100 KB'
+                         * the second %s is the size as a number e.g. '100,000 bytes'
+                         */
+                        str = g_strdup_printf (_("%s (%s bytes)"), pow10_str, size_str);
+                        g_free (pow10_str);
+                }
 
                 g_free (size_str);
-                g_free (pow10_str);
-                g_free (pow2_str);
         } else {
-                str = get_pow10_size (size);
+                if (use_pow2) {
+                        str = get_pow2_size (size);
+                } else {
+                        str = get_pow10_size (size);
+                }
         }
 
         return str;
diff --git a/src/gdu/gdu-util.h b/src/gdu/gdu-util.h
index 4305a98..049a23d 100644
--- a/src/gdu/gdu-util.h
+++ b/src/gdu/gdu-util.h
@@ -30,7 +30,10 @@
 
 G_BEGIN_DECLS
 
-char *gdu_util_get_size_for_display (guint64 size, gboolean long_string);
+gchar *gdu_util_get_size_for_display (guint64  size,
+                                      gboolean use_pow2,
+                                      gboolean long_string);
+
 char *gdu_util_get_fstype_for_display (const char *fstype, const char *fsversion, gboolean long_string);
 
 char *gdu_util_fstype_get_description (char *fstype);
diff --git a/src/gdu/gdu-volume-hole.c b/src/gdu/gdu-volume-hole.c
index 11c8564..64c3821 100644
--- a/src/gdu/gdu-volume-hole.c
+++ b/src/gdu/gdu-volume-hole.c
@@ -143,7 +143,7 @@ gdu_volume_hole_get_name (GduPresentable *presentable)
         char *result;
         char *strsize;
 
-        strsize = gdu_util_get_size_for_display (volume_hole->priv->size, FALSE);
+        strsize = gdu_util_get_size_for_display (volume_hole->priv->size, FALSE, FALSE);
         /* Translators: label for an unallocated space on a disk
          * %s is the size, formatted like '45 GB'
          */
diff --git a/src/gdu/gdu-volume.c b/src/gdu/gdu-volume.c
index e4504ab..2db8ac8 100644
--- a/src/gdu/gdu-volume.c
+++ b/src/gdu/gdu-volume.c
@@ -237,7 +237,7 @@ get_names_and_desc (GduPresentable  *presentable,
                 size = gdu_device_partition_get_size (volume->priv->device);
         else
                 size = gdu_device_get_size (volume->priv->device);
-        strsize = gdu_util_get_size_for_display (size, FALSE);
+        strsize = gdu_util_get_size_for_display (size, FALSE, FALSE);
 
         presentation_name = gdu_device_get_presentation_name (volume->priv->device);
         if (presentation_name != NULL && strlen (presentation_name) > 0) {
diff --git a/src/palimpsest/Makefile.am b/src/palimpsest/Makefile.am
index 42c8bdc..66685a8 100644
--- a/src/palimpsest/Makefile.am
+++ b/src/palimpsest/Makefile.am
@@ -18,6 +18,8 @@ palimpsest_SOURCES = 									\
 	gdu-section-encrypted.h			gdu-section-encrypted.c			\
 	gdu-section-linux-md-drive.h		gdu-section-linux-md-drive.c		\
 	gdu-section-no-media.h			gdu-section-no-media.c			\
+	gdu-section-drive.h			gdu-section-drive.c			\
+	gdu-section-volumes.h			gdu-section-volumes.c			\
 	$(NULL)
 
 palimpsest_CPPFLAGS = 					\
diff --git a/src/palimpsest/gdu-section-drive.c b/src/palimpsest/gdu-section-drive.c
new file mode 100644
index 0000000..340b5c9
--- /dev/null
+++ b/src/palimpsest/gdu-section-drive.c
@@ -0,0 +1,605 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-section-drive.c
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <gdu-gtk/gdu-gtk.h>
+#include "gdu-section-drive.h"
+
+struct _GduSectionDrivePrivate
+{
+        GduDetailsElement *model_element;
+        GduDetailsElement *firmware_element;
+        GduDetailsElement *serial_element;
+        GduDetailsElement *wwn_element;
+        GduDetailsElement *capacity_element;
+        GduDetailsElement *connection_element;
+        GduDetailsElement *partitioning_element;
+        GduDetailsElement *smart_element;
+};
+
+G_DEFINE_TYPE (GduSectionDrive, gdu_section_drive, GDU_TYPE_SECTION)
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_section_drive_finalize (GObject *object)
+{
+        //GduSectionDrive *section = GDU_SECTION_DRIVE (object);
+
+        if (G_OBJECT_CLASS (gdu_section_drive_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_section_drive_parent_class)->finalize (object);
+}
+
+static void
+gdu_section_drive_update (GduSection *_section)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (_section);
+        GduPresentable *p;
+        GduDevice *d;
+        gchar *s;
+        gchar *s2;
+        const gchar *vendor;
+        const gchar *model;
+        const gchar *firmware;
+        const gchar *serial;
+        const gchar *wwn;
+        GIcon *icon;
+
+        d = NULL;
+        p = gdu_section_get_presentable (_section);
+
+        d = gdu_presentable_get_device (p);
+        if (d == NULL)
+                goto out;
+
+        model = gdu_device_drive_get_model (d);
+        vendor = gdu_device_drive_get_vendor (d);
+        if (vendor != NULL && strlen (vendor) == 0)
+                vendor = NULL;
+        if (model != NULL && strlen (model) == 0)
+                model = NULL;
+        s = g_strdup_printf ("%s%s%s",
+                             vendor != NULL ? vendor : "",
+                             vendor != NULL ? " " : "",
+                             model != NULL ? model : "");
+        gdu_details_element_set_text (section->priv->model_element, s);
+        g_free (s);
+
+        firmware = gdu_device_drive_get_revision (d);
+        if (firmware == NULL || strlen (firmware) == 0)
+                firmware = "â??";
+        gdu_details_element_set_text (section->priv->firmware_element, firmware);
+
+        serial = gdu_device_drive_get_serial (d);
+        if (serial == NULL || strlen (serial) == 0)
+                serial = "â??";
+        gdu_details_element_set_text (section->priv->serial_element, serial);
+
+        wwn = NULL; /*TODO: gdu_device_drive_get_wwn (d)*/
+        if (wwn == NULL || strlen (wwn) == 0)
+                wwn = "â??";
+        gdu_details_element_set_text (section->priv->wwn_element, wwn);
+
+        if (gdu_device_is_partition_table (d)) {
+                const gchar *scheme;
+
+                scheme = gdu_device_partition_table_get_scheme (d);
+                if (g_strcmp0 (scheme, "apm") == 0) {
+                        s = g_strdup (_("Apple Partition Map"));
+                } else if (g_strcmp0 (scheme, "mbr") == 0) {
+                        s = g_strdup (_("Master Boot Record"));
+                } else if (g_strcmp0 (scheme, "gpt") == 0) {
+                        s = g_strdup (_("GUID Partition Table"));
+                } else {
+                        /* Translators: 'scheme' refers to a partition table format here, like 'mbr' or 'gpt' */
+                        s = g_strdup_printf (_("Unknown Scheme: %s"), scheme);
+                }
+                gdu_details_element_set_text (section->priv->partitioning_element, s);
+                g_free (s);
+        } else {
+                gdu_details_element_set_text (section->priv->partitioning_element,
+                                              _("Not Partitioned"));
+        }
+
+        if (gdu_device_drive_ata_smart_get_is_available (d) &&
+            gdu_device_drive_ata_smart_get_time_collected (d) > 0) {
+                gboolean highlight;
+
+                s = gdu_util_ata_smart_status_to_desc (gdu_device_drive_ata_smart_get_status (d),
+                                                       &highlight,
+                                                       NULL,
+                                                       &icon);
+                if (highlight) {
+                        s2 = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", s);
+                        g_free (s);
+                        s = s2;
+                }
+
+                gdu_details_element_set_text (section->priv->smart_element, s);
+                gdu_details_element_set_icon (section->priv->smart_element, icon);
+
+                g_free (s);
+                g_object_unref (icon);
+        } else {
+                gdu_details_element_set_text (section->priv->smart_element,
+                                              _("Not Supported"));
+                icon = g_themed_icon_new ("gdu-smart-unknown");
+                gdu_details_element_set_icon (section->priv->smart_element, icon);
+                g_object_unref (icon);
+        }
+
+        if (gdu_device_is_media_available (d)) {
+                s = gdu_util_get_size_for_display (gdu_device_get_size (d),
+                                                   FALSE,
+                                                   TRUE);
+                gdu_details_element_set_text (section->priv->capacity_element, s);
+                g_free (s);
+        } else {
+                gdu_details_element_set_text (section->priv->capacity_element,
+                                              _("No Media Detected"));
+        }
+
+        s = gdu_util_get_connection_for_display (gdu_device_drive_get_connection_interface (d),
+                                                 gdu_device_drive_get_connection_speed (d));
+        gdu_details_element_set_text (section->priv->connection_element, s);
+        g_free (s);
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *
+create_button (const gchar *icon_name,
+               const gchar *button_primary,
+               const gchar *button_secondary)
+{
+        GtkWidget *hbox;
+        GtkWidget *label;
+        GtkWidget *image;
+        GtkWidget *button;
+        gchar *s;
+
+        image = gtk_image_new_from_icon_name (icon_name,
+                                              GTK_ICON_SIZE_BUTTON);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+        gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
+        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+        gtk_label_set_single_line_mode (GTK_LABEL (label), FALSE);
+        s = g_strdup_printf ("%s\n"
+                             "<span fgcolor='#404040'><small>%s</small></span>",
+                             button_primary,
+                             button_secondary);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
+        g_free (s);
+
+        hbox = gtk_hbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+        button = gtk_button_new ();
+        gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+        gtk_container_add (GTK_CONTAINER (button), hbox);
+
+        gtk_widget_set_size_request (label, 250, -1);
+
+        return button;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+has_strv0 (gchar **strv, const gchar *str)
+{
+        gboolean ret;
+        guint n;
+
+        ret = FALSE;
+
+        for (n = 0; strv != NULL && strv[n] != NULL; n++) {
+                if (g_strcmp0 (strv[n], str) == 0) {
+                        ret = TRUE;
+                        goto out;
+                }
+        }
+
+ out:
+        return ret;
+}
+
+static void
+on_cddvd_button_clicked (GtkButton *button,
+                         gpointer   user_data)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (user_data);
+        GAppLaunchContext *launch_context;
+        GAppInfo *app_info;
+        GtkWidget *dialog;
+        GError *error;
+
+        app_info = NULL;
+        launch_context = NULL;
+
+        app_info = G_APP_INFO (g_desktop_app_info_new ("brasero.desktop"));
+        if (app_info == NULL) {
+                /* TODO: Use PackageKit to install Brasero */
+                dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section)))),
+                                                             GTK_DIALOG_MODAL,
+                                                             GTK_MESSAGE_ERROR,
+                                                             GTK_BUTTONS_OK,
+                                                             "<b><big><big>%s</big></big></b>\n\n%s",
+                                                             _("Error launching Brasero"),
+                                                             _("The application is not installed"));
+                gtk_widget_show_all (dialog);
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                goto out;
+        }
+
+        launch_context = G_APP_LAUNCH_CONTEXT (gdk_app_launch_context_new ());
+
+        error = NULL;
+        if (!g_app_info_launch (app_info,
+                                NULL, /* no files */
+                                launch_context,
+                                &error)) {
+                dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section)))),
+                                                             GTK_DIALOG_MODAL,
+                                                             GTK_MESSAGE_ERROR,
+                                                             GTK_BUTTONS_OK,
+                                                             "<b><big><big>%s</big></big></b>\n\n%s",
+                                                             _("Error launching Brasero"),
+                                                             error->message);
+                g_error_free (error);
+                gtk_widget_show_all (dialog);
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+        }
+
+ out:
+        if (app_info != NULL)
+                g_object_unref (app_info);
+        if (launch_context != NULL)
+                g_object_unref (launch_context);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_smart_button_clicked (GtkButton *button,
+                         gpointer   user_data)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (user_data);
+        GtkWindow *toplevel;
+        GtkWidget *dialog;
+
+        toplevel = GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))));
+        dialog = gdu_ata_smart_dialog_new (toplevel,
+                                           GDU_DRIVE (gdu_section_get_presentable (GDU_SECTION (section))));
+        gtk_widget_show_all (dialog);
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+eject_op_callback (GduDevice *device,
+                   GError    *error,
+                   gpointer   user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        if (error != NULL) {
+                gdu_shell_raise_error (shell,
+                                       NULL,
+                                       error,
+                                       _("Error ejecting device"));
+                g_error_free (error);
+        }
+        g_object_unref (shell);
+}
+
+static void
+on_eject_button_clicked (GtkButton *button,
+                         gpointer   user_data)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (user_data);
+        GduDevice *d;
+
+        d = gdu_presentable_get_device (gdu_section_get_presentable (GDU_SECTION (section)));
+        if (d == NULL)
+                goto out;
+
+        gdu_device_op_drive_eject (d,
+                                   eject_op_callback,
+                                   g_object_ref (gdu_section_get_shell (GDU_SECTION (section))));
+
+        g_object_unref (d);
+ out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+detach_op_callback (GduDevice *device,
+                    GError    *error,
+                    gpointer   user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        if (error != NULL) {
+                gdu_shell_raise_error (shell,
+                                       NULL,
+                                       error,
+                                       _("Error detaching device"));
+                g_error_free (error);
+        }
+        g_object_unref (shell);
+}
+
+static void
+on_detach_button_clicked (GtkButton *button,
+                          gpointer   user_data)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (user_data);
+        GduDevice *d;
+
+        d = gdu_presentable_get_device (gdu_section_get_presentable (GDU_SECTION (section)));
+        if (d == NULL)
+                goto out;
+
+        gdu_device_op_drive_detach (d,
+                                    detach_op_callback,
+                                    g_object_ref (gdu_section_get_shell (GDU_SECTION (section))));
+
+        g_object_unref (d);
+ out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_button (GtkWidget *table,
+            GtkWidget *button,
+            guint     *row,
+            guint     *column)
+{
+        guint num_columns;
+
+        gtk_table_attach (GTK_TABLE (table),
+                          button,
+                          *column, *column + 1,
+                          *row, *row + 1,
+                          GTK_FILL,
+                          GTK_FILL,
+                          0, 0);
+
+        g_object_get (table,
+                      "n-columns", &num_columns,
+                      NULL);
+
+        *column += 1;
+        if (*column >= num_columns) {
+                *column = 0;
+                *row +=1;
+        }
+}
+
+static void
+gdu_section_drive_constructed (GObject *object)
+{
+        GduSectionDrive *section = GDU_SECTION_DRIVE (object);
+        GtkWidget *align;
+        GtkWidget *label;
+        GtkWidget *table;
+        GtkWidget *vbox;
+        GtkWidget *button;
+        gchar *s;
+        GduPresentable *p;
+        GduDevice *d;
+        GPtrArray *elements;
+        GduDetailsElement *element;
+        guint row;
+        guint column;
+
+        d = NULL;
+
+        gtk_box_set_spacing (GTK_BOX (section), 12);
+
+        /*------------------------------------- */
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        s = g_strconcat ("<b>", _("Drive"), "</b>", NULL);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_box_pack_start (GTK_BOX (section), label, FALSE, FALSE, 0);
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (section), align, FALSE, FALSE, 0);
+
+        vbox = gtk_vbox_new (FALSE, 6);
+        gtk_container_add (GTK_CONTAINER (align), vbox);
+
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+        element = gdu_details_element_new (_("Model:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->model_element = element;
+
+        element = gdu_details_element_new (_("Serial Number:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->serial_element = element;
+
+        element = gdu_details_element_new (_("Firmware Version:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->firmware_element = element;
+
+        element = gdu_details_element_new (_("World Wide Name:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->wwn_element = element;
+
+        element = gdu_details_element_new (_("Capacity:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->capacity_element = element;
+
+        element = gdu_details_element_new (_("Connection:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->connection_element = element;
+
+        element = gdu_details_element_new (_("Partitioning:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->partitioning_element = element;
+
+        element = gdu_details_element_new (_("SMART Status:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->smart_element = element;
+
+        table = gdu_details_table_new (2,
+                                       elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+
+        /* -------------------------------------------------------------------------------- */
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
+
+        row = 0;
+        column = 0;
+        table = gtk_table_new (1, 2, FALSE);
+        gtk_table_set_row_spacings (GTK_TABLE (table), 0);
+        gtk_table_set_col_spacings (GTK_TABLE (table), 0);
+        gtk_container_add (GTK_CONTAINER (align), table);
+
+        p = gdu_section_get_presentable (GDU_SECTION (section));
+        d = gdu_presentable_get_device (p);
+        if (d != NULL && has_strv0 (gdu_device_drive_get_media_compatibility (d), "optical_cd")) {
+                button = create_button ("brasero",
+                                        _("Open CD/_DVD Application"),
+                                        _("Create and copy CDs and DVDs"));
+                g_signal_connect (button,
+                                  "clicked",
+                                  G_CALLBACK (on_cddvd_button_clicked),
+                                  section);
+                add_button (table, button, &row, &column);
+        } else {
+                button = create_button ("nautilus-gdu",
+                                        _("Format _Drive"),
+                                        _("Delete all data and partition the drive"));
+                add_button (table, button, &row, &column);
+        }
+
+        if (d != NULL &&
+            gdu_device_drive_ata_smart_get_is_available (d) &&
+            gdu_device_drive_ata_smart_get_time_collected (d) > 0) {
+                button = create_button ("gdu-check-disk",
+                                        _("SM_ART Data"),
+                                        _("View SMART data and run self-tests"));
+                g_signal_connect (button,
+                                  "clicked",
+                                  G_CALLBACK (on_smart_button_clicked),
+                                  section);
+                add_button (table, button, &row, &column);
+        }
+
+        if (d != NULL && gdu_device_drive_get_is_media_ejectable (d)) {
+                button = create_button ("gdu-eject",
+                                        _("_Eject"),
+                                        _("Eject media from the drive"));
+                g_signal_connect (button,
+                                  "clicked",
+                                  G_CALLBACK (on_eject_button_clicked),
+                                  section);
+                add_button (table, button, &row, &column);
+        }
+
+        if (d != NULL && gdu_device_drive_get_can_detach (d)) {
+                button = create_button ("gdu-detach",
+                                        _("Safe Rem_oval"),
+                                        _("Power down the drive so it can be removed"));
+                g_signal_connect (button,
+                                  "clicked",
+                                  G_CALLBACK (on_detach_button_clicked),
+                                  section);
+                add_button (table, button, &row, &column);
+        }
+
+        /* -------------------------------------------------------------------------------- */
+
+        gtk_widget_show_all (GTK_WIDGET (section));
+
+        if (d != NULL)
+                g_object_unref (d);
+
+        if (G_OBJECT_CLASS (gdu_section_drive_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_section_drive_parent_class)->constructed (object);
+}
+
+static void
+gdu_section_drive_class_init (GduSectionDriveClass *klass)
+{
+        GObjectClass *gobject_class;
+        GduSectionClass *section_class;
+
+        gobject_class = G_OBJECT_CLASS (klass);
+        section_class = GDU_SECTION_CLASS (klass);
+
+        gobject_class->finalize    = gdu_section_drive_finalize;
+        gobject_class->constructed = gdu_section_drive_constructed;
+        section_class->update      = gdu_section_drive_update;
+
+        g_type_class_add_private (klass, sizeof (GduSectionDrivePrivate));
+}
+
+static void
+gdu_section_drive_init (GduSectionDrive *section)
+{
+        section->priv = G_TYPE_INSTANCE_GET_PRIVATE (section, GDU_TYPE_SECTION_DRIVE, GduSectionDrivePrivate);
+}
+
+GtkWidget *
+gdu_section_drive_new (GduShell       *shell,
+                       GduPresentable *presentable)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_SECTION_DRIVE,
+                                         "shell", shell,
+                                         "presentable", presentable,
+                                         NULL));
+}
diff --git a/src/palimpsest/gdu-section-drive.h b/src/palimpsest/gdu-section-drive.h
new file mode 100644
index 0000000..901df80
--- /dev/null
+++ b/src/palimpsest/gdu-section-drive.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-section-drive.h
+ *
+ * Copyright (C) 2007 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "gdu-section.h"
+
+#ifndef GDU_SECTION_DRIVE_H
+#define GDU_SECTION_DRIVE_H
+
+#define GDU_TYPE_SECTION_DRIVE           (gdu_section_drive_get_type ())
+#define GDU_SECTION_DRIVE(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SECTION_DRIVE, GduSectionDrive))
+#define GDU_SECTION_DRIVE_CLASS(k)       (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_SECTION_DRIVE,  GduSectionDriveClass))
+#define GDU_IS_SECTION_DRIVE(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SECTION_DRIVE))
+#define GDU_IS_SECTION_DRIVE_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_SECTION_DRIVE))
+#define GDU_SECTION_DRIVE_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_SECTION_DRIVE, GduSectionDriveClass))
+
+typedef struct _GduSectionDriveClass       GduSectionDriveClass;
+typedef struct _GduSectionDrive            GduSectionDrive;
+
+struct _GduSectionDrivePrivate;
+typedef struct _GduSectionDrivePrivate     GduSectionDrivePrivate;
+
+struct _GduSectionDrive
+{
+        GduSection parent;
+
+        /* private */
+        GduSectionDrivePrivate *priv;
+};
+
+struct _GduSectionDriveClass
+{
+        GduSectionClass parent_class;
+};
+
+GType            gdu_section_drive_get_type (void);
+GtkWidget       *gdu_section_drive_new      (GduShell       *shell,
+                                             GduPresentable *presentable);
+
+#endif /* GDU_SECTION_DRIVE_H */
diff --git a/src/palimpsest/gdu-section-linux-md-drive.c b/src/palimpsest/gdu-section-linux-md-drive.c
index c3f6941..fcb9b0d 100644
--- a/src/palimpsest/gdu-section-linux-md-drive.c
+++ b/src/palimpsest/gdu-section-linux-md-drive.c
@@ -171,6 +171,7 @@ on_add_clicked (GtkButton *button,
         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
                                              GTK_SHADOW_IN);
         model = gdu_pool_tree_model_new (pool,
+                                         NULL,
                                          GDU_POOL_TREE_MODEL_FLAGS_NONE);
         tree_view = gdu_pool_tree_view_new (model, GDU_POOL_TREE_VIEW_FLAGS_NONE);
         g_object_unref (model);
@@ -707,7 +708,9 @@ update (GduSectionLinuxMdDrive *section)
                 level_str = g_strdup (level);
         }
 
-        s = gdu_util_get_size_for_display (component_size, FALSE);
+        s = gdu_util_get_size_for_display (component_size,
+                                           FALSE,
+                                           FALSE);
         if (strcmp (level, "linear") == 0) {
                 /* Translators: %d is the number of components in the RAID */
                 components_str = g_strdup_printf (ngettext ("%d Component", "%d Components", num_raid_devices), num_raid_devices);
@@ -722,7 +725,9 @@ update (GduSectionLinuxMdDrive *section)
         if (raid_size == 0) {
                 raid_size_str = g_strdup_printf ("-");
         } else {
-                raid_size_str = gdu_util_get_size_for_display (raid_size, TRUE);
+                raid_size_str = gdu_util_get_size_for_display (raid_size,
+                                                               FALSE,
+                                                               TRUE);
         }
 
         if (!gdu_drive_is_active (GDU_DRIVE (linux_md_drive))) {
diff --git a/src/palimpsest/gdu-section-volumes.c b/src/palimpsest/gdu-section-volumes.c
new file mode 100644
index 0000000..9952e8f
--- /dev/null
+++ b/src/palimpsest/gdu-section-volumes.c
@@ -0,0 +1,601 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-section-volumes.c
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <gdu-gtk/gdu-gtk.h>
+#include "gdu-section-volumes.h"
+
+struct _GduSectionVolumesPrivate
+{
+        GduPresentable *cur_volume;
+
+        GtkWidget *grid;
+        GtkWidget *details_table;
+        GtkWidget *buttons_align;
+
+        /* shared between all volume types */
+        GduDetailsElement *usage_element;
+        GduDetailsElement *capacity_element;
+        GduDetailsElement *partition_element;
+        GduDetailsElement *device_element;
+
+        /* elements for the 'filesystem' usage */
+        GduDetailsElement *fs_type_element;
+        GduDetailsElement *fs_available_element;
+        GduDetailsElement *fs_label_element;
+        GduDetailsElement *fs_mount_point_element;
+
+        GtkWidget *fs_mount_button;
+        GtkWidget *fs_unmount_button;
+};
+
+G_DEFINE_TYPE (GduSectionVolumes, gdu_section_volumes, GDU_TYPE_SECTION)
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_section_volumes_finalize (GObject *object)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (object);
+
+        if (section->priv->cur_volume != NULL)
+                g_object_unref (section->priv->cur_volume);
+
+        if (G_OBJECT_CLASS (gdu_section_volumes_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_section_volumes_parent_class)->finalize (object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *
+create_button (const gchar *icon_name,
+               const gchar *button_primary,
+               const gchar *button_secondary)
+{
+        GtkWidget *hbox;
+        GtkWidget *label;
+        GtkWidget *image;
+        GtkWidget *button;
+        gchar *s;
+
+        image = gtk_image_new_from_icon_name (icon_name,
+                                              GTK_ICON_SIZE_BUTTON);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+        gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
+        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+        gtk_label_set_single_line_mode (GTK_LABEL (label), FALSE);
+        s = g_strdup_printf ("%s\n"
+                             "<span fgcolor='#404040'><small>%s</small></span>",
+                             button_primary,
+                             button_secondary);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
+        g_free (s);
+
+        hbox = gtk_hbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+        button = gtk_button_new ();
+        gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+        gtk_container_add (GTK_CONTAINER (button), hbox);
+
+        gtk_widget_set_size_request (label, 250, -1);
+
+        return button;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_button (GtkWidget *table,
+            GtkWidget *button,
+            guint     *row,
+            guint     *column)
+{
+        guint num_columns;
+
+        gtk_table_attach (GTK_TABLE (table),
+                          button,
+                          *column, *column + 1,
+                          *row, *row + 1,
+                          GTK_FILL,
+                          GTK_FILL,
+                          0, 0);
+
+        g_object_get (table,
+                      "n-columns", &num_columns,
+                      NULL);
+
+        *column += 1;
+        if (*column >= num_columns) {
+                *column = 0;
+                *row +=1;
+        }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+unmount_op_callback (GduDevice *device,
+                     GError    *error,
+                     gpointer   user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        /* TODO: handle busy mounts using GtkMountOperation */
+
+        if (error != NULL) {
+                gdu_shell_raise_error (shell,
+                                       NULL,
+                                       error,
+                                       _("Error unmounting device"));
+                g_error_free (error);
+        }
+        g_object_unref (shell);
+}
+
+static void
+on_unmount_button_clicked (GtkButton *button,
+                           gpointer   user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+
+        GduPresentable *v;
+        GduDevice *d;
+
+        v = NULL;
+        d = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        d = gdu_presentable_get_device (v);
+        if (d == NULL)
+                goto out;
+
+        gdu_device_op_filesystem_unmount (d,
+                                          unmount_op_callback,
+                                          g_object_ref (gdu_section_get_shell (GDU_SECTION (section))));
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+mount_op_callback (GduDevice *device,
+                   gchar     *mount_point,
+                   GError    *error,
+                   gpointer   user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        if (error != NULL) {
+                gdu_shell_raise_error (shell,
+                                       NULL,
+                                       error,
+                                       _("Error mounting device"));
+                g_error_free (error);
+        } else {
+                g_free (mount_point);
+        }
+        g_object_unref (shell);
+}
+
+static void
+on_mount_button_clicked (GtkButton *button,
+                         gpointer   user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+        GduPresentable *v;
+        GduDevice *d;
+
+        v = NULL;
+        d = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        d = gdu_presentable_get_device (v);
+        if (d == NULL)
+                goto out;
+
+        gdu_device_op_filesystem_mount (d,
+                                        NULL,
+                                        mount_op_callback,
+                                        g_object_ref (gdu_section_get_shell (GDU_SECTION (section))));
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_section_volumes_update (GduSection *_section)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (_section);
+        GduPresentable *v;
+        GduDevice *d;
+        gchar *s;
+        gchar *s2;
+        const gchar *usage;
+
+        v = NULL;
+        d = NULL;
+        usage = "";
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+
+        if (v != NULL) {
+                d = gdu_presentable_get_device (v);
+                if (d != NULL) {
+                        usage = gdu_device_id_get_usage (d);
+                }
+        }
+
+        /* ---------------------------------------------------------------------------------------------------- */
+        /* rebuild table if the selected volume has changed */
+
+        if (section->priv->cur_volume != v) {
+                GPtrArray *elements;
+                GtkWidget *child;
+                GtkWidget *table;
+                GtkWidget *button;
+                guint row;
+                guint column;
+
+                child = gtk_bin_get_child (GTK_BIN (section->priv->buttons_align));
+                if (child != NULL)
+                        gtk_container_remove (GTK_CONTAINER (section->priv->buttons_align), child);
+                row = 0;
+                column = 0;
+                table = gtk_table_new (1, 2, FALSE);
+                gtk_table_set_row_spacings (GTK_TABLE (table), 0);
+                gtk_table_set_col_spacings (GTK_TABLE (table), 0);
+                gtk_container_add (GTK_CONTAINER (section->priv->buttons_align), table);
+
+                if (section->priv->cur_volume != NULL)
+                        g_object_unref (section->priv->cur_volume);
+                section->priv->cur_volume = v != NULL ? g_object_ref (v) : NULL;
+
+                section->priv->usage_element = NULL;
+                section->priv->capacity_element = NULL;
+                section->priv->partition_element = NULL;
+                section->priv->device_element = NULL;
+                section->priv->fs_type_element = NULL;
+                section->priv->fs_label_element = NULL;
+                section->priv->fs_available_element = NULL;
+                section->priv->fs_mount_point_element = NULL;
+
+                elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+                section->priv->usage_element = gdu_details_element_new (_("Usage:"), NULL, NULL);
+                g_ptr_array_add (elements, section->priv->usage_element);
+
+                section->priv->device_element = gdu_details_element_new (_("Device:"), NULL, NULL);
+                g_ptr_array_add (elements, section->priv->device_element);
+
+                section->priv->partition_element = gdu_details_element_new (_("Partition:"), NULL, NULL);
+                g_ptr_array_add (elements, section->priv->partition_element);
+
+                section->priv->capacity_element = gdu_details_element_new (_("Capacity:"), NULL, NULL);
+                g_ptr_array_add (elements, section->priv->capacity_element);
+
+                if (g_strcmp0 (usage, "filesystem") == 0) {
+                        section->priv->fs_type_element = gdu_details_element_new (_("Type:"), NULL, NULL);
+                        g_ptr_array_add (elements, section->priv->fs_type_element);
+
+                        section->priv->fs_available_element = gdu_details_element_new (_("Available:"), NULL, NULL);
+                        g_ptr_array_add (elements, section->priv->fs_available_element);
+
+                        section->priv->fs_label_element = gdu_details_element_new (_("Label:"), NULL, NULL);
+                        g_ptr_array_add (elements, section->priv->fs_label_element);
+
+                        section->priv->fs_mount_point_element = gdu_details_element_new (_("Mount Point:"), NULL, NULL);
+                        g_ptr_array_add (elements, section->priv->fs_mount_point_element);
+
+                        button = create_button ("gdu-mount",
+                                                _("_Mount Volume"),
+                                                _("Mount the volume"));
+                        g_signal_connect (button,
+                                          "clicked",
+                                          G_CALLBACK (on_mount_button_clicked),
+                                          section);
+                        section->priv->fs_mount_button = button;
+                        add_button (table, button, &row, &column);
+
+                        button = create_button ("gdu-unmount",
+                                                _("_Unmount Volume"),
+                                                _("Unmount the volume"));
+                        g_signal_connect (button,
+                                          "clicked",
+                                          G_CALLBACK (on_unmount_button_clicked),
+                                          section);
+                        section->priv->fs_unmount_button = button;
+                        add_button (table, button, &row, &column);
+
+                        button = create_button ("gdu-check-disk",
+                                                _("_Check Filesystem"),
+                                                _("Check the filesystem for errors"));
+                        add_button (table, button, &row, &column);
+
+                        button = create_button ("nautilus-gdu",
+                                                _("Fo_rmat Volume"),
+                                                _("Format the volume"));
+                        add_button (table, button, &row, &column);
+
+                        if (d != NULL && gdu_device_is_partition (d)) {
+                                button = create_button (GTK_STOCK_EDIT,
+                                                        _("Ed_it Partition"),
+                                                        _("Change partition type and flags"));
+                                add_button (table, button, &row, &column);
+
+                                button = create_button (GTK_STOCK_DELETE,
+                                                        _("D_elete Partition"),
+                                                        _("Delete the partition"));
+                                add_button (table, button, &row, &column);
+                        }
+                }
+
+                gdu_details_table_set_elements (GDU_DETAILS_TABLE (section->priv->details_table), elements);
+                g_ptr_array_unref (elements);
+
+                gtk_widget_show_all (table);
+        }
+
+        /* ---------------------------------------------------------------------------------------------------- */
+        /* reset all elements */
+
+        if (section->priv->usage_element != NULL)
+                gdu_details_element_set_text (section->priv->usage_element, "â??");
+        if (section->priv->capacity_element != NULL) {
+                if (v != NULL) {
+                        s = gdu_util_get_size_for_display (gdu_presentable_get_size (v), FALSE, TRUE);
+                        gdu_details_element_set_text (section->priv->capacity_element, s);
+                        g_free (s);
+                } else {
+                        gdu_details_element_set_text (section->priv->capacity_element, "â??");
+                }
+        }
+        if (section->priv->partition_element != NULL) {
+                if (d != NULL && gdu_device_is_partition (d)) {
+                        s = gdu_util_get_desc_for_part_type (gdu_device_partition_get_scheme (d),
+                                                             gdu_device_partition_get_type (d));
+                        gdu_details_element_set_text (section->priv->partition_element, s);
+                        /* TODO: include partition flags... */
+                        g_free (s);
+                } else {
+                        gdu_details_element_set_text (section->priv->partition_element, "â??");
+                }
+        }
+        if (section->priv->device_element != NULL) {
+                if (d != NULL) {
+                        gdu_details_element_set_text (section->priv->device_element,
+                                                      gdu_device_get_device_file (d));
+                } else {
+                        gdu_details_element_set_text (section->priv->device_element, "â??");
+                }
+        }
+        if (section->priv->fs_type_element != NULL)
+                gdu_details_element_set_text (section->priv->fs_type_element, "â??");
+        if (section->priv->fs_available_element != NULL)
+                gdu_details_element_set_text (section->priv->fs_available_element, "â??");
+        if (section->priv->fs_label_element != NULL)
+                gdu_details_element_set_text (section->priv->fs_label_element, "â??");
+        if (section->priv->fs_mount_point_element != NULL)
+                gdu_details_element_set_text (section->priv->fs_mount_point_element, "â??");
+
+        if (v == NULL)
+                goto out;
+
+        /* ---------------------------------------------------------------------------------------------------- */
+        /* populate according to usage */
+
+        if (g_strcmp0 (usage, "filesystem") == 0) {
+                gdu_details_element_set_text (section->priv->usage_element, _("Filesystem"));
+                s = gdu_util_get_fstype_for_display (gdu_device_id_get_type (d),
+                                                     gdu_device_id_get_version (d),
+                                                     TRUE);
+                gdu_details_element_set_text (section->priv->fs_type_element, s);
+                g_free (s);
+                gdu_details_element_set_text (section->priv->fs_label_element,
+                                              gdu_device_id_get_label (d));
+
+                /* TODO: figure out amount of free space */
+                gdu_details_element_set_text (section->priv->fs_available_element, "â??");
+
+
+                if (gdu_device_is_mounted (d)) {
+                        const gchar* const *mount_paths;
+
+                        /* For now we ignore if the device is mounted in multiple places */
+                        mount_paths = (const gchar* const *) gdu_device_get_mount_paths (d);
+                        s = g_strdup_printf ("<a title=\"%s\" href=\"file://%s\">%s</a>",
+                                              /* Translators: this the mount point hyperlink tooltip */
+                                              _("View files on the volume"),
+                                             mount_paths[0],
+                                             mount_paths[0]);
+                        /* Translators: this the the text for the mount point
+                         * item - %s is the mount point, e.g. '/media/disk'
+                         */
+                        s2 = g_strdup_printf (_("Mounted at %s"), s);
+                        gdu_details_element_set_text (section->priv->fs_mount_point_element, s2);
+                        g_free (s);
+                        g_free (s2);
+
+                        gtk_widget_set_sensitive (section->priv->fs_mount_button, FALSE);
+                        gtk_widget_set_sensitive (section->priv->fs_unmount_button, TRUE);
+                } else {
+                        gdu_details_element_set_text (section->priv->fs_mount_point_element, _("Not Mounted"));
+
+                        gtk_widget_set_sensitive (section->priv->fs_mount_button, TRUE);
+                        gtk_widget_set_sensitive (section->priv->fs_unmount_button, FALSE);
+                }
+
+        } else if (g_strcmp0 (usage, "") == 0 &&
+                   d != NULL && gdu_device_is_partition (d) &&
+                   g_strcmp0 (gdu_device_partition_get_scheme (d), "mbr") == 0 &&
+                   (g_strcmp0 (gdu_device_partition_get_type (d), "0x05") == 0 ||
+                    g_strcmp0 (gdu_device_partition_get_type (d), "0x0f") == 0 ||
+                    g_strcmp0 (gdu_device_partition_get_type (d), "0x85") == 0)) {
+                gdu_details_element_set_text (section->priv->usage_element, _("Container for Logical Partitions"));
+
+        } else if (GDU_IS_VOLUME_HOLE (v)) {
+                GduDevice *drive_device;
+                gdu_details_element_set_text (section->priv->usage_element, _("Unallocated Space"));
+                drive_device = gdu_presentable_get_device (gdu_section_get_presentable (GDU_SECTION (section)));
+                gdu_details_element_set_text (section->priv->device_element,
+                                              gdu_device_get_device_file (drive_device));
+                g_object_unref (drive_device);
+        }
+
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_grid_changed (GduVolumeGrid *grid,
+                 gpointer       user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+
+        gdu_section_volumes_update (GDU_SECTION (section));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_section_volumes_constructed (GObject *object)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (object);
+        GtkWidget *grid;
+        GtkWidget *align;
+        GtkWidget *label;
+        GtkWidget *vbox2;
+        GtkWidget *table;
+        gchar *s;
+
+        gtk_box_set_spacing (GTK_BOX (section), 12);
+
+        /*------------------------------------- */
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        s = g_strconcat ("<b>", _("_Volumes"), "</b>", NULL);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
+        g_free (s);
+        gtk_box_pack_start (GTK_BOX (section), label, FALSE, FALSE, 0);
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (section), align, FALSE, FALSE, 0);
+
+        vbox2 = gtk_vbox_new (FALSE, 6);
+        gtk_container_add (GTK_CONTAINER (align), vbox2);
+
+        grid = gdu_volume_grid_new (GDU_DRIVE (gdu_section_get_presentable (GDU_SECTION (section))));
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), grid);
+        section->priv->grid = grid;
+        gtk_box_pack_start (GTK_BOX (vbox2),
+                            grid,
+                            FALSE,
+                            FALSE,
+                            0);
+        g_signal_connect (grid,
+                          "changed",
+                          G_CALLBACK (on_grid_changed),
+                          section);
+
+        table = gdu_details_table_new (2, NULL);
+        section->priv->details_table = table;
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        section->priv->buttons_align = align;
+        gtk_box_pack_start (GTK_BOX (vbox2), align, FALSE, FALSE, 0);
+
+        /* -------------------------------------------------------------------------------- */
+
+        gtk_widget_show_all (GTK_WIDGET (section));
+
+        if (G_OBJECT_CLASS (gdu_section_volumes_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_section_volumes_parent_class)->constructed (object);
+}
+
+static void
+gdu_section_volumes_class_init (GduSectionVolumesClass *klass)
+{
+        GObjectClass *gobject_class;
+        GduSectionClass *section_class;
+
+        gobject_class = G_OBJECT_CLASS (klass);
+        section_class = GDU_SECTION_CLASS (klass);
+
+        gobject_class->finalize    = gdu_section_volumes_finalize;
+        gobject_class->constructed = gdu_section_volumes_constructed;
+        section_class->update      = gdu_section_volumes_update;
+
+        g_type_class_add_private (klass, sizeof (GduSectionVolumesPrivate));
+}
+
+static void
+gdu_section_volumes_init (GduSectionVolumes *section)
+{
+        section->priv = G_TYPE_INSTANCE_GET_PRIVATE (section, GDU_TYPE_SECTION_VOLUMES, GduSectionVolumesPrivate);
+}
+
+GtkWidget *
+gdu_section_volumes_new (GduShell       *shell,
+                         GduPresentable *presentable)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_SECTION_VOLUMES,
+                                         "shell", shell,
+                                         "presentable", presentable,
+                                         NULL));
+}
diff --git a/src/palimpsest/gdu-section-volumes.h b/src/palimpsest/gdu-section-volumes.h
new file mode 100644
index 0000000..d2aa63e
--- /dev/null
+++ b/src/palimpsest/gdu-section-volumes.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-section-volumes.h
+ *
+ * Copyright (C) 2007 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "gdu-section.h"
+
+#ifndef GDU_SECTION_VOLUMES_H
+#define GDU_SECTION_VOLUMES_H
+
+#define GDU_TYPE_SECTION_VOLUMES           (gdu_section_volumes_get_type ())
+#define GDU_SECTION_VOLUMES(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SECTION_VOLUMES, GduSectionVolumes))
+#define GDU_SECTION_VOLUMES_CLASS(k)       (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_SECTION_VOLUMES,  GduSectionVolumesClass))
+#define GDU_IS_SECTION_VOLUMES(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SECTION_VOLUMES))
+#define GDU_IS_SECTION_VOLUMES_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_SECTION_VOLUMES))
+#define GDU_SECTION_VOLUMES_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_SECTION_VOLUMES, GduSectionVolumesClass))
+
+typedef struct _GduSectionVolumesClass       GduSectionVolumesClass;
+typedef struct _GduSectionVolumes            GduSectionVolumes;
+
+struct _GduSectionVolumesPrivate;
+typedef struct _GduSectionVolumesPrivate     GduSectionVolumesPrivate;
+
+struct _GduSectionVolumes
+{
+        GduSection parent;
+
+        /* private */
+        GduSectionVolumesPrivate *priv;
+};
+
+struct _GduSectionVolumesClass
+{
+        GduSectionClass parent_class;
+};
+
+GType            gdu_section_volumes_get_type (void);
+GtkWidget       *gdu_section_volumes_new      (GduShell       *shell,
+                                               GduPresentable *presentable);
+
+#endif /* GDU_SECTION_VOLUMES_H */
diff --git a/src/palimpsest/gdu-section.c b/src/palimpsest/gdu-section.c
index 80c161b..a3d0e92 100644
--- a/src/palimpsest/gdu-section.c
+++ b/src/palimpsest/gdu-section.c
@@ -135,7 +135,8 @@ gdu_section_class_init (GduSectionClass *klass)
                                                               NULL,
                                                               GDU_TYPE_SHELL,
                                                               G_PARAM_WRITABLE |
-                                                              G_PARAM_READABLE));
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_CONSTRUCT));
 
         /**
          * GduSection:presentable:
@@ -149,7 +150,8 @@ gdu_section_class_init (GduSectionClass *klass)
                                                               NULL,
                                                               GDU_TYPE_PRESENTABLE,
                                                               G_PARAM_WRITABLE |
-                                                              G_PARAM_READABLE));
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_CONSTRUCT));
 }
 
 static void
diff --git a/src/palimpsest/gdu-section.h b/src/palimpsest/gdu-section.h
index b590275..d265a20 100644
--- a/src/palimpsest/gdu-section.h
+++ b/src/palimpsest/gdu-section.h
@@ -26,12 +26,12 @@
 #ifndef GDU_SECTION_H
 #define GDU_SECTION_H
 
-#define GDU_TYPE_SECTION             (gdu_section_get_type ())
-#define GDU_SECTION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_SECTION, GduSection))
-#define GDU_SECTION_CLASS(obj)       (G_TYPE_CHECK_CLASS_CAST ((obj), GDU_SECTION,  GduSectionClass))
-#define GDU_IS_SECTION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_SECTION))
-#define GDU_IS_SECTION_CLASS(obj)    (G_TYPE_CHECK_CLASS_TYPE ((obj), GDU_TYPE_SECTION))
-#define GDU_SECTION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_SECTION, GduSectionClass))
+#define GDU_TYPE_SECTION           (gdu_section_get_type ())
+#define GDU_SECTION(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SECTION, GduSection))
+#define GDU_SECTION_CLASS(k)       (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_SECTION,  GduSectionClass))
+#define GDU_IS_SECTION(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SECTION))
+#define GDU_IS_SECTION_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_SECTION))
+#define GDU_SECTION_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_SECTION, GduSectionClass))
 
 typedef struct _GduSectionClass       GduSectionClass;
 typedef struct _GduSection            GduSection;
diff --git a/src/palimpsest/gdu-shell.c b/src/palimpsest/gdu-shell.c
index ef121a0..8b5f2f9 100644
--- a/src/palimpsest/gdu-shell.c
+++ b/src/palimpsest/gdu-shell.c
@@ -43,6 +43,8 @@
 #include "gdu-section-encrypted.h"
 #include "gdu-section-linux-md-drive.h"
 #include "gdu-section-no-media.h"
+#include "gdu-section-drive.h"
+#include "gdu-section-volumes.h"
 #include "bling-spinner.h"
 
 struct _GduShellPrivate
@@ -198,7 +200,9 @@ details_update (GduShell *shell)
 
         presentable_size = gdu_presentable_get_size (presentable);
         if (presentable_size > 0) {
-                strsize_long = gdu_util_get_size_for_display (presentable_size, TRUE);
+                strsize_long = gdu_util_get_size_for_display (presentable_size,
+                                                              FALSE,
+                                                              TRUE);
         } else {
                 strsize_long = g_strdup (_("Unknown Size"));
         }
@@ -504,15 +508,21 @@ compute_sections_to_show (GduShell *shell)
 
         if (GDU_IS_LINUX_MD_DRIVE (shell->priv->presentable_now_showing)) {
 
+                sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_DRIVE);
+                sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_VOLUMES);
+
                 sections_to_show = g_list_append (sections_to_show,
                                                   (gpointer) GDU_TYPE_SECTION_LINUX_MD_DRIVE);
 
 
         } else if (GDU_IS_DRIVE (shell->priv->presentable_now_showing) && device != NULL) {
 
+                sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_DRIVE);
+                sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_VOLUMES);
+
                 if (gdu_device_is_removable (device) && !gdu_device_is_media_available (device)) {
 
-                        sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_NO_MEDIA);
+                        //sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_NO_MEDIA);
 
                 }
 
@@ -720,6 +730,21 @@ gdu_shell_update (GduShell *shell)
         last_presentable = shell->priv->presentable_now_showing;
 
         sections_to_show = compute_sections_to_show (shell);
+        if (GDU_IS_DRIVE (shell->priv->presentable_now_showing)) {
+                gtk_widget_hide (shell->priv->icon_image);
+                gtk_widget_hide (shell->priv->name_label);
+                gtk_widget_hide (shell->priv->details0_label);
+                gtk_widget_hide (shell->priv->details1_label);
+                gtk_widget_hide (shell->priv->details2_label);
+                gtk_widget_hide (shell->priv->details3_label);
+        } else {
+                gtk_widget_show (shell->priv->icon_image);
+                gtk_widget_show (shell->priv->name_label);
+                gtk_widget_show (shell->priv->details0_label);
+                gtk_widget_show (shell->priv->details1_label);
+                gtk_widget_show (shell->priv->details2_label);
+                gtk_widget_show (shell->priv->details3_label);
+        }
 
         if (job_in_progress) {
                 gchar *desc;
@@ -797,7 +822,7 @@ gdu_shell_update (GduShell *shell)
 
                         gtk_box_pack_start (GTK_BOX (shell->priv->sections_vbox),
                                             section,
-                                            TRUE, TRUE, 0);
+                                            FALSE, FALSE, 0);
                 }
 
         }
@@ -1867,7 +1892,7 @@ about_action_callback (GtkAction *action, gpointer user_data)
                                          NULL);
 
         gtk_show_about_dialog (GTK_WINDOW (shell->priv->app_window),
-                               "program-name", _("Palimpsest Disk Utility"),
+                               "program-name", _("Disk Utility"),
                                "version", VERSION,
                                "copyright", "\xc2\xa9 2008 Red Hat, Inc.",
                                "authors", authors,
@@ -1888,6 +1913,7 @@ static const gchar *ui =
         "      </menu>"
         "      <menuitem action='quit'/>"
         "    </menu>"
+#if 0
         "    <menu action='edit'>"
         "      <menuitem action='mount'/>"
         "      <menuitem action='unmount'/>"
@@ -1904,6 +1930,7 @@ static const gchar *ui =
         "      <separator/>"
         "      <menuitem action='erase'/>"
         "    </menu>"
+#endif
         "    <menu action='help'>"
         "      <menuitem action='contents'/>"
         "      <menuitem action='about'/>"
@@ -1945,9 +1972,8 @@ static GtkActionEntry entries[] = {
         {"stop", "gdu-raid-array-stop", N_("_Stop"), NULL, N_("Stop the array"), G_CALLBACK (stop_action_callback)},
         {"erase", "nautilus-gdu", N_("_Erase"), NULL, N_("Erase the contents of the device"), G_CALLBACK (erase_action_callback)},
 
-
         {"quit", GTK_STOCK_QUIT, N_("_Quit"), "<Ctrl>Q", N_("Quit"), G_CALLBACK (quit_action_callback)},
-        {"contents", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Get Help on Palimpsest Disk Utility"), G_CALLBACK (help_contents_action_callback)},
+        {"contents", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Get Help on Disk Utility"), G_CALLBACK (help_contents_action_callback)},
         {"about", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, G_CALLBACK (about_action_callback)}
 };
 
@@ -2176,7 +2202,6 @@ create_window (GduShell *shell)
         GtkWidget *vbox1;
         GtkWidget *vbox2;
         GtkWidget *menubar;
-        GtkWidget *toolbar;
         GtkAccelGroup *accel_group;
         GtkWidget *hpane;
         GtkWidget *tree_view_scrolled_window;
@@ -2189,13 +2214,14 @@ create_window (GduShell *shell)
         GtkWidget *hbox;
         GtkWidget *image;
         GduPoolTreeModel *model;
+        GtkTreeViewColumn *column;
 
         shell->priv->pool = gdu_pool_new ();
 
         shell->priv->app_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
         gtk_window_set_resizable (GTK_WINDOW (shell->priv->app_window), TRUE);
         gtk_window_set_default_size (GTK_WINDOW (shell->priv->app_window), 800, 600);
-        gtk_window_set_title (GTK_WINDOW (shell->priv->app_window), _("Palimpsest Disk Utility"));
+        gtk_window_set_title (GTK_WINDOW (shell->priv->app_window), _("Disk Utility"));
 
         vbox = gtk_vbox_new (FALSE, 0);
         gtk_container_add (GTK_CONTAINER (shell->priv->app_window), vbox);
@@ -2206,8 +2232,6 @@ create_window (GduShell *shell)
 
         menubar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/menubar");
         gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
-        toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/toolbar");
-        gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
 
         /* tree view */
         tree_view_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
@@ -2217,12 +2241,14 @@ create_window (GduShell *shell)
         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (tree_view_scrolled_window),
                                              GTK_SHADOW_IN);
         model = gdu_pool_tree_model_new (shell->priv->pool,
-                                         GDU_POOL_TREE_MODEL_FLAGS_NONE);
+                                         NULL,
+                                         GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES);
         shell->priv->tree_view = gdu_pool_tree_view_new (model,
                                                          GDU_POOL_TREE_VIEW_FLAGS_NONE);
         g_object_unref (model);
         gtk_container_add (GTK_CONTAINER (tree_view_scrolled_window), shell->priv->tree_view);
 
+
         /* --- */
 
         vbox1 = gtk_vbox_new (FALSE, 0);
@@ -2261,7 +2287,7 @@ create_window (GduShell *shell)
         /* --- */
 
         vbox2 = gtk_vbox_new (FALSE, 0);
-        gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
+        //gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0);
 
         /* --- */
@@ -2321,12 +2347,24 @@ create_window (GduShell *shell)
 
         /* --- */
 
-        shell->priv->sections_vbox = gtk_vbox_new (FALSE, 18);
-        gtk_container_set_border_width (GTK_CONTAINER (shell->priv->sections_vbox), 8);
+        shell->priv->sections_vbox = gtk_vbox_new (FALSE, 12);
+        gtk_container_set_border_width (GTK_CONTAINER (shell->priv->sections_vbox), 6);
         gtk_box_pack_start (GTK_BOX (vbox2), shell->priv->sections_vbox, TRUE, TRUE, 0);
 
         /* setup and add horizontal pane */
         hpane = gtk_hpaned_new ();
+
+        label = gtk_label_new (NULL);
+        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label),
+                                            _("_Storage Devices"));
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), shell->priv->tree_view);
+
+        column = gtk_tree_view_get_column (GTK_TREE_VIEW (shell->priv->tree_view), 0);
+        gtk_tree_view_column_set_widget (column, label);
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (shell->priv->tree_view), TRUE);
+        gtk_widget_show (label);
+
         gtk_paned_add1 (GTK_PANED (hpane), tree_view_scrolled_window);
         gtk_paned_add2 (GTK_PANED (hpane), vbox1);
         //gtk_paned_set_position (GTK_PANED (hpane), 260);



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