[gnome-disk-utility] More detail in the TreeView and incremental work on the Create RAID dialog



commit 38fd7da105fbf80db78be64641c898aa59f8a08f
Author: David Zeuthen <davidz redhat com>
Date:   Tue Aug 4 13:10:47 2009 -0400

    More detail in the TreeView and incremental work on the Create RAID dialog

 src/gdu-gtk/Makefile.am                     |   40 ++-
 src/gdu-gtk/gdu-create-linux-md-dialog.c    |  673 +++++++++++++++++++++++++--
 src/gdu-gtk/gdu-create-linux-md-dialog.h    |   20 +-
 src/gdu-gtk/gdu-gtk-enums.h                 |   61 +++
 src/gdu-gtk/gdu-gtk-enumtypes.c.template    |   39 ++
 src/gdu-gtk/gdu-gtk-enumtypes.h.template    |   24 +
 src/gdu-gtk/gdu-gtk-types.h                 |    4 +
 src/gdu-gtk/gdu-gtk.h                       |    2 +
 src/gdu-gtk/gdu-pool-tree-model.c           |   91 +++--
 src/gdu-gtk/gdu-pool-tree-model.h           |    6 -
 src/gdu-gtk/gdu-pool-tree-view.c            |  189 +++++++-
 src/gdu-gtk/gdu-pool-tree-view.h            |   12 +-
 src/gdu-gtk/gdu-size-widget.c               |  408 ++++++++++++++++
 src/gdu-gtk/gdu-size-widget.h               |   72 +++
 src/gdu/gdu-drive.c                         |  219 ++++++++-
 src/gdu/gdu-linux-md-drive.c                |  114 ++++-
 src/gdu/gdu-presentable.c                   |   23 +-
 src/gdu/gdu-presentable.h                   |    6 +-
 src/gdu/gdu-volume-hole.c                   |    9 +-
 src/gdu/gdu-volume.c                        |   78 +++-
 src/palimpsest/gdu-section-linux-md-drive.c |    2 +-
 src/palimpsest/gdu-shell.c                  |    3 +-
 22 files changed, 1907 insertions(+), 188 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 62608e6..21fa964 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -1,6 +1,26 @@
 
 NULL =
 
+gdu-gtk-enumtypes.h: gdu-gtk-enums.h gdu-gtk-enumtypes.h.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	 cd $(srcdir) && glib-mkenums --template gdu-gtk-enumtypes.h.template gdu-gtk-enums.h ) > \
+	   gdu-gtk-enumtypes.h.tmp && mv gdu-gtk-enumtypes.h.tmp gdu-gtk-enumtypes.h
+
+gdu-gtk-enumtypes.c: gdu-gtk-enums.h gdu-gtk-enumtypes.c.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	 cd $(srcdir) && glib-mkenums --template gdu-gtk-enumtypes.c.template gdu-gtk-enums.h ) > \
+	   gdu-gtk-enumtypes.c.tmp && mv gdu-gtk-enumtypes.c.tmp gdu-gtk-enumtypes.c
+
+BUILT_SOURCES =            	\
+	gdu-gtk-enumtypes.c     \
+	gdu-gtk-enumtypes.h     \
+	$(NULL)
+
+EXTRADIST =                         	\
+	gdu-gtk-enumtypes.h.template    \
+	gdu-gtk-enumtypes.c.template    \
+	$(NULL)
+
 lib_LTLIBRARIES=libgdu-gtk.la
 
 libgdu_gtkincludedir=$(includedir)/gnome-disk-utility/gdu-gtk
@@ -8,19 +28,25 @@ libgdu_gtkincludedir=$(includedir)/gnome-disk-utility/gdu-gtk
 libgdu_gtkinclude_HEADERS =              				\
 	gdu-gtk.h							\
 	gdu-gtk-types.h							\
+	gdu-gtk-enums.h							\
+	gdu-gtk-enumtypes.h						\
 	gdu-time-label.h						\
 	gdu-pool-tree-view.h						\
 	gdu-pool-tree-model.h						\
+	gdu-size-widget.h						\
 	gdu-create-linux-md-dialog.h					\
 	$(NULL)
 
-libgdu_gtk_la_SOURCES =                 	               		\
-	gdu-gtk.h			gdu-gtk.c			\
-	gdu-gtk-types.h							\
-	gdu-time-label.h		gdu-time-label.c		\
-	gdu-pool-tree-view.h		gdu-pool-tree-view.c		\
-	gdu-pool-tree-model.h		gdu-pool-tree-model.c		\
-	gdu-create-linux-md-dialog.h	gdu-create-linux-md-dialog.c	\
+libgdu_gtk_la_SOURCES =                 	               				\
+	gdu-gtk.h				gdu-gtk.c				\
+	gdu-gtk-enums.h									\
+	gdu-gtk-enumtypes.h			gdu-gtk-enumtypes.c			\
+	gdu-gtk-types.h									\
+	gdu-time-label.h			gdu-time-label.c			\
+	gdu-pool-tree-view.h			gdu-pool-tree-view.c			\
+	gdu-pool-tree-model.h			gdu-pool-tree-model.c			\
+	gdu-size-widget.h			gdu-size-widget.c			\
+	gdu-create-linux-md-dialog.h		gdu-create-linux-md-dialog.c		\
 	$(NULL)
 
 libgdu_gtk_la_CPPFLAGS = 				\
diff --git a/src/gdu-gtk/gdu-create-linux-md-dialog.c b/src/gdu-gtk/gdu-create-linux-md-dialog.c
index 82a66ae..45dbc25 100644
--- a/src/gdu-gtk/gdu-create-linux-md-dialog.c
+++ b/src/gdu-gtk/gdu-create-linux-md-dialog.c
@@ -20,16 +20,44 @@
  * Author: David Zeuthen <davidz redhat com>
  */
 
+#define _GNU_SOURCE
+
 #include "config.h"
 
 #include <glib/gi18n-lib.h>
+#include <math.h>
+
 #include "gdu-create-linux-md-dialog.h"
+#include "gdu-size-widget.h"
 
 struct GduCreateLinuxMdDialogPrivate
 {
-        gchar *level;
         GduPool *pool;
+        GduPoolTreeModel *model;
+
+        /* represents user selected options */
+        gchar *level;
+        guint num_disks;
+        guint64 component_size;
+        guint64 total_size;
+
+        /* Number of disks with room for components */
+        guint available_num_disks;
+
+        /* The maximum possible size of the array - this is a function of available_num_disks
+         * and num_disks and how each disk is laid out. Is 0 if an array for the given
+         * configuration cannot be created.
+         */
+        guint64 available_total_size;
+
         GtkWidget *name_entry;
+        GtkWidget *num_disks_spin_button;
+        GtkWidget *size_widget;
+        GtkWidget *tree_view;
+
+        GtkWidget *tip_container;
+        GtkWidget *tip_image;
+        GtkWidget *tip_label;
 };
 
 enum
@@ -42,6 +70,32 @@ enum
 
 static void gdu_create_linux_md_dialog_constructed (GObject *object);
 
+static void update (GduCreateLinuxMdDialog *dialog);
+
+static void on_presentable_added   (GduPool          *pool,
+                                    GduPresentable   *presentable,
+                                    gpointer          user_data);
+static void on_presentable_removed (GduPool          *pool,
+                                    GduPresentable   *presentable,
+                                    gpointer          user_data);
+static void on_presentable_changed (GduPool          *pool,
+                                    GduPresentable   *presentable,
+                                    gpointer          user_data);
+
+static void on_row_changed (GtkTreeModel *tree_model,
+                            GtkTreePath  *path,
+                            GtkTreeIter  *iter,
+                            gpointer      user_data);
+
+static void on_row_deleted (GtkTreeModel *tree_model,
+                            GtkTreePath  *path,
+                            gpointer      user_data);
+
+static void on_row_inserted (GtkTreeModel *tree_model,
+                             GtkTreePath  *path,
+                             GtkTreeIter  *iter,
+                             gpointer      user_data);
+
 G_DEFINE_TYPE (GduCreateLinuxMdDialog, gdu_create_linux_md_dialog, GTK_TYPE_DIALOG)
 
 static void
@@ -49,7 +103,15 @@ gdu_create_linux_md_dialog_finalize (GObject *object)
 {
         GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (object);
 
+        g_signal_handlers_disconnect_by_func (dialog->priv->pool, on_presentable_added, dialog);
+        g_signal_handlers_disconnect_by_func (dialog->priv->pool, on_presentable_removed, dialog);
+        g_signal_handlers_disconnect_by_func (dialog->priv->pool, on_presentable_changed, dialog);
+        g_signal_handlers_disconnect_by_func (dialog->priv->model, on_row_changed, dialog);
+        g_signal_handlers_disconnect_by_func (dialog->priv->model, on_row_deleted, dialog);
+        g_signal_handlers_disconnect_by_func (dialog->priv->model, on_row_inserted, dialog);
+
         g_object_unref (dialog->priv->pool);
+        g_object_unref (dialog->priv->model);
         g_free (dialog->priv->level);
 
         if (G_OBJECT_CLASS (gdu_create_linux_md_dialog_parent_class)->finalize != NULL)
@@ -58,9 +120,9 @@ gdu_create_linux_md_dialog_finalize (GObject *object)
 
 static void
 gdu_create_linux_md_dialog_get_property (GObject    *object,
-                                guint       property_id,
-                                GValue     *value,
-                                GParamSpec *pspec)
+                                         guint       property_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
 {
         GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (object);
 
@@ -85,9 +147,9 @@ gdu_create_linux_md_dialog_get_property (GObject    *object,
 
 static void
 gdu_create_linux_md_dialog_set_property (GObject      *object,
-                                   guint         property_id,
-                                   const GValue *value,
-                                   GParamSpec   *pspec)
+                                         guint         property_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
 {
         GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (object);
 
@@ -231,7 +293,7 @@ on_combo_box_changed (GtkWidget *combo_box,
 
 static void
 on_name_entry_activated (GtkWidget *combo_box,
-                             gpointer   user_data)
+                         gpointer   user_data)
 {
         GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
 
@@ -241,6 +303,28 @@ on_name_entry_activated (GtkWidget *combo_box,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+on_num_disks_spin_button_value_changed (GtkSpinButton *spin_button,
+                                        gpointer       user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+
+        dialog->priv->num_disks = (gint) gtk_spin_button_get_value (spin_button);
+
+        update (dialog);
+}
+
+static void
+on_size_widget_changed (GduSizeWidget *widget,
+                        gpointer       user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+
+        dialog->priv->total_size = (guint64) gdu_size_widget_get_size (widget);
+
+        update (dialog);
+}
+
+static void
 gdu_create_linux_md_dialog_constructed (GObject *object)
 {
         GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (object);
@@ -252,8 +336,10 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         GtkWidget *table;
         GtkWidget *entry;
         GtkWidget *combo_box;
+        GtkWidget *vbox;
         GtkWidget *vbox2;
-        GdkPixbuf *pixbuf;
+        GtkWidget *spin_button;
+        GtkWidget *size_widget;
         gint row;
         gboolean ret;
         GtkWidget *align;
@@ -261,44 +347,39 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
 
         ret = FALSE;
 
-        pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
-                                           "gdu-raid-array",
-                                           48,
-                                           0,
-                                           NULL);
-
         gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
         gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
         gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 0);
         gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5);
         gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6);
 
-        gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-        gtk_window_set_title (GTK_WINDOW (dialog), "");
-        gtk_window_set_icon_name (GTK_WINDOW (dialog), "nautilus-gdu");
+        gtk_window_set_title (GTK_WINDOW (dialog), _("Create RAID Array"));
+        gtk_window_set_icon_name (GTK_WINDOW (dialog), "gdu-raid-array");
 
         gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
         button = gtk_dialog_add_button (GTK_DIALOG (dialog),
-                                        _("C_reate"),
+                                        _("Cr_eate"),
                                         GTK_RESPONSE_OK);
         gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 
         content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
         gtk_container_set_border_width (GTK_CONTAINER (content_area), 10);
 
-        /*  icon and text labels  */
-        hbox = gtk_hbox_new (FALSE, 0);
-        gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, TRUE, 0);
-
-        image = gtk_image_new_from_pixbuf (pixbuf);
-        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
-        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 12);
+        //vbox = gtk_vbox_new (FALSE, 0);
+        //gtk_container_add (GTK_CONTAINER (content_area), vbox);
+        vbox = content_area;
 
-        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
-        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 12, 0, 0);
-        gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0);
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        s = g_strconcat ("<b>", _("General"), "</b>", NULL);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
 
         vbox2 = gtk_vbox_new (FALSE, 12);
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
         gtk_container_add (GTK_CONTAINER (align), vbox2);
 
         row = 0;
