[gnome-disk-utility] Add UI for editing (add/remove) PVs in a VG



commit 045bd10597d982064e07d525845d3be097e26d67
Author: David Zeuthen <davidz redhat com>
Date:   Wed Jan 13 17:50:26 2010 -0500

    Add UI for editing (add/remove) PVs in a VG

 src/gdu-gtk/Makefile.am                            |    4 +
 src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.c         |  345 +++++++
 src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.h         |   63 ++
 src/gdu-gtk/gdu-disk-selection-widget.c            |   45 +-
 src/gdu-gtk/gdu-edit-linux-lvm2-dialog.c           |  946 ++++++++++++++++++++
 src/gdu-gtk/gdu-edit-linux-lvm2-dialog.h           |   64 ++
 src/gdu-gtk/gdu-gtk-types.h                        |    2 +
 src/gdu-gtk/gdu-gtk.h                              |    2 +
 src/gdu/gdu-callbacks.h                            |    8 +
 src/gdu/gdu-linux-lvm2-volume-group.c              |  102 ++-
 src/gdu/gdu-linux-lvm2-volume-group.h              |    5 +
 src/gdu/gdu-pool.c                                 |   88 ++
 src/gdu/gdu-pool.h                                 |   12 +
 .../gdu-section-linux-lvm2-volume-group.c          |  399 +++++++++
 14 files changed, 2080 insertions(+), 5 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 4cb665b..a2e3db9 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -53,7 +53,9 @@ libgdu_gtkinclude_HEADERS =              				\
 	gdu-edit-name-dialog.h						\
 	gdu-disk-selection-widget.h					\
 	gdu-add-component-linux-md-dialog.h				\
+	gdu-add-pv-linux-lvm2-dialog.h				\
 	gdu-edit-linux-md-dialog.h					\
+	gdu-edit-linux-lvm2-dialog.h					\
 	gdu-drive-benchmark-dialog.h					\
 	gdu-connect-to-server-dialog.h					\
 	$(NULL)
@@ -86,7 +88,9 @@ libgdu_gtk_la_SOURCES =                 	               				\
 	gdu-edit-name-dialog.h			gdu-edit-name-dialog.c			\
 	gdu-disk-selection-widget.h		gdu-disk-selection-widget.c		\
 	gdu-add-component-linux-md-dialog.h	gdu-add-component-linux-md-dialog.c	\
+	gdu-add-pv-linux-lvm2-dialog.h		gdu-add-pv-linux-lvm2-dialog.c		\
 	gdu-edit-linux-md-dialog.h		gdu-edit-linux-md-dialog.c		\
+	gdu-edit-linux-lvm2-dialog.h		gdu-edit-linux-lvm2-dialog.c		\
 	gdu-drive-benchmark-dialog.h		gdu-drive-benchmark-dialog.c		\
 	gdu-connect-to-server-dialog.h		gdu-connect-to-server-dialog.c		\
 	$(NULL)
