[gnome-disk-utility] Resize, Check and Repair Dialogs



commit e0500357a4a0410265ac0f8618b378cac0a1722e
Author: Kai Lüke <kailueke riseup net>
Date:   Sat Jul 15 14:38:27 2017 +0200

    Resize, Check and Repair Dialogs
    
    If supported by UDisks the resize and
    repair actions for filesystems (and partitions)
    are offered in the volume gear menu.
    The resize dialog only supports volumes
    where the underlaying block device can
    be resized as well in order to prevent
    confusion if no difference can be seen.

 po/POTFILES.in                      |    1 +
 src/disks/gduresizedialog.c         |  919 +++++++++++++++++++++++++++++++++++
 src/disks/gduresizedialog.h         |   22 +
 src/disks/gduwindow.c               |  343 +++++++++++++
 src/disks/gnome-disks.gresource.xml |    1 +
 src/disks/meson.build               |    5 +
 src/disks/ui/disks.ui               |   30 ++
 src/disks/ui/gdu.css                |   11 +
 src/disks/ui/resize-dialog.ui       |  281 +++++++++++
 9 files changed, 1613 insertions(+), 0 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 63be297..dbe20af 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -24,6 +24,7 @@ src/disks/gdufstabdialog.c
 src/disks/gdunewdiskimagedialog.c
 src/disks/gdupartitiondialog.c
 src/disks/gdupasswordstrengthwidget.c
+src/disks/gduresizedialog.c
 src/disks/gdurestorediskimagedialog.c
 src/disks/gduunlockdialog.c
 src/disks/gduvolumegrid.c