@@ -308,7 +389,7 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         gtk_table_set_row_spacings (GTK_TABLE (table), 6);
         gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
 
-        /*  filesystem type  */
+        /*  RAID level  */
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("_Level:"));
@@ -337,7 +418,7 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box);
         row++;
 
-        /*  filesystem label  */
+        /*  array name  */
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("_Name:"));
@@ -353,31 +434,91 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         dialog->priv->name_entry = entry;
         row++;
 
-        /* -------------------------------------------------------------------------------- */
+        /* Number of components */
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("Number of _Disks:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                          GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+
+
+        spin_button = gtk_spin_button_new_with_range (2, 10000, 1);
+        dialog->priv->num_disks_spin_button = spin_button;
+        g_signal_connect (spin_button,
+                          "value-changed",
+                          G_CALLBACK (on_num_disks_spin_button_value_changed),
+                          dialog);
 
-        /* component tree-views */
+        align = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
+        gtk_container_add (GTK_CONTAINER (align), spin_button);
+
+        gtk_table_attach (GTK_TABLE (table),
+                          align,
+                          1, 2,
+                          row, row + 1,
+                          GTK_EXPAND | GTK_FILL,
+                          GTK_EXPAND | GTK_FILL, 2, 2);
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
+        row++;
+
+        /* Size */
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("Array _Size:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                          GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
 
+        size_widget = gdu_size_widget_new (1000 * 1000,
+                                           1000 * 1000,
+                                           10 * 1000 * 1000);
+        dialog->priv->size_widget = size_widget;
+        g_signal_connect (size_widget,
+                          "changed",
+                          G_CALLBACK (on_size_widget_changed),
+                          dialog);
 
+        gtk_table_attach (GTK_TABLE (table), size_widget, 1, 2, row, row + 1,
+                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), size_widget);
+        row++;
 
         /* -------------------------------------------------------------------------------- */
 
-        hbox = gtk_hbox_new (FALSE, 6);
-        gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+        /* Tree view for showing selected volumes */
+        GtkWidget *tree_view;
+        GtkWidget *scrolled_window;
+
+        dialog->priv->model = gdu_pool_tree_model_new (dialog->priv->pool);
+
+        tree_view = gdu_pool_tree_view_new (dialog->priv->model,
+                                            GDU_POOL_TREE_VIEW_FLAGS_SHOW_TOGGLE);
+        dialog->priv->tree_view = tree_view;
 
-        image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_MENU);
-        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_NEVER,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+                                             GTK_SHADOW_IN);
 
-	label = gtk_label_new (NULL);
-        s = g_strconcat ("<i>",
-                         _("Warning: All data on the volume will be irrevocably lost."),
-                         "</i>",
-                         NULL);
+        gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        s = g_strconcat ("<b>", _("Components"), "</b>", NULL);
         gtk_label_set_markup (GTK_LABEL (label), s);
         g_free (s);
-	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
-	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+        vbox2 = gtk_vbox_new (FALSE, 12);
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
+        gtk_container_add (GTK_CONTAINER (align), vbox2);
 
+        gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0);
+
+        /* -------------------------------------------------------------------------------- */
 
         g_signal_connect (combo_box,
                           "changed",
@@ -392,8 +533,450 @@ gdu_create_linux_md_dialog_constructed (GObject *object)
         gtk_widget_grab_focus (dialog->priv->name_entry);
         gtk_editable_select_region (GTK_EDITABLE (dialog->priv->name_entry), 0, 1000);
 
+
+        /* -------------------------------------------------------------------------------- */
+
+        g_signal_connect (dialog->priv->pool,
+                          "presentable-added",
+                          G_CALLBACK (on_presentable_added),
+                          dialog);
+        g_signal_connect (dialog->priv->pool,
+                          "presentable-removed",
+                          G_CALLBACK (on_presentable_removed),
+                          dialog);
+        g_signal_connect (dialog->priv->pool,
+                          "presentable-changed",
+                          G_CALLBACK (on_presentable_changed),
+                          dialog);
+        g_signal_connect (dialog->priv->model,
+                          "row-changed",
+                          G_CALLBACK (on_row_changed),
+                          dialog);
+        g_signal_connect (dialog->priv->model,
+                          "row-deleted",
+                          G_CALLBACK (on_row_deleted),
+                          dialog);
+        g_signal_connect (dialog->priv->model,
+                          "row-inserted",
+                          G_CALLBACK (on_row_inserted),
+                          dialog);
+
+        hbox = gtk_hbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+
+        image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_MENU);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+        dialog->priv->tip_container = hbox;
+        dialog->priv->tip_image = image;
+        dialog->priv->tip_label = label;
+        gtk_widget_set_no_show_all (hbox, TRUE);
+
+        /* update the dialog */
+        update (dialog);
+
+        /* and start out with selecting max size */
+        gdu_size_widget_set_size (GDU_SIZE_WIDGET (dialog->priv->size_widget),
+                                  dialog->priv->available_total_size);
+
+        /* select a sane size for the dialog and allow resizing */
+        gtk_widget_set_size_request (GTK_WIDGET (dialog), 400, 450);
+        gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
         if (G_OBJECT_CLASS (gdu_create_linux_md_dialog_parent_class)->constructed != NULL)
                 G_OBJECT_CLASS (gdu_create_linux_md_dialog_parent_class)->constructed (object);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_presentable_added (GduPool          *pool,
+                      GduPresentable   *presentable,
+                      gpointer          user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+static void
+on_presentable_removed (GduPool          *pool,
+                        GduPresentable   *presentable,
+                        gpointer          user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+static void
+on_presentable_changed (GduPool          *pool,
+                        GduPresentable   *presentable,
+                        gpointer          user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+static void
+on_row_changed (GtkTreeModel *tree_model,
+                GtkTreePath  *path,
+                GtkTreeIter  *iter,
+                gpointer      user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+static void
+on_row_deleted (GtkTreeModel *tree_model,
+                GtkTreePath  *path,
+                gpointer      user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+static void
+on_row_inserted (GtkTreeModel *tree_model,
+                 GtkTreePath  *path,
+                 GtkTreeIter  *iter,
+                 gpointer      user_data)
+{
+        GduCreateLinuxMdDialog *dialog = GDU_CREATE_LINUX_MD_DIALOG (user_data);
+        update (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
+guint64_compare (gconstpointer a,
+                 gconstpointer b)
+{
+        guint64 va, vb;
+        va = *((guint64 *) a);
+        vb = *((guint64 *) b);
+        if (va > vb)
+                return 1;
+        else if (va < vb)
+                return -1;
+        else
+                return 0;
+}
+
+static gboolean
+get_selected_foreach_func (GtkTreeModel *model,
+                           GtkTreePath  *path,
+                           GtkTreeIter  *iter,
+                           gpointer      user_data)
+{
+        GList **ret = user_data;
+        gboolean toggled;
+        GduPresentable *presentable;
+
+        gtk_tree_model_get (model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &presentable,
+                            GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, &toggled,
+                            -1);
+        if (presentable != NULL) {
+                if (toggled)
+                        *ret = g_list_prepend (*ret, g_object_ref (presentable));
+                g_object_unref (presentable);
+        }
+
+        return FALSE; /* keep iterating */
+}
+
+
+static GList *
+get_selected_presentables (GduCreateLinuxMdDialog *dialog)
+{
+        GList *ret;
+        ret = NULL;
+        gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->priv->model),
+                                get_selected_foreach_func,
+                                &ret);
+        return ret;
+}
+
+static void
+update (GduCreateLinuxMdDialog *dialog)
+{
+        GList *l;
+        GList *presentables;
+        GHashTable *map_disks_to_biggest_component_size;
+        GList *selected_presentables;
+        GList *drives_for_selected_presentables;
+        gboolean can_create;
+        guint num_toggled;
+        gchar *tip_text;
+        const gchar *tip_stock_icon;
+
+        tip_text = NULL;
+        tip_stock_icon = NULL;
+
+        can_create = FALSE;
+
+        if (dialog->priv->num_disks < 2)
+                dialog->priv->num_disks = 2;
+
+        presentables = gdu_pool_get_presentables (dialog->priv->pool);
+        selected_presentables = get_selected_presentables (dialog);
+        drives_for_selected_presentables = NULL;
+        for (l = selected_presentables; l != NULL; l = l->next) {
+                GduPresentable *p = GDU_PRESENTABLE (l->data);
+                GduPresentable *drive;
+
+                drive = gdu_presentable_get_toplevel (p);
+                if (drive == NULL)
+                        continue;
+
+                g_object_unref (drive); /* don't own the ref */
+                if (!GDU_IS_DRIVE (drive))
+                        continue;
+
+                if (g_list_find (drives_for_selected_presentables, drive) == NULL)
+                        drives_for_selected_presentables = g_list_prepend (drives_for_selected_presentables, drive);
+        }
+
+        /* hash from drive to largest free component */
+        map_disks_to_biggest_component_size = g_hash_table_new_full ((GHashFunc) gdu_presentable_hash,
+                                                                     (GEqualFunc) gdu_presentable_equals,
+                                                                     g_object_unref,
+                                                                     g_free);
+        for (l = presentables; l != NULL; l = l->next) {
+                GduPresentable *p = GDU_PRESENTABLE (l->data);
+                GduPresentable *drive;
+                guint64 size;
+                guint64 *existing_size;
+
+                if (gdu_presentable_is_allocated (p))
+                        continue;
+
+                drive = gdu_presentable_get_toplevel (p);
+                if (drive == NULL)
+                        continue;
+
+                g_object_unref (drive); /* don't own the ref */
+                if (!GDU_IS_DRIVE (drive))
+                        continue;
+
+                size = gdu_presentable_get_size (p);
+
+                existing_size = g_hash_table_lookup (map_disks_to_biggest_component_size,
+                                                     drive);
+                if ((existing_size == NULL) || (existing_size != NULL && (*existing_size) > size)) {
+                        g_hash_table_insert (map_disks_to_biggest_component_size,
+                                             g_object_ref (drive),
+                                             g_memdup (&size, sizeof (guint64)));
+                }
+        }
+
+        dialog->priv->available_num_disks = g_hash_table_size (map_disks_to_biggest_component_size);
+
+        if (dialog->priv->num_disks > dialog->priv->available_num_disks) {
+                dialog->priv->available_total_size = 0;
+        } else {
+                GList *sizes;
+
+                sizes = g_hash_table_get_values (map_disks_to_biggest_component_size);
+                sizes = g_list_sort (sizes, guint64_compare);
+                sizes = g_list_reverse (sizes);
+                /* biggest is now first */
+
+                dialog->priv->available_total_size = dialog->priv->num_disks *
+                        (*((guint64 *) g_list_nth_data (sizes, dialog->priv->num_disks - 1)));
+
+                g_list_free (sizes);
+        }
+
+        /* clamp total_size and num_disks to what is available */
+        if (dialog->priv->total_size > dialog->priv->available_total_size)
+                dialog->priv->total_size = dialog->priv->available_total_size;
+        if (dialog->priv->num_disks > dialog->priv->available_num_disks)
+                dialog->priv->num_disks = dialog->priv->available_num_disks;
+
+        //if (dialog->priv->total_size == 0)
+        //        dialog->priv->total_size = dialog->priv->available_total_size;
+
+        g_debug ("==========");
+        g_debug ("available_num_disks = %d", dialog->priv->available_num_disks);
+        g_debug ("available_total_size = %" G_GUINT64_FORMAT, dialog->priv->available_total_size);
+        g_debug ("num_disks = %d", dialog->priv->num_disks);
+        g_debug ("total_size = %" G_GUINT64_FORMAT, dialog->priv->total_size);
+
+        if (dialog->priv->available_total_size == 0) {
+                gtk_widget_set_sensitive (dialog->priv->size_widget, FALSE);
+                gtk_widget_set_sensitive (dialog->priv->tree_view, FALSE);
+                tip_text = g_strdup (_("Not enough disks with available space. "
+                                       "Plug in more disks or make space available."));
+                tip_stock_icon = GTK_STOCK_DIALOG_ERROR;
+        } else {
+                gtk_widget_set_sensitive (dialog->priv->size_widget, TRUE);
+                gtk_widget_set_sensitive (dialog->priv->tree_view, TRUE);
+        }
+
+        /* set range for num_disks_spin_button according to what we've found out */
+        gtk_spin_button_set_range (GTK_SPIN_BUTTON (dialog->priv->num_disks_spin_button),
+                                   2.0,
+                                   dialog->priv->available_num_disks);
+        if (gtk_spin_button_get_value (GTK_SPIN_BUTTON (dialog->priv->num_disks_spin_button)) != dialog->priv->num_disks)
+                gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->num_disks_spin_button),
+                                           dialog->priv->num_disks);
+
+        /* ditto for the size widget */
+        gdu_size_widget_set_max_size (GDU_SIZE_WIDGET (dialog->priv->size_widget),
+                                      dialog->priv->available_total_size);
+        if (gdu_size_widget_get_size (GDU_SIZE_WIDGET (dialog->priv->size_widget)) != dialog->priv->total_size)
+                gdu_size_widget_set_size (GDU_SIZE_WIDGET (dialog->priv->size_widget),
+                                          dialog->priv->total_size);
+
+        /* --- */
+
+        /* now update the tree model toggles to make free space objects selectable */
+        num_toggled = 0;
+        for (l = presentables; l != NULL; l = l->next) {
+                GduPresentable *p = GDU_PRESENTABLE (l->data);
+                GduPresentable *drive;
+                guint64 size;
+                gboolean can_be_toggled;
+                GtkTreeIter iter;
+                gboolean cur_can_be_toggled;
+                gboolean cur_toggled;
+
+                can_be_toggled = FALSE;
+
+                if (gdu_presentable_is_allocated (p))
+                        goto determined;
+
+                drive = gdu_presentable_get_toplevel (p);
+                if (drive == NULL)
+                        goto determined;
+
+                g_object_unref (drive); /* don't own the ref */
+                if (!GDU_IS_DRIVE (drive))
+                        goto determined;
+
+                size = gdu_presentable_get_size (p);
+
+                if (size < dialog->priv->total_size / dialog->priv->num_disks)
+                        goto determined;
+
+                /* don't allow selecting two volumes on the same drive */
+                if (g_list_find (drives_for_selected_presentables, drive) != NULL &&
+                    g_list_find (selected_presentables, p) == NULL)
+                        goto determined;
+
+                can_be_toggled = TRUE;
+
+        determined:
+                if (!gdu_pool_tree_model_get_iter_for_presentable (dialog->priv->model,
+                                                                   p,
+                                                                   &iter)) {
+                        g_warning ("Cannot find tree iter for presentable");
+                        continue;
+                }
+
+                /* only update if there's a change - otherwise we'll cause loops */
+                gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->model),
+                                    &iter,
+                                    GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED, &cur_can_be_toggled,
+                                    GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, &cur_toggled,
+                                    -1);
+
+                if (cur_can_be_toggled != can_be_toggled) {
+                        gtk_tree_store_set (GTK_TREE_STORE (dialog->priv->model),
+                                            &iter,
+                                            GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED, can_be_toggled,
+                                            -1);
+                }
+                if (!can_be_toggled) {
+                        if (cur_toggled) {
+                                gtk_tree_store_set (GTK_TREE_STORE (dialog->priv->model),
+                                                    &iter,
+                                                    GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, FALSE,
+                                                    -1);
+                                cur_toggled = FALSE;
+                        }
+                }
+
+                if (cur_toggled)
+                        num_toggled++;
+        }
+
+        /* --- */
+
+        if (num_toggled == dialog->priv->num_disks) {
+                can_create = TRUE;
+                if (tip_text == NULL) {
+                        if (dialog->priv->total_size < 1000 * 1000) {
+                                tip_text = g_strdup (_("Increase the size of the array."));
+                                tip_stock_icon = GTK_STOCK_DIALOG_INFO;
+                                can_create = FALSE;
+                        } else {
+                                tip_text = g_strdup (_("Array is ready to be created."));
+                                tip_stock_icon = GTK_STOCK_DIALOG_INFO;
+                        }
+                }
+        } else if (num_toggled < dialog->priv->num_disks) {
+                if (tip_text == NULL) {
+                        tip_text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
+                                                                 N_("Select one more component."),
+                                                                 N_("Select %d more components."),
+                                                                 dialog->priv->num_disks - num_toggled),
+                                                    dialog->priv->num_disks - num_toggled);
+                        tip_stock_icon = GTK_STOCK_DIALOG_INFO;
+                }
+        } else {
+                /* num_toggled > dialog->priv->num_disks */
+                if (tip_text == NULL) {
+                        tip_text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
+                                                                 N_("Deselect one more component."),
+                                                                 N_("Deselect %d more components."),
+                                                                 dialog->priv->num_disks - num_toggled),
+                                                    dialog->priv->num_disks - num_toggled);
+                        tip_stock_icon = GTK_STOCK_DIALOG_INFO;
+                }
+        }
+
+        gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_OK,
+                                           can_create);
+
+        if (tip_text != NULL) {
+                gchar *s;
+                s = g_strconcat ("<i>",
+                                 tip_text,
+                                 "</i>",
+                                 NULL);
+                gtk_label_set_markup (GTK_LABEL (dialog->priv->tip_label), s);
+                g_free (s);
+                gtk_image_set_from_stock (GTK_IMAGE (dialog->priv->tip_image),
+                                          tip_stock_icon,
+                                          GTK_ICON_SIZE_MENU);
+                gtk_widget_show (dialog->priv->tip_container);
+                gtk_widget_show (dialog->priv->tip_image);
+                gtk_widget_show (dialog->priv->tip_label);
+        } else {
+                gtk_widget_hide (dialog->priv->tip_container);
+                gtk_widget_hide (dialog->priv->tip_image);
+                gtk_widget_hide (dialog->priv->tip_label);
+        }
+
+        /* --- */
+
+        g_list_foreach (presentables, (GFunc) g_object_unref, NULL);
+        g_list_free (presentables);
+        g_list_foreach (selected_presentables, (GFunc) g_object_unref, NULL);
+        g_list_free (selected_presentables);
+        g_list_free (drives_for_selected_presentables);
+        g_hash_table_unref (map_disks_to_biggest_component_size);
+        g_free (tip_text);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/gdu-gtk/gdu-create-linux-md-dialog.h b/src/gdu-gtk/gdu-create-linux-md-dialog.h
index 7fad710..51420e6 100644
--- a/src/gdu-gtk/gdu-create-linux-md-dialog.h
+++ b/src/gdu-gtk/gdu-create-linux-md-dialog.h
@@ -27,12 +27,12 @@
 
 G_BEGIN_DECLS
 