diff --git a/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.c b/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.c
new file mode 100644
index 0000000..3f942a9
--- /dev/null
+++ b/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.c
@@ -0,0 +1,345 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include <atasmart.h>
+#include <glib/gstdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "gdu-gtk.h"
+#include "gdu-add-pv-linux-lvm2-dialog.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct GduAddPvLinuxLvm2DialogPrivate
+{
+        GtkWidget *size_widget;
+        GtkWidget *disk_selection_widget;
+};
+
+enum {
+        PROP_0,
+        PROP_DRIVE,
+        PROP_SIZE
+};
+
+G_DEFINE_TYPE (GduAddPvLinuxLvm2Dialog, gdu_add_pv_linux_lvm2_dialog, GDU_TYPE_DIALOG)
+
+static void
+gdu_add_pv_linux_lvm2_dialog_finalize (GObject *object)
+{
+        /*GduAddPvLinuxLvm2Dialog *dialog = GDU_ADD_PV_LINUX_LVM2_DIALOG (object);*/
+
+        if (G_OBJECT_CLASS (gdu_add_pv_linux_lvm2_dialog_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_add_pv_linux_lvm2_dialog_parent_class)->finalize (object);
+}
+
+static void
+gdu_add_pv_linux_lvm2_dialog_get_property (GObject    *object,
+                                                guint       property_id,
+                                                GValue     *value,
+                                                GParamSpec *pspec)
+{
+        GduAddPvLinuxLvm2Dialog *dialog = GDU_ADD_PV_LINUX_LVM2_DIALOG (object);
+
+        switch (property_id) {
+        case PROP_DRIVE:
+                g_value_take_object (value, gdu_add_pv_linux_lvm2_dialog_get_drive (dialog));
+                break;
+
+        case PROP_SIZE:
+                g_value_set_uint64 (value, gdu_add_pv_linux_lvm2_dialog_get_size (dialog));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_add_sensitivity (GduAddPvLinuxLvm2Dialog *dialog)
+{
+        GPtrArray *drives;
+        gboolean add_is_sensitive;
+
+        drives = gdu_disk_selection_widget_get_selected_drives (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+        add_is_sensitive = (drives->len > 0);
+        g_ptr_array_unref (drives);
+
+        gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_APPLY,
+                                           add_is_sensitive);
+}
+
+static void
+update (GduAddPvLinuxLvm2Dialog *dialog)
+{
+        guint64 largest_segment;
+
+        largest_segment = gdu_disk_selection_widget_get_largest_segment_for_selected (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+
+        if (largest_segment == 0)
+                largest_segment = gdu_disk_selection_widget_get_largest_segment_for_all (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+
+        gdu_size_widget_set_max_size (GDU_SIZE_WIDGET (dialog->priv->size_widget), largest_segment);
+
+        update_add_sensitivity (dialog);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_disk_selection_widget_changed (GduDiskSelectionWidget *widget,
+                                  gpointer                user_data)
+{
+        GduAddPvLinuxLvm2Dialog *dialog = GDU_ADD_PV_LINUX_LVM2_DIALOG (user_data);
+        update (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_size_widget_changed (GduSizeWidget *size_widget,
+                        gpointer       user_data)
+{
+        GduAddPvLinuxLvm2Dialog *dialog = GDU_ADD_PV_LINUX_LVM2_DIALOG (user_data);
+        guint64 chosen_size;
+
+        chosen_size = gdu_size_widget_get_size (size_widget);
+
+        gdu_disk_selection_widget_set_component_size (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget),
+                                                      chosen_size);
+
+        update (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_add_pv_linux_lvm2_dialog_constructed (GObject *object)
+{
+        GduAddPvLinuxLvm2Dialog *dialog = GDU_ADD_PV_LINUX_LVM2_DIALOG (object);
+        GtkWidget *content_area;
+        GtkWidget *hbox;
+        GtkWidget *vbox;
+        GtkWidget *image;
+        GtkWidget *label;
+        gchar *s;
+        GIcon *icon;
+        GduPresentable *p;
+        GduPool *pool;
+        GtkWidget *disk_selection_widget;
+        gchar *vg_name;
+        gchar *vg_name_vpd;
+        guint row;
+        GtkWidget *table;
+        GtkWidget *size_widget;
+        guint64 initial_largest_segment;
+
+        vg_name = gdu_presentable_get_name (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+        vg_name_vpd = gdu_presentable_get_vpd_name (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+
+        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+
+        gtk_dialog_add_button (GTK_DIALOG (dialog),
+                               GTK_STOCK_CANCEL,
+                               GTK_RESPONSE_CANCEL);
+
+        gtk_dialog_add_button (GTK_DIALOG (dialog),
+                               GTK_STOCK_ADD,
+                               GTK_RESPONSE_APPLY);
+
+        content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+        icon = gdu_presentable_get_icon (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+
+        hbox = gtk_hbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+
+        image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+        vbox = gtk_vbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+        p = gdu_dialog_get_presentable (GDU_DIALOG (dialog));
+        pool = gdu_presentable_get_pool (p);
+
+        s = g_strdup_printf (_("Add Physical Volume to %s (%s)"), vg_name, vg_name_vpd);
+        gtk_window_set_title (GTK_WINDOW (dialog), s);
+        g_free (s);
+
+        /* --- */
+
+        row = 0;
+
+        table = gtk_table_new (2, 2, FALSE);
+        gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+        gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+        gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+
+        /*  Array 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), _("_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 (0,
+                                           0,
+                                           0);
+        gtk_table_attach (GTK_TABLE (table), size_widget, 1, 2, row, row + 1,
+                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+        dialog->priv->size_widget = size_widget;
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), size_widget);
+        row++;
+
+        /* --- */
+
+        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_label_set_width_chars (GTK_LABEL (label), 70); /* TODO: hate */
+
+        /* --- */
+
+        disk_selection_widget = gdu_disk_selection_widget_new (pool,
+                                                               NULL, /* TODO: ignored_drives */
+                                                               GDU_DISK_SELECTION_WIDGET_FLAGS_SHOW_DISKS_WITH_INSUFFICIENT_SPACE);
+        dialog->priv->disk_selection_widget = disk_selection_widget;
+        gtk_box_pack_start (GTK_BOX (vbox), disk_selection_widget, TRUE, TRUE, 0);
+
+        /* --- */
+
+        /* Initial selection - the largest one */
+        initial_largest_segment = gdu_disk_selection_widget_get_largest_segment_for_all (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+        gdu_size_widget_set_max_size (GDU_SIZE_WIDGET (dialog->priv->size_widget), initial_largest_segment);
+        gdu_size_widget_set_size (GDU_SIZE_WIDGET (dialog->priv->size_widget), initial_largest_segment);
+        gdu_disk_selection_widget_set_component_size (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget),
+                                                      initial_largest_segment);
+
+        g_signal_connect (dialog->priv->size_widget,
+                          "changed",
+                          G_CALLBACK (on_size_widget_changed),
+                          dialog);
+        g_signal_connect (dialog->priv->disk_selection_widget,
+                          "changed",
+                          G_CALLBACK (on_disk_selection_widget_changed),
+                          dialog);
+
+        g_object_unref (icon);
+        g_object_unref (pool);
+
+        /* select a sane size for the widget and allow resizing */
+        gtk_widget_set_size_request (GTK_WIDGET (dialog), 550, 450);
+        gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+        update (dialog);
+
+        g_free (vg_name);
+        g_free (vg_name_vpd);
+
+        if (G_OBJECT_CLASS (gdu_add_pv_linux_lvm2_dialog_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_add_pv_linux_lvm2_dialog_parent_class)->constructed (object);
+}
+
+static void
+gdu_add_pv_linux_lvm2_dialog_class_init (GduAddPvLinuxLvm2DialogClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduAddPvLinuxLvm2DialogPrivate));
+
+        object_class->get_property = gdu_add_pv_linux_lvm2_dialog_get_property;
+        object_class->constructed  = gdu_add_pv_linux_lvm2_dialog_constructed;
+        object_class->finalize     = gdu_add_pv_linux_lvm2_dialog_finalize;
+
+        g_object_class_install_property (object_class,
+                                         PROP_DRIVE,
+                                         g_param_spec_object ("drive",
+                                                              NULL,
+                                                              NULL,
+                                                              GDU_TYPE_DRIVE,
+                                                              G_PARAM_READABLE));
+
+        g_object_class_install_property (object_class,
+                                         PROP_SIZE,
+                                         g_param_spec_uint64 ("size",
+                                                              NULL,
+                                                              NULL,
+                                                              0,
+                                                              G_MAXUINT64,
+                                                              0,
+                                                              G_PARAM_READABLE));
+}
+
+static void
+gdu_add_pv_linux_lvm2_dialog_init (GduAddPvLinuxLvm2Dialog *dialog)
+{
+        dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+                                                    GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG,
+                                                    GduAddPvLinuxLvm2DialogPrivate);
+}
+
+GtkWidget *
+gdu_add_pv_linux_lvm2_dialog_new (GtkWindow               *parent,
+                                  GduLinuxLvm2VolumeGroup *vg)
+{
+        g_return_val_if_fail (GDU_IS_LINUX_LVM2_VOLUME_GROUP (vg), NULL);
+        return GTK_WIDGET (g_object_new (GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG,
+                                         "transient-for", parent,
+                                         "presentable", vg,
+                                         NULL));
+}
+
+GduDrive *
+gdu_add_pv_linux_lvm2_dialog_get_drive (GduAddPvLinuxLvm2Dialog *dialog)
+{
+        GPtrArray *drives;
+        GduDrive *ret;
+
+        g_return_val_if_fail (GDU_IS_ADD_PV_LINUX_LVM2_DIALOG (dialog), NULL);
+
+        ret = NULL;
+        drives = gdu_disk_selection_widget_get_selected_drives (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+        if (drives->len > 0)
+                ret = g_object_ref (GDU_DRIVE (drives->pdata[0]));
+        g_ptr_array_unref (drives);
+
+        return ret;
+}
+
+guint64
+gdu_add_pv_linux_lvm2_dialog_get_size  (GduAddPvLinuxLvm2Dialog *dialog)
+{
+        g_return_val_if_fail (GDU_IS_ADD_PV_LINUX_LVM2_DIALOG (dialog), 0);
+        return gdu_disk_selection_widget_get_component_size (GDU_DISK_SELECTION_WIDGET (dialog->priv->disk_selection_widget));
+}
+
diff --git a/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.h b/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.h
new file mode 100644
index 0000000..cff978b
--- /dev/null
+++ b/src/gdu-gtk/gdu-add-pv-linux-lvm2-dialog.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#if !defined (__GDU_GTK_INSIDE_GDU_GTK_H) && !defined (GDU_GTK_COMPILATION)
+#error "Only <gdu-gtk/gdu-gtk.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __GDU_ADD_PV_LINUX_LVM2_DIALOG_H
+#define __GDU_ADD_PV_LINUX_LVM2_DIALOG_H
+
+#include <gdu-gtk/gdu-gtk-types.h>
+#include <gdu-gtk/gdu-dialog.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG            gdu_add_pv_linux_lvm2_dialog_get_type()
+#define GDU_ADD_PV_LINUX_LVM2_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG, GduAddPvLinuxLvm2Dialog))
+#define GDU_ADD_PV_LINUX_LVM2_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG, GduAddPvLinuxLvm2DialogClass))
+#define GDU_IS_ADD_PV_LINUX_LVM2_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG))
+#define GDU_IS_ADD_PV_LINUX_LVM2_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG))
+#define GDU_ADD_PV_LINUX_LVM2_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_ADD_PV_LINUX_LVM2_DIALOG, GduAddPvLinuxLvm2DialogClass))
+
+typedef struct GduAddPvLinuxLvm2DialogClass   GduAddPvLinuxLvm2DialogClass;
+typedef struct GduAddPvLinuxLvm2DialogPrivate GduAddPvLinuxLvm2DialogPrivate;
+
+struct GduAddPvLinuxLvm2Dialog
+{
+        GduDialog parent;
+
+        /*< private >*/
+        GduAddPvLinuxLvm2DialogPrivate *priv;
+};
+
+struct GduAddPvLinuxLvm2DialogClass
+{
+        GduDialogClass parent_class;
+};
+
+GType         gdu_add_pv_linux_lvm2_dialog_get_type  (void) G_GNUC_CONST;
+GtkWidget    *gdu_add_pv_linux_lvm2_dialog_new       (GtkWindow                    *parent,
+                                                      GduLinuxLvm2VolumeGroup      *vg);
+GduDrive     *gdu_add_pv_linux_lvm2_dialog_get_drive (GduAddPvLinuxLvm2Dialog      *dialog);
+guint64       gdu_add_pv_linux_lvm2_dialog_get_size  (GduAddPvLinuxLvm2Dialog      *dialog);
+
+G_END_DECLS
+
+#endif /* __GDU_ADD_PV_LINUX_LVM2_DIALOG_H */
diff --git a/src/gdu-gtk/gdu-disk-selection-widget.c b/src/gdu-gtk/gdu-disk-selection-widget.c
index 3726007..84e69ff 100644
--- a/src/gdu-gtk/gdu-disk-selection-widget.c
+++ b/src/gdu-gtk/gdu-disk-selection-widget.c
@@ -623,6 +623,39 @@ count_num_available_disks_func (GtkTreeModel *model,
         return FALSE;
 }
 
+static gboolean
+find_largest_segment_for_all_func (GtkTreeModel *model,
+                                   GtkTreePath  *path,
+                                   GtkTreeIter  *iter,
+                                   gpointer      data)
+{
+        GduPresentable *p;
+        guint64 *largest_segment_for_all = data;
+
+        gtk_tree_model_get (model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
+                            -1);
+
+        if (GDU_IS_DRIVE (p)) {
+                guint64 largest_segment;
+
+                g_warn_if_fail (gdu_drive_has_unallocated_space (GDU_DRIVE (p),
+                                                                 NULL,
+                                                                 &largest_segment,
+                                                                 NULL, /* total_free */
+                                                                 NULL));
+
+                if (largest_segment > *largest_segment_for_all)
+                        *largest_segment_for_all = largest_segment;
+
+        }
+
+        g_object_unref (p);
+
+        return FALSE;
+}
+
 static void
 get_sizes (GduDiskSelectionWidget *widget,
            guint                  *out_num_disks,
@@ -639,7 +672,7 @@ get_sizes (GduDiskSelectionWidget *widget,
         num_disks = 0;
         num_available_disks = 0;
         largest_segment_for_selected = G_MAXUINT64;
-        largest_segment_for_all = G_MAXUINT64;
+        largest_segment_for_all = 0;
 
         for (l = widget->priv->selected_drives; l != NULL; l = l->next) {
                 GduPresentable *p = GDU_PRESENTABLE (l->data);
@@ -664,6 +697,10 @@ get_sizes (GduDiskSelectionWidget *widget,
                                 count_num_available_disks_func,
                                 &num_available_disks);
 
+        gtk_tree_model_foreach (widget->priv->model,
+                                find_largest_segment_for_all_func,
+                                &largest_segment_for_all);
+
         if (out_num_disks != NULL)
                 *out_num_disks = num_disks;
 
@@ -1108,8 +1145,10 @@ gdu_disk_selection_widget_set_component_size (GduDiskSelectionWidget *widget,
                                               guint64                 component_size)
 {
         g_return_if_fail (GDU_IS_DISK_SELECTION_WIDGET (widget));
-        widget->priv->component_size = component_size;
-        gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (widget->priv->model));
+        if (widget->priv->component_size != component_size) {
+                widget->priv->component_size = component_size;
+                gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (widget->priv->model));
+        }
 }
 
 guint
diff --git a/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.c b/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.c
new file mode 100644
index 0000000..5bd8a72
--- /dev/null
+++ b/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.c
@@ -0,0 +1,946 @@
+/* -*- 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-edit-linux-lvm2-dialog.h"
+#include "gdu-size-widget.h"
+
+struct GduEditLinuxLvm2DialogPrivate
+{
+        gulong changed_id;
+        gulong removed_id;
+
+        GtkWidget *pvs_tree_view;
+        GtkTreeStore *pvs_tree_store;
+
+        GduDetailsElement *pv_smart_element;
+        GduDetailsElement *pv_device_element;
+
+        GduButtonElement *pv_new_button;
+        GduButtonElement *pv_remove_button;
+
+};
+
+enum {
+        NEW_BUTTON_CLICKED_SIGNAL,
+        REMOVE_BUTTON_CLICKED_SIGNAL,
+        LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+        LINUX_LVM2_PV_POSITION_COLUMN,
+        LINUX_LVM2_PV_UUID_COLUMN,
+        LINUX_LVM2_PV_ICON_COLUMN,
+        LINUX_LVM2_PV_VOLUME_COLUMN,
+        LINUX_LVM2_PV_DRIVE_COLUMN,
+        LINUX_LVM2_PV_SIZE_STRING_COLUMN,
+        LINUX_LVM2_PV_UNALLOCATED_SIZE_STRING_COLUMN,
+        LINUX_LVM2_PV_N_COLUMNS,
+};
+
+static void gdu_edit_linux_lvm2_dialog_constructed (GObject *object);
+
+static void update_tree (GduEditLinuxLvm2Dialog *dialog);
+static void update_details (GduEditLinuxLvm2Dialog *dialog);
+
+static GduDevice *get_selected_pv (GduEditLinuxLvm2Dialog *dialog);
+
+G_DEFINE_TYPE (GduEditLinuxLvm2Dialog, gdu_edit_linux_lvm2_dialog, GDU_TYPE_DIALOG)
+
+static void
+gdu_edit_linux_lvm2_dialog_finalize (GObject *object)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (object);
+
+        if (dialog->priv->changed_id > 0) {
+                g_signal_handler_disconnect (gdu_dialog_get_presentable (GDU_DIALOG (dialog)),
+                                             dialog->priv->changed_id);
+        }
+        if (dialog->priv->removed_id > 0) {
+                g_signal_handler_disconnect (gdu_dialog_get_presentable (GDU_DIALOG (dialog)),
+                                             dialog->priv->removed_id);
+        }
+
+        if (G_OBJECT_CLASS (gdu_edit_linux_lvm2_dialog_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_edit_linux_lvm2_dialog_parent_class)->finalize (object);
+}
+
+static void
+gdu_edit_linux_lvm2_dialog_class_init (GduEditLinuxLvm2DialogClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduEditLinuxLvm2DialogPrivate));
+
+        object_class->constructed  = gdu_edit_linux_lvm2_dialog_constructed;
+        object_class->finalize     = gdu_edit_linux_lvm2_dialog_finalize;
+
+        signals[NEW_BUTTON_CLICKED_SIGNAL] =
+                g_signal_new ("new-button-clicked",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GduEditLinuxLvm2DialogClass, new_button_clicked),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+
+        signals[REMOVE_BUTTON_CLICKED_SIGNAL] =
+                g_signal_new ("remove-button-clicked",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GduEditLinuxLvm2DialogClass, remove_button_clicked),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE,
+                              1,
+                              GDU_TYPE_DEVICE);
+}
+
+static void
+gdu_edit_linux_lvm2_dialog_init (GduEditLinuxLvm2Dialog *dialog)
+{
+        dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+                                                    GDU_TYPE_EDIT_LINUX_LVM2_DIALOG,
+                                                    GduEditLinuxLvm2DialogPrivate);
+}
+
+GtkWidget *
+gdu_edit_linux_lvm2_dialog_new (GtkWindow                *parent,
+                                GduLinuxLvm2VolumeGroup  *vg)
+{
+        g_return_val_if_fail (GDU_IS_LINUX_LVM2_VOLUME_GROUP (vg), NULL);
+        return GTK_WIDGET (g_object_new (GDU_TYPE_EDIT_LINUX_LVM2_DIALOG,
+                                         "transient-for", parent,
+                                         "presentable", vg,
+                                         NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_pv_new_button_clicked (GduButtonElement *button_element,
+                          gpointer          user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+        g_signal_emit (dialog, signals[NEW_BUTTON_CLICKED_SIGNAL], 0);
+}
+
+static void
+on_pv_remove_button_clicked (GduButtonElement *button_element,
+                                    gpointer          user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+        GduDevice *slave;
+
+        slave = get_selected_pv (dialog);
+        g_signal_emit (dialog, signals[REMOVE_BUTTON_CLICKED_SIGNAL], 0, slave);
+        g_object_unref (slave);
+}
+
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+format_markup (GtkCellLayout   *cell_layout,
+               GtkCellRenderer *renderer,
+               GtkTreeModel    *tree_model,
+               GtkTreeIter     *iter,
+               gpointer         user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+        GtkTreeSelection *tree_selection;
+        gchar *name;
+        gchar *drive_name;
+        GduPresentable *volume_for_pv;
+        GduPresentable *drive_for_pv;
+        gchar *pv_uuid;
+        gchar *markup;
+        GtkStyle *style;
+        GdkColor desc_gdk_color = {0};
+        gchar *desc_color;
+        GtkStateType state;
+
+        tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->pvs_tree_view));
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            LINUX_LVM2_PV_VOLUME_COLUMN, &volume_for_pv,
+                            LINUX_LVM2_PV_DRIVE_COLUMN, &drive_for_pv,
+                            LINUX_LVM2_PV_UUID_COLUMN, &pv_uuid,
+                            -1);
+
+        /* This color business shouldn't be this hard... */
+        style = gtk_widget_get_style (GTK_WIDGET (dialog->priv->pvs_tree_view));
+        if (gtk_tree_selection_iter_is_selected (tree_selection, iter)) {
+                if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (dialog->priv->pvs_tree_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));
+
+        if (volume_for_pv != NULL) {
+                name = gdu_presentable_get_vpd_name (volume_for_pv);
+                if (drive_for_pv != NULL) {
+                        drive_name = gdu_presentable_get_vpd_name (drive_for_pv);
+                } else {
+                        drive_name = g_strdup ("");
+                }
+        } else {
+                name = g_strdup (_("Missing Physical Volume"));
+                drive_name = g_strdup_printf (_("UUID: %s"), pv_uuid);
+        }
+
+        markup = g_strdup_printf ("<b>%s</b>\n"
+                                  "<span fgcolor=\"%s\"><small>%s</small></span>",
+                                  name,
+                                  desc_color,
+                                  drive_name);
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        g_free (name);
+        g_free (drive_name);
+        g_free (markup);
+        g_free (desc_color);
+        if (volume_for_pv != NULL)
+                g_object_unref (volume_for_pv);
+        if (drive_for_pv != NULL)
+                g_object_unref (drive_for_pv);
+        g_free (pv_uuid);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_pv_tree_selection_changed  (GtkTreeSelection *selection,
+                                      gpointer          user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+
+        update_details (dialog);
+}
+
+static void
+on_linux_lvm2_vg_changed (GduPresentable   *presentable,
+                          gpointer          user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+
+        update_tree (dialog);
+
+        /* close the dialog if the RAID array stops */
+        if (!gdu_drive_is_active (GDU_DRIVE (gdu_dialog_get_presentable (GDU_DIALOG (dialog))))) {
+                gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+        }
+}
+
+static void
+on_linux_lvm2_vg_removed (GduPresentable   *presentable,
+                          gpointer          user_data)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (user_data);
+
+        /* close the dialog if the RAID array disappears */
+        gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+}
+
+static void
+gdu_edit_linux_lvm2_dialog_constructed (GObject *object)
+{
+        GduEditLinuxLvm2Dialog *dialog = GDU_EDIT_LINUX_LVM2_DIALOG (object);
+        GduLinuxLvm2VolumeGroup *vg;
+        GtkWidget *content_area;
+        GtkWidget *vbox;
+        GtkWidget *vbox2;
+        GtkWidget *hbox;
+        GtkWidget *align;
+        GIcon *icon;
+        GtkWidget *image;
+        GtkWidget *table;
+        GtkWidget *label;
+        gchar *s;
+        gchar *name;
+        gchar *vpd_name;
+        GPtrArray *elements;
+        GduDetailsElement *element;
+        GtkWidget *tree_view;
+        GtkWidget *scrolled_window;
+        GtkTreeSelection *selection;
+        GtkTreeViewColumn *column;
+        GtkCellRenderer *renderer;
+        GduButtonElement *button_element;
+
+        vg = GDU_LINUX_LVM2_VOLUME_GROUP (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+
+        icon = gdu_presentable_get_icon (GDU_PRESENTABLE (vg));
+
+        content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+        gtk_container_set_border_width (GTK_CONTAINER (content_area), 10);
+
+        hbox = gtk_hbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+
+        image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+        vbox = gtk_vbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+        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);
+
+        name = gdu_presentable_get_name (GDU_PRESENTABLE (vg));
+        vpd_name = gdu_presentable_get_vpd_name (GDU_PRESENTABLE (vg));
+        s = g_strdup_printf (_("Edit PVs on %s (%s)"), name, vpd_name);
+        gtk_window_set_title (GTK_WINDOW (dialog), s);
+        g_free (s);
+
+        gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+
+        /* -------------------------------------------------------------------------------- */
+
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        s = g_strconcat ("<b>", _("Physical _Volumes"), "</b>", NULL);
+        gtk_label_set_markup_with_mnemonic (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, TRUE, TRUE, 0);
+        gtk_container_add (GTK_CONTAINER (align), vbox2);
+
+        /* -------------------------------------------------------------------------------- */
+
+        tree_view = gtk_tree_view_new ();
+        dialog->priv->pvs_tree_view = tree_view;
+        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);
+        gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+        gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0);
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view);
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+        g_signal_connect (selection, "changed", G_CALLBACK (on_pv_tree_selection_changed), dialog);
+
+        column = gtk_tree_view_column_new ();
+        gtk_tree_view_column_set_title (column, _("Physical Volume"));
+        renderer = gtk_cell_renderer_pixbuf_new ();
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_tree_view_column_set_attributes (column, renderer,
+                                             "pixbuf", LINUX_LVM2_PV_ICON_COLUMN,
+                                             NULL);
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, TRUE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup,
+                                            dialog,
+                                            NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+        column = gtk_tree_view_column_new ();
+        gtk_tree_view_column_set_title (column, _("Capacity"));
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_tree_view_column_set_attributes (column, renderer,
+                                             "markup", LINUX_LVM2_PV_SIZE_STRING_COLUMN,
+                                             NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+        column = gtk_tree_view_column_new ();
+        gtk_tree_view_column_set_title (column, _("Unallocated"));
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_tree_view_column_set_attributes (column, renderer,
+                                             "markup", LINUX_LVM2_PV_UNALLOCATED_SIZE_STRING_COLUMN,
+                                             NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), TRUE);
+
+        /* -------------------------------------------------------------------------------- */
+
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+        element = gdu_details_element_new (_("SMART Status:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->pv_smart_element = element;
+
+        element = gdu_details_element_new (_("Device:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->pv_device_element = element;
+
+        table = gdu_details_table_new (2, elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+
+        /* -------------------------------------------------------------------------------- */
+
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+        button_element = gdu_button_element_new (GTK_STOCK_NEW,
+                                                 _("_New Physical Volume"),
+                                                 _("Add a new PV to the VG"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_pv_new_button_clicked),
+                          dialog);
+        g_ptr_array_add (elements, button_element);
+        dialog->priv->pv_new_button = button_element;
+
+        button_element = gdu_button_element_new (GTK_STOCK_DELETE,
+                                                 _("_Remove Physical Volumes"),
+                                                 _("Remove the PV from the VG"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_pv_remove_button_clicked),
+                          dialog);
+        g_ptr_array_add (elements, button_element);
+        dialog->priv->pv_remove_button = button_element;
+
+        table = gdu_button_table_new (2, elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+
+        /* -------------------------------------------------------------------------------- */
+
+        dialog->priv->changed_id = g_signal_connect (vg,
+                                                     "changed",
+                                                     G_CALLBACK (on_linux_lvm2_vg_changed),
+                                                     dialog);
+        dialog->priv->removed_id = g_signal_connect (vg,
+                                                     "removed",
+                                                     G_CALLBACK (on_linux_lvm2_vg_removed),
+                                                     dialog);
+        update_tree (dialog);
+        update_details (dialog);
+
+        g_free (name);
+        g_free (vpd_name);
+
+        /* select a sane size for the dialog and allow resizing */
+        gtk_widget_set_size_request (GTK_WIDGET (dialog), 650, 450);
+        gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+        if (G_OBJECT_CLASS (gdu_edit_linux_lvm2_dialog_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_edit_linux_lvm2_dialog_parent_class)->constructed (object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GduDevice *
+find_pv_by_uuid (GduPool     *pool,
+                 const gchar *uuid)
+{
+        GduDevice *ret;
+        GList *devices;
+        GList *l;
+
+        ret = NULL;
+
+        devices = gdu_pool_get_devices (pool);
+        for (l = devices; l != NULL; l = l->next) {
+                GduDevice *d = GDU_DEVICE (l->data);
+
+                if (gdu_device_is_linux_lvm2_pv (d) && g_strcmp0 (gdu_device_linux_lvm2_pv_get_uuid (d), uuid) == 0) {
+                        ret = g_object_ref (d);
+                        goto out;
+                }
+        }
+
+ out:
+        g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+        g_list_free (devices);
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GduDevice *
+get_selected_pv (GduEditLinuxLvm2Dialog *dialog)
+{
+        gchar *pv_uuid;
+        GduPool *pool;
+        GduDevice *pv;
+        GtkTreeSelection *tree_selection;
+        GtkTreeIter iter;
+
+        pv_uuid = NULL;
+        pv = NULL;
+        pool = NULL;
+
+        tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->pvs_tree_view));
+        if (gtk_tree_selection_get_selected (tree_selection, NULL, &iter)) {
+                gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->pvs_tree_store), &iter,
+                                    LINUX_LVM2_PV_UUID_COLUMN,
+                                    &pv_uuid,
+                                    -1);
+        }
+
+        if (pv_uuid == NULL) {
+                goto out;
+        }
+
+        pool = gdu_presentable_get_pool (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+        pv = find_pv_by_uuid (pool, pv_uuid);
+
+ out:
+        g_free (pv_uuid);
+        if (pool != NULL)
+                g_object_unref (pool);
+
+        return pv;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
+pv_tree_sort_func (GtkTreeModel *model,
+                          GtkTreeIter  *a,
+                          GtkTreeIter  *b,
+                          gpointer      user_data)
+{
+        gint ret;
+        gint a_position;
+        gint b_position;
+        gchar *a_uuid;
+        gchar *b_uuid;
+
+        gtk_tree_model_get (model,
+                            a,
+                            LINUX_LVM2_PV_UUID_COLUMN, &a_uuid,
+                            LINUX_LVM2_PV_POSITION_COLUMN, &a_position,
+                            -1);
+
+        gtk_tree_model_get (model,
+                            b,
+                            LINUX_LVM2_PV_UUID_COLUMN, &b_uuid,
+                            LINUX_LVM2_PV_POSITION_COLUMN, &b_position,
+                            -1);
+
+        if (a_position != b_position)
+                ret = a_position - b_position;
+        else
+                ret = g_strcmp0 (a_uuid, b_uuid);
+
+        g_free (a_uuid);
+        g_free (b_uuid);
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_tree (GduEditLinuxLvm2Dialog *dialog)
+{
+        GduLinuxLvm2VolumeGroup *vg;
+        GtkTreeStore *store;
+        gchar *selected_pv_uuid;
+        GtkTreeSelection *tree_selection;
+        GtkTreeIter *iter_to_select;
+        GduDevice *pv_device;
+        GduPool *pool;
+        gchar **pvs;
+        guint n;
+
+        pv_device = NULL;
+        pool = NULL;
+
+        tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->pvs_tree_view));
+
+        iter_to_select = NULL;
+        selected_pv_uuid = NULL;
+        if (dialog->priv->pvs_tree_store != NULL) {
+                GtkTreeIter iter;
+                if (gtk_tree_selection_get_selected (tree_selection, NULL, &iter)) {
+                        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->pvs_tree_store), &iter,
+                                            LINUX_LVM2_PV_UUID_COLUMN,
+                                            &selected_pv_uuid,
+                                            -1);
+                }
+                g_object_unref (dialog->priv->pvs_tree_store);
+        }
+
+        store = gtk_tree_store_new (LINUX_LVM2_PV_N_COLUMNS,
+                                    G_TYPE_INT,
+                                    G_TYPE_STRING,
+                                    GDK_TYPE_PIXBUF,
+                                    GDU_TYPE_VOLUME,
+                                    GDU_TYPE_DRIVE,
+                                    G_TYPE_STRING,
+                                    G_TYPE_STRING);
+        dialog->priv->pvs_tree_store = store;
+
+        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
+                                         LINUX_LVM2_PV_UUID_COLUMN,
+                                         pv_tree_sort_func,
+                                         NULL,
+                                         NULL);
+        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+                                              LINUX_LVM2_PV_UUID_COLUMN,
+                                              GTK_SORT_ASCENDING);
+
+
+        vg = GDU_LINUX_LVM2_VOLUME_GROUP (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+        if (vg == NULL)
+                goto out;
+
+        pv_device = gdu_linux_lvm2_volume_group_get_pv_device (vg);
+        if (pv_device == NULL)
+                goto out;
+
+        pool = gdu_presentable_get_pool (GDU_PRESENTABLE (vg));
+
+        pvs = gdu_device_linux_lvm2_pv_get_group_physical_volumes (pv_device);
+        for (n = 0; pvs != NULL && pvs[n] != NULL; n++) {
+                const gchar *pv_info = (const gchar *) pvs[n];
+                gchar *pv_uuid;
+                GduDevice *pv;
+                GdkPixbuf *pixbuf;
+                GduPresentable *volume_for_pv;
+                GduPresentable *drive_for_pv;
+                GtkTreeIter iter;
+                guint64 allocated_size;
+                guint64 size;
+                gchar *size_str;
+                gchar *unallocated_size_str;
+                gchar *s;
+
+                pv_uuid = NULL;
+                pixbuf = NULL;
+                volume_for_pv = NULL;
+                drive_for_pv = NULL;
+                size = 0;
+
+                /* TODO: this is kind of a hack */
+                pv_uuid = g_strdup (pv_info + 5); /* skip leading 'uuid=' */
+                s = strstr (pv_uuid, ";");
+                if (s != NULL)
+                        *s = '\0';
+
+                if (!gdu_linux_lvm2_volume_group_get_pv_info (vg,
+                                                              pv_uuid,
+                                                              NULL,
+                                                              &size,
+                                                              &allocated_size)) {
+                        g_warning ("Unable to get information about PV with uuid `%s'", pv_uuid);
+                        continue;
+                }
+
+                pv = find_pv_by_uuid (pool, pv_uuid);
+                if (pv != NULL) {
+
+                        volume_for_pv = gdu_pool_get_volume_by_device (pool, pv);
+
+                        if (volume_for_pv == NULL) {
+                                g_warning ("Cannot find volume for device `%s'", gdu_device_get_object_path (pv));
+                                g_object_unref (pv);
+                                continue;
+                        }
+
+                        if (gdu_device_is_partition (pv)) {
+                                GduDevice *drive_device;
+                                drive_device = gdu_pool_get_by_object_path (pool, gdu_device_partition_get_slave (pv));
+                                if (drive_device != NULL) {
+                                        drive_for_pv = gdu_pool_get_drive_by_device (pool, drive_device);
+                                        g_object_unref (drive_device);
+                                } else {
+                                        g_warning ("No device with objpath %s", gdu_device_partition_get_slave (pv));
+                                }
+                        } else {
+                                drive_for_pv = gdu_pool_get_drive_by_device (pool, pv);
+                        }
+
+                        pixbuf = gdu_util_get_pixbuf_for_presentable (volume_for_pv, GTK_ICON_SIZE_SMALL_TOOLBAR);
+
+                        /* TODO: we can remove this once we export SIZE and ALLOCATED_SIZE */
+                        if (size == G_MAXUINT64)
+                                size = gdu_device_get_size (pv);
+
+                        g_object_unref (pv);
+                }
+
+                if (size != G_MAXUINT64)
+                        size_str = gdu_util_get_size_for_display (size, FALSE, FALSE);
+                else
+                        size_str = g_strdup ("?");
+
+                if (size != G_MAXUINT64 && allocated_size != G_MAXUINT64)
+                        unallocated_size_str = gdu_util_get_size_for_display (size - allocated_size, FALSE, FALSE);
+                else
+                        unallocated_size_str = g_strdup ("?");
+
+                gtk_tree_store_append (store, &iter, NULL);
+                gtk_tree_store_set (store,
+                                    &iter,
+                                    LINUX_LVM2_PV_POSITION_COLUMN, n,
+                                    LINUX_LVM2_PV_ICON_COLUMN, pixbuf,
+                                    LINUX_LVM2_PV_UUID_COLUMN, pv_uuid,
+                                    LINUX_LVM2_PV_VOLUME_COLUMN, volume_for_pv,
+                                    LINUX_LVM2_PV_DRIVE_COLUMN, drive_for_pv,
+                                    LINUX_LVM2_PV_SIZE_STRING_COLUMN, size_str,
+                                    LINUX_LVM2_PV_UNALLOCATED_SIZE_STRING_COLUMN, unallocated_size_str,
+                                    -1);
+
+                if (g_strcmp0 (pv_uuid, selected_pv_uuid) == 0) {
+                        g_assert (iter_to_select == NULL);
+                        iter_to_select = gtk_tree_iter_copy (&iter);
+                }
+
+                g_free (size_str);
+                g_free (unallocated_size_str);
+                if (pixbuf != NULL)
+                        g_object_unref (pixbuf);
+                if (volume_for_pv != NULL)
+                        g_object_unref (volume_for_pv);
+                if (drive_for_pv != NULL)
+                        g_object_unref (drive_for_pv);
+
+                g_free (pv_uuid);
+        }
+
+#if 0
+        /* add all slaves */
+        for (l = slaves; l != NULL; l = l->next) {
+                GduDevice *sd = GDU_DEVICE (l->data);
+                GdkPixbuf *pixbuf;
+                GtkTreeIter iter;
+                GduPool *pool;
+                GduPresentable *volume_for_slave;
+                GduPresentable *drive_for_slave;
+                gchar *position_str;
+                const gchar *object_path;
+                gint position;
+                gchar *slave_state_str;
+
+                pool = gdu_device_get_pool (sd);
+                volume_for_slave = gdu_pool_get_volume_by_device (pool, sd);
+                g_object_unref (pool);
+
+                if (volume_for_slave == NULL) {
+                        g_warning ("Cannot find volume for device `%s'", gdu_device_get_object_path (sd));
+                        continue;
+                }
+
+                if (gdu_device_is_partition (sd)) {
+                        GduDevice *drive_device;
+                        drive_device = gdu_pool_get_by_object_path (pool, gdu_device_partition_get_slave (sd));
+                        if (drive_device != NULL) {
+                                drive_for_slave = gdu_pool_get_drive_by_device (pool, drive_device);
+                                g_object_unref (drive_device);
+                        } else {
+                                g_warning ("No slave with objpath %s", gdu_device_partition_get_slave (sd));
+                        }
+                } else {
+                        drive_for_slave = gdu_pool_get_drive_by_device (pool, sd);
+                 }
+
+                if (gdu_drive_is_active (GDU_DRIVE (linux_lvm2_drive))) {
+                        position = gdu_device_linux_lvm2_pv_get_position (sd);
+                        if (position >= 0) {
+                                position_str = g_strdup_printf ("%d", position);
+                        } else {
+                                position = G_MAXINT; /* to appear last in list */
+                                position_str = g_strdup ("â??");
+                        }
+                } else {
+                        position = G_MAXINT; /* to appear last in list */
+                        position_str = g_strdup ("â??");
+                }
+
+                pixbuf = gdu_util_get_pixbuf_for_presentable (volume_for_slave, GTK_ICON_SIZE_SMALL_TOOLBAR);
+                object_path = gdu_device_get_object_path (sd);
+
+                slave_state_str = gdu_linux_lvm2_drive_get_slave_state_markup (linux_lvm2_drive, sd);
+                if (slave_state_str == NULL)
+                        slave_state_str = g_strdup ("â??");
+
+                gtk_tree_store_append (store, &iter, NULL);
+                gtk_tree_store_set (store,
+                                    &iter,
+                                    LINUX_LVM2_SLAVE_VOLUME_COLUMN, volume_for_slave,
+                                    LINUX_LVM2_SLAVE_DRIVE_COLUMN, drive_for_slave,
+                                    LINUX_LVM2_POSITION_COLUMN, position,
+                                    LINUX_LVM2_POSITION_STRING_COLUMN, position_str,
+                                    LINUX_LVM2_PV_ICON_COLUMN, pixbuf,
+                                    LINUX_LVM2_OBJPATH_COLUMN, object_path,
+                                    LINUX_LVM2_SLAVE_STATE_STRING_COLUMN, slave_state_str,
+                                    -1);
+
+                if (g_strcmp0 (object_path, selected_pv_uuid) == 0) {
+                        g_assert (iter_to_select == NULL);
+                        iter_to_select = gtk_tree_iter_copy (&iter);
+                }
+
+                g_free (position_str);
+                g_free (slave_state_str);
+                if (pixbuf != NULL)
+                        g_object_unref (pixbuf);
+
+                g_object_unref (volume_for_slave);
+                if (drive_for_slave != NULL)
+                        g_object_unref (drive_for_slave);
+        }
+#endif
+
+ out:
+        if (pool != NULL)
+                g_object_unref (pool);
+        if (pv_device != NULL)
+                g_object_unref (pv_device);
+
+        gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->pvs_tree_view), GTK_TREE_MODEL (store));
+
+        /* Select the first iter if nothing is currently selected */
+        if (iter_to_select == NULL) {
+                GtkTreeIter iter;
+                if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
+                        iter_to_select = gtk_tree_iter_copy (&iter);
+        }
+        /* (Re-)select the pv */
+        if (iter_to_select != NULL) {
+                gtk_tree_selection_select_iter (tree_selection, iter_to_select);
+                gtk_tree_iter_free (iter_to_select);
+        }
+
+        g_free (selected_pv_uuid);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_details (GduEditLinuxLvm2Dialog *dialog)
+{
+        GduLinuxLvm2VolumeGroup *vg;
+        GduDevice *pv;
+        GduDevice *slave_drive_device;
+        gchar *s;
+        gchar *s2;
+        GIcon *icon;
+        gboolean show_pv_new_button;
+        gboolean show_pv_remove_button;
+        //GduLinuxLvm2DriveSlaveFlags slave_flags;
+
+        show_pv_new_button = TRUE; /* It's always possible to add a PV */
+        show_pv_remove_button = FALSE;
+
+        slave_drive_device = NULL;
+
+        pv = get_selected_pv (dialog);
+        if (pv == NULL) {
+                gdu_details_element_set_text (dialog->priv->pv_smart_element, "â??");
+                gdu_details_element_set_icon (dialog->priv->pv_smart_element, NULL);
+                gdu_details_element_set_text (dialog->priv->pv_device_element, "â??");
+                goto out;
+        }
+
+        show_pv_remove_button = TRUE;
+
+        vg = GDU_LINUX_LVM2_VOLUME_GROUP (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+
+        if (gdu_device_is_partition (pv)) {
+                GduPool *pool;
+                pool = gdu_device_get_pool (pv);
+                slave_drive_device = gdu_pool_get_by_object_path (pool, gdu_device_partition_get_slave (pv));
+                g_object_unref (pool);
+        } else {
+                slave_drive_device = g_object_ref (pv);
+        }
+
+        if (gdu_device_drive_ata_smart_get_is_available (slave_drive_device) &&
+            gdu_device_drive_ata_smart_get_time_collected (slave_drive_device) > 0) {
+                gboolean highlight;
+
+                s = gdu_util_ata_smart_status_to_desc (gdu_device_drive_ata_smart_get_status (slave_drive_device),
+                                                       &highlight,
+                                                       NULL,
+                                                       &icon);
+                if (highlight) {
+                        s2 = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", s);
+                        g_free (s);
+                        s = s2;
+                }
+
+                gdu_details_element_set_text (dialog->priv->pv_smart_element, s);
+                gdu_details_element_set_icon (dialog->priv->pv_smart_element, icon);
+
+                g_free (s);
+                g_object_unref (icon);
+        } else {
+                /* Translators: Used when SMART is not supported on the RAID pv */
+                gdu_details_element_set_text (dialog->priv->pv_smart_element, _("Not Supported"));
+                icon = g_themed_icon_new ("gdu-smart-unknown");
+                gdu_details_element_set_icon (dialog->priv->pv_smart_element, icon);
+                g_object_unref (icon);
+        }
+
+ out:
+        gdu_button_element_set_visible (dialog->priv->pv_new_button, show_pv_new_button);
+        gdu_button_element_set_visible (dialog->priv->pv_remove_button, show_pv_remove_button);
+
+        if (pv != NULL)
+                g_object_unref (pv);
+        if (slave_drive_device != NULL)
+                g_object_unref (slave_drive_device);
+}
+
diff --git a/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.h b/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.h
new file mode 100644
index 0000000..4286925
--- /dev/null
+++ b/src/gdu-gtk/gdu-edit-linux-lvm2-dialog.h
@@ -0,0 +1,64 @@
+/* -*- 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_EDIT_LINUX_LVM2_DIALOG_H
+#define __GDU_EDIT_LINUX_LVM2_DIALOG_H
+
+#include <gdu-gtk/gdu-gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_EDIT_LINUX_LVM2_DIALOG         (gdu_edit_linux_lvm2_dialog_get_type())
+#define GDU_EDIT_LINUX_LVM2_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_EDIT_LINUX_LVM2_DIALOG, GduEditLinuxLvm2Dialog))
+#define GDU_EDIT_LINUX_LVM2_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_EDIT_LINUX_LVM2_DIALOG, GduEditLinuxLvm2DialogClass))
+#define GDU_IS_EDIT_LINUX_LVM2_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_EDIT_LINUX_LVM2_DIALOG))
+#define GDU_IS_EDIT_LINUX_LVM2_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_EDIT_LINUX_LVM2_DIALOG))
+#define GDU_EDIT_LINUX_LVM2_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_EDIT_LINUX_LVM2_DIALOG, GduEditLinuxLvm2DialogClass))
+
+typedef struct GduEditLinuxLvm2DialogClass   GduEditLinuxLvm2DialogClass;
+typedef struct GduEditLinuxLvm2DialogPrivate GduEditLinuxLvm2DialogPrivate;
+
+struct GduEditLinuxLvm2Dialog
+{
+        GduDialog parent;
+
+        /*< private >*/
+        GduEditLinuxLvm2DialogPrivate *priv;
+};
+
+struct GduEditLinuxLvm2DialogClass
+{
+        GduDialogClass parent_class;
+
+        void (*new_button_clicked)    (GduEditLinuxLvm2Dialog *dialog);
+        void (*remove_button_clicked) (GduEditLinuxLvm2Dialog *dialog,
+                                       GduDevice              *physical_volume);
+};
+
+GType       gdu_edit_linux_lvm2_dialog_get_type           (void) G_GNUC_CONST;
+GtkWidget*  gdu_edit_linux_lvm2_dialog_new                (GtkWindow                *parent,
+                                                           GduLinuxLvm2VolumeGroup  *vg);
+
+G_END_DECLS
+
+#endif  /* __GDU_EDIT_LINUX_LVM2_DIALOG_H */
+
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index d0845ab..7b20569 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -63,6 +63,8 @@ typedef struct GduAddComponentLinuxMdDialog   GduAddComponentLinuxMdDialog;
 typedef struct GduEditLinuxMdDialog           GduEditLinuxMdDialog;
 typedef struct GduDriveBenchmarkDialog        GduDriveBenchmarkDialog;
 typedef struct GduConnectToServerDialog       GduConnectToServerDialog;
+typedef struct GduEditLinuxLvm2Dialog         GduEditLinuxLvm2Dialog;
+typedef struct GduAddPvLinuxLvm2Dialog        GduAddPvLinuxLvm2Dialog;
 
 G_END_DECLS
 
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index 5e5d45f..cd450d9 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -52,9 +52,11 @@
 #include <gdu-gtk/gdu-disk-selection-widget.h>
 #include <gdu-gtk/gdu-add-component-linux-md-dialog.h>
 #include <gdu-gtk/gdu-edit-linux-md-dialog.h>
+#include <gdu-gtk/gdu-edit-linux-lvm2-dialog.h>
 #include <gdu-gtk/gdu-drive-benchmark-dialog.h>
 #include <gdu-gtk/gdu-connect-to-server-dialog.h>
 #include <gdu-gtk/gdu-create-linux-lvm2-volume-dialog.h>
+#include <gdu-gtk/gdu-add-pv-linux-lvm2-dialog.h>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
 G_BEGIN_DECLS
diff --git a/src/gdu/gdu-callbacks.h b/src/gdu/gdu-callbacks.h
index f77af80..5d20ced 100644
--- a/src/gdu/gdu-callbacks.h
+++ b/src/gdu/gdu-callbacks.h
@@ -193,6 +193,14 @@ typedef void (*GduPoolLinuxLvm2LVCreateCompletedFunc) (GduPool    *pool,
                                                        GError     *error,
                                                        gpointer    user_data);
 
+typedef void (*GduPoolLinuxLvm2VGAddPVCompletedFunc) (GduPool    *pool,
+                                                      GError     *error,
+                                                      gpointer    user_data);
+
+typedef void (*GduPoolLinuxLvm2VGRemovePVCompletedFunc) (GduPool    *pool,
+                                                         GError     *error,
+                                                         gpointer    user_data);
+
 /* ---------------------------------------------------------------------------------------------------- */
 /* GduDrive */
 
diff --git a/src/gdu/gdu-linux-lvm2-volume-group.c b/src/gdu/gdu-linux-lvm2-volume-group.c
index 430b703..3cca70c 100644
--- a/src/gdu/gdu-linux-lvm2-volume-group.c
+++ b/src/gdu/gdu-linux-lvm2-volume-group.c
@@ -120,12 +120,24 @@ gdu_linux_lvm2_volume_group_init (GduLinuxLvm2VolumeGroup *vg)
         vg->priv = G_TYPE_INSTANCE_GET_PRIVATE (vg, GDU_TYPE_LINUX_LVM2_VOLUME_GROUP, GduLinuxLvm2VolumeGroupPrivate);
 }
 
-static void
-emit_changed (GduLinuxLvm2VolumeGroup *vg)
+static gboolean
+emit_changed_idle_cb (gpointer user_data)
 {
+        GduLinuxLvm2VolumeGroup *vg = GDU_LINUX_LVM2_VOLUME_GROUP (user_data);
         //g_debug ("emitting changed for uuid '%s'", vg->priv->uuid);
         g_signal_emit_by_name (vg, "changed");
         g_signal_emit_by_name (vg->priv->pool, "presentable-changed", vg);
+        g_object_unref (vg);
+        return FALSE; /* remove idle source */
+}
+
+static void
+emit_changed (GduLinuxLvm2VolumeGroup *vg)
+{
+        /* emit changed in idle so GduPool has a chance to build GduPresentable objects - this is
+         * needed for e.g. GduEditLinuxLvm2 where it wants a GduVolume for each PV
+         */
+        g_idle_add (emit_changed_idle_cb, g_object_ref (vg));
 }
 
 static gboolean
@@ -136,6 +148,8 @@ find_pvs (GduLinuxLvm2VolumeGroup *vg)
         guint64 seq_num;
         GduDevice *pv_to_use;
         gboolean emitted_changed;
+        gchar *old_pvs;
+        gchar *new_pvs;
 
         emitted_changed = FALSE;
 
@@ -170,6 +184,11 @@ find_pvs (GduLinuxLvm2VolumeGroup *vg)
                 }
         }
 
+        old_pvs = NULL;
+        if (vg->priv->pv != NULL) {
+                old_pvs = g_strjoinv (",", gdu_device_linux_lvm2_pv_get_group_physical_volumes (vg->priv->pv));
+        }
+
         if (pv_to_use == NULL) {
                 /* ok, switch to the new LV */
                 if (vg->priv->pv != NULL)
@@ -193,6 +212,20 @@ find_pvs (GduLinuxLvm2VolumeGroup *vg)
                 emitted_changed = TRUE;
         }
 
+        new_pvs = NULL;
+        if (vg->priv->pv != NULL) {
+                new_pvs = g_strjoinv (",", gdu_device_linux_lvm2_pv_get_group_physical_volumes (vg->priv->pv));
+        }
+
+        g_free (old_pvs);
+        g_free (new_pvs);
+
+        /* If *anything* on the PVs changed - also emit ::changed ourselves */
+        if (g_strcmp0 (old_pvs, new_pvs) != 0) {
+                emit_changed (vg);
+                emitted_changed = TRUE;
+        }
+
         return emitted_changed;
 }
 
@@ -646,6 +679,71 @@ gdu_linux_lvm2_volume_group_get_lv_info (GduLinuxLvm2VolumeGroup  *vg,
         return ret;
 }
 
+gboolean
+gdu_linux_lvm2_volume_group_get_pv_info (GduLinuxLvm2VolumeGroup  *vg,
+                                         const gchar              *pv_uuid,
+                                         guint                    *out_position,
+                                         guint64                  *out_size,
+                                         guint64                  *out_allocated_size)
+{
+        gchar **pvs;
+        gboolean ret;
+        guint position;
+        guint64 size;
+        guint64 allocated_size;
+        guint n;
+
+        position = G_MAXUINT;
+        size = G_MAXUINT64;
+        allocated_size = G_MAXUINT64;
+        ret = FALSE;
+
+        if (vg->priv->pv == NULL)
+                goto out;
+
+        pvs = gdu_device_linux_lvm2_pv_get_group_physical_volumes (vg->priv->pv);
+
+        for (n = 0; pvs != NULL && pvs[n] != NULL; n++) {
+                gchar **tokens;
+                guint m;
+
+                tokens = g_strsplit (pvs[n], ";", 0);
+
+                for (m = 0; tokens[m] != NULL; m++) {
+
+                        /* TODO: we need to unescape values */
+                        if (g_str_has_prefix (tokens[m], "uuid=") && g_strcmp0 (tokens[m] + 5, pv_uuid) == 0) {
+                                guint p;
+
+                                for (p = 0; tokens[p] != NULL; p++) {
+                                        /* TODO: we need to unescape values */
+                                        if (g_str_has_prefix (tokens[p], "size="))
+                                                size = g_ascii_strtoull (tokens[p] + 5, NULL, 10);
+                                        else if (g_str_has_prefix (tokens[p], "allocated_size="))
+                                                allocated_size = g_ascii_strtoull (tokens[p] + 15, NULL, 10);
+                                }
+                                position = n;
+
+                                g_strfreev (tokens);
+                                ret = TRUE;
+                                goto out;
+                        }
+                }
+                g_strfreev (tokens);
+        }
+
+ out:
+        if (ret) {
+                if (out_position != NULL)
+                        *out_position = position;
+                if (out_size != NULL)
+                        *out_size = size;
+                if (out_allocated_size != NULL)
+                        *out_allocated_size = allocated_size;
+        }
+        return ret;
+}
+
 static gboolean
 lv_name_exists (GduLinuxLvm2VolumeGroup *vg, const gchar *name)
 {
diff --git a/src/gdu/gdu-linux-lvm2-volume-group.h b/src/gdu/gdu-linux-lvm2-volume-group.h
index b065fb6..7e7df64 100644
--- a/src/gdu/gdu-linux-lvm2-volume-group.h
+++ b/src/gdu/gdu-linux-lvm2-volume-group.h
@@ -70,6 +70,11 @@ gboolean                      gdu_linux_lvm2_volume_group_get_lv_info   (GduLinu
                                                                          guint                    *out_position,
                                                                          gchar                   **out_name,
                                                                          guint64                  *out_size);
+gboolean                      gdu_linux_lvm2_volume_group_get_pv_info   (GduLinuxLvm2VolumeGroup  *vg,
+                                                                         const gchar              *pv_uuid,
+                                                                         guint                    *out_position,
+                                                                         guint64                  *out_size,
+                                                                         guint64                  *out_allocated_size);
 gchar                        *gdu_linux_lvm2_volume_group_get_compute_new_lv_name (GduLinuxLvm2VolumeGroup  *vg);
 
 G_END_DECLS
diff --git a/src/gdu/gdu-pool.c b/src/gdu/gdu-pool.c
index 4000fee..d6c5900 100644
--- a/src/gdu/gdu-pool.c
+++ b/src/gdu/gdu-pool.c
@@ -3083,6 +3083,94 @@ gdu_pool_op_linux_lvm2_lv_create (GduPool *pool,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct {
+        GduPool *pool;
+        GduPoolLinuxLvm2VGAddPVCompletedFunc callback;
+        gpointer user_data;
+} LinuxLvm2VGAddPVData;
+
+static void
+op_linux_lvm2_vg_add_pv_cb (DBusGProxy *proxy, GError *error, gpointer user_data)
+{
+        LinuxLvm2VGAddPVData *data = user_data;
+        _gdu_error_fixup (error);
+        if (data->callback != NULL)
+                data->callback (data->pool, error, data->user_data);
+        g_object_unref (data->pool);
+        g_free (data);
+}
+
+void
+gdu_pool_op_linux_lvm2_vg_add_pv (GduPool *pool,
+                                  const gchar *uuid,
+                                  const gchar *physical_volume_object_path,
+                                  GduPoolLinuxLvm2VGAddPVCompletedFunc callback,
+                                  gpointer user_data)
+{
+        LinuxLvm2VGAddPVData *data;
+        char *options[16];
+
+        options[0] = NULL;
+
+        data = g_new0 (LinuxLvm2VGAddPVData, 1);
+        data->pool = g_object_ref (pool);
+        data->callback = callback;
+        data->user_data = user_data;
+
+        org_freedesktop_UDisks_linux_lvm2_vg_add_pv_async (pool->priv->proxy,
+                                                           uuid,
+                                                           physical_volume_object_path,
+                                                           (const gchar **) options,
+                                                           op_linux_lvm2_vg_add_pv_cb,
+                                                           data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+        GduPool *pool;
+        GduPoolLinuxLvm2VGRemovePVCompletedFunc callback;
+        gpointer user_data;
+} LinuxLvm2VGRemovePVData;
+
+static void
+op_linux_lvm2_vg_remove_pv_cb (DBusGProxy *proxy, GError *error, gpointer user_data)
+{
+        LinuxLvm2VGRemovePVData *data = user_data;
+        _gdu_error_fixup (error);
+        if (data->callback != NULL)
+                data->callback (data->pool, error, data->user_data);
+        g_object_unref (data->pool);
+        g_free (data);
+}
+
+void
+gdu_pool_op_linux_lvm2_vg_remove_pv (GduPool *pool,
+                                  const gchar *uuid,
+                                  const gchar *physical_volume_object_path,
+                                  GduPoolLinuxLvm2VGRemovePVCompletedFunc callback,
+                                  gpointer user_data)
+{
+        LinuxLvm2VGRemovePVData *data;
+        char *options[16];
+
+        options[0] = NULL;
+
+        data = g_new0 (LinuxLvm2VGRemovePVData, 1);
+        data->pool = g_object_ref (pool);
+        data->callback = callback;
+        data->user_data = user_data;
+
+        org_freedesktop_UDisks_linux_lvm2_vg_remove_pv_async (pool->priv->proxy,
+                                                              uuid,
+                                                              physical_volume_object_path,
+                                                              (const gchar **) options,
+                                                              op_linux_lvm2_vg_remove_pv_cb,
+                                                              data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 /**
  * gdu_pool_get_daemon_version:
  * @pool: A #GduPool.
diff --git a/src/gdu/gdu-pool.h b/src/gdu/gdu-pool.h
index 132be60..b860a10 100644
--- a/src/gdu/gdu-pool.h
+++ b/src/gdu/gdu-pool.h
@@ -182,6 +182,18 @@ void gdu_pool_op_linux_lvm2_lv_create (GduPool *pool,
                                        GduPoolLinuxLvm2LVCreateCompletedFunc callback,
                                        gpointer user_data);
 
+void gdu_pool_op_linux_lvm2_vg_add_pv (GduPool *pool,
+                                       const gchar *uuid,
+                                       const gchar *physical_volume_object_path,
+                                       GduPoolLinuxLvm2VGAddPVCompletedFunc callback,
+                                       gpointer user_data);
+
+void gdu_pool_op_linux_lvm2_vg_remove_pv (GduPool *pool,
+                                          const gchar *uuid,
+                                          const gchar *physical_volume_object_path,
+                                          GduPoolLinuxLvm2VGAddPVCompletedFunc callback,
+                                          gpointer user_data);
+
 G_END_DECLS
 
 #endif /* __GDU_POOL_H */
diff --git a/src/palimpsest/gdu-section-linux-lvm2-volume-group.c b/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
index 095f55f..de68828 100644
--- a/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
+++ b/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
@@ -45,6 +45,7 @@ struct _GduSectionLinuxLvm2VolumeGroupPrivate
         GduButtonElement *vg_start_button;
         GduButtonElement *vg_stop_button;
         GduButtonElement *vg_edit_name_button;
+        GduButtonElement *vg_edit_pvs_button;
 };
 
 G_DEFINE_TYPE (GduSectionLinuxLvm2VolumeGroup, gdu_section_linux_lvm2_volume_group, GDU_TYPE_SECTION)
@@ -221,6 +222,395 @@ on_vg_edit_name_clicked (GduButtonElement *button_element,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct {
+        GduShell *shell;
+        GduLinuxLvm2VolumeGroup *vg;
+        GduDrive *drive_to_add_to;
+        guint64 size;
+} AddPvData;
+
+static void
+add_pv_data_free (AddPvData *data)
+{
+        if (data->shell != NULL)
+                g_object_unref (data->shell);
+        if (data->vg != NULL)
+                g_object_unref (data->vg);
+        if (data->drive_to_add_to != NULL)
+                g_object_unref (data->drive_to_add_to);
+        g_free (data);
+}
+
+static void
+add_pv_cb (GduPool    *pool,
+           GError     *error,
+           gpointer    user_data)
+{
+        AddPvData *data = user_data;
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_new (GTK_WINDOW (gdu_shell_get_toplevel (data->shell)),
+                                               GDU_PRESENTABLE (data->vg),
+                                               _("Error adding Physical Volume to Volume Group"),
+                                               error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+        }
+
+        if (data != NULL)
+                add_pv_data_free (data);
+}
+
+static void
+add_pv_create_part_cb (GduDevice  *device,
+                       gchar      *created_device_object_path,
+                       GError     *error,
+                       gpointer    user_data)
+{
+        AddPvData *data = user_data;
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_new_for_drive (GTK_WINDOW (gdu_shell_get_toplevel (data->shell)),
+                                                         device,
+                                                         _("Error creating partition for RAID pv"),
+                                                         error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+
+        if (data != NULL)
+                add_pv_data_free (data);
+        } else {
+                GduPool *pool;
+                pool = gdu_device_get_pool (device);
+                gdu_pool_op_linux_lvm2_vg_add_pv (pool,
+                                                  gdu_linux_lvm2_volume_group_get_uuid (data->vg),
+                                                  created_device_object_path,
+                                                  add_pv_cb,
+                                                  data);
+                g_free (created_device_object_path);
+                g_object_unref (pool);
+        }
+}
+
+static void do_add_pv (AddPvData *data);
+
+static void
+add_pv_create_part_table_cb (GduDevice  *device,
+                                    GError     *error,
+                                    gpointer    user_data)
+{
+        AddPvData *data = user_data;
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_new_for_drive (GTK_WINDOW (gdu_shell_get_toplevel (data->shell)),
+                                                         device,
+                                                         _("Error creating partition table for LVM2 PV"),
+                                                         error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+
+                add_pv_data_free (data);
+        } else {
+                do_add_pv (data);
+        }
+}
+
+static void
+do_add_pv (AddPvData *data)
+{
+        gboolean whole_disk_is_uninitialized;
+        guint64 largest_segment;
+        GduPresentable *p;
+        GduDevice *d;
+
+        p = NULL;
+        d = NULL;
+
+        g_warn_if_fail (gdu_drive_has_unallocated_space (data->drive_to_add_to,
+                                                         &whole_disk_is_uninitialized,
+                                                         &largest_segment,
+                                                         NULL, /* total_free */
+                                                         &p));
+        g_assert (p != NULL);
+        g_assert_cmpint (data->size, <=, largest_segment);
+
+        d = gdu_presentable_get_device (GDU_PRESENTABLE (data->drive_to_add_to));
+
+        if (GDU_IS_VOLUME_HOLE (p)) {
+                guint64 offset;
+                const gchar *scheme;
+                const gchar *type;
+                gchar *name;
+                gchar *label;
+
+                offset = gdu_presentable_get_offset (p);
+
+                g_debug ("Creating partition for PV of "
+                         "size %" G_GUINT64_FORMAT " bytes at offset %" G_GUINT64_FORMAT " on %s",
+                         data->size,
+                         offset,
+                         gdu_device_get_device_file (d));
+
+                scheme = gdu_device_partition_table_get_scheme (d);
+                type = "";
+                label = NULL;
+                name = gdu_presentable_get_name (GDU_PRESENTABLE (data->vg));
+
+                if (g_strcmp0 (scheme, "mbr") == 0) {
+                        type = "0x8e";
+                } else if (g_strcmp0 (scheme, "gpt") == 0) {
+                        type = "E6D6D379-F507-44C2-A23C-238F2A3DF928";
+                        /* Limited to 36 UTF-16LE characters according to on-disk format..
+                         * Since a RAID array name is limited to 32 chars this should fit */
+                        if (name != NULL && strlen (name) > 0) {
+                                gchar cut_name[31 * 4 + 1];
+                                g_utf8_strncpy (cut_name, name, 31);
+                                label = g_strdup_printf ("LVM2: %s", cut_name);
+                        } else {
+                                label = g_strdup ("LVM2 Physical Volume");
+                        }
+                } else if (g_strcmp0 (scheme, "apt") == 0) {
+                        type = "Apple_Unix_SVR2";
+                        if (name != NULL && strlen (name) > 0)
+                                label = g_strdup_printf ("LVM2: %s", name);
+                        else
+                                label = g_strdup ("LVM2 Physical Volume");
+                }
+
+                gdu_device_op_partition_create (d,
+                                                offset,
+                                                data->size,
+                                                type,
+                                                label != NULL ? label : "",
+                                                NULL,
+                                                "",
+                                                "",
+                                                "",
+                                                FALSE,
+                                                add_pv_create_part_cb,
+                                                data);
+                g_free (label);
+                g_free (name);
+        } else {
+                /* otherwise the whole disk must be uninitialized... */
+                g_assert (whole_disk_is_uninitialized);
+
+                /* so create a partition table... */
+                gdu_device_op_partition_table_create (d,
+                                                      "mbr",
+                                                      add_pv_create_part_table_cb,
+                                                      data);
+        }
+
+        if (p != NULL)
+                g_object_unref (p);
+        if (d != NULL)
+                g_object_unref (d);
+}
+
+static void
+on_pvs_dialog_new_button_clicked (GduEditLinuxMdDialog *_dialog,
+                                  gpointer              user_data)
+{
+        GduSectionLinuxLvm2VolumeGroup *section = GDU_SECTION_LINUX_LVM2_VOLUME_GROUP (user_data);
+        GduLinuxLvm2VolumeGroup *vg;
+        GtkWidget *dialog;
+        gint response;
+        GtkWindow *toplevel;
+        AddPvData *data;
+
+        dialog = NULL;
+
+        toplevel = GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))));
+
+        vg = GDU_LINUX_LVM2_VOLUME_GROUP (gdu_section_get_presentable (GDU_SECTION (section)));
+
+        dialog = gdu_add_pv_linux_lvm2_dialog_new (toplevel, vg);
+        gtk_widget_show_all (dialog);
+        response = gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_hide (dialog);
+        if (response != GTK_RESPONSE_APPLY)
+                goto out;
+
+        data = g_new0 (AddPvData, 1);
+        data->shell = g_object_ref (gdu_section_get_shell (GDU_SECTION (section)));
+        data->vg = g_object_ref (vg);
+        data->drive_to_add_to = gdu_add_pv_linux_lvm2_dialog_get_drive (GDU_ADD_PV_LINUX_LVM2_DIALOG (dialog));
+        data->size = gdu_add_pv_linux_lvm2_dialog_get_size (GDU_ADD_PV_LINUX_LVM2_DIALOG (dialog));
+
+        do_add_pv (data);
+
+ out:
+        if (dialog != NULL)
+                gtk_widget_destroy (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+        GduShell *shell;
+        GduLinuxLvm2VolumeGroup *vg;
+        GduDevice *pv;
+} RemovePvData;
+
+static void
+remove_pv_data_free (RemovePvData *data)
+{
+        g_object_unref (data->shell);
+        g_object_unref (data->vg);
+        g_object_unref (data->pv);
+        g_free (data);
+}
+
+static void
+remove_pv_delete_partition_op_callback (GduDevice  *device,
+                                        GError     *error,
+                                        gpointer    user_data)
+{
+        RemovePvData *data = user_data;
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_new_for_drive (GTK_WINDOW (gdu_shell_get_toplevel (data->shell)),
+                                                         device,
+                                                         _("Error deleting partition for Physical Volume in Volume Group"),
+                                                         error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+        }
+
+        remove_pv_data_free (data);
+}
+
+static void
+remove_pv_op_callback (GduPool    *pool,
+                       GError     *error,
+                       gpointer    user_data)
+{
+        RemovePvData *data = user_data;
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_new_for_volume (GTK_WINDOW (gdu_shell_get_toplevel (data->shell)),
+                                                          data->pv,
+                                                          _("Error removing Physical Volume from Volume Group"),
+                                                          error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+
+                remove_pv_data_free (data);
+        } else {
+                /* if the device is a partition, also remove the partition */
+                if (gdu_device_is_partition (data->pv)) {
+                        gdu_device_op_partition_delete (data->pv,
+                                                        remove_pv_delete_partition_op_callback,
+                                                        data);
+                } else {
+                        remove_pv_data_free (data);
+                }
+        }
+}
+
+
+static void
+on_pvs_dialog_remove_button_clicked (GduEditLinuxMdDialog   *_dialog,
+                                     GduDevice              *physical_volume,
+                                     gpointer                user_data)
+{
+        GduSectionLinuxLvm2VolumeGroup *section = GDU_SECTION_LINUX_LVM2_VOLUME_GROUP (user_data);
+        GduLinuxLvm2VolumeGroup *vg;
+        GtkWindow *toplevel;
+        GtkWidget *dialog;
+        gint response;
+        RemovePvData *data;
+        GduPool *pool;
+
+        pool = NULL;
+
+        toplevel = GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))));
+
+        vg = GDU_LINUX_LVM2_VOLUME_GROUP (gdu_section_get_presentable (GDU_SECTION (section)));
+
+        /* TODO: more details in this dialog - e.g. "The VG may degrade" etc etc */
+        dialog = gdu_confirmation_dialog_new_for_volume (toplevel,
+                                                         physical_volume,
+                                                         _("Are you sure you want the remove the Physical Volume?"),
+                                                         _("_Remove"));
+        gtk_widget_show_all (dialog);
+        response = gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_hide (dialog);
+        gtk_widget_destroy (dialog);
+        if (response != GTK_RESPONSE_OK)
+                goto out;
+
+        data = g_new0 (RemovePvData, 1);
+        data->shell = g_object_ref (gdu_section_get_shell (GDU_SECTION (section)));
+        data->vg = g_object_ref (vg);
+        data->pv = g_object_ref (physical_volume);
+
+        pool = gdu_device_get_pool (data->pv);
+        gdu_pool_op_linux_lvm2_vg_remove_pv (pool,
+                                             gdu_linux_lvm2_volume_group_get_uuid (data->vg),
+                                             gdu_device_get_object_path (physical_volume),
+                                             remove_pv_op_callback,
+                                             data);
+
+ out:
+        if (pool != NULL)
+                g_object_unref (pool);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_vg_edit_pvs_button_clicked (GduButtonElement *button_element,
+                               gpointer          user_data)
+{
+        GduSectionLinuxLvm2VolumeGroup *section = GDU_SECTION_LINUX_LVM2_VOLUME_GROUP (user_data);
+        GduPresentable *p;
+        GtkWindow *toplevel;
+        GtkWidget *dialog;
+
+        p = gdu_section_get_presentable (GDU_SECTION (section));
+        toplevel = GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))));
+
+        dialog = gdu_edit_linux_lvm2_dialog_new (toplevel, GDU_LINUX_LVM2_VOLUME_GROUP (p));
+
+        g_signal_connect (dialog,
+                          "new-button-clicked",
+                          G_CALLBACK (on_pvs_dialog_new_button_clicked),
+                          section);
+        g_signal_connect (dialog,
+                          "remove-button-clicked",
+                          G_CALLBACK (on_pvs_dialog_remove_button_clicked),
+                          section);
+
+        gtk_widget_show_all (dialog);
+        gtk_window_present (GTK_WINDOW (dialog));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 gdu_section_linux_lvm2_volume_group_update (GduSection *_section)
 {
@@ -415,6 +805,15 @@ gdu_section_linux_lvm2_volume_group_constructed (GObject *object)
         g_ptr_array_add (elements, button_element);
         section->priv->vg_edit_name_button = button_element;
 
+        button_element = gdu_button_element_new (GTK_STOCK_EDIT,
+                                                 _("Edit Ph_ysical Volumes"),
+                                                 _("Create and remove PVs"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_vg_edit_pvs_button_clicked),
+                          section);
+        g_ptr_array_add (elements, button_element);
+        section->priv->vg_edit_pvs_button = button_element;
 
         gdu_button_table_set_elements (GDU_BUTTON_TABLE (table), elements);
         g_ptr_array_unref (elements);



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