diff --git a/src/disks/gduresizedialog.c b/src/disks/gduresizedialog.c
new file mode 100644
index 0000000..c224bcc
--- /dev/null
+++ b/src/disks/gduresizedialog.c
@@ -0,0 +1,919 @@
+/*
+ * Copyright (C) 2017 Kai Lüke
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: Kai Lüke <kailueke riseup net>
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <glib/gi18n.h>
+#include <math.h>
+
+#include "gduapplication.h"
+#include "gduwindow.h"
+#include "gduresizedialog.h"
+#include "gduutils.h"
+
+#define FILESYSTEM_WAIT_STEP_MS 500
+#define FILESYSTEM_WAIT_SEC 10
+
+typedef struct
+{
+  volatile guint ref_count;
+
+  GduWindow *window;
+  UDisksClient *client;
+  UDisksObject *object;
+  UDisksBlock *block;
+  UDisksPartition *partition;
+  UDisksFilesystem *filesystem;
+  UDisksPartitionTable *table;
+  ResizeFlags support;
+  guint64 min_size;
+  guint64 max_size;
+  guint64 current_size;
+
+  GtkBuilder *builder;
+  GtkWidget *dialog;
+  GtkWidget *size_stack;
+  GtkWidget *resize_number_grid;
+  GtkWidget *size_scale;
+  GtkWidget *size_spinbutton;
+  GtkWidget *size_difference_spinbutton;
+  GtkWidget *free_following_spinbutton;
+  GtkAdjustment *size_adjustment;
+  GtkAdjustment *free_following_adjustment;
+  GtkAdjustment *difference_adjustment;
+
+  GtkWidget *size_unit_combobox;
+  GtkWidget *size_unit_following_label;
+  GtkWidget *size_unit_difference_label;
+  gint cur_unit_num;
+  GtkStyleProvider *css_provider;
+
+  GtkWidget *difference_label;
+  GtkWidget *explanation_label;
+  GtkWidget *spinner;
+  GtkWidget *apply;
+
+  GCancellable *mount_cancellable;
+  guint running_id;
+  guint wait_for_filesystem;
+} ResizeDialogData;
+
+static ResizeDialogData *
+resize_dialog_data_ref (ResizeDialogData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
+
+static void
+resize_dialog_data_unref (ResizeDialogData *data)
+{
+  if (g_atomic_int_dec_and_test (&data->ref_count))
+    {
+      if (data->running_id)
+        {
+          g_source_remove (data->running_id);
+        }
+
+      g_object_unref (data->window);
+      g_object_unref (data->object);
+      g_object_unref (data->block);
+      g_clear_object (&data->filesystem);
+      g_clear_object (&data->partition);
+      if (data->dialog != NULL)
+        {
+          gtk_widget_hide (data->dialog);
+          gtk_widget_destroy (data->dialog);
+        }
+
+      g_clear_object (&data->builder);
+      g_clear_object (&data->mount_cancellable);
+      g_free (data);
+    }
+}
+
+static void
+resize_dialog_update (ResizeDialogData *data)
+{
+  gchar *s;
+
+  s = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (data->size_unit_combobox));
+  gtk_label_set_text (GTK_LABEL (data->size_unit_following_label), s);
+  gtk_label_set_text (GTK_LABEL (data->size_unit_difference_label), s);
+
+  g_free (s);
+}
+
+static void
+set_unit_num (ResizeDialogData *data,
+              gint              unit_num)
+{
+  GtkStyleContext *context;
+  gchar *css;
+  gdouble unit_size;
+  gdouble value;
+  gdouble value_units;
+  gdouble min_size_units;
+  gdouble max_size_units;
+  gdouble current_units;
+  gint num_digits;
+
+  g_assert (unit_num < NUM_UNITS);
+
+  gtk_combo_box_set_active (GTK_COMBO_BOX (data->size_unit_combobox), unit_num);
+
+  if (data->cur_unit_num == -1)
+    {
+      value = data->current_size;
+    }
+  else
+    {
+      value = gtk_adjustment_get_value (data->size_adjustment) * ((gdouble) unit_sizes[data->cur_unit_num]);
+    }
+
+  unit_size = unit_sizes[unit_num];
+  value_units = value / unit_size;
+  min_size_units = ((gdouble) data->min_size) / unit_size;
+  max_size_units = ((gdouble) data->max_size) / unit_size;
+  current_units = ((gdouble) data->current_size) / unit_size;
+
+  /* show at least three digits in the spin buttons */
+  num_digits = 3.0 - ceil (log10 (max_size_units));
+  if (num_digits < 0)
+    num_digits = 0;
+
+  g_object_freeze_notify (G_OBJECT (data->size_adjustment));
+  g_object_freeze_notify (G_OBJECT (data->free_following_adjustment));
+  g_object_freeze_notify (G_OBJECT (data->difference_adjustment));
+
+  data->cur_unit_num = unit_num;
+
+  gtk_adjustment_configure (data->size_adjustment,
+                            value_units,
+                            min_size_units,         /* lower */
+                            max_size_units,         /* upper */
+                            1,                      /* step increment */
+                            100,                    /* page increment */
+                            0.0);                   /* page_size */
+  gtk_adjustment_configure (data->free_following_adjustment,
+                            max_size_units - value_units,
+                            0.0,                             /* lower */
+                            max_size_units - min_size_units, /* upper */
+                            1,                               /* step increment */
+                            100,                             /* page increment */
+                            0.0);                            /* page_size */
+  gtk_adjustment_configure (data->difference_adjustment,
+                            value_units - current_units,
+                            min_size_units - current_units, /* lower */
+                            max_size_units - current_units, /* upper */
+                            1,                              /* step increment */
+                            100,                            /* page increment */
+                            0.0);                           /* page_size */
+
+  gtk_spin_button_set_digits (GTK_SPIN_BUTTON (data->size_spinbutton), num_digits);
+  gtk_spin_button_set_digits (GTK_SPIN_BUTTON (data->size_difference_spinbutton), num_digits);
+  gtk_spin_button_set_digits (GTK_SPIN_BUTTON (data->free_following_spinbutton), num_digits);
+
+  gtk_adjustment_set_value (data->size_adjustment, value_units);
+  gtk_adjustment_set_value (data->free_following_adjustment, max_size_units - value_units);
+  gtk_adjustment_set_value (data->difference_adjustment, value_units - current_units);
+
+  gtk_scale_clear_marks (GTK_SCALE (data->size_scale));
+  gtk_scale_add_mark (GTK_SCALE (data->size_scale), current_units, GTK_POS_TOP, _("Current Size"));
+
+  context = gtk_widget_get_style_context (data->size_scale);
+  if (data->css_provider)
+    {
+      gtk_style_context_remove_provider (context, data->css_provider);
+      g_clear_object (&data->css_provider);
+    }
+
+  if (data->min_size > 1)
+    {
+      gtk_scale_add_mark (GTK_SCALE (data->size_scale), min_size_units, GTK_POS_BOTTOM, _("Minimal Size"));
+      css = g_strdup_printf (".partition-scale contents {\n"
+                             "  border-left-width: %dpx;\n"
+                             "}\n",
+                             (gint) (min_size_units / max_size_units * gtk_widget_get_allocated_width 
(data->size_stack)));
+      data->css_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
+      gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (data->css_provider), css, -1, NULL);
+      gtk_style_context_add_provider (context, data->css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+      g_free (css);
+    }
+
+  g_object_thaw_notify (G_OBJECT (data->size_adjustment));
+  g_object_thaw_notify (G_OBJECT (data->free_following_adjustment));
+  g_object_thaw_notify (G_OBJECT (data->difference_adjustment));
+}
+
+static void
+resize_dialog_property_changed (GObject     *object,
+                                GParamSpec  *pspec,
+                                gpointer     user_data)
+{
+  ResizeDialogData *data = user_data;
+
+  resize_dialog_update (data);
+}
+
+static void
+on_size_unit_combobox_changed (GtkComboBox *combobox,
+                               gpointer     user_data)
+{
+  ResizeDialogData *data = user_data;
+  gint unit_num;
+
+  unit_num = gtk_combo_box_get_active (GTK_COMBO_BOX (data->size_unit_combobox));
+  set_unit_num (data, unit_num);
+
+  resize_dialog_update (data);
+}
+
+static void
+resize_dialog_populate (ResizeDialogData *data)
+{
+
+  data->dialog = GTK_WIDGET (gdu_application_new_widget (gdu_window_get_application (data->window),
+                                                         "resize-dialog.ui",
+                                                         "resize-dialog",
+                                                         &data->builder));
+
+  gtk_dialog_add_button (GTK_DIALOG (data->dialog), "gtk-cancel", GTK_RESPONSE_CANCEL);
+  data->apply = gtk_dialog_add_button (GTK_DIALOG (data->dialog), _("_Resize"), GTK_RESPONSE_APPLY);
+  gtk_style_context_add_class (gtk_widget_get_style_context (data->apply), "destructive-action");
+  gtk_widget_grab_default (data->apply);
+
+  data->size_spinbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, "size-spinbutton"));
+  data->size_difference_spinbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"size-difference-spinbutton"));
+  data->free_following_spinbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"free-following-spinbutton"));
+  data->size_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (data->builder, "size-adjustment"));
+  g_signal_connect (data->size_adjustment, "notify::value", G_CALLBACK (resize_dialog_property_changed), 
data);
+  data->free_following_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (data->builder, 
"free-following-adjustment"));
+  data->difference_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (data->builder, 
"difference-adjustment"));
+  data->size_unit_combobox = GTK_WIDGET (gtk_builder_get_object (data->builder, "size-unit-combobox"));
+  g_signal_connect (data->size_unit_combobox, "changed", G_CALLBACK (on_size_unit_combobox_changed), data);
+  data->size_unit_difference_label = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"size-unit-difference-label"));
+  data->size_unit_following_label = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"size-unit-following-label"));
+  data->difference_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "difference-label"));
+  data->explanation_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "explanation"));
+  data->spinner = GTK_WIDGET (gtk_builder_get_object (data->builder, "spinner"));
+  data->resize_number_grid = GTK_WIDGET (gtk_builder_get_object (data->builder, "resize-number-grid"));;
+  data->size_scale = GTK_WIDGET (gtk_builder_get_object (data->builder, "size-scale"));;
+  data->size_stack = GTK_WIDGET (gtk_builder_get_object (data->builder, "size-stack"));
+
+  gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->window));
+  gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_APPLY);
+  set_unit_num (data, gdu_utils_get_default_unit (data->max_size));
+}
+
+static gboolean
+free_size_binding_func (GBinding     *binding,
+                        const GValue *source_value,
+                        GValue       *target_value,
+                        gpointer      user_data)
+{
+  ResizeDialogData *data = user_data;
+  gdouble max_size_units;
+
+  max_size_units = ((gdouble) data->max_size) / unit_sizes[data->cur_unit_num];
+  g_value_set_double (target_value, max_size_units - g_value_get_double (source_value));
+
+  return TRUE;
+}
+
+static gboolean
+difference_binding_func (GBinding     *binding,
+                         const GValue *source_value,
+                         GValue       *target_value,
+                         gpointer      user_data)
+{
+  ResizeDialogData *data = user_data;
+  gdouble current_units;
+
+  current_units = ((gdouble) data->current_size) / unit_sizes[data->cur_unit_num];
+  g_value_set_double (target_value, g_value_get_double (source_value) - current_units);
+
+  return TRUE;
+}
+
+static gboolean
+difference_binding_func_back (GBinding     *binding,
+                              const GValue *source_value,
+                              GValue       *target_value,
+                              gpointer      user_data)
+{
+  ResizeDialogData *data = user_data;
+  gdouble current_units;
+
+  current_units = ((gdouble) data->current_size) / unit_sizes[data->cur_unit_num];
+  g_value_set_double (target_value, g_value_get_double (source_value) + current_units);
+
+  return TRUE;
+}
+
+static guint64
+get_size (ResizeDialogData *data)
+{
+  guint64 size;
+
+  size = gtk_adjustment_get_value (data->size_adjustment) * unit_sizes[data->cur_unit_num];
+  /* choose 0 as maximum in order to avoid errors
+   * if partition is later 3 MiB smaller due to alignment etc
+   */
+  if (size >= data->max_size - 3*1024*1024)
+    size = 0;
+
+  return size;
+}
+
+static gboolean
+is_shrinking (ResizeDialogData *data)
+{
+  return get_size (data) < data->current_size && get_size (data) != 0;
+}
+
+static void
+fs_resize_cb (UDisksFilesystem *filesystem,
+              GAsyncResult     *res,
+              ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_resize_finish (filesystem, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing filesystem"),
+                            error);
+      g_error_free (error);
+    }
+
+  resize_dialog_data_unref (data);
+}
+
+static void
+part_resize_cb (UDisksPartition  *partition,
+                GAsyncResult     *res,
+                ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_partition_call_resize_finish (partition, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing partition"),
+                            error);
+      g_error_free (error);
+    }
+
+  resize_dialog_data_unref (data);
+}
+
+static void
+fs_repair_cb_next_part_resize (UDisksFilesystem *filesystem,
+                               GAsyncResult     *res,
+                               ResizeDialogData *data)
+{
+  GError *error = NULL;
+  gboolean success = FALSE;
+
+  if (!udisks_filesystem_call_repair_finish (filesystem, &success, res, &error) || !success)
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error repairing filesystem after resize"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      udisks_partition_call_resize (data->partition, get_size (data),
+                                    g_variant_new ("a{sv}", NULL), NULL,
+                                    (GAsyncReadyCallback) part_resize_cb, data);
+    }
+}
+
+static void
+fs_repair_cb (UDisksFilesystem *filesystem,
+              GAsyncResult     *res,
+              ResizeDialogData *data)
+{
+  GError *error = NULL;
+  gboolean success = FALSE;
+
+  if (!udisks_filesystem_call_repair_finish (filesystem, &success, res, &error) || !success)
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error repairing filesystem after resize"),
+                            error);
+      g_error_free (error);
+    }
+
+  resize_dialog_data_unref (data);
+}
+
+static void
+fs_resize_cb_offline_next_repair (UDisksFilesystem *filesystem,
+                                  GAsyncResult     *res,
+                                  ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_resize_finish (filesystem, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing filesystem"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      udisks_filesystem_call_repair (data->filesystem,
+                                     g_variant_new ("a{sv}", NULL), NULL,
+                                     (GAsyncReadyCallback) fs_repair_cb, data);
+    }
+}
+
+static gboolean
+resize_filesystem_waiter (gpointer user_data)
+{
+  ResizeDialogData *data = user_data;
+
+  if (data->wait_for_filesystem < FILESYSTEM_WAIT_SEC * (1000 / FILESYSTEM_WAIT_STEP_MS)
+      && udisks_object_peek_filesystem (data->object) == NULL)
+    {
+      data->wait_for_filesystem++;
+      return G_SOURCE_CONTINUE;
+    }
+  else if (udisks_object_peek_filesystem (data->object) == NULL)
+    {
+      GtkWidget *dialog;
+
+      dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (data->dialog),
+                                                   GTK_DIALOG_MODAL,
+                                                   GTK_MESSAGE_ERROR,
+                                                   GTK_BUTTONS_CLOSE,
+                                                   "<big><b>%s</b></big>",
+                                                   _("Resizing not ready"));
+      gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
+                                                  _("Waited too long for the filesystem"));
+      gtk_dialog_run (GTK_DIALOG (dialog));
+
+      gtk_widget_destroy (dialog);
+      resize_dialog_data_unref (data);
+
+      return G_SOURCE_REMOVE;
+    }
+  else
+    {
+      udisks_filesystem_call_resize (data->filesystem, get_size (data),
+                                     g_variant_new ("a{sv}", NULL), NULL,
+                                     (GAsyncReadyCallback) fs_resize_cb, data);
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+part_resize_cb_online_next_fs_resize (UDisksPartition  *partition,
+                                      GAsyncResult     *res,
+                                      ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_partition_call_resize_finish (partition, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing partition"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      /* After the partition is resized the filesystem interface might still
+       * take some time to show up again. UDisks would have to open the partition
+       * block device, issue the ioctl BLKSIZE64 and wait for the size property
+       * of the partition interface to be the same and then wait for the filesystem
+       * interface to show up (only if it was there before). This depends on inclusion
+       * of https://github.com/storaged-project/libblockdev/pull/264
+       */
+      udisks_client_settle (gdu_window_get_client (data->window));
+      if (resize_filesystem_waiter (data) == G_SOURCE_CONTINUE)
+        {
+          g_timeout_add (FILESYSTEM_WAIT_STEP_MS, resize_filesystem_waiter, data);
+        }
+    }
+}
+
+static void
+part_resize_cb_offline_next_fs_resize (UDisksPartition  *partition,
+                                       GAsyncResult     *res,
+                                       ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_partition_call_resize_finish (partition, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing partition"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      udisks_filesystem_call_resize (data->filesystem, get_size (data),
+                                     g_variant_new ("a{sv}", NULL), NULL,
+                                     (GAsyncReadyCallback) fs_resize_cb_offline_next_repair, data);
+    }
+}
+
+static void
+fs_resize_cb_online_next_part_resize (UDisksFilesystem *filesystem,
+                                      GAsyncResult     *res,
+                                      ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_resize_finish (filesystem, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing filesystem"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      udisks_partition_call_resize (data->partition, get_size (data),
+                                    g_variant_new ("a{sv}", NULL), NULL,
+                                    (GAsyncReadyCallback) part_resize_cb, data);
+    }
+}
+
+static void
+fs_resize_cb_offline_next_fs_repair (UDisksFilesystem *filesystem,
+                                       GAsyncResult     *res,
+                                       ResizeDialogData *data)
+{
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_resize_finish (filesystem, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error resizing filesystem"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      udisks_filesystem_call_repair (data->filesystem,
+                                     g_variant_new ("a{sv}", NULL), NULL,
+                                     (GAsyncReadyCallback) fs_repair_cb_next_part_resize, data);
+    }
+}
+
+static void
+fs_repair_cb_offline_next_resize (UDisksFilesystem *filesystem,
+                                  GAsyncResult     *res,
+                                  ResizeDialogData *data)
+{
+  GError *error = NULL;
+  gboolean success = FALSE;
+
+  if (!udisks_filesystem_call_repair_finish (filesystem, &success, res, &error) || !success)
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error repairing filesystem"),
+                            error);
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      if (is_shrinking (data))
+        {
+          /* shrinking: next is to resize filesystem, run repair, then resize partition */
+          udisks_filesystem_call_resize (data->filesystem, get_size (data),
+                                         g_variant_new ("a{sv}", NULL), NULL,
+                                         (GAsyncReadyCallback) fs_resize_cb_offline_next_fs_repair, data);
+        }
+      else
+        {
+          /* growing: next is to resize partition, then resize filesystem, run repair */
+          udisks_partition_call_resize (data->partition, get_size (data),
+                                        g_variant_new ("a{sv}", NULL), NULL,
+                                        (GAsyncReadyCallback) part_resize_cb_offline_next_fs_resize, data);
+        }
+    }
+}
+
+static void
+online_resize_no_repair (ResizeDialogData *data)
+{
+  if (is_shrinking (data))
+    {
+      /* shrinking: resize filesystem first, then partition: fs-resize, part-resize */
+      udisks_filesystem_call_resize (data->filesystem, get_size (data),
+                                     g_variant_new ("a{sv}", NULL), NULL,
+                                     (GAsyncReadyCallback) fs_resize_cb_online_next_part_resize, data);
+    }
+  else
+    {
+      /* growing: resize partition first, then filesystem: part-resize, fs-resize */
+      udisks_partition_call_resize (data->partition, get_size (data),
+                                    g_variant_new ("a{sv}", NULL), NULL,
+                                    (GAsyncReadyCallback) part_resize_cb_online_next_fs_resize, data);
+    }
+}
+
+static void
+offline_resize_with_repair (ResizeDialogData *data)
+{
+  /* partition and filesystem resize:
+   * if growing fs-repair, part-resize, fs-resize, fs-repair
+   * if shrinking fs-repair, fs-resize, fs-repair, part-resize
+   */
+  udisks_filesystem_call_repair (data->filesystem,
+                                 g_variant_new ("a{sv}", NULL), NULL,
+                                 (GAsyncReadyCallback) fs_repair_cb_offline_next_resize, data);
+}
+
+static void
+unmount_cb (GObject      *source_object,
+            GAsyncResult *res,
+            gpointer      user_data)
+{
+  ResizeDialogData *data = user_data;
+  GError *error = NULL;
+
+  if (!gdu_utils_ensure_unused_finish (data->client, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (data->window),
+                            _("Error unmounting filesystem for resizing"),
+                            error);
+
+      g_error_free (error);
+      resize_dialog_data_unref (data);
+    }
+  else
+    {
+      offline_resize_with_repair (data);
+    }
+}
+
+static gboolean
+calc_usage (gpointer user_data)
+{
+  ResizeDialogData *data = user_data;
+  gint64 unused;
+
+  /* filesystem was mounted before opening the dialog but it still can take some seconds */
+  unused = gdu_utils_get_unused_for_block (data->client, data->block);
+
+  if (unused == -1)
+    {
+      return G_SOURCE_CONTINUE;
+    }
+  else
+    {
+      data->running_id = 0;
+
+      if (data->support & ONLINE_SHRINK || data->support & OFFLINE_SHRINK)
+        {
+          /* set minimal filesystem size from usage if shrinking is supported */
+          data->min_size = data->current_size - unused;
+        }
+      else
+        {
+          data->min_size = data->current_size; /* do not allow shrinking */
+        }
+
+      gtk_spinner_stop (GTK_SPINNER (data->spinner));
+      gtk_stack_set_visible_child (GTK_STACK (data->size_stack), data->resize_number_grid);
+      if (data->min_size == data->max_size)
+        gtk_button_set_label (GTK_BUTTON (data->apply), _("Fit to size"));
+
+      gtk_widget_set_sensitive (data->apply, TRUE);
+      set_unit_num (data, data->cur_unit_num);
+      resize_dialog_update (data);
+
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+resize_get_usage_mount_cb (UDisksFilesystem *filesystem,
+                           GAsyncResult     *res,
+                           gpointer          user_data)
+{
+  ResizeDialogData *data = (ResizeDialogData *) user_data;
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_mount_finish (filesystem,
+                                            NULL, /* out_mount_path */
+                                            res,
+                                            &error))
+    {
+      if (data->dialog != NULL)
+        {
+          /* close dialog if still open */
+          if (data->running_id)
+            {
+              g_source_remove (data->running_id);
+              data->running_id = 0;
+            }
+
+          g_clear_object (&data->mount_cancellable);
+          gtk_dialog_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CANCEL);
+          gdu_utils_show_error (GTK_WINDOW (data->window),
+                                _("Error mounting filesystem to calculate minimum size"),
+                                error);
+        }
+
+      g_error_free (error);
+    }
+
+  resize_dialog_data_unref (data);
+}
+
+void
+gdu_resize_dialog_show (GduWindow    *window,
+                        UDisksObject *object)
+{
+  const gchar *const *mount_points;
+  ResizeDialogData *data;
+
+  data = g_new0 (ResizeDialogData, 1);
+  data->ref_count = 1;
+  data->cur_unit_num = -1;
+  data->window = g_object_ref (window);
+  data->support = 0;
+  data->client = gdu_window_get_client (window);
+  data->object = g_object_ref (object);
+  data->block = udisks_object_get_block (object);
+  g_assert (data->block != NULL);
+  data->partition = udisks_object_get_partition (object);
+  data->filesystem = udisks_object_get_filesystem (object);
+  data->table = NULL;
+  data->running_id = 0;
+  data->mount_cancellable = NULL;
+  data->wait_for_filesystem = 0;
+  data->css_provider = NULL;
+
+  /* In general no way to find the real filesystem size, just use the partition
+   * size. That is ok unless the filesystem was not fitted to the underlaying
+   * block size which would just harm the calculation of the minimum shrink size.
+   * A workaround then is to resize it first to the partition size.
+   * The more serious implication of not detecting the filesystem size is:
+   * Resizing just the filesystem without resizing the underlying block device/
+   * partition would work but is confusing because the only thing that changes
+   * is the free space (since Disks can't show the filesystem size). Therefore
+   * this option is not available now. We'll have to add cases like resizing
+   * the LUKS, LVM partition or the mounted loop device file.
+   */
+  g_assert (data->partition != NULL);
+  data->current_size = udisks_partition_get_size (data->partition);
+  data->table = udisks_client_get_partition_table (data->client, data->partition);
+  data->min_size = 1;
+  if (data->partition != NULL && udisks_partition_get_is_container (data->partition))
+    {
+      data->min_size = gdu_utils_calc_space_to_shrink_extended (data->client, data->table, data->partition);
+    }
+
+  if (data->filesystem != NULL)
+    {
+      gboolean available;
+
+      available = gdu_utils_can_resize (data->client, udisks_block_get_id_type (data->block), FALSE,
+                                        &data->support, NULL);
+      g_assert (available);
+    }
+
+  data->max_size = data->current_size;
+  if (data->partition != NULL)
+    {
+      data->max_size = gdu_utils_calc_space_to_grow (data->client, data->table, data->partition);
+    }
+
+  resize_dialog_populate (data);
+  resize_dialog_update (data);
+
+  if (data->filesystem == NULL)
+    {
+      gtk_spinner_stop (GTK_SPINNER (data->spinner));
+      gtk_stack_set_visible_child (GTK_STACK (data->size_stack), data->resize_number_grid);
+      gtk_widget_set_no_show_all (data->explanation_label, TRUE);
+      gtk_widget_hide (data->explanation_label);
+    }
+  else
+    {
+      gtk_widget_set_sensitive (data->apply, FALSE);
+      if (calc_usage (data) == G_SOURCE_CONTINUE)
+        {
+          data->running_id = g_timeout_add (FILESYSTEM_WAIT_STEP_MS, calc_usage, data);
+        }
+    }
+
+  g_object_bind_property_full (data->size_adjustment,
+                               "value",
+                               data->free_following_adjustment,
+                               "value",
+                               G_BINDING_BIDIRECTIONAL,
+                               free_size_binding_func,
+                               free_size_binding_func,
+                               data,
+                               NULL);
+  g_object_bind_property_full (data->size_adjustment,
+                               "value",
+                               data->difference_adjustment,
+                               "value",
+                               G_BINDING_BIDIRECTIONAL,
+                               difference_binding_func,
+                               difference_binding_func_back,
+                               data,
+                               NULL);
+
+  gtk_widget_show_all (data->dialog);
+  set_unit_num (data, data->cur_unit_num);
+
+  if (data->filesystem != NULL)
+    mount_points = udisks_filesystem_get_mount_points (data->filesystem);
+
+  if (data->filesystem != NULL && g_strv_length ((gchar **) mount_points) == 0)
+    {
+      /* mount FS to aquire fill level */
+      data->mount_cancellable = g_cancellable_new ();
+      udisks_filesystem_call_mount (data->filesystem,
+                                    g_variant_new ("a{sv}", NULL), /* options */
+                                    data->mount_cancellable,
+                                    (GAsyncReadyCallback) resize_get_usage_mount_cb,
+                                    resize_dialog_data_ref (data));
+    }
+
+  if (gtk_dialog_run (GTK_DIALOG (data->dialog)) == GTK_RESPONSE_APPLY)
+    {
+      gboolean shrinking;
+
+      shrinking = is_shrinking (data);
+
+      gtk_widget_hide (data->dialog);
+      g_clear_pointer (&data->dialog, gtk_widget_destroy);
+
+      if (data->filesystem != NULL)
+        {
+          mount_points = udisks_filesystem_get_mount_points (data->filesystem);
+
+          if (g_strv_length ((gchar **) mount_points) == 0)
+            {
+              offline_resize_with_repair (data);
+            }
+          else if ((!(data->support & ONLINE_SHRINK) && shrinking) ||
+                   (!(data->support & ONLINE_GROW) && !shrinking))
+            {
+              gdu_utils_ensure_unused (data->client,
+                                       GTK_WINDOW (window),
+                                       object,
+                                       unmount_cb,
+                                       NULL,
+                                       data);
+            }
+          else
+            {
+              online_resize_no_repair (data);
+            }
+        }
+      else
+        {
+          /* no filesystem present, just resize partition */
+          udisks_partition_call_resize (data->partition, get_size (data),
+                                        g_variant_new ("a{sv}", NULL), NULL,
+                                        (GAsyncReadyCallback) part_resize_cb, data);
+        }
+    }
+  else
+    {
+      /* close dialog now, does not need to be closed anymore through the mount error handler */
+      if (data->running_id)
+        {
+          g_source_remove (data->running_id);
+          data->running_id = 0;
+        }
+
+      gtk_widget_hide (data->dialog);
+      g_clear_pointer (&data->dialog, gtk_widget_destroy);
+      if (data->mount_cancellable)
+        g_cancellable_cancel (data->mount_cancellable);
+
+      resize_dialog_data_unref (data);
+    }
+}
+
diff --git a/src/disks/gduresizedialog.h b/src/disks/gduresizedialog.h
new file mode 100644
index 0000000..446bd10
--- /dev/null
+++ b/src/disks/gduresizedialog.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 Kai Lüke
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: Kai Lüke <kailueke riseup net>
+ */
+
+#ifndef __GDU_RESIZE_DIALOG_H_H__
+#define __GDU_RESIZE_DIALOG_H_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+void gdu_resize_dialog_show (GduWindow    *window,
+                             UDisksObject *object);
+
+G_END_DECLS
+
+#endif /* __GDU_RESIZE_DIALOG_H__ */
diff --git a/src/disks/gduwindow.c b/src/disks/gduwindow.c
index f565f0e..5a07085 100644
--- a/src/disks/gduwindow.c
+++ b/src/disks/gduwindow.c
@@ -37,6 +37,7 @@
 #include "gdurestorediskimagedialog.h"
 #include "gduchangepassphrasedialog.h"
 #include "gdudisksettingsdialog.h"