-#define GDU_TYPE_CREATE_LINUX_MD_DIALOG            gdu_create_linux_md_dialog_get_type()
-#define GDU_CREATE_LINUX_MD_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialog))
-#define GDU_CREATE_LINUX_MD_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialogClass))
-#define GDU_IS_CREATE_LINUX_MD_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_CREATE_LINUX_MD_DIALOG))
-#define GDU_IS_CREATE_LINUX_MD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDU_TYPE_CREATE_LINUX_MD_DIALOG))
-#define GDU_CREATE_LINUX_MD_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialogClass))
+#define GDU_TYPE_CREATE_LINUX_MD_DIALOG         (gdu_create_linux_md_dialog_get_type())
+#define GDU_CREATE_LINUX_MD_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialog))
+#define GDU_CREATE_LINUX_MD_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialogClass))
+#define GDU_IS_CREATE_LINUX_MD_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_CREATE_LINUX_MD_DIALOG))
+#define GDU_IS_CREATE_LINUX_MD_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_CREATE_LINUX_MD_DIALOG))
+#define GDU_CREATE_LINUX_MD_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_CREATE_LINUX_MD_DIALOG, GduCreateLinuxMdDialogClass))
 
 typedef struct GduCreateLinuxMdDialogClass   GduCreateLinuxMdDialogClass;
 typedef struct GduCreateLinuxMdDialogPrivate GduCreateLinuxMdDialogPrivate;
@@ -51,10 +51,10 @@ struct GduCreateLinuxMdDialogClass
 };
 
 GType       gdu_create_linux_md_dialog_get_type  (void) G_GNUC_CONST;
-GtkWidget*  gdu_create_linux_md_dialog_new       (GtkWindow              *parent,
-                                                  GduPool                *pool);
-gchar      *gdu_create_linux_md_dialog_get_level (GduCreateLinuxMdDialog *dialog);
-gchar      *gdu_create_linux_md_dialog_get_name  (GduCreateLinuxMdDialog *dialog);
+GtkWidget*  gdu_create_linux_md_dialog_new       (GtkWindow               *parent,
+                                                  GduPool                 *pool);
+gchar      *gdu_create_linux_md_dialog_get_level (GduCreateLinuxMdDialog  *dialog);
+gchar      *gdu_create_linux_md_dialog_get_name  (GduCreateLinuxMdDialog  *dialog);
 
 G_END_DECLS
 
diff --git a/src/gdu-gtk/gdu-gtk-enums.h b/src/gdu-gtk/gdu-gtk-enums.h
new file mode 100644
index 0000000..9f2cc66
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enums.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-gtk-enums.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.
+ */
+
+#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_GTK_ENUMS_H
+#define GDU_GTK_ENUMS_H
+
+#include <glib-object.h>
+
+/**
+ * GduPoolTreeModelColumn:
+ * @GDU_POOL_TREE_MODEL_COLUMN_ICON: The icon for the presentable.
+ * @GDU_POOL_TREE_MODEL_COLUMN_VPD_NAME: Name for the presentable derived from Vital Product Data,
+ * e.g. "ATA INTEL SSDSA2MH080G1GC".
+ * @GDU_POOL_TREE_MODEL_COLUMN_NAME: Human readable name of the presentable, e.g. "80 GB Solid-state Disk" or
+ * "Fedora (Rawhide)".
+ * @GDU_POOL_TREE_MODEL_COLUMN_DESCRIPTION: Human readable description of the presentable, e.g. "MBR Partition Table"
+ * or "32GB Linux ext3".
+ * @GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE: The #GduPresentable object.
+ * @GDU_POOL_TREE_MODEL_COLUMN_TOGGLED: Whether the item can be toggled.
+ * @GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED: Whether the item is toggled.
+ *
+ * Columns used in #GduPoolTreeModel.
+ */
+typedef enum {
+        GDU_POOL_TREE_MODEL_COLUMN_ICON,
+        GDU_POOL_TREE_MODEL_COLUMN_VPD_NAME,
+        GDU_POOL_TREE_MODEL_COLUMN_NAME,
+        GDU_POOL_TREE_MODEL_COLUMN_DESCRIPTION,
+        GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE,
+        GDU_POOL_TREE_MODEL_COLUMN_TOGGLED,
+        GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED,
+} GduPoolTreeModelColumn;
+
+typedef enum {
+        GDU_POOL_TREE_VIEW_FLAGS_NONE        = 0,
+        GDU_POOL_TREE_VIEW_FLAGS_SHOW_TOGGLE = (1<<0),
+} GduPoolTreeViewFlags;
+
+#endif /* GDU_GTK_ENUMS_H */
diff --git a/src/gdu-gtk/gdu-gtk-enumtypes.c.template b/src/gdu-gtk/gdu-gtk-enumtypes.c.template
new file mode 100644
index 0000000..9ffe156
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enumtypes.c.template
@@ -0,0 +1,39 @@
+/*** BEGIN file-header ***/
+#include <gdu-gtk/gdu-gtk.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/src/gdu-gtk/gdu-gtk-enumtypes.h.template b/src/gdu-gtk/gdu-gtk-enumtypes.h.template
new file mode 100644
index 0000000..4737f89
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enumtypes.h.template
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __GDU_GTK_ENUM_TYPES_H__
+#define __GDU_GTK_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX _TYPE_@ENUMSHORT@ (@enum_name _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __GDU_GTK_ENUM_TYPES_H__ */
+/*** END file-tail ***/
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index e419d75..ca89682 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -29,6 +29,7 @@
 #include <glib-object.h>
 #include <gdu/gdu.h>
 #include <gtk/gtk.h>
