[gnome-disk-utility] Add UI for editing (add/remove) PVs in a VG
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-disk-utility] Add UI for editing (add/remove) PVs in a VG
- Date: Wed, 13 Jan 2010 22:51:57 +0000 (UTC)
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]