+#include "gduresizedialog.h"
 #include "gdulocaljob.h"
 
 struct _GduWindow
@@ -100,6 +101,10 @@ struct _GduWindow
   GtkWidget *generic_menu_item_configure_fstab;
   GtkWidget *generic_menu_item_configure_crypttab;
   GtkWidget *generic_menu_item_change_passphrase;
+  GtkWidget *generic_menu_item_resize;
+  GtkWidget *generic_menu_item_repair;
+  GtkWidget *generic_menu_item_check;
+  GtkWidget *generic_menu_item_separator;
   GtkWidget *generic_menu_item_edit_label;
   GtkWidget *generic_menu_item_edit_partition;
   GtkWidget *generic_menu_item_format_volume;
@@ -173,6 +178,10 @@ static const struct {
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_configure_fstab), "generic-menu-item-configure-fstab"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_configure_crypttab), 
"generic-menu-item-configure-crypttab"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_change_passphrase), "generic-menu-item-change-passphrase"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_resize), "generic-menu-item-resize"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_check), "generic-menu-item-check"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_repair), "generic-menu-item-repair"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_separator), "generic-menu-item-separator"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_edit_label), "generic-menu-item-edit-label"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_edit_partition), "generic-menu-item-edit-partition"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_format_volume), "generic-menu-item-format-volume"},
@@ -255,6 +264,9 @@ typedef enum
   SHOW_FLAGS_VOLUME_MENU_CREATE_VOLUME_IMAGE   = (1<<6),
   SHOW_FLAGS_VOLUME_MENU_RESTORE_VOLUME_IMAGE  = (1<<7),
   SHOW_FLAGS_VOLUME_MENU_BENCHMARK             = (1<<8),