+#include <gdu-gtk/gdu-gtk-enums.h>
 
 G_BEGIN_DECLS
 
@@ -44,6 +45,9 @@ typedef struct GduPoolTreeView          GduPoolTreeView;
 struct GduCreateLinuxMdDialog;
 typedef struct GduCreateLinuxMdDialog   GduCreateLinuxMdDialog;
 
+struct GduSizeWidget;
+typedef struct GduSizeWidget            GduSizeWidget;
+
 G_END_DECLS
 
 #endif /* __GDU_GTK_TYPES_H */
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index 85965e2..054b054 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -28,9 +28,11 @@
 
 #define __GDU_GTK_INSIDE_GDU_GTK_H
 #include <gdu-gtk/gdu-gtk-types.h>
+#include <gdu-gtk/gdu-gtk-enumtypes.h>
 #include <gdu-gtk/gdu-time-label.h>
 #include <gdu-gtk/gdu-pool-tree-view.h>
 #include <gdu-gtk/gdu-pool-tree-model.h>
+#include <gdu-gtk/gdu-size-widget.h>
 #include <gdu-gtk/gdu-create-linux-md-dialog.h>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
diff --git a/src/gdu-gtk/gdu-pool-tree-model.c b/src/gdu-gtk/gdu-pool-tree-model.c
index d29c8a2..56368fa 100644
--- a/src/gdu-gtk/gdu-pool-tree-model.c
+++ b/src/gdu-gtk/gdu-pool-tree-model.c
@@ -142,14 +142,17 @@ static void
 gdu_pool_tree_model_constructed (GObject *object)
 {
         GduPoolTreeModel *model = GDU_POOL_TREE_MODEL (object);
-        GType column_types[4];
+        GType column_types[7];
         GList *presentables;
         GList *l;
 
         column_types[0] = GDK_TYPE_PIXBUF;
         column_types[1] = G_TYPE_STRING;
-        column_types[2] = GDU_TYPE_PRESENTABLE;
+        column_types[2] = G_TYPE_STRING;
         column_types[3] = G_TYPE_STRING;
+        column_types[4] = GDU_TYPE_PRESENTABLE;
+        column_types[5] = G_TYPE_BOOLEAN;
+        column_types[6] = G_TYPE_BOOLEAN;
 
         gtk_tree_store_set_column_types (GTK_TREE_STORE (object),
                                          G_N_ELEMENTS (column_types),
@@ -297,6 +300,49 @@ gdu_pool_tree_model_get_iter_for_presentable (GduPoolTreeModel *model,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+set_data_for_presentable (GduPoolTreeModel *model,
+                          GtkTreeIter      *iter,
+                          GduPresentable   *presentable)
+{
+        GduDevice *device;
+        GdkPixbuf *pixbuf;
+        gchar *vpd_name;
+        gchar *name;
+        gchar *desc;
+
+        device = gdu_presentable_get_device (presentable);
+
+        name = gdu_presentable_get_name (presentable);
+        desc = gdu_presentable_get_description (presentable);
+
+        // TODO:
+        //vpd_name = gdu_presentable_get_vpd_name (presentable);
+        vpd_name = g_strdup ("foo");
+
+        pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_SMALL_TOOLBAR);
+
+        /* TODO: insert NAME */
+        gtk_tree_store_set (GTK_TREE_STORE (model),
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_ICON, pixbuf,
+                            GDU_POOL_TREE_MODEL_COLUMN_VPD_NAME, vpd_name,
+                            GDU_POOL_TREE_MODEL_COLUMN_NAME, name,
+                            GDU_POOL_TREE_MODEL_COLUMN_DESCRIPTION, desc,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, presentable,
+                            GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, FALSE,
+                            GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED, FALSE,
+                            -1);
+
+        if (pixbuf != NULL)
+                g_object_unref (pixbuf);
+        g_free (vpd_name);
+        g_free (name);
+        g_free (desc);
+        if (device != NULL)
+                g_object_unref (device);
+}
+
+static void
 add_presentable (GduPoolTreeModel *model,
                  GduPresentable   *presentable,
                  GtkTreeIter      *iter_out)
@@ -304,8 +350,6 @@ add_presentable (GduPoolTreeModel *model,
         GtkTreeIter  iter;
         GtkTreeIter  iter2;
         GtkTreeIter *parent_iter;
-        GdkPixbuf   *pixbuf;
-        char        *name;
         GduPresentable *enclosing_presentable;
 
         /* check to see if presentable is already added */
@@ -327,28 +371,21 @@ add_presentable (GduPoolTreeModel *model,
                 g_object_unref (enclosing_presentable);
         }
 
-        name = gdu_presentable_get_name (presentable);
-        pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU);
 
         /*g_debug ("adding %s (%p)", gdu_presentable_get_id (presentable), presentable);*/
 
         gtk_tree_store_append (GTK_TREE_STORE (model),
                                &iter,
                                parent_iter);
-        gtk_tree_store_set (GTK_TREE_STORE (model),
-                            &iter,
-                            GDU_POOL_TREE_MODEL_COLUMN_ICON, pixbuf,
-                            GDU_POOL_TREE_MODEL_COLUMN_NAME, name,
-                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, presentable,
-                            -1);
+
+        set_data_for_presentable (model,
+                                  &iter,
+                                  presentable);
+
 
         if (iter_out != NULL)
                 *iter_out = iter;
 
-        g_free (name);
-        if (pixbuf != NULL)
-                g_object_unref (pixbuf);
-
 out:
         ;
 }
@@ -382,29 +419,13 @@ on_presentable_changed (GduPool          *pool,
                         gpointer          user_data)
 {
         GduPoolTreeModel *model = GDU_POOL_TREE_MODEL (user_data);
-        char *name;
         GtkTreeIter iter;
-        GdkPixbuf *pixbuf;
-        GduDevice *device;
 
         /* update name and icon */
         if (gdu_pool_tree_model_get_iter_for_presentable (model, presentable, &iter)) {
 
-                name = gdu_presentable_get_name (presentable);
-                device = gdu_presentable_get_device (presentable);
-
-                pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU);
-
-                gtk_tree_store_set (GTK_TREE_STORE (model),
-                                    &iter,
-                                    GDU_POOL_TREE_MODEL_COLUMN_ICON, pixbuf,
-                                    GDU_POOL_TREE_MODEL_COLUMN_NAME, name,
-                                    -1);
-
-                g_free (name);
-                if (pixbuf != NULL)
-                        g_object_unref (pixbuf);
-                if (device != NULL)
-                        g_object_unref (device);
+                set_data_for_presentable (model,
+                                          &iter,
+                                          presentable);
         }
 }
diff --git a/src/gdu-gtk/gdu-pool-tree-model.h b/src/gdu-gtk/gdu-pool-tree-model.h
index 1a492fb..2e911de 100644
--- a/src/gdu-gtk/gdu-pool-tree-model.h
+++ b/src/gdu-gtk/gdu-pool-tree-model.h
@@ -39,12 +39,6 @@
 typedef struct GduPoolTreeModelClass       GduPoolTreeModelClass;
 typedef struct GduPoolTreeModelPrivate     GduPoolTreeModelPrivate;
 
-typedef enum {
-        GDU_POOL_TREE_MODEL_COLUMN_ICON,
-        GDU_POOL_TREE_MODEL_COLUMN_NAME,
-        GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE,
-} GduPoolTreeModelColumn;
-
 struct GduPoolTreeModel
 {
         GtkTreeStore parent;
diff --git a/src/gdu-gtk/gdu-pool-tree-view.c b/src/gdu-gtk/gdu-pool-tree-view.c
index b1084e7..ccb1264 100644
--- a/src/gdu-gtk/gdu-pool-tree-view.c
+++ b/src/gdu-gtk/gdu-pool-tree-view.c
@@ -26,11 +26,12 @@
 
 #include "gdu-pool-tree-view.h"
 #include "gdu-pool-tree-model.h"
-
+#include "gdu-gtk-enumtypes.h"
 
 struct GduPoolTreeViewPrivate
 {
         GduPoolTreeModel *model;
+        GduPoolTreeViewFlags flags;
 };
 
 G_DEFINE_TYPE (GduPoolTreeView, gdu_pool_tree_view, GTK_TYPE_TREE_VIEW)
@@ -38,6 +39,7 @@ G_DEFINE_TYPE (GduPoolTreeView, gdu_pool_tree_view, GTK_TYPE_TREE_VIEW)
 enum {
         PROP_0,
         PROP_POOL_TREE_MODEL,
+        PROP_FLAGS,
 };
 
 static void
@@ -53,6 +55,10 @@ gdu_pool_tree_view_set_property (GObject      *object,
                 view->priv->model = g_value_dup_object (value);
                 break;
 
+        case PROP_FLAGS:
+                view->priv->flags = g_value_get_flags (value);
+                break;
+
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -72,6 +78,10 @@ gdu_pool_tree_view_get_property (GObject     *object,
                 g_value_set_object (value, view->priv->model);
                 break;
 
+        case PROP_FLAGS:
+                g_value_set_flags (value, view->priv->flags);
+                break;
+
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -101,6 +111,97 @@ on_row_inserted (GtkTreeModel *tree_model,
 }
 
 static void
+on_toggled (GtkCellRendererToggle *renderer,
+            const gchar           *path_string,
+            gpointer               user_data)
+{
+        GduPoolTreeView *view = GDU_POOL_TREE_VIEW (user_data);
+        GtkTreeIter iter;
+        gboolean value;
+
+        if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (view->priv->model),
+                                                  &iter,
+                                                  path_string))
+                goto out;
+
+        gtk_tree_model_get (GTK_TREE_MODEL (view->priv->model),
+                            &iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, &value,
+                            -1);
+
+        gtk_tree_store_set (GTK_TREE_STORE (view->priv->model),
+                            &iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_TOGGLED, !value,
+                            -1);
+ out:
+        ;
+}
+
+static void
+format_markup (GtkCellLayout   *cell_layout,
+               GtkCellRenderer *renderer,
+               GtkTreeModel    *tree_model,
+               GtkTreeIter     *iter,
+               gpointer         user_data)
+{
+        GduPoolTreeView *view = GDU_POOL_TREE_VIEW (user_data);
+        GtkTreeSelection *tree_selection;
+        gchar *name;
+        gchar *desc;
+        gchar *markup;
+        GtkStyle *style;
+        GdkColor desc_gdk_color = {0};
+        gchar *desc_color;
+        GtkStateType state;
+
+        tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_NAME, &name,
+                            GDU_POOL_TREE_MODEL_COLUMN_DESCRIPTION, &desc,
+                            -1);
+
+        /* This color business shouldn't be this hard... */
+        style = gtk_widget_get_style (GTK_WIDGET (view));
+        if (gtk_tree_selection_iter_is_selected (tree_selection, iter)) {
+                if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (view)))
+                        state = GTK_STATE_SELECTED;
+                else
+                        state = GTK_STATE_ACTIVE;
+        } else {
+                state = GTK_STATE_NORMAL;
+        }
+#define BLEND_FACTOR 0.7
+        desc_gdk_color.red   = style->text[state].red   * BLEND_FACTOR +
+                               style->base[state].red   * (1.0 - BLEND_FACTOR);
+        desc_gdk_color.green = style->text[state].green * BLEND_FACTOR +
+                               style->base[state].green * (1.0 - BLEND_FACTOR);
+        desc_gdk_color.blue  = style->text[state].blue  * BLEND_FACTOR +
+                               style->base[state].blue  * (1.0 - BLEND_FACTOR);
+#undef BLEND_FACTOR
+        desc_color = g_strdup_printf ("#%02x%02x%02x",
+                                      (desc_gdk_color.red >> 8),
+                                      (desc_gdk_color.green >> 8),
+                                      (desc_gdk_color.blue >> 8));
+
+        markup = g_strdup_printf ("<b>%s</b>\n"
+                                  "<span fgcolor=\"%s\"><small>%s</small></span>",
+                                  name,
+                                  desc_color,
+                                  desc);
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        g_free (name);
+        g_free (desc);
+        g_free (markup);
+        g_free (desc_color);
+}
+
+static void
 gdu_pool_tree_view_constructed (GObject *object)
 {
         GduPoolTreeView *view = GDU_POOL_TREE_VIEW (object);
@@ -111,17 +212,51 @@ gdu_pool_tree_view_constructed (GObject *object)
                                  GTK_TREE_MODEL (view->priv->model));
 
         column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Title");
