[gnome-disk-utility] Add app menu entry to create an empty disk image
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-disk-utility] Add app menu entry to create an empty disk image
- Date: Sat, 25 Mar 2017 14:36:55 +0000 (UTC)
commit 68aec0af8e6a4ce1a435282a5ec7ba8bc235963e
Author: Kai Lüke <kailueke riseup net>
Date: Sat Mar 25 14:50:52 2017 +0100
Add app menu entry to create an empty disk image
New images can be created with a given size in the selected folder
(being sparse files depends on the OS, works on Linux).
They will directly be attached as loop device for further
formatting/partitioning. The feature is useful in order to set up e.g.
encrypted LUKS containers which should be shared with other people.
src/disks/Makefile.am | 1 +
src/disks/gduapplication.c | 11 +
src/disks/gdunewdiskimagedialog.c | 430 +++++++++++++++++++++++++++++++++
src/disks/gdunewdiskimagedialog.h | 22 ++
src/disks/gduwindow.c | 71 +++---
src/disks/gduwindow.h | 4 +
src/disks/gnome-disks.gresource.xml | 1 +
src/disks/ui/app-menu.ui | 4 +
src/disks/ui/new-disk-image-dialog.ui | 221 +++++++++++++++++
9 files changed, 734 insertions(+), 31 deletions(-)
---
diff --git a/src/disks/Makefile.am b/src/disks/Makefile.am
index 6e49695..0689618 100644
--- a/src/disks/Makefile.am
+++ b/src/disks/Makefile.am
@@ -52,6 +52,7 @@ gnome_disks_SOURCES = \
gducreatefilesystemwidget.h gducreatefilesystemwidget.c \
gduformatdiskdialog.h gduformatdiskdialog.c \
gducreatediskimagedialog.h gducreatediskimagedialog.c \
+ gdunewdiskimagedialog.h gdunewdiskimagedialog.c \
gdurestorediskimagedialog.h gdurestorediskimagedialog.c \
gdupasswordstrengthwidget.h gdupasswordstrengthwidget.c \
gduestimator.h gduestimator.c \
diff --git a/src/disks/gduapplication.c b/src/disks/gduapplication.c
index 84deafa..4901ec4 100644
--- a/src/disks/gduapplication.c
+++ b/src/disks/gduapplication.c
@@ -19,6 +19,7 @@
#include "gduapplication.h"
#include "gduformatvolumedialog.h"
#include "gdurestorediskimagedialog.h"
+#include "gdunewdiskimagedialog.h"
#include "gduwindow.h"
#include "gdulocaljob.h"
@@ -267,6 +268,15 @@ gdu_application_activate (GApplication *_app)
/* ---------------------------------------------------------------------------------------------------- */
static void
+new_disk_image_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ GduApplication *app = GDU_APPLICATION (user_data);
+ gdu_new_disk_image_dialog_show (app->window);
+}
+
+static void
attach_disk_image_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
@@ -334,6 +344,7 @@ help_activated (GSimpleAction *action,
static GActionEntry app_entries[] =
{
+ { "new_disk_image", new_disk_image_activated, NULL, NULL, NULL },
{ "attach_disk_image", attach_disk_image_activated, NULL, NULL, NULL },
{ "about", about_activated, NULL, NULL, NULL },
{ "help", help_activated, NULL, NULL, NULL },
diff --git a/src/disks/gdunewdiskimagedialog.c b/src/disks/gdunewdiskimagedialog.c
new file mode 100644
index 0000000..a950d32
--- /dev/null
+++ b/src/disks/gdunewdiskimagedialog.c
@@ -0,0 +1,430 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2017 Kai Lüke
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: Kai Lüke <kailueke riseup net>
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <math.h>
+#include <glib/gi18n.h>
+#include <gio/gunixfdlist.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gfiledescriptorbased.h>
+
+#include <glib-unix.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include "gduapplication.h"
+#include "gduwindow.h"
+#include "gdunewdiskimagedialog.h"
+
+#define NUM_UNITS 11
+
+/* Keep in sync with Glade file */
+static const guint64 unit_sizes[NUM_UNITS] = {
+ (1ULL), /* 0: bytes */
+ (1000ULL), /* 1: kB */
+ (1000000ULL), /* 2: MB */
+ (1000000000ULL), /* 3: GB */
+ (1000000000000ULL), /* 4: TB */
+ (1000000000000000ULL), /* 5: PB */
+ ((1ULL)<<10), /* 6: KiB */
+ ((1ULL)<<20), /* 7: MiB */
+ ((1ULL)<<30), /* 8: GiB */
+ ((1ULL)<<40), /* 9: TiB */
+ ((1ULL)<<50), /* 10: PiB */
+};
+
+/* TODOs / ideas for New Disk Image creation
+ * include a radio toggle to create either
+ * - a full disk image with a partition table or
+ * - just a partition image with a filesystem
+ * (embed respective widgets like gducreatepartitiondialog does)
+ */
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GduWindow *window;
+
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+
+ gint cur_unit_num;
+
+ GtkWidget *size_spinbutton;
+ GtkWidget *size_unit_combobox;
+ GtkAdjustment *size_adjustment;
+ GtkWidget *name_entry;
+ GtkWidget *folder_fcbutton;
+
+ GtkWidget *create_image_button;
+
+ GFile *output_file;
+ GFileOutputStream *output_file_stream;
+
+ gulong response_signal_handler_id;
+
+} DialogData;
+
+static const struct {
+ goffset offset;
+ const gchar *name;
+} widget_mapping[] = {
+ {G_STRUCT_OFFSET (DialogData, size_spinbutton), "size-spinbutton"},
+ {G_STRUCT_OFFSET (DialogData, size_unit_combobox), "size-unit-combobox"},
+ {G_STRUCT_OFFSET (DialogData, size_adjustment), "size-adjustment"},
+ {G_STRUCT_OFFSET (DialogData, name_entry), "name-entry"},
+ {G_STRUCT_OFFSET (DialogData, folder_fcbutton), "folder-fcbutton"},
+ {0, NULL}
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+dialog_data_hide (DialogData *data)
+{
+ if (data->dialog != NULL)
+ {
+ GtkWidget *dialog;
+ if (data->response_signal_handler_id != 0)
+ g_signal_handler_disconnect (data->dialog, data->response_signal_handler_id);
+ dialog = data->dialog;
+ data->dialog = NULL;
+ gtk_widget_hide (dialog);
+ gtk_widget_destroy (dialog);
+ data->dialog = NULL;
+ }
+}
+
+static void
+dialog_data_unref (DialogData *data)
+{
+ dialog_data_hide (data);
+
+ g_clear_object (&data->output_file_stream);
+ g_object_unref (data->window);
+ if (data->builder != NULL)
+ g_object_unref (data->builder);
+ g_free (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+dialog_data_complete_and_unref (DialogData *data)
+{
+ dialog_data_hide (data);
+ dialog_data_unref (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+new_disk_image_update (DialogData *data)
+{
+ gboolean can_proceed = FALSE;
+
+ if (gtk_adjustment_get_value (data->size_adjustment) > 0 &&
+ strlen (gtk_entry_get_text (GTK_ENTRY (data->name_entry))) > 0)
+ can_proceed = TRUE;
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, can_proceed);
+}
+
+static void
+on_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ new_disk_image_update (data);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+set_unit_num (DialogData *data,
+ gint unit_num)
+{
+ gdouble unit_size;
+ gdouble value;
+ gdouble value_units;
+
+ g_assert (unit_num < NUM_UNITS);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (data->size_unit_combobox), unit_num);
+
+ 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;
+
+ g_object_freeze_notify (G_OBJECT (data->size_adjustment));
+
+ data->cur_unit_num = unit_num;
+
+ gtk_adjustment_configure (data->size_adjustment,
+ value_units,
+ 0.0, /* lower */
+ 10000000000000.0, /* upper */
+ 1, /* step increment */
+ 100, /* page increment */
+ 0.0); /* page_size */
+
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (data->size_spinbutton), 3);
+
+ gtk_adjustment_set_value (data->size_adjustment, value_units);
+
+ g_object_thaw_notify (G_OBJECT (data->size_adjustment));
+}
+
+static void
+new_disk_image_populate (DialogData *data)
+{
+ gchar *now_string;
+ gchar *proposed_filename = NULL;
+ GTimeZone *tz;
+ GDateTime *now;
+
+ tz = g_time_zone_new_local ();
+ now = g_date_time_new_now (tz);
+ now_string = g_date_time_format (now, "%Y-%m-%d %H%M");
+
+ if (proposed_filename == NULL)
+ {
+ /* Translators: The suggested name for the disk image to create.
+ * The %s is today's date and time, e.g. "March 2, 1976 6:25AM".
+ */
+ proposed_filename = g_strdup_printf (_("Unnamed (%s).img"),
+ now_string);
+ }
+
+ gtk_entry_set_text (GTK_ENTRY (data->name_entry), proposed_filename);
+ g_free (proposed_filename);
+ g_date_time_unref (now);
+ g_time_zone_unref (tz);
+ g_free (now_string);
+
+ gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->folder_fcbutton),
+ FALSE, /* set file types */
+ FALSE); /* allow_compressed */
+ set_unit_num (data, data->cur_unit_num);
+}
+
+static void
+on_size_unit_combobox_changed (GtkComboBox *combobox,
+ gpointer user_data)
+{
+ DialogData *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);
+
+ new_disk_image_update (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+start_creating (DialogData *data)
+{
+ gboolean ret = TRUE;
+ const gchar *name;
+ GFile *folder;
+ GError *error;
+ guint64 size;
+ gchar *filename = NULL;
+
+ name = gtk_entry_get_text (GTK_ENTRY (data->name_entry));
+ folder = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->folder_fcbutton));
+
+ error = NULL;
+ data->output_file = g_file_get_child (folder, name);
+ filename = g_file_get_path(data->output_file);
+ data->output_file_stream = g_file_replace (data->output_file,
+ NULL, /* etag */
+ FALSE, /* make_backup */
+ G_FILE_CREATE_NONE,
+ NULL,
+ &error);
+ if (data->output_file_stream == NULL)
+ {
+ gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error opening file for writing"), error);
+ g_clear_error (&error);
+ g_object_unref (folder);
+ dialog_data_complete_and_unref (data);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* now that we know the user picked a folder, update file chooser settings */
+ gdu_utils_file_chooser_for_disk_images_set_default_folder (folder);
+
+ size = gtk_adjustment_get_value (data->size_adjustment) * unit_sizes[data->cur_unit_num];
+ /* will result in a sparse file if supported */
+ if (!g_seekable_truncate (G_SEEKABLE(data->output_file_stream),
+ size,
+ NULL,
+ &error))
+ {
+ g_warning ("Error truncating file output stream: %s (%s, %d)",
+ error->message, g_quark_to_string (error->domain), error->code);
+ gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error writing file"), error);
+ g_clear_error (&error);
+ }
+
+ if (!g_output_stream_close (G_OUTPUT_STREAM (data->output_file_stream),
+ NULL, /* cancellable */
+ &error))
+ {
+ g_warning ("Error closing file output stream: %s (%s, %d)",
+ error->message, g_quark_to_string (error->domain), error->code);
+ g_clear_error (&error);
+ }
+ g_clear_object (&data->output_file_stream);
+
+ /* load loop device*/
+ gdu_window_attach_disk_image_helper (data->window, filename, FALSE);
+
+ dialog_data_hide (data);
+ out:
+ g_clear_object (&(data->output_file));
+ g_free (filename);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+/* returns TRUE if OK to overwrite or file doesn't exist */
+static gboolean
+check_overwrite (DialogData *data)
+{
+ GFile *folder = NULL;
+ const gchar *name;
+ gboolean ret = TRUE;
+ GFile *file = NULL;
+ GFileInfo *folder_info = NULL;
+ GtkWidget *dialog;
+ gint response;
+
+ name = gtk_entry_get_text (GTK_ENTRY (data->name_entry));
+ folder = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->folder_fcbutton));
+ file = g_file_get_child (folder, name);
+ if (!g_file_query_exists (file, NULL))
+ goto out;
+
+ folder_info = g_file_query_info (folder,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (folder_info == NULL)
+ goto out;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("A file named “%s” already exists. Do you want to replace it?"),
+ name);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("The file already exists in “%s”. Replacing it will overwrite
its contents."),
+ g_file_info_get_display_name (folder_info));
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Replace"), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response != GTK_RESPONSE_ACCEPT)
+ ret = FALSE;
+
+ gtk_widget_destroy (dialog);
+
+ out:
+ g_clear_object (&folder_info);
+ g_clear_object (&file);
+ g_clear_object (&folder);
+ return ret;
+}
+
+
+static void
+on_dialog_response (GtkDialog *dialog,
+ gint response,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response)
+ {
+ case GTK_RESPONSE_OK:
+ if (check_overwrite (data))
+ {
+ start_creating (data);
+ }
+ break;
+
+ case GTK_RESPONSE_CLOSE:
+ dialog_data_complete_and_unref (data);
+ break;
+
+ default: /* explicit fallthrough */
+ case GTK_RESPONSE_CANCEL:
+ dialog_data_complete_and_unref (data);
+ break;
+ }
+}
+
+void
+gdu_new_disk_image_dialog_show (GduWindow *window)
+{
+ DialogData *data;
+ guint n;
+
+ data = g_new0 (DialogData, 1);
+ data->window = g_object_ref (window);
+ data->cur_unit_num = 3; /* initalize with GB as unit */
+
+ data->dialog = GTK_WIDGET (gdu_application_new_widget (gdu_window_get_application (window),
+ "new-disk-image-dialog.ui",
+ "new-disk-image-dialog",
+ &data->builder));
+ for (n = 0; widget_mapping[n].name != NULL; n++)
+ {
+ gpointer *p = (gpointer *) ((char *) data + widget_mapping[n].offset);
+ *p = gtk_builder_get_object (data->builder, widget_mapping[n].name);
+ }
+ g_signal_connect (data->name_entry, "notify::text", G_CALLBACK (on_notify), data);
+ g_signal_connect (data->size_adjustment, "notify::value", G_CALLBACK (on_notify), data);
+ g_signal_connect (data->size_unit_combobox, "changed", G_CALLBACK (on_size_unit_combobox_changed), data);
+
+ new_disk_image_populate (data);
+ new_disk_image_update (data);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK);
+
+ data->response_signal_handler_id = g_signal_connect (data->dialog,
+ "response",
+ G_CALLBACK (on_dialog_response),
+ data);
+
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
+ gtk_window_present (GTK_WINDOW (data->dialog));
+
+ /* Only select the precomputed filename, not the .img extension */
+ gtk_editable_select_region (GTK_EDITABLE (data->name_entry), 0,
+ strlen (gtk_entry_get_text (GTK_ENTRY (data->name_entry))) - 4);
+}
+
diff --git a/src/disks/gdunewdiskimagedialog.h b/src/disks/gdunewdiskimagedialog.h
new file mode 100644
index 0000000..3118405
--- /dev/null
+++ b/src/disks/gdunewdiskimagedialog.h
@@ -0,0 +1,22 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2017 Kai Lüke
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: Kai Lüke <kailueke riseup net>
+ */
+
+#ifndef __GDU_NEW_DISK_IMAGE_DIALOG_H__
+#define __GDU_NEW_DISK_IMAGE_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+void gdu_new_disk_image_dialog_show (GduWindow *window);
+
+G_END_DECLS
+
+#endif /* __GDU_NEW_DISK_IMAGE_DIALOG_H__ */
diff --git a/src/disks/gduwindow.c b/src/disks/gduwindow.c
index b8f2d8e..ffe83e4 100644
--- a/src/disks/gduwindow.c
+++ b/src/disks/gduwindow.c
@@ -710,15 +710,50 @@ loop_setup_cb (UDisksManager *manager,
/* ---------------------------------------------------------------------------------------------------- */
+gboolean
+gdu_window_attach_disk_image_helper (GduWindow *window, gchar *filename, gboolean readonly)
+{
+ gint fd = -1;
+ GUnixFDList *fd_list;
+ GVariantBuilder options_builder;
+ fd = open (filename, O_RDWR);
+ if (fd == -1)
+ fd = open (filename, O_RDONLY);
+ if (fd == -1)
+ {
+ GError *error;
+ error = g_error_new (G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ "%s", strerror (errno));
+ gdu_utils_show_error (GTK_WINDOW (window),
+ _("Error attaching disk image"),
+ error);
+ g_error_free (error);
+ return FALSE;
+ }
+ g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
+ if (readonly)
+ g_variant_builder_add (&options_builder, "{sv}", "read-only", g_variant_new_boolean (TRUE));
+ fd_list = g_unix_fd_list_new_from_array (&fd, 1); /* adopts the fd */
+ udisks_manager_call_loop_setup (udisks_client_get_manager (window->client),
+ g_variant_new_handle (0),
+ g_variant_builder_end (&options_builder),
+ fd_list,
+ NULL, /* GCancellable */
+ (GAsyncReadyCallback) loop_setup_cb,
+ loop_setup_data_new (window, filename));
+ g_object_unref (fd_list);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
void
gdu_window_show_attach_disk_image (GduWindow *window)
{
GtkWidget *dialog;
GFile *folder = NULL;
gchar *filename = NULL;
- gint fd = -1;
- GUnixFDList *fd_list;
- GVariantBuilder options_builder;
GtkWidget *ro_checkbutton;
dialog = gtk_file_chooser_dialog_new (_("Select Disk Image to Attach"),
@@ -744,39 +779,13 @@ gdu_window_show_attach_disk_image (GduWindow *window)
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
gtk_widget_hide (dialog);
- fd = open (filename, O_RDWR);
- if (fd == -1)
- fd = open (filename, O_RDONLY);
- if (fd == -1)
- {
- GError *error;
- error = g_error_new (G_IO_ERROR,
- g_io_error_from_errno (errno),
- "%s", strerror (errno));
- gdu_utils_show_error (GTK_WINDOW (window),
- _("Error attaching disk image"),
- error);
- g_error_free (error);
- goto out;
- }
+ if (!gdu_window_attach_disk_image_helper (window, filename, gtk_toggle_button_get_active
(GTK_TOGGLE_BUTTON (ro_checkbutton))))
+ goto out;
/* now that we know the user picked a folder, update file chooser settings */
folder = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (dialog));
gdu_utils_file_chooser_for_disk_images_set_default_folder (folder);
- g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ro_checkbutton)))
- g_variant_builder_add (&options_builder, "{sv}", "read-only", g_variant_new_boolean (TRUE));
- fd_list = g_unix_fd_list_new_from_array (&fd, 1); /* adopts the fd */
- udisks_manager_call_loop_setup (udisks_client_get_manager (window->client),
- g_variant_new_handle (0),
- g_variant_builder_end (&options_builder),
- fd_list,
- NULL, /* GCancellable */
- (GAsyncReadyCallback) loop_setup_cb,
- loop_setup_data_new (window, filename));
- g_object_unref (fd_list);
-
out:
gtk_widget_destroy (dialog);
g_free (filename);
diff --git a/src/disks/gduwindow.h b/src/disks/gduwindow.h
index d3ab15e..53c5408 100644
--- a/src/disks/gduwindow.h
+++ b/src/disks/gduwindow.h
@@ -51,6 +51,10 @@ gboolean gdu_window_ensure_unused_list_finish (GduWindow *window,
GAsyncResult *res,
GError **error);
+gboolean gdu_window_attach_disk_image_helper (GduWindow *window,
+ gchar *filename,
+ gboolean readonly);
+
G_END_DECLS
#endif /* __GDU_WINDOW_H__ */
diff --git a/src/disks/gnome-disks.gresource.xml b/src/disks/gnome-disks.gresource.xml
index d1d8a40..c3f8589 100644
--- a/src/disks/gnome-disks.gresource.xml
+++ b/src/disks/gnome-disks.gresource.xml
@@ -19,6 +19,7 @@
<file preprocess="xml-stripblanks">ui/filesystem-create.ui</file>
<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/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/ui/app-menu.ui b/src/disks/ui/app-menu.ui
index 1665f86..eea2886 100644
--- a/src/disks/ui/app-menu.ui
+++ b/src/disks/ui/app-menu.ui
@@ -3,6 +3,10 @@
<menu id='app-menu'>
<section>
<item>
+ <attribute name="label" translatable="yes">_New Disk Image…</attribute>
+ <attribute name="action">app.new_disk_image</attribute>
+ </item>
+ <item>
<attribute name="label" translatable="yes">Attach Disk _Image…</attribute>
<attribute name="action">app.attach_disk_image</attribute>
</item>
diff --git a/src/disks/ui/new-disk-image-dialog.ui b/src/disks/ui/new-disk-image-dialog.ui
new file mode 100644
index 0000000..ca7e468
--- /dev/null
+++ b/src/disks/ui/new-disk-image-dialog.ui
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="new-disk-image-dialog">
+ <property name="width_request">550</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="title" translatable="yes">New Disk Image</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Partition _Size</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">size-spinbutton</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_Name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">name-entry</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="folder-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Save in _Folder</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">folder-fcbutton</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">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_markup" translatable="yes">The size of the partition to create, in
megabytes</property>
+ <property name="tooltip_text" translatable="yes">The size of the partition to create, in
megabytes</property>
+ <property name="invisible_char">●</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ <property name="adjustment">size-adjustment</property>
+ <property name="digits">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">●</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="folder-fcbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="action">select-folder</property>
+ <property name="local_only">False</property>
+ <property name="title" translatable="yes">Select a Folder</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ <property name="height">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="active">2</property>
+ <property name="entry_text_column">0</property>
+ <property name="id_column">1</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">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel-button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="create-image-button">
+ <property name="label" translatable="yes">Attach new _Image…</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel-button</action-widget>
+ <action-widget response="-5">create-image-button</action-widget>
+ </action-widgets>
+ </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]