+  SHOW_FLAGS_VOLUME_MENU_RESIZE                = (1<<9),
+  SHOW_FLAGS_VOLUME_MENU_REPAIR                = (1<<10),
+  SHOW_FLAGS_VOLUME_MENU_CHECK                 = (1<<11),
 } ShowFlagsVolumeMenu;
 
 typedef struct
@@ -311,6 +323,16 @@ static void on_generic_menu_item_configure_crypttab (GtkMenuItem *menu_item,
                                                      gpointer   user_data);
 static void on_generic_menu_item_change_passphrase (GtkMenuItem *menu_item,
                                                     gpointer   user_data);
+
+#ifdef HAVE_UDISKS2_7_2
+static void on_generic_menu_item_resize (GtkMenuItem *menu_item,
+                                         gpointer     user_data);
+static void on_generic_menu_item_repair (GtkMenuItem *menu_item,
+                                         gpointer     user_data);
+static void on_generic_menu_item_check (GtkMenuItem *menu_item,
+                                        gpointer     user_data);
+#endif
+
 static void on_generic_menu_item_edit_label (GtkMenuItem *menu_item,
                                              gpointer   user_data);
 static void on_generic_menu_item_edit_partition (GtkMenuItem *menu_item,
@@ -460,6 +482,12 @@ update_for_show_flags (GduWindow *window,
                             show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_CONFIGURE_CRYPTTAB);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_change_passphrase),
                             show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_CHANGE_PASSPHRASE);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_resize),