+        if (view->priv->flags & GDU_POOL_TREE_VIEW_FLAGS_SHOW_TOGGLE) {
+                renderer = gtk_cell_renderer_toggle_new ();
+                gtk_tree_view_column_pack_start (column,
+                                                 renderer,
+                                                 FALSE);
+                gtk_tree_view_column_set_attributes (column,
+                                                     renderer,
+                                                     "visible", GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED,
+                                                     "active", GDU_POOL_TREE_MODEL_COLUMN_TOGGLED,
+                                                     NULL);
+                g_signal_connect (renderer,
+                                  "toggled",
+                                  G_CALLBACK (on_toggled),
+                                  view);
+        }
+
         renderer = gtk_cell_renderer_pixbuf_new ();
         gtk_tree_view_column_pack_start (column, renderer, FALSE);
-        gtk_tree_view_column_set_attributes (column, renderer,
+        gtk_tree_view_column_set_attributes (column,
+                                             renderer,
                                              "pixbuf", GDU_POOL_TREE_MODEL_COLUMN_ICON,
                                              NULL);
+        if (view->priv->flags & GDU_POOL_TREE_VIEW_FLAGS_SHOW_TOGGLE) {
+                gtk_tree_view_column_add_attribute (column,
+                                                    renderer,
+                                                    "sensitive", GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED);
+        }
+
         renderer = gtk_cell_renderer_text_new ();
         gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", GDU_POOL_TREE_MODEL_COLUMN_NAME,
-                                             NULL);
+        //gtk_tree_view_column_set_attributes (column,
+        //                                     renderer,
+        //                                     "markup", GDU_POOL_TREE_MODEL_COLUMN_NAME,
+        //                                     NULL);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup,
+                                            view,
+                                            NULL);
+
+        if (view->priv->flags & GDU_POOL_TREE_VIEW_FLAGS_SHOW_TOGGLE) {
+                gtk_tree_view_column_add_attribute (column,
+                                                    renderer,
+                                                    "sensitive", GDU_POOL_TREE_MODEL_COLUMN_CAN_BE_TOGGLED);
+        }
         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
 
         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
@@ -166,6 +301,23 @@ gdu_pool_tree_view_class_init (GduPoolTreeViewClass *klass)
                                                               G_PARAM_WRITABLE |
                                                               G_PARAM_READABLE |
                                                               G_PARAM_CONSTRUCT_ONLY));
+
+        /**
+         * GduPoolTreeView:flags:
+         *
+         * Flags from the #GduPoolTreeViewFlags enumeration to
+         * customize behavior of the view.
+         */
+        g_object_class_install_property (gobject_class,
+                                         PROP_FLAGS,
+                                         g_param_spec_flags ("flags",
+                                                             NULL,
+                                                             NULL,
+                                                             GDU_TYPE_POOL_TREE_VIEW_FLAGS,
+                                                             GDU_POOL_TREE_VIEW_FLAGS_NONE,
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -175,10 +327,12 @@ gdu_pool_tree_view_init (GduPoolTreeView *view)
 }
 
 GtkWidget *
-gdu_pool_tree_view_new (GduPoolTreeModel *model)
+gdu_pool_tree_view_new (GduPoolTreeModel     *model,
+                        GduPoolTreeViewFlags  flags)
 {
         return GTK_WIDGET (g_object_new (GDU_TYPE_POOL_TREE_VIEW,
                                          "pool-tree-model", model,
+                                         "flags", flags,
                                          NULL));
 }
 
@@ -216,18 +370,19 @@ gdu_pool_tree_view_select_presentable (GduPoolTreeView *view,
         GtkTreePath *path;
         GtkTreeIter iter;
 
-        if (presentable == NULL)
-                goto out;
-
-        if (!gdu_pool_tree_model_get_iter_for_presentable (view->priv->model, presentable, &iter))
-                goto out;
+        if (presentable == NULL) {
+                gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)));
+        } else {
+                if (!gdu_pool_tree_model_get_iter_for_presentable (view->priv->model, presentable, &iter))
+                        goto out;
 
-        path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), &iter);
-        if (path == NULL)
-                goto out;
+                path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), &iter);
+                if (path == NULL)
+                        goto out;
 
-        gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE);
-        gtk_tree_path_free (path);
+                gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE);
+                gtk_tree_path_free (path);
+        }
 out:
         ;
 }
diff --git a/src/gdu-gtk/gdu-pool-tree-view.h b/src/gdu-gtk/gdu-pool-tree-view.h
index 71c3828..2f5499e 100644
--- a/src/gdu-gtk/gdu-pool-tree-view.h
+++ b/src/gdu-gtk/gdu-pool-tree-view.h
@@ -52,12 +52,12 @@ struct GduPoolTreeViewClass
         GtkTreeViewClass parent_class;
 };
 
-
 GType            gdu_pool_tree_view_get_type                 (void) G_GNUC_CONST;
-GtkWidget       *gdu_pool_tree_view_new                      (GduPoolTreeModel *model);
-GduPresentable  *gdu_pool_tree_view_get_selected_presentable (GduPoolTreeView  *view);
-void             gdu_pool_tree_view_select_presentable       (GduPoolTreeView  *view,
-                                                              GduPresentable   *presentable);
-void             gdu_pool_tree_view_select_first_presentable (GduPoolTreeView  *view);
+GtkWidget       *gdu_pool_tree_view_new                      (GduPoolTreeModel     *model,
+                                                              GduPoolTreeViewFlags  flags);
+GduPresentable  *gdu_pool_tree_view_get_selected_presentable (GduPoolTreeView      *view);
+void             gdu_pool_tree_view_select_presentable       (GduPoolTreeView      *view,
+                                                              GduPresentable       *presentable);
+void             gdu_pool_tree_view_select_first_presentable (GduPoolTreeView      *view);
 
 #endif /* GDU_POOL_TREE_VIEW_H */