+                            show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_RESIZE);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_repair),
+                            show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_REPAIR);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_check),
+                            show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_CHECK);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_edit_label),
                             show_flags->volume_menu & SHOW_FLAGS_VOLUME_MENU_EDIT_LABEL);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_edit_partition),
@@ -1309,6 +1337,27 @@ gdu_window_constructed (GObject *object)
                     "activate",
                     G_CALLBACK (on_generic_menu_item_change_passphrase),
                     window);
+
+#ifdef HAVE_UDISKS2_7_2
+  g_signal_connect (window->generic_menu_item_resize,
+                    "activate",
+                    G_CALLBACK (on_generic_menu_item_resize),
+                    window);
+  g_signal_connect (window->generic_menu_item_repair,
+                    "activate",
+                    G_CALLBACK (on_generic_menu_item_repair),
+                    window);
+  g_signal_connect (window->generic_menu_item_check,
+                    "activate",
+                    G_CALLBACK (on_generic_menu_item_check),
+                    window);
+#else
+  gtk_widget_hide (window->generic_menu_item_resize);
+  gtk_widget_hide (window->generic_menu_item_repair);
+  gtk_widget_hide (window->generic_menu_item_check);
+  gtk_widget_hide (window->generic_menu_item_separator);
+#endif
+
   g_signal_connect (window->generic_menu_item_edit_label,
                     "activate",
                     G_CALLBACK (on_generic_menu_item_edit_label),
@@ -2724,6 +2773,29 @@ update_device_page_for_block (GduWindow          *window,
         show_flags->drive_buttons |= SHOW_FLAGS_DRIVE_BUTTONS_EJECT;
     }
 
+#ifdef HAVE_UDISKS2_7_2
+
+  if (partition != NULL && g_strcmp0 (usage, "") == 0 && !read_only)
+    {
+
+      /* allow partition resize if no known structured data was found on the device */
+      show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_RESIZE;
+    }
+  else if (filesystem != NULL)
+    {
+      /* for now the filesystem resize on just any block device is not shown, see resize_dialog_show */
+      if (!read_only && partition != NULL && gdu_utils_can_resize (window->client, type, FALSE, NULL, NULL))
+        show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_RESIZE;
+
+      if (!read_only && gdu_utils_can_repair (window->client, type, FALSE, NULL))
+        show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_REPAIR;
+
+      if (gdu_utils_can_check (window->client, type, FALSE, NULL))
+        show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_CHECK;
+    }
+
+#endif
+
   /* Only show jobs if the volume is a partition (if it's not, we're already showing
    * the jobs in the drive section)
    */
@@ -2965,6 +3037,277 @@ update_device_page (GduWindow      *window,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+#ifdef HAVE_UDISKS2_7_2
+
+static void
+on_generic_menu_item_resize (GtkMenuItem *menu_item,
+                             gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  UDisksObject *object;
+
+  object = gdu_volume_grid_get_selected_device (GDU_VOLUME_GRID (window->volume_grid));
+  g_assert (object != NULL);
+  gdu_resize_dialog_show (window, object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+fs_repair_cb (UDisksFilesystem *filesystem,
+              GAsyncResult     *res,
+              GduWindow        *window)
+{
+  gboolean success;
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_repair_finish (filesystem, &success, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (window),
+                            _("Error while repairing filesystem"),
+                            error);
+      g_error_free (error);
+    }
+  else
+    {
+      GtkWidget *message_dialog;
+      UDisksObjectInfo *info;
+      UDisksBlock *block;
+      UDisksObject *object;
+      const gchar *name;
+      gchar *s;
+
+      object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)));
+      block = udisks_object_peek_block (object);
+      g_assert (block != NULL);
+      info = udisks_client_get_object_info (window->client, object);
+      name = udisks_block_get_id_label (block);
+
+      if (name == NULL || strlen (name) == 0)
+        name = udisks_block_get_id_type (block);
+
+      message_dialog = gtk_message_dialog_new_with_markup  (GTK_WINDOW (window),
+                                                            GTK_DIALOG_MODAL,
+                                                            GTK_MESSAGE_INFO,
+                                                            GTK_BUTTONS_CLOSE,
+                                                            "<big><b>%s</b></big>",
+                                                            success ? _("Repair successful") : _("Repair 
failed"));
+      if (success)
+        {
+          s = g_strdup_printf (_("Filesystem %s on %s has been repaired."),
+                               name, udisks_object_info_get_name (info));
+        }
+      else
+        {
+          /* show as result and not error message, because it's not a malfunction of GDU */
+          s = g_strdup_printf (_("Filesystem %s on %s could not be repaired."),
+                               name, udisks_object_info_get_name (info));
+        }
+
+      gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (message_dialog), "%s", s);
+      gtk_dialog_run (GTK_DIALOG (message_dialog));
+
+      gtk_widget_destroy (message_dialog);
+      g_free (s);
+    }
+
+}
+
+static void
+fs_repair_unmount_cb (GduWindow        *window,
+                      GAsyncResult     *res,
+                      gpointer          user_data)
+{
+  UDisksObject *object = UDISKS_OBJECT (user_data);
+  GError *error = NULL;
+
+  if (!gdu_window_ensure_unused_finish (window,
+                                        res,
+                                        &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (window),
+                            _("Error unmounting filesystem"),
+                            error);
+      g_error_free (error);
+    }
+  else
+    {
+      UDisksFilesystem *filesystem;
+
+      filesystem = udisks_object_peek_filesystem (object);
+      g_assert (filesystem != NULL);
+      udisks_filesystem_call_repair (filesystem,
+                                     g_variant_new ("a{sv}", NULL),
+                                     NULL,
+                                     (GAsyncReadyCallback) fs_repair_cb,
+                                     window);
+    }
+
+  g_object_unref (object);
+}
+
+static void
+on_generic_menu_item_repair (GtkMenuItem *menu_item,
+                             gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  GtkWidget *message_dialog, *ok_button;
+  UDisksObject *object;
+
+  object = gdu_volume_grid_get_selected_device (GDU_VOLUME_GRID (window->volume_grid));
+  g_assert (object != NULL);
+
+  message_dialog = gtk_message_dialog_new_with_markup  (GTK_WINDOW (window),
+                                                        GTK_DIALOG_MODAL,
+                                                        GTK_MESSAGE_WARNING,
+                                                        GTK_BUTTONS_OK_CANCEL,
+                                                        "<big><b>%s</b></big>",
+                                                        _("Confirm Repair"));
+
+  gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (message_dialog), "%s",
+                                              _("A filesystem repair is not always possible and can cause 
data loss. "
+                                                "Consider backing it up first in order to use forensic 
recovery tools "
+                                                "that retrieve lost files. "
+                                                "Depending on the amount of data this operation takes longer 
time."));
+
+  ok_button = gtk_dialog_get_widget_for_response (GTK_DIALOG (message_dialog), GTK_RESPONSE_OK);
+  gtk_style_context_add_class (gtk_widget_get_style_context (ok_button), "destructive-action");
+
+  if (gtk_dialog_run (GTK_DIALOG (message_dialog)) == GTK_RESPONSE_OK)
+    gdu_window_ensure_unused (window, object, (GAsyncReadyCallback) fs_repair_unmount_cb,
+                              NULL, g_object_ref (object));
+
+  gtk_widget_destroy (message_dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+fs_check_cb (UDisksFilesystem *filesystem,
+             GAsyncResult     *res,
+             GduWindow        *window)
+{
+  gboolean consistent;
+  GError *error = NULL;
+
+  if (!udisks_filesystem_call_check_finish (filesystem, &consistent, res, &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (window),
+                            _("Error while checking filesystem"),
+                            error);
+      g_error_free (error);
+    }
+  else
+    {
+      GtkWidget *message_dialog;
+      UDisksObjectInfo *info;
+      UDisksBlock *block;
+      UDisksObject *object;
+      const gchar *name;
+      gchar *s;
+
+      object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)));
+      block = udisks_object_peek_block (object);
+      g_assert (block != NULL);
+      info = udisks_client_get_object_info (window->client, object);
+      name = udisks_block_get_id_label (block);
+
+      if (name == NULL || strlen (name) == 0)
+        name = udisks_block_get_id_type (block);
+
+      message_dialog = gtk_message_dialog_new_with_markup  (GTK_WINDOW (window),
+                                                            GTK_DIALOG_MODAL,
+                                                            GTK_MESSAGE_INFO,
+                                                            GTK_BUTTONS_CLOSE,
+                                                            "<big><b>%s</b></big>",
+                                                            consistent ? _("Filesystem intact") : 
_("Filesystem damaged"));
+      if (consistent)
+        {
+          s = g_strdup_printf (_("Filesystem %s on %s is undamaged."),
+                               name, udisks_object_info_get_name (info));
+        }
+      else
+        {
+          /* show as result and not error message, because it's not a malfunction of GDU */
+          s = g_strdup_printf (_("Filesystem %s on %s needs repairing."),
+                               name, udisks_object_info_get_name (info));
+        }
+
+      gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (message_dialog), "%s", s);
+      gtk_dialog_run (GTK_DIALOG (message_dialog));
+
+      gtk_widget_destroy (message_dialog);
+      g_free (s);
+    }
+}
+
+static void
+fs_check_unmount_cb (GduWindow        *window,
+                     GAsyncResult     *res,
+                     gpointer          user_data)
+{
+  UDisksObject *object = UDISKS_OBJECT (user_data);
+  GError *error = NULL;
+
+  if (!gdu_window_ensure_unused_finish (window,
+                                        res,
+                                        &error))
+    {
+      gdu_utils_show_error (GTK_WINDOW (window),
+                            _("Error unmounting filesystem"),
+                            error);
+      g_error_free (error);
+    }
+  else
+    {
+      UDisksFilesystem *filesystem;
+
+      filesystem = udisks_object_peek_filesystem (object);
+      udisks_filesystem_call_check (filesystem,
+                                    g_variant_new ("a{sv}", NULL),
+                                    NULL,
+                                    (GAsyncReadyCallback) fs_check_cb,
+                                    window);
+    }
+
+  g_object_unref (object);
+}
+
+static void
+on_generic_menu_item_check (GtkMenuItem *menu_item,
+                            gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  UDisksObject *object;
+  GtkWidget *message_dialog, *ok_button;
+
+  object = gdu_volume_grid_get_selected_device (GDU_VOLUME_GRID (window->volume_grid));
+  g_assert (object != NULL);
+
+  message_dialog = gtk_message_dialog_new_with_markup  (GTK_WINDOW (window),
+                                                        GTK_DIALOG_MODAL,
+                                                        GTK_MESSAGE_WARNING,
+                                                        GTK_BUTTONS_OK_CANCEL,
+                                                        "<big><b>%s</b></big>",
+                                                        _("Confirm Check"));
+
+  gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (message_dialog), "%s",
+                                              _("Depending on the amount of data the filesystem check takes 
longer time."));
+
+  ok_button = gtk_dialog_get_widget_for_response (GTK_DIALOG (message_dialog), GTK_RESPONSE_OK);
+  gtk_style_context_add_class (gtk_widget_get_style_context (ok_button), "suggested-action");
+
+  if (gtk_dialog_run (GTK_DIALOG (message_dialog)) == GTK_RESPONSE_OK)
+    gdu_window_ensure_unused (window, object, (GAsyncReadyCallback) fs_check_unmount_cb,
+                              NULL, g_object_ref (object));
+
+  gtk_widget_destroy (message_dialog);
+}
+
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 on_generic_menu_item_edit_label (GtkMenuItem *menu_item,
                                  gpointer   user_data)
diff --git a/src/disks/gnome-disks.gresource.xml b/src/disks/gnome-disks.gresource.xml
index c3f8589..17dbfc9 100644
--- a/src/disks/gnome-disks.gresource.xml
+++ b/src/disks/gnome-disks.gresource.xml
@@ -20,6 +20,7 @@
     <file preprocess="xml-stripblanks">ui/format-disk-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/format-volume-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/new-disk-image-dialog.ui</file>
+    <file preprocess="xml-stripblanks">ui/resize-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/restore-disk-image-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/smart-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/unlock-device-dialog.ui</file>
diff --git a/src/disks/meson.build b/src/disks/meson.build
index 6c0c0e7..5238163 100644
--- a/src/disks/meson.build
+++ b/src/disks/meson.build
@@ -31,6 +31,10 @@ sources = files(
   'main.c'
 )
 
+if config_h.get('HAVE_UDISKS2_7_2')
+  sources += files('gduresizedialog.c')
+endif
+
 resource_data = files(
   'ui/about-dialog.ui',
   'ui/app-menu.ui',
@@ -52,6 +56,7 @@ resource_data = files(
   'ui/format-volume-dialog.ui',
   'ui/gdu.css',
   'ui/new-disk-image-dialog.ui',
+  'ui/resize-dialog.ui',
   'ui/restore-disk-image-dialog.ui',
   'ui/smart-dialog.ui',
   'ui/unlock-device-dialog.ui'
diff --git a/src/disks/ui/disks.ui b/src/disks/ui/disks.ui
index 49b938e..12824de 100644
--- a/src/disks/ui/disks.ui
+++ b/src/disks/ui/disks.ui
@@ -1297,6 +1297,36 @@
       </object>
     </child>
     <child>
+      <object class="GtkMenuItem" id="generic-menu-item-resize">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label" translatable="yes">Resize…</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-menu-item-check">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label" translatable="yes">Check Filesystem…</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-menu-item-repair">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label" translatable="yes">Repair Filesystem…</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkSeparatorMenuItem" id="generic-menu-item-separator">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+    </child>
+    <child>
       <object class="GtkMenuItem" id="generic-menu-item-configure-fstab">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
diff --git a/src/disks/ui/gdu.css b/src/disks/ui/gdu.css
index 0e5c5c1..f3a482e 100644
--- a/src/disks/ui/gdu.css
+++ b/src/disks/ui/gdu.css
@@ -27,3 +27,14 @@
                                      to(shade (@theme_unfocused_selected_bg_color, 0.80)));
        -adwaita-focus-border-color: mix(@theme_unfocused_selected_fg_color, 
@theme_unfocused_selected_bg_color, 0.30);
 }
+
+.partition-scale contents {
+       padding-left: 0px;
+       border-style: solid;
+       border-color: black;
+       border-radius: 3px;
+}
+
+.partition-scale contents trough {
+       margin-left: -2px;
+}
diff --git a/src/disks/ui/resize-dialog.ui b/src/disks/ui/resize-dialog.ui
new file mode 100644
index 0000000..62189ea
--- /dev/null
+++ b/src/disks/ui/resize-dialog.ui
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkAdjustment" id="difference-adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="free-following-adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="resize-dialog">
+    <property name="width_request">600</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">20</property>
+    <property name="title" translatable="yes">Resize Volume</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="use-header-bar">1</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="explanation">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_top">20</property>
+            <property name="margin_bottom">20</property>
+            <property name="label" translatable="yes">Resizing a filesystem can lead to data loss. You are 
adviced to backup your data before. The resize operation will take longer if a lot of data has to be moved. 
The minimal size is calculated according to the current content. Keep additional free space for the 
filesystem to work fast and reliably.</property>
+            <property name="wrap">True</property>
+            <property name="width_chars">60</property>
+            <property name="max_width_chars">60</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkStack" id="size-stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="transition_type">crossfade</property>
+            <child>
+              <object class="GtkSpinner" id="spinner">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="active">True</property>
+              </object>
+              <packing>
+                <property name="name">page0</property>
+                <property name="title" translatable="yes">page0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="resize-number-grid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_bottom">40</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">end</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Partition _Size</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">size-spinbutton</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="size-spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_text" translatable="yes">The size of the partition to create, in 
megabytes</property>
+                    <property name="activates_default">True</property>
+                    <property name="text" translatable="yes">0</property>
+                    <property name="adjustment">size-adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBoxText" id="size-unit-combobox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="active">2</property>
+                    <items>
+                      <item translatable="yes">bytes</item>
+                      <item translatable="yes">kB</item>
+                      <item translatable="yes">MB</item>
+                      <item translatable="yes">GB</item>
+                      <item translatable="yes">TB</item>
+                      <item translatable="yes">PB</item>
+                      <item translatable="yes">KiB</item>
+                      <item translatable="yes">MiB</item>
+                      <item translatable="yes">GiB</item>
+                      <item translatable="yes">TiB</item>
+                      <item translatable="yes">PiB</item>
+                    </items>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">end</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Free Space _Following</property>
+                    <property name="use_underline">True</property>
+                    <style>
+                      <class name="explanation-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="free-following-spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_text" translatable="yes">The free space following the partition, 
in megabytes</property>
+                    <property name="activates_default">True</property>
+                    <property name="text" translatable="yes">0</property>
+                    <property name="adjustment">free-following-adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="size-unit-following-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label">MB</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="difference-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">end</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Difference</property>
+                    <property name="use_underline">True</property>
+                    <style>
+                      <class name="explanation-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="size-difference-spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_text" translatable="yes">The free space following the partition, 
in megabytes</property>
+                    <property name="activates_default">True</property>
+                    <property name="text" translatable="yes">0</property>
+                    <property name="adjustment">difference-adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="size-unit-difference-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label">MB</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScale" id="size-scale">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="adjustment">size-adjustment</property>
+                    <property name="restrict_to_fill_level">False</property>
+                    <property name="fill_level">0</property>
+                    <property name="draw_value">False</property>
+                    <property name="value_pos">bottom</property>
+                    <property name="margin_bottom">20</property>
+                    <style>
+                      <class name="partition-scale"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">page1</property>
+                <property name="title" translatable="yes">page1</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="size-adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+</interface>


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