diff --git a/src/gdu-gtk/gdu-size-widget.c b/src/gdu-gtk/gdu-size-widget.c
new file mode 100644
index 0000000..a5f9c17
--- /dev/null
+++ b/src/gdu-gtk/gdu-size-widget.c
@@ -0,0 +1,408 @@
+/* -*- 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 GduSizeWidgetPrivate
+{
+        guint64 size;
+        guint64 min_size;
+        guint64 max_size;
+        GtkWidget *hscale;
+};
+
+enum
+{
+        PROP_0,
+        PROP_SIZE,
+        PROP_MIN_SIZE,
+        PROP_MAX_SIZE,
+};
+
+enum
+{
+        CHANGED_SIGNAL,
+        LAST_SIGNAL,
+};
+
+guint signals[LAST_SIGNAL] = {0,};
+
+G_DEFINE_TYPE (GduSizeWidget, gdu_size_widget, GTK_TYPE_HBOX)
+
+static void
+gdu_size_widget_finalize (GObject *object)
+{
+        //GduSizeWidget *widget = GDU_SIZE_WIDGET (object);
+
+        if (G_OBJECT_CLASS (gdu_size_widget_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_size_widget_parent_class)->finalize (object);
+}
+
+static void
+gdu_size_widget_get_property (GObject    *object,
+                              guint       property_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+        GduSizeWidget *widget = GDU_SIZE_WIDGET (object);
+
+        switch (property_id) {
+        case PROP_SIZE:
+                g_value_set_uint64 (value, widget->priv->size);
+                break;
+
+        case PROP_MIN_SIZE:
+                g_value_set_uint64 (value, widget->priv->min_size);
+                break;
+
+        case PROP_MAX_SIZE:
+                g_value_set_uint64 (value, widget->priv->max_size);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_size_widget_set_property (GObject      *object,
+                              guint         property_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+        GduSizeWidget *widget = GDU_SIZE_WIDGET (object);
+
+        switch (property_id) {
+        case PROP_SIZE:
+                gdu_size_widget_set_size (widget, g_value_get_uint64 (value));
+                break;
+
+        case PROP_MIN_SIZE:
+                gdu_size_widget_set_min_size (widget, g_value_get_uint64 (value));
+                break;
+
+        case PROP_MAX_SIZE:
+                gdu_size_widget_set_max_size (widget, g_value_get_uint64 (value));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static gchar *
+on_hscale_format_value (GtkScale *scale,
+                        gdouble   value,
+                        gpointer  user_data)
+{
+        gchar *ret;
+
+        ret = gdu_util_get_size_for_display ((guint64 ) value, FALSE);
+
+        return ret;
+}
+
+static void
+on_hscale_value_changed (GtkRange  *range,
+                         gpointer   user_data)
+{
+        GduSizeWidget *widget = GDU_SIZE_WIDGET (user_data);
+        guint64 old_size;
+
+        old_size = widget->priv->size;
+        widget->priv->size = (guint64) gtk_range_get_value (range);
+
+        if (old_size != widget->priv->size) {
+                g_signal_emit (widget,
+                               signals[CHANGED_SIGNAL],
+                               0);
+                g_object_notify (G_OBJECT (widget), "size");
+        }
+}
+
+static void
+gdu_size_widget_init (GduSizeWidget *widget)
+{
+        widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (widget,
+                                                    GDU_TYPE_SIZE_WIDGET,
+                                                    GduSizeWidgetPrivate);
+
+        widget->priv->hscale = gtk_hscale_new_with_range (0,
+                                                          10,
+                                                          1);
+        gtk_scale_set_draw_value (GTK_SCALE (widget->priv->hscale), TRUE);
+}
+
+static gboolean
+on_query_tooltip (GtkWidget  *w,
+                  gint        x,
+                  gint        y,
+                  gboolean    keyboard_mode,
+                  GtkTooltip *tooltip,
+                  gpointer    user_data)
+{
+        GduSizeWidget *widget = GDU_SIZE_WIDGET (w);
+        gchar *s;
+        gchar *s1;
+
+        s1 = gdu_util_get_size_for_display (widget->priv->size, TRUE);
+        /* TODO: handle this use-case
+        s = g_strdup_printf ("<b>%s</b>\n"
+                             "\n"
+                             "%s",
+                             s1,
+                             _("Right click to specify an exact size."));*/
+        s = g_strdup_printf ("%s", s1);
+        g_free (s1);
+        gtk_tooltip_set_markup (tooltip, s);
+        g_free (s);
+
+        return TRUE;
+}
+
+static void
+gdu_size_widget_constructed (GObject *object)
+{
+        GduSizeWidget *widget = GDU_SIZE_WIDGET (object);
+
+        gtk_widget_show (widget->priv->hscale);
+        gtk_box_pack_start (GTK_BOX (widget),
+                            widget->priv->hscale,
+                            TRUE,
+                            TRUE,
+                            0);
+
+        g_signal_connect (widget->priv->hscale,
+                          "format-value",
+                          G_CALLBACK (on_hscale_format_value),
+                          widget);
+        g_signal_connect (widget->priv->hscale,
+                          "value-changed",
+                          G_CALLBACK (on_hscale_value_changed),
+                          widget);
+
+        gtk_widget_set_has_tooltip (GTK_WIDGET (widget),
+                                    TRUE);
+        g_signal_connect (widget,
+                          "query-tooltip",
+                          G_CALLBACK (on_query_tooltip),
+                          widget);
+
+        if (G_OBJECT_CLASS (gdu_size_widget_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_size_widget_parent_class)->constructed (object);
+}
+
+static void
+gdu_size_widget_class_init (GduSizeWidgetClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduSizeWidgetPrivate));
+
+        gobject_class->get_property        = gdu_size_widget_get_property;
+        gobject_class->set_property        = gdu_size_widget_set_property;
+        gobject_class->constructed         = gdu_size_widget_constructed;
+        gobject_class->finalize            = gdu_size_widget_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_SIZE,
+                                         g_param_spec_uint64 ("size",
+                                                              _("Size"),
+                                                              _("The currently selected size"),
+                                                              0,
+                                                              G_MAXUINT64,
+                                                              0,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT |
+                                                              G_PARAM_STATIC_NAME |
+                                                              G_PARAM_STATIC_NICK |
+                                                              G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_MIN_SIZE,
+                                         g_param_spec_uint64 ("min-size",
+                                                              _("Minimum Size"),
+                                                              _("The minimum size that can be selected"),
+                                                              0,
+                                                              G_MAXUINT64,
+                                                              0,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT |
+                                                              G_PARAM_STATIC_NAME |
+                                                              G_PARAM_STATIC_NICK |
+                                                              G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_MAX_SIZE,
+                                         g_param_spec_uint64 ("max-size",
+                                                              _("Maximum Size"),
+                                                              _("The maximum size that can be selected"),
+                                                              0,
+                                                              G_MAXUINT64,
+                                                              0,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT |
+                                                              G_PARAM_STATIC_NAME |
+                                                              G_PARAM_STATIC_NICK |
+                                                              G_PARAM_STATIC_BLURB));
+
+        signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                                G_TYPE_FROM_CLASS (klass),
+                                                G_SIGNAL_RUN_LAST,
+                                                G_STRUCT_OFFSET (GduSizeWidgetClass, changed),
+                                                NULL,
+                                                NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE,
+                                                0);
+}
+
+GtkWidget *
+gdu_size_widget_new (guint64 size,
+                     guint64 min_size,
+                     guint64 max_size)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_SIZE_WIDGET,
+                                         "size", size,
+                                         "min-size", min_size,
+                                         "max-size", max_size,
+                                         NULL));
+}
+
+static void
+update_stepping (GduSizeWidget *widget)
+{
+        gdouble extent;
+
+        extent = widget->priv->max_size - widget->priv->min_size;
+
+        /* set steps in hscale according to magnitude of extent und so weiter */
+        if (extent > 0) {
+                gdouble increment;
+                increment = exp10 (floor (log10 (extent))) / 10.0;
+                gtk_range_set_increments (GTK_RANGE (widget->priv->hscale),
+                                          increment,
+                                          increment * 10.0);
+        }
+
+
+        /* add markers at 0, 25%, 50%, 75% and 100% */
+        gtk_scale_clear_marks (GTK_SCALE (widget->priv->hscale));
+        gtk_scale_add_mark (GTK_SCALE (widget->priv->hscale),
+                            widget->priv->min_size,
+                            GTK_POS_BOTTOM,
+                            NULL);
+        gtk_scale_add_mark (GTK_SCALE (widget->priv->hscale),
+                            widget->priv->min_size + extent * 0.25,
+                            GTK_POS_BOTTOM,
+                            NULL);
+        gtk_scale_add_mark (GTK_SCALE (widget->priv->hscale),
+                            widget->priv->min_size + extent * 0.50,
+                            GTK_POS_BOTTOM,
+                            NULL);
+        gtk_scale_add_mark (GTK_SCALE (widget->priv->hscale),
+                            widget->priv->min_size + extent * 0.75,
+                            GTK_POS_BOTTOM,
+                            NULL);
+        gtk_scale_add_mark (GTK_SCALE (widget->priv->hscale),
+                            widget->priv->min_size + extent * 1.0,
+                            GTK_POS_BOTTOM,
+                            NULL);
+}
+
+void
+gdu_size_widget_set_size (GduSizeWidget *widget,
+                          guint64        size)
+{
+        g_return_if_fail (GDU_IS_SIZE_WIDGET (widget));
+        if (widget->priv->size != size) {
+                gtk_range_set_value (GTK_RANGE (widget->priv->hscale),
+                                     size);
+        }
+}
+
+void
+gdu_size_widget_set_min_size (GduSizeWidget *widget,
+                              guint64        min_size)
+{
+        g_return_if_fail (GDU_IS_SIZE_WIDGET (widget));
+        if (widget->priv->min_size != min_size) {
+                widget->priv->min_size = min_size;
+                gtk_range_set_range (GTK_RANGE (widget->priv->hscale),
+                                     widget->priv->min_size,
+                                     widget->priv->max_size);
+                update_stepping (widget);
+                g_signal_emit (widget,
+                               signals[CHANGED_SIGNAL],
+                               0);
+                g_object_notify (G_OBJECT (widget), "min-size");
+        }
+}
+
+void
+gdu_size_widget_set_max_size (GduSizeWidget *widget,
+                              guint64        max_size)
+{
+        g_return_if_fail (GDU_IS_SIZE_WIDGET (widget));
+        if (widget->priv->max_size != max_size) {
+                widget->priv->max_size = max_size;
+                gtk_range_set_range (GTK_RANGE (widget->priv->hscale),
+                                     widget->priv->min_size,
+                                     widget->priv->max_size);
+                update_stepping (widget);
+                g_signal_emit (widget,
+                               signals[CHANGED_SIGNAL],
+                               0);
+                g_object_notify (G_OBJECT (widget), "max-size");
+        }
+}
+
+guint64
+gdu_size_widget_get_size     (GduSizeWidget *widget)
+{
+        g_return_val_if_fail (GDU_IS_SIZE_WIDGET (widget), 0);
+        return widget->priv->size;
+}
+
+guint64
+gdu_size_widget_get_min_size (GduSizeWidget *widget)
+{
+        g_return_val_if_fail (GDU_IS_SIZE_WIDGET (widget), 0);
+        return widget->priv->min_size;
+}
+
+guint64
+gdu_size_widget_get_max_size (GduSizeWidget *widget)
+{
+        g_return_val_if_fail (GDU_IS_SIZE_WIDGET (widget), 0);
+        return widget->priv->max_size;
+}
+
diff --git a/src/gdu-gtk/gdu-size-widget.h b/src/gdu-gtk/gdu-size-widget.h
new file mode 100644
index 0000000..5cd7578
--- /dev/null
+++ b/src/gdu-gtk/gdu-size-widget.h
@@ -0,0 +1,72 @@
+/* -*- 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_SIZE_WIDGET_H
+#define __GDU_SIZE_WIDGET_H
+
+#include <gdu-gtk/gdu-gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_SIZE_WIDGET         (gdu_size_widget_get_type())
+#define GDU_SIZE_WIDGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SIZE_WIDGET, GduSizeWidget))
+#define GDU_SIZE_WIDGET_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_SIZE_WIDGET, GduSizeWidgetClass))
+#define GDU_IS_SIZE_WIDGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SIZE_WIDGET))
+#define GDU_IS_SIZE_WIDGET_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_SIZE_WIDGET))
+#define GDU_SIZE_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_SIZE_WIDGET, GduSizeWidgetClass))
+
+typedef struct GduSizeWidgetClass   GduSizeWidgetClass;
+typedef struct GduSizeWidgetPrivate GduSizeWidgetPrivate;
+
+struct GduSizeWidget
+{
+        GtkHBox parent;
+
+        /*< private >*/
+        GduSizeWidgetPrivate *priv;
+};
+
+struct GduSizeWidgetClass
+{
+        GtkHBoxClass parent_class;
+
+        void (*changed) (GduSizeWidget *widget);
+};
+
+GType       gdu_size_widget_get_type     (void) G_GNUC_CONST;
+GtkWidget*  gdu_size_widget_new          (guint64 size,
+                                          guint64 min_size,
+                                          guint64 max_size);
+void        gdu_size_widget_set_size     (GduSizeWidget *widget,
+                                          guint64        size);
+void        gdu_size_widget_set_min_size (GduSizeWidget *widget,
+                                          guint64        min_size);
+void        gdu_size_widget_set_max_size (GduSizeWidget *widget,
+                                          guint64        max_size);
+guint64     gdu_size_widget_get_size     (GduSizeWidget *widget);
+guint64     gdu_size_widget_get_min_size (GduSizeWidget *widget);
+guint64     gdu_size_widget_get_max_size (GduSizeWidget *widget);
+
+G_END_DECLS
+
+#endif  /* __GDU_SIZE_WIDGET_H */
+
diff --git a/src/gdu/gdu-drive.c b/src/gdu/gdu-drive.c
index 010c36b..e825355 100644
--- a/src/gdu/gdu-drive.c
+++ b/src/gdu/gdu-drive.c
@@ -242,23 +242,27 @@ gdu_drive_get_enclosing_presentable (GduPresentable *presentable)
         return NULL;
 }
 
-static char *
+static gchar *
 gdu_drive_get_name (GduPresentable *presentable)
 {
         GduDrive *drive = GDU_DRIVE (presentable);
-        const char *vendor;
-        const char *model;
+        const gchar *vendor;
+        const gchar *model;
         const char *presentation_name;
+        const gchar* const *media_compat;
         guint64 size;
         gboolean is_removable;
-        char *strsize;
-        char *result;
+        GString *result;
+        gboolean is_rotational;
+        gboolean has_media;
+        gchar *strsize;
 
         strsize = NULL;
+        result = g_string_new (NULL);
 
         presentation_name = gdu_device_get_presentation_name (drive->priv->device);
         if (presentation_name != NULL && strlen (presentation_name) > 0) {
-                result = g_strdup (presentation_name);
+                g_string_append (result, presentation_name);
                 goto out;
         }
 
@@ -266,33 +270,199 @@ gdu_drive_get_name (GduPresentable *presentable)
         model = gdu_device_drive_get_model (drive->priv->device);
         size = gdu_device_get_size (drive->priv->device);
         is_removable = gdu_device_is_removable (drive->priv->device);
+        media_compat = (const gchar* const *) gdu_device_drive_get_media_compatibility (drive->priv->device);
+        has_media = gdu_device_is_media_available (drive->priv->device);
+        is_rotational = TRUE; /* TODO: add support in DKD for this */
 
-        if (vendor != NULL && strlen (vendor) == 0)
-                vendor = NULL;
-
-        if (model != NULL && strlen (model) == 0)
-                model = NULL;
-
-        if (!is_removable && size > 0) {
+        if (has_media && size > 0) {
                 strsize = gdu_util_get_size_for_display (size, FALSE);
         }
 
-        if (strsize != NULL) {
-                result = g_strdup_printf ("%s %s%s%s",
-                                        strsize,
-                                        vendor != NULL ? vendor : "",
-                                        vendor != NULL ? " " : "",
-                                        model != NULL ? model : "");
+
+        if (is_removable) {
+                guint n;
+                gboolean optical_cd;
+                gboolean optical_dvd;
+                gboolean optical_bd;
+                gboolean optical_hddvd;
+
+                /* TODO: should move to gdu-util.c */
+                optical_cd = FALSE;
+                optical_dvd = FALSE;
+                optical_bd = FALSE;
+                optical_hddvd = FALSE;
+                for (n = 0; media_compat != NULL && media_compat[n] != NULL; n++) {
+                        const gchar *media_name;
+                        const gchar *media;
+
+                        media = media_compat[n];
+                        media_name = NULL;
+                        if (g_strcmp0 (media, "flash_cf") == 0) {
+                                media_name = _("CompactFlash");
+                        } else if (g_strcmp0 (media, "flash_ms") == 0) {
+                                media_name = _("MemoryStick");
+                        } else if (g_strcmp0 (media, "flash_sm") == 0) {
+                                media_name = _("SmartMedia");
+                        } else if (g_strcmp0 (media, "flash_sd") == 0) {
+                                media_name = _("SecureDigital");
+                        } else if (g_strcmp0 (media, "flash_sdhc") == 0) {
+                                media_name = _("SD High Capcity");
+                        } else if (g_strcmp0 (media, "floppy") == 0) {
+                                media_name = _("Floppy");
+                        } else if (g_strcmp0 (media, "floppy_zip") == 0) {
+                                media_name = _("Zip");
+                        } else if (g_strcmp0 (media, "floppy_jaz") == 0) {
+                                media_name = _("Jaz");
+                        } else if (g_str_has_prefix (media, "flash")) {
+                                media_name = _("Flash");
+                        } else if (g_str_has_prefix (media, "optical_cd")) {
+                                optical_cd = TRUE;
+                        } else if (g_str_has_prefix (media, "optical_dvd")) {
+                                optical_dvd = TRUE;
+                        } else if (g_str_has_prefix (media, "optical_bd")) {
+                                optical_bd = TRUE;
+                        } else if (g_str_has_prefix (media, "optical_hddvd")) {
+                                optical_hddvd = TRUE;
+                        }
+
+                        if (media_name != NULL) {
+                                if (result->len > 0)
+                                        g_string_append_c (result, '/');
+                                g_string_append (result, media_name);
+                        }
+                }
+                if (optical_cd) {
+                        if (result->len > 0)
+                                g_string_append_c (result, '/');
+                        g_string_append (result, _("CD"));
+                }
+                if (optical_dvd) {
+                        if (result->len > 0)
+                                g_string_append_c (result, '/');
+                        g_string_append (result, _("DVD"));
+                }
+                if (optical_bd) {
+                        if (result->len > 0)
+                                g_string_append_c (result, '/');
+                        g_string_append (result, _("Blu-Ray"));
+                }
+                if (optical_hddvd) {
+                        if (result->len > 0)
+                                g_string_append_c (result, '/');
+                        g_string_append (result, _("HDDVD"));
+                }
+
+                /* If we know the media type, just append Drive */
+                if (result->len > 0) {
+                        g_string_append_c (result, ' ');
+                        g_string_append (result, _("Drive"));
+                } else {
+                        /* Otherwise use Vendor/Model */
+                        if (vendor != NULL && strlen (vendor) == 0)
+                                vendor = NULL;
+
+                        if (model != NULL && strlen (model) == 0)
+                                model = NULL;
+
+                        g_string_append_printf (result,
+                                                "%s%s%s",
+                                                vendor != NULL ? vendor : "",
+                                                vendor != NULL ? " " : "",
+                                                model != NULL ? model : "");
+                }
+
         } else {
-                result = g_strdup_printf ("%s%s%s",
-                                          vendor != NULL ? vendor : "",
-                                          vendor != NULL ? " " : "",
-                                          model != NULL ? model : "");
+                /* Media is not removable, use "Hard Disk" resp. "Solid-State Disk" */
+
+                if (is_rotational) {
+                        if (strsize != NULL) {
+                                g_string_append_printf (result,
+                                                        _("%s Hard Disk"),
+                                                        strsize);
+                        } else {
+                                g_string_append (result,
+                                                 _("%s Hard Disk"));
+                        }
+                } else {
+                        if (strsize != NULL) {
+                                g_string_append_printf (result,
+                                                        _("%s Solid-State Disk"),
+                                                        strsize);
+                        } else {
+                                g_string_append (result,
+                                                 _("%s Solid-State Disk"));
+                        }
+                }
         }
 
  out:
         g_free (strsize);
-        return result;
+        return g_string_free (result, FALSE);
+}
+
+static gchar *
+gdu_drive_get_description (GduPresentable *presentable)
+{
+        GduDrive *drive = GDU_DRIVE (presentable);
+        const gchar *vendor;
+        const gchar *model;
+        const gchar *part_table_scheme;
+        GString *result;
+        guint64 size;
+        gboolean is_removable;
+        gboolean has_media;
+
+        result = g_string_new (NULL);
+
+        vendor = gdu_device_drive_get_vendor (drive->priv->device);
+        model = gdu_device_drive_get_model (drive->priv->device);
+
+        size = gdu_device_get_size (drive->priv->device);
+        is_removable = gdu_device_is_removable (drive->priv->device);
+        has_media = gdu_device_is_media_available (drive->priv->device);
+        part_table_scheme = gdu_device_partition_table_get_scheme (drive->priv->device);
+
+        /* If removable, include size of media or the fact there is no media */
+        if (is_removable) {
+                if (has_media && size > 0) {
+                        gchar *strsize;
+                        strsize = gdu_util_get_size_for_display (size, FALSE);
+                        g_string_append_printf (result,
+                                                _("%s Media"),
+                                                strsize);
+                        g_free (strsize);
+                } else {
+                        g_string_append_printf (result,
+                                                _("No Media Detected"));
+                }
+        }
+
+        /* If we have media, include whether partitioned or not */
+        if (has_media && size > 0) {
+                if (result->len > 0)
+                        g_string_append (result, ", ");
+                if (gdu_device_is_partition_table (drive->priv->device)) {
+                        if (g_strcmp0 (part_table_scheme, "mbr") == 0) {
+                                g_string_append (result,
+                                                 _("MBR Partition Table"));
+                        } else if (g_strcmp0 (part_table_scheme, "gpt") == 0) {
+                                g_string_append (result,
+                                                 _("GUID Partition Table"));
+                        } else if (g_strcmp0 (part_table_scheme, "apm") == 0) {
+                                g_string_append (result,
+                                                 _("Apple Partition Table"));
+                        } else {
+                                g_string_append (result,
+                                                 _("Partitioned"));
+                        }
+                } else {
+                        g_string_append (result,
+                                         _("Not Partitioned"));
+                }
+        }
+
+
+        return g_string_free (result, FALSE);
 }
 
 static gboolean
@@ -465,6 +635,7 @@ gdu_drive_presentable_iface_init (GduPresentableIface *iface)
         iface->get_device = gdu_drive_get_device;
         iface->get_enclosing_presentable = gdu_drive_get_enclosing_presentable;
         iface->get_name = gdu_drive_get_name;
+        iface->get_description = gdu_drive_get_description;
         iface->get_icon = gdu_drive_get_icon;
         iface->get_offset = gdu_drive_get_offset;
         iface->get_size = gdu_drive_get_size;
diff --git a/src/gdu/gdu-linux-md-drive.c b/src/gdu/gdu-linux-md-drive.c
index 614ac9d..f250c39 100644
--- a/src/gdu/gdu-linux-md-drive.c
+++ b/src/gdu/gdu-linux-md-drive.c
@@ -467,13 +467,15 @@ gdu_linux_md_drive_get_enclosing_presentable (GduPresentable *presentable)
         return NULL;
 }
 
-static char *
-gdu_linux_md_drive_get_name (GduPresentable *presentable)
+static gchar *
+get_name_and_desc (GduPresentable  *presentable,
+                   gchar          **out_desc)
 {
         GduLinuxMdDrive *drive = GDU_LINUX_MD_DRIVE (presentable);
-        GduDevice *device;
-        char *ret;
-        char *level_str;
+        GduDevice *component_device;
+        gchar *ret;
+        gchar *ret_desc;
+        gchar *level_str;
         guint64 component_size;
         int num_slaves;
         int num_raid_devices;
@@ -481,30 +483,86 @@ gdu_linux_md_drive_get_name (GduPresentable *presentable)
         const char *name;
 
         ret = NULL;
+        ret_desc = NULL;
 
         if (drive->priv->slaves != NULL) {
-                device = GDU_DEVICE (drive->priv->slaves->data);
+                guint64 size;
+                gchar *strsize;
+
+                component_device = GDU_DEVICE (drive->priv->slaves->data);
 
-                level = gdu_device_linux_md_component_get_level (device);
-                name = gdu_device_linux_md_component_get_name (device);
-                num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device);
+                level = gdu_device_linux_md_component_get_level (component_device);
+                name = gdu_device_linux_md_component_get_name (component_device);
+                num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (component_device);
                 num_slaves = g_list_length (drive->priv->slaves);
-                component_size = gdu_device_get_size (device);
+                component_size = gdu_device_get_size (component_device);
 
                 level_str = gdu_linux_md_get_raid_level_for_display (level);
 
+                strsize = NULL;
+                if (drive->priv->device != NULL) {
+                        size = gdu_device_get_size (component_device);
+                        strsize = gdu_util_get_size_for_display (size, FALSE);
+                }
+                /* TODO: Maybe guess size from level, num_raid_devices and component_size? */
+
                 if (name == NULL || strlen (name) == 0) {
-                        /* Translators: %s is a RAID level, e.g. 'RAID-5' */
-                        ret = g_strdup_printf (_("%s Drive"), level_str);
+                        if (strsize != NULL) {
+                                /* Translators: First %s is the size, second %s is a RAID level, e.g. 'RAID-5' */
+                                ret = g_strdup_printf (_("%s %s Drive"),
+                                                       strsize,
+                                                       level_str);
+                        } else {
+                                /* Translators: %s is a RAID level, e.g. 'RAID-5' */
+                                ret = g_strdup_printf (_("%s Drive"),
+                                                       level_str);
+                        }
                 } else {
-                        /* Translators: First %s is a RAID level, e.g. 'RAID-5'
-                         * second %s is a drive name
-                         */
-                        ret = g_strdup_printf (_("%s (%s)"), name, level_str);
+                        ret = g_strdup (name);
+                        if (strsize != NULL) {
+                                ret_desc = g_strdup_printf ("%s %s",
+                                                            strsize,
+                                                            level_str);
+                        } else {
+                                ret_desc = g_strdup (level_str);
+                        }
+                }
+
+                if (drive->priv->device != NULL && gdu_device_is_partition_table (drive->priv->device)) {
+                        const gchar *part_table_scheme;
+                        const gchar *scheme_str;
+
+                        part_table_scheme = gdu_device_partition_table_get_scheme (drive->priv->device);
+
+                        if (g_strcmp0 (part_table_scheme, "mbr") == 0) {
+                                scheme_str = _("MBR Partition Table");
+                        } else if (g_strcmp0 (part_table_scheme, "gpt") == 0) {
+                                scheme_str = _("GUID Partition Table");
+                        } else if (g_strcmp0 (part_table_scheme, "apm") == 0) {
+                                scheme_str = _("Apple Partition Table");
+                        } else {
+                                scheme_str = _("Partitioned");
+                        }
+
+                        if (ret_desc != NULL) {
+                                gchar *tmp;
+                                tmp = ret_desc;
+                                ret_desc = g_strconcat (ret_desc,
+                                                        ", ",
+                                                        scheme_str,
+                                                        NULL);
+                                g_free (tmp);
+                        } else {
+                                ret_desc = g_strdup (scheme_str);
+                        }
                 }
 
                 g_free (level_str);
 
+                /* lame fallback */
+                if (ret_desc == NULL)
+                        ret_desc = g_strdup (_("RAID Array"));
+
         } else if (drive->priv->device != NULL) {
                 /* Translators: First %s is a device file such as /dev/sda4
                  * second %s is the state of the device
@@ -520,9 +578,32 @@ gdu_linux_md_drive_get_name (GduPresentable *presentable)
                 ret = g_strdup_printf (_("RAID device %s"), drive->priv->device_file);
         }
 
+        if (out_desc != NULL)
+                *out_desc = ret_desc;
+        else
+                g_free (ret_desc);
+
         return ret;
 }
 
+static char *
+gdu_linux_md_drive_get_name (GduPresentable *presentable)
+{
+        return get_name_and_desc (presentable, NULL);
+}
+
+static gchar *
+gdu_linux_md_drive_get_description (GduPresentable *presentable)
+{
+        gchar *desc;
+        gchar *name;
+
+        name = get_name_and_desc (presentable, &desc);
+        g_free (name);
+
+        return desc;
+}
+
 static GIcon *
 gdu_linux_md_drive_get_icon (GduPresentable *presentable)
 {
@@ -617,6 +698,7 @@ gdu_linux_md_drive_presentable_iface_init (GduPresentableIface *iface)
         iface->get_device = gdu_linux_md_drive_get_device;
         iface->get_enclosing_presentable = gdu_linux_md_drive_get_enclosing_presentable;
         iface->get_name = gdu_linux_md_drive_get_name;
+        iface->get_description = gdu_linux_md_drive_get_description;
         iface->get_icon = gdu_linux_md_drive_get_icon;
         iface->get_offset = gdu_linux_md_drive_get_offset;
         iface->get_size = gdu_linux_md_drive_get_size;
diff --git a/src/gdu/gdu-presentable.c b/src/gdu/gdu-presentable.c
index 002792b..8f75afb 100644
--- a/src/gdu/gdu-presentable.c
+++ b/src/gdu/gdu-presentable.c
@@ -320,7 +320,7 @@ gdu_presentable_get_enclosing_presentable (GduPresentable *presentable)
  *
  * Returns: The name. Caller must free the string with g_free().
  **/
-char *
+gchar *
 gdu_presentable_get_name (GduPresentable *presentable)
 {
   GduPresentableIface *iface;
@@ -333,6 +333,27 @@ gdu_presentable_get_name (GduPresentable *presentable)
 }
 
 /**
+ * gdu_presentable_get_description:
+ * @presentable: A #GduPresentable.
+ *
+ * Gets a description for @presentable suitable for presentation in an user
+ * interface.
+ *
+ * Returns: The description. Caller must free the string with g_free().
+ */
+gchar *
+gdu_presentable_get_description (GduPresentable *presentable)
+{
+  GduPresentableIface *iface;
+
+  g_return_val_if_fail (GDU_IS_PRESENTABLE (presentable), NULL);
+
+  iface = GDU_PRESENTABLE_GET_IFACE (presentable);
+
+  return (* iface->get_description) (presentable);
+}
+
+/**
  * gdu_presentable_get_icon:
  * @presentable: A #GduPresentable.
  *
diff --git a/src/gdu/gdu-presentable.h b/src/gdu/gdu-presentable.h
index 8f8cff1..2a384e7 100644
--- a/src/gdu/gdu-presentable.h
+++ b/src/gdu/gdu-presentable.h
@@ -69,7 +69,8 @@ struct _GduPresentableIface
         const gchar *    (*get_id)                    (GduPresentable *presentable);
         GduDevice *      (*get_device)                (GduPresentable *presentable);
         GduPresentable * (*get_enclosing_presentable) (GduPresentable *presentable);
-        char *           (*get_name)                  (GduPresentable *presentable);
+        gchar *          (*get_name)                  (GduPresentable *presentable);
+        gchar *          (*get_description)           (GduPresentable *presentable);
         GIcon *          (*get_icon)                  (GduPresentable *presentable);
         guint64          (*get_offset)                (GduPresentable *presentable);
         guint64          (*get_size)                  (GduPresentable *presentable);
@@ -82,7 +83,8 @@ GType           gdu_presentable_get_type                  (void) G_GNUC_CONST;
 const gchar    *gdu_presentable_get_id                    (GduPresentable *presentable);
 GduDevice      *gdu_presentable_get_device                (GduPresentable *presentable);
 GduPresentable *gdu_presentable_get_enclosing_presentable (GduPresentable *presentable);
-char           *gdu_presentable_get_name                  (GduPresentable *presentable);
+gchar          *gdu_presentable_get_name                  (GduPresentable *presentable);
+gchar          *gdu_presentable_get_description           (GduPresentable *presentable);
 GIcon          *gdu_presentable_get_icon                  (GduPresentable *presentable);
 guint64         gdu_presentable_get_offset                (GduPresentable *presentable);
 guint64         gdu_presentable_get_size                  (GduPresentable *presentable);
diff --git a/src/gdu/gdu-volume-hole.c b/src/gdu/gdu-volume-hole.c
index d94db02..cef5dab 100644
--- a/src/gdu/gdu-volume-hole.c
+++ b/src/gdu/gdu-volume-hole.c
@@ -147,12 +147,18 @@ gdu_volume_hole_get_name (GduPresentable *presentable)
         /* Translators: label for an unallocated space on a disk
          * %s is the size, formatted like '45 GB'
          */
-        result = g_strdup_printf (_("%s Unallocated"), strsize);
+        result = g_strdup_printf (_("%s Free"), strsize);
         g_free (strsize);
 
         return result;
 }
 
+static gchar *
+gdu_volume_hole_get_description (GduPresentable *presentable)
+{
+        return g_strdup (_("Unallocated Space"));
+}
+
 static GIcon *
 gdu_volume_hole_get_icon (GduPresentable *presentable)
 {
@@ -298,6 +304,7 @@ gdu_volume_hole_presentable_iface_init (GduPresentableIface *iface)
         iface->get_device = gdu_volume_hole_get_device;
         iface->get_enclosing_presentable = gdu_volume_hole_get_enclosing_presentable;
         iface->get_name = gdu_volume_hole_get_name;
+        iface->get_description = gdu_volume_hole_get_description;
         iface->get_icon = gdu_volume_hole_get_icon;
         iface->get_offset = gdu_volume_hole_get_offset;
         iface->get_size = gdu_volume_hole_get_size;
diff --git a/src/gdu/gdu-volume.c b/src/gdu/gdu-volume.c
index 6e91d22..d0f51f1 100644
--- a/src/gdu/gdu-volume.c
+++ b/src/gdu/gdu-volume.c
@@ -196,7 +196,8 @@ gdu_volume_get_enclosing_presentable (GduPresentable *presentable)
 }
 
 static char *
-gdu_volume_get_name (GduPresentable *presentable)
+get_name_and_desc (GduPresentable  *presentable,
+                   gchar          **out_desc)
 {
         GduVolume *volume = GDU_VOLUME (presentable);
         GduPresentable *drive_presentable;
@@ -204,6 +205,7 @@ gdu_volume_get_name (GduPresentable *presentable)
         const char *label;
         const char *usage;
         const char *type;
+        const char *version;
         const char *drive_media;
         const char *presentation_name;
         char *result;
@@ -211,20 +213,16 @@ gdu_volume_get_name (GduPresentable *presentable)
         char *strsize;
         guint64 size;
         guint n;
+        gchar *result_desc;
 
         result = NULL;
+        result_desc = NULL;
 
         drive_presentable = NULL;
         drive_device = NULL;
         drive_media = NULL;
         strsize = NULL;
 
-        presentation_name = gdu_device_get_presentation_name (volume->priv->device);
-        if (presentation_name != NULL && strlen (presentation_name) > 0) {
-                result = g_strdup (presentation_name);
-                goto out;
-        }
-
         drive_presentable = gdu_presentable_get_toplevel (presentable);
         if (drive_presentable != NULL) {
                 drive_device = gdu_presentable_get_device (drive_presentable);
@@ -238,18 +236,25 @@ gdu_volume_get_name (GduPresentable *presentable)
                 size = gdu_device_get_size (volume->priv->device);
         strsize = gdu_util_get_size_for_display (size, FALSE);
 
+        presentation_name = gdu_device_get_presentation_name (volume->priv->device);
+        if (presentation_name != NULL && strlen (presentation_name) > 0) {
+                result = g_strdup (presentation_name);
+                goto out;
+        }
+
         /* see comment in gdu_pool_add_device_by_object_path() for how to avoid hardcoding 0x05 etc. types */
         is_extended_partition = FALSE;
         if (gdu_device_is_partition (volume->priv->device) &&
             strcmp (gdu_device_partition_get_scheme (volume->priv->device), "mbr") == 0) {
-                int type;
-                type = strtol (gdu_device_partition_get_type (volume->priv->device), NULL, 0);
-                if (type == 0x05 || type == 0x0f || type == 0x85)
+                int part_type;
+                part_type = strtol (gdu_device_partition_get_type (volume->priv->device), NULL, 0);
+                if (part_type == 0x05 || part_type == 0x0f || part_type == 0x85)
                         is_extended_partition = TRUE;
         }
 
         usage = gdu_device_id_get_usage (volume->priv->device);
         type = gdu_device_id_get_type (volume->priv->device);
+        version = gdu_device_id_get_version (volume->priv->device);
 
         /* handle optical discs */
         if (gdu_device_is_optical_disc (volume->priv->device) &&
@@ -276,7 +281,13 @@ gdu_volume_get_name (GduPresentable *presentable)
                 result = g_strdup_printf (_("%s Extended"), strsize);
         } else if ((usage != NULL && strcmp (usage, "filesystem") == 0) &&
                    (label != NULL && strlen (label) > 0)) {
+                gchar *fsdesc;
                 result = g_strdup (label);
+                fsdesc = gdu_util_get_fstype_for_display (type, version, TRUE);
+                result_desc = g_strdup_printf ("%s %s",
+                                               strsize,
+                                               fsdesc);
+                g_free (fsdesc);
         } else if (usage != NULL) {
                 if (strcmp (usage, "crypto") == 0) {
                         /* Translators: Label for an extended partition
@@ -299,6 +310,7 @@ gdu_volume_get_name (GduPresentable *presentable)
                          * %s is the size, formatted like '45 GB'
                          */
                         result = g_strdup_printf (_("%s Filesystem"), strsize);
+                        result_desc = gdu_util_get_fstype_for_display (type, version, TRUE);
                 } else if (strcmp (usage, "partitiontable") == 0) {
                         /* Translators: Label for a partition table
                          * %s is the size, formatted like '45 GB'
@@ -327,16 +339,21 @@ gdu_volume_get_name (GduPresentable *presentable)
                                 if (array_name != NULL && strlen (array_name) > 0) {
                                         /* Translators: label for a RAID component
                                          * First %s is the size, formatted like '45 GB'
+                                         */
+                                        result = g_strdup_printf (_("%s RAID Component"), strsize);
+                                        /* Translators: description for a RAID component
+                                         * First %s is the array name, e.g. 'My Photos RAID',
                                          * second %s is the RAID level string, e.g 'RAID-5'
-                                         * third %s is the name of the array
                                          */
-                                        result = g_strdup_printf (_("%s %s (%s)"), strsize, level_str, array_name);
+                                        result_desc = g_strdup_printf (_("Part of \"%s\" %s array"),
+                                                                       array_name,
+                                                                       level_str);
                                 } else {
                                         /* Translators: label for a RAID component
                                          * First %s is the size, formatted like '45 GB'
-                                         * second %s is the RAID level string, e.g 'RAID-5'
                                          */
-                                        result = g_strdup_printf (_("%s %s"), strsize, level_str);
+                                        result = g_strdup_printf (_("%s RAID Component"), strsize);
+                                        result_desc = g_strdup (level_str);
                                 }
 
                                 g_free (level_str);
@@ -358,6 +375,7 @@ gdu_volume_get_name (GduPresentable *presentable)
                          * %s is the size, formatted like '45 GB'
                          */
                         result = g_strdup_printf (_("%s Unrecognized"), strsize);
+                        result_desc = g_strdup (_("Unknown or Unused"));
                 }
         } else {
                 if (gdu_device_is_partition (volume->priv->device)) {
@@ -374,16 +392,43 @@ gdu_volume_get_name (GduPresentable *presentable)
                 result = g_strdup_printf (_("%s Unrecognized"), strsize);
 
  out:
-        g_free (strsize);
-
         if (drive_device != NULL)
                 g_object_unref (drive_device);
         if (drive_presentable != NULL)
                 g_object_unref (drive_presentable);
 
+        if (result_desc == NULL) {
+                result_desc = g_strdup (strsize);
+        }
+
+        if (out_desc != NULL)
+                *out_desc = result_desc;
+        else
+                g_free (result_desc);
+
+        g_free (strsize);
+
         return result;
 }
 
+static char *
+gdu_volume_get_name (GduPresentable *presentable)
+{
+        return get_name_and_desc (presentable, NULL);
+}
+
+static gchar *
+gdu_volume_get_description (GduPresentable *presentable)
+{
+        gchar *desc;
+        gchar *name;
+
+        name = get_name_and_desc (presentable, &desc);
+        g_free (name);
+
+        return desc;
+}
+
 static GIcon *
 gdu_volume_get_icon (GduPresentable *presentable)
 {
@@ -617,6 +662,7 @@ gdu_volume_presentable_iface_init (GduPresentableIface *iface)
         iface->get_device = gdu_volume_get_device;
         iface->get_enclosing_presentable = gdu_volume_get_enclosing_presentable;
         iface->get_name = gdu_volume_get_name;
+        iface->get_description = gdu_volume_get_description;
         iface->get_icon = gdu_volume_get_icon;
         iface->get_offset = gdu_volume_get_offset;
         iface->get_size = gdu_volume_get_size;
diff --git a/src/palimpsest/gdu-section-linux-md-drive.c b/src/palimpsest/gdu-section-linux-md-drive.c
index 976512b..edc2a76 100644
--- a/src/palimpsest/gdu-section-linux-md-drive.c
+++ b/src/palimpsest/gdu-section-linux-md-drive.c
@@ -171,7 +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);
-        tree_view = gdu_pool_tree_view_new (model);
+        tree_view = gdu_pool_tree_view_new (model, GDU_POOL_TREE_VIEW_FLAGS_NONE);
         g_object_unref (model);
         gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
 
diff --git a/src/palimpsest/gdu-shell.c b/src/palimpsest/gdu-shell.c
index df32b7e..2da8eb3 100644
--- a/src/palimpsest/gdu-shell.c
+++ b/src/palimpsest/gdu-shell.c
@@ -1922,7 +1922,8 @@ 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);
-        shell->priv->tree_view = gdu_pool_tree_view_new (model);
+        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);
 



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