[gnome-disk-utility] Update "Restore Disk Image" to match look and feel of "Create Disk Image"



commit 6ac6b7138e1f54bf5899b7ace4beb3b94848b146
Author: David Zeuthen <zeuthen gmail com>
Date:   Wed Oct 17 12:45:45 2012 -0400

    Update "Restore Disk Image" to match look and feel of "Create Disk Image"
    
    Related to the previous commit (5762bd304c14332644d2f5bdc96cfce29f4dbfcd),
    but don't add error tolerance. Basically, when copying is complete
    instead of dismissing the dialog, we now:
    
     - show a "Close" button instead of a "Cancel" button
    
     - play the "complete" sound
    
     - slightly change the progress string to convey that the operation
       is complete
    
    As with the "Create Disk Image" dialog, we also use a dedicated thread
    for copying - again: much easier to read than the async mess we had
    before.
    
    Signed-off-by: David Zeuthen <zeuthen gmail com>

 data/ui/create-disk-image-dialog.ui   |    1 +
 data/ui/restore-disk-image-dialog.ui  |  234 ++++++----
 src/disks/gducreatediskimagedialog.c  |   78 ++--
 src/disks/gdurestorediskimagedialog.c |  779 +++++++++++++++++++++------------
 src/libgdu/gduutils.c                 |   34 ++
 src/libgdu/gduutils.h                 |    3 +
 6 files changed, 718 insertions(+), 411 deletions(-)
---
diff --git a/data/ui/create-disk-image-dialog.ui b/data/ui/create-disk-image-dialog.ui
index 5e41a0a..9ea558f 100644
--- a/data/ui/create-disk-image-dialog.ui
+++ b/data/ui/create-disk-image-dialog.ui
@@ -145,6 +145,7 @@
                 <property name="can_focus">False</property>
                 <property name="xalign">0</property>
                 <property name="selectable">True</property>
+                <property name="ellipsize">middle</property>
               </object>
               <packing>
                 <property name="left_attach">1</property>
diff --git a/data/ui/restore-disk-image-dialog.ui b/data/ui/restore-disk-image-dialog.ui
index 3dfc1c9..f455059 100644
--- a/data/ui/restore-disk-image-dialog.ui
+++ b/data/ui/restore-disk-image-dialog.ui
@@ -5,6 +5,7 @@
     <property name="width_request">500</property>
     <property name="can_focus">False</property>
     <property name="border_width">12</property>
+    <property name="title" translatable="yes">Restore Disk Image</property>
     <property name="resizable">False</property>
     <property name="type_hint">dialog</property>
     <child internal-child="vbox">
@@ -28,127 +29,163 @@
           </packing>
         </child>
         <child>
-          <object class="GtkNotebook" id="notebook">
+          <object class="GtkGrid" id="grid1">
+            <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="margin_left">12</property>
-            <property name="show_tabs">False</property>
-            <property name="show_border">False</property>
+            <property name="row_spacing">12</property>
+            <property name="column_spacing">12</property>
             <child>
-              <object class="GtkGrid" id="grid1">
+              <object class="GtkLabel" id="image-key-label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="row_spacing">12</property>
-                <property name="column_spacing">12</property>
-                <child>
-                  <object class="GtkLabel" id="label6">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
-                    <property name="label" translatable="yes">_Image to restore</property>
-                    <property name="use_underline">True</property>
-                    <property name="mnemonic_widget">source_file_fcbutton</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="GtkFileChooserButton" id="source_file_fcbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="orientation">vertical</property>
-                    <property name="local_only">False</property>
-                    <property name="title" translatable="yes">Select Disk Image to restore</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>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">_Image to Restore</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">image-fcbutton</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
               </object>
-            </child>
-            <child type="tab">
-              <placeholder/>
+              <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="GtkBox" id="vbox1">
+              <object class="GtkFileChooserButton" id="image-fcbutton">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="orientation">vertical</property>
-                <property name="spacing">12</property>
+                <property name="local_only">False</property>
+                <property name="title" translatable="yes">Select Disk Image to restore</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="GtkLabel" id="destination-key-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Destination</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="GtkLabel" id="destination-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="selectable">True</property>
+                <property name="ellipsize">middle</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="copying-vbox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">6</property>
                 <child>
-                  <object class="GtkLabel" id="copying_label">
+                  <object class="GtkProgressBar" id="copying-progressbar">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Copying data to device...</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
+                    <property name="expand">True</property>
                     <property name="fill">True</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkVBox" id="vbox3">
+                  <object class="GtkLabel" id="copying-label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="spacing">6</property>
-                    <child>
-                      <object class="GtkProgressBar" id="copying_progressbar">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="copying_progress_label">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
+                    <property name="xalign">0</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
+                    <property name="expand">True</property>
                     <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
               </object>
               <packing>
-                <property name="position">1</property>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
               </packing>
             </child>
-            <child type="tab">
-              <placeholder/>
-            </child>
             <child>
-              <placeholder/>
-            </child>
-            <child type="tab">
-              <placeholder/>
+              <object class="GtkLabel" id="copying-key-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="yalign">0</property>
+                <property name="label" translatable="yes">Progress</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkLabel" id="source-key-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Source</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 type="tab">
-              <placeholder/>
+            <child>
+              <object class="GtkLabel" id="source-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="selectable">True</property>
+                <property name="ellipsize">middle</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
             </child>
           </object>
           <packing>
@@ -162,13 +199,11 @@
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
             <child>
-              <object class="GtkButton" id="button1">
+              <object class="GtkButton" id="cancel-button">
                 <property name="label">gtk-cancel</property>
-                <property name="use_action_appearance">False</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -178,14 +213,12 @@
               </packing>
             </child>
             <child>
-              <object class="GtkButton" id="start_copying_button">
+              <object class="GtkButton" id="start-copying-button">
                 <property name="label" translatable="yes">_Start Restoring...</property>
-                <property name="use_action_appearance">False</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_action_appearance">False</property>
                 <property name="use_underline">True</property>
               </object>
               <packing>
@@ -194,6 +227,20 @@
                 <property name="position">1</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkButton" id="close-button">
+                <property name="label">gtk-close</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">2</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -205,8 +252,9 @@
       </object>
     </child>
     <action-widgets>
-      <action-widget response="-6">button1</action-widget>
-      <action-widget response="-5">start_copying_button</action-widget>
+      <action-widget response="-6">cancel-button</action-widget>
+      <action-widget response="-5">start-copying-button</action-widget>
+      <action-widget response="-7">close-button</action-widget>
     </action-widgets>
   </object>
 </interface>
diff --git a/src/disks/gducreatediskimagedialog.c b/src/disks/gducreatediskimagedialog.c
index b0badad..e57ab5f 100644
--- a/src/disks/gducreatediskimagedialog.c
+++ b/src/disks/gducreatediskimagedialog.c
@@ -263,40 +263,7 @@ create_disk_image_populate (DialogData *data)
   /* Source label */
   info = udisks_client_get_object_info (gdu_window_get_client (data->window), data->object);
   gtk_label_set_text (GTK_LABEL (data->source_label), info->one_liner);
-  if (info != NULL)
-    udisks_object_info_unref (info);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static gchar *
-get_pretty_uri (GFile *file)
-{
-  gchar *ret;
-  gchar *s;
-
-  if (g_file_is_native (file))
-    {
-      const gchar *homedir;
-
-      ret = g_file_get_path (file);
-
-      homedir = g_get_home_dir ();
-      if (g_str_has_prefix (ret, homedir))
-        {
-          s = ret;
-          ret = g_strdup_printf ("~/%s", ret + strlen (homedir) + 1);
-          g_free (s);
-        }
-    }
-  else
-    {
-      s = g_file_get_uri (file);
-      ret = g_uri_unescape_string (s, NULL);
-      g_free (s);
-    }
-
-  return ret;
+  udisks_object_info_unref (info);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -400,6 +367,22 @@ update_gui (DialogData *data,
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar), progress);
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+play_complete_sound (DialogData *data)
+{
+  const gchar *sound_message;
+  /* Translators: A descriptive string for the 'complete' sound, see CA_PROP_EVENT_DESCRIPTION */
+  sound_message = _("Disk image creation complete");
+  ca_gtk_play_for_widget (GTK_WIDGET (data->dialog), 0,
+                          CA_PROP_EVENT_ID, "complete",
+                          CA_PROP_EVENT_DESCRIPTION, sound_message,
+                          NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gboolean
 on_update_ui (gpointer user_data)
 {
@@ -416,6 +399,8 @@ on_show_error (gpointer user_data)
 {
   DialogData *data = user_data;
 
+  play_complete_sound (data);
+
   g_assert (data->copy_error != NULL);
   gdu_utils_show_error (GTK_WINDOW (data->dialog),
                         _("Error creating disk image"),
@@ -434,7 +419,6 @@ static gboolean
 on_success (gpointer user_data)
 {
   DialogData *data = user_data;
-  const gchar *sound_message;
 
   update_gui (data, TRUE);
 
@@ -442,12 +426,7 @@ on_success (gpointer user_data)
   gtk_widget_show (data->close_button);
   gtk_widget_show (data->show_in_folder_button);
 
-  /* Translators: A descriptive string for the 'complete' sound, see CA_PROP_EVENT_DESCRIPTION */
-  sound_message = _("Disk image creation complete");
-  ca_gtk_play_for_widget (GTK_WIDGET (data->dialog), 0,
-                          CA_PROP_EVENT_ID, "complete",
-                          CA_PROP_EVENT_DESCRIPTION, sound_message,
-                          NULL);
+  play_complete_sound (data);
 
   dialog_data_unref (data);
   return FALSE; /* remove source */
@@ -707,7 +686,7 @@ copy_thread_func (gpointer user_data)
                               &error2))
     {
       g_warning ("Error closing file output stream: %s (%s, %d)",
-                 error2->message, g_quark_to_string (error->domain), error->code);
+                 error2->message, g_quark_to_string (error2->domain), error2->code);
       g_clear_error (&error2);
     }
   g_clear_object (&data->output_file_stream);
@@ -807,7 +786,7 @@ check_overwrite (DialogData *data)
 }
 
 static gboolean
-open_device (DialogData *data)
+start_copying (DialogData *data)
 {
   gboolean ret = TRUE;
   const gchar *name;
@@ -836,7 +815,7 @@ open_device (DialogData *data)
       goto out;
     }
 
-  uri = get_pretty_uri (data->output_file);
+  uri = gdu_utils_get_pretty_uri (data->output_file);
   gtk_label_set_text (GTK_LABEL (data->destination_label), uri);
 
   /* now that we know the user picked a folder, update file chooser settings */
@@ -926,14 +905,10 @@ on_dialog_response (GtkDialog     *dialog,
 
           gtk_widget_hide (data->start_copying_button);
 
-          open_device (data);
+          start_copying (data);
         }
       break;
 
-    case GTK_RESPONSE_CANCEL:
-      dialog_data_complete_and_unref (data);
-      break;
-
     case GTK_RESPONSE_CLOSE:
       dialog_data_complete_and_unref (data);
       break;
@@ -941,6 +916,11 @@ on_dialog_response (GtkDialog     *dialog,
     case 1: /* show_in_folder */
       show_in_folder (data);
       break;
+
+    default: /* explicit fallthrough */
+    case GTK_RESPONSE_CANCEL:
+      dialog_data_complete_and_unref (data);
+      break;
     }
 }
 
diff --git a/src/disks/gdurestorediskimagedialog.c b/src/disks/gdurestorediskimagedialog.c
index 01770f8..406fdbc 100644
--- a/src/disks/gdurestorediskimagedialog.c
+++ b/src/disks/gdurestorediskimagedialog.c
@@ -17,6 +17,8 @@
 #include <sys/ioctl.h>
 #include <linux/fs.h>
 
+#include <canberra-gtk.h>
+
 #include "gduapplication.h"
 #include "gduwindow.h"
 #include "gdurestorediskimagedialog.h"
@@ -40,25 +42,36 @@ typedef struct
   GtkBuilder *builder;
   GtkWidget *dialog;
 
-  GtkWidget *notebook;
-  GtkWidget *start_copying_button;
-  GtkWidget *source_file_fcbutton;
-
   GtkWidget *infobar_vbox;
   GtkWidget *warning_infobar;
   GtkWidget *warning_label;
   GtkWidget *error_infobar;
   GtkWidget *error_label;
 
-  GtkWidget *copying_label;
+  GtkWidget *image_key_label;
+  GtkWidget *image_fcbutton;
+
+  GtkWidget *source_key_label;
+  GtkWidget *source_label;
+  GtkWidget *destination_key_label;
+  GtkWidget *destination_label;
+
+  GtkWidget *copying_key_label;
+  GtkWidget *copying_vbox;
   GtkWidget *copying_progressbar;
-  GtkWidget *copying_progress_label;
+  GtkWidget *copying_label;
+
+  GtkWidget *start_copying_button;
+  GtkWidget *close_button;
+  GtkWidget *cancel_button;
 
   guint64 block_size;
+  gint64 start_time_usec;
+  gint64 end_time_usec;
 
   GCancellable *cancellable;
   GOutputStream *block_stream;
-  GFileInputStream *input_file_stream;
+  GFileInputStream *file_input_stream;
   guint64 file_size;
 
   guchar *buffer;
@@ -66,21 +79,51 @@ typedef struct
   guint64 buffer_bytes_written;
   guint64 buffer_bytes_to_write;
 
+  /* must hold copy_lock when reading/writing these */
+  GMutex copy_lock;
   GduEstimator *estimator;
+  guint update_id;
+  GError *copy_error;
 
-  gulong response_signal_handler_id;
   gboolean completed;
-} RestoreDiskImageData;
+} DialogData;
+
+
+static const struct {
+  goffset offset;
+  const gchar *name;
+} widget_mapping[] = {
+  {G_STRUCT_OFFSET (DialogData, infobar_vbox), "infobar-vbox"},
+
+  {G_STRUCT_OFFSET (DialogData, image_key_label), "image-key-label"},
+  {G_STRUCT_OFFSET (DialogData, image_fcbutton), "image-fcbutton"},
+  {G_STRUCT_OFFSET (DialogData, source_key_label), "source-key-label"},
+  {G_STRUCT_OFFSET (DialogData, source_label), "source-label"},
+  {G_STRUCT_OFFSET (DialogData, destination_key_label), "destination-key-label"},
+  {G_STRUCT_OFFSET (DialogData, destination_label), "destination-label"},
+
+  {G_STRUCT_OFFSET (DialogData, copying_key_label), "copying-key-label"},
+  {G_STRUCT_OFFSET (DialogData, copying_vbox), "copying-vbox"},
+  {G_STRUCT_OFFSET (DialogData, copying_progressbar), "copying-progressbar"},
+  {G_STRUCT_OFFSET (DialogData, copying_label), "copying-label"},
+
+  {G_STRUCT_OFFSET (DialogData, start_copying_button), "start-copying-button"},
+  {G_STRUCT_OFFSET (DialogData, cancel_button), "cancel-button"},
+  {G_STRUCT_OFFSET (DialogData, close_button), "close-button"},
+  {0, NULL}
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
 
-static RestoreDiskImageData *
-restore_disk_image_data_ref (RestoreDiskImageData *data)
+static DialogData *
+dialog_data_ref (DialogData *data)
 {
   g_atomic_int_inc (&data->ref_count);
   return data;
 }
 
 static void
-restore_disk_image_data_unref (RestoreDiskImageData *data)
+dialog_data_unref (DialogData *data)
 {
   if (g_atomic_int_dec_and_test (&data->ref_count))
     {
@@ -88,16 +131,14 @@ restore_disk_image_data_unref (RestoreDiskImageData *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);
         }
-      g_clear_object (&data->cancellable);
-      g_clear_object (&data->input_file_stream);
-      g_clear_object (&data->block_stream);
+      g_object_unref (data->warning_infobar);
+      g_object_unref (data->error_infobar);
+
       g_object_unref (data->window);
       g_object_unref (data->object);
       g_object_unref (data->block);
@@ -106,50 +147,58 @@ restore_disk_image_data_unref (RestoreDiskImageData *data)
         g_object_unref (data->builder);
       g_free (data->buffer);
       g_clear_object (&data->estimator);
+
+      g_clear_object (&data->cancellable);
+      g_clear_object (&data->file_input_stream);
+      g_clear_object (&data->block_stream);
+      g_mutex_clear (&data->copy_lock);
       g_free (data);
     }
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gboolean
+unref_in_idle (gpointer user_data)
+{
+  DialogData *data = user_data;
+  dialog_data_unref (data);
+  return FALSE; /* remove source */
+}
+
 static void
-rescan_cb (UDisksBlock   *block,
-           GAsyncResult  *res,
-           gpointer       user_data)
+dialog_data_unref_in_idle (DialogData *data)
 {
-  RestoreDiskImageData *data = user_data;
-  GError *error = NULL;
-  if (!udisks_block_call_rescan_finish (block,
-                                        res,
-                                        &error))
-    {
-      gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error rescanning device"), error);
-      g_clear_error (&error);
-    }
-  restore_disk_image_data_unref (data);
+  g_idle_add (unref_in_idle, data);
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
-restore_disk_image_data_complete (RestoreDiskImageData *data)
+dialog_data_complete_and_unref (DialogData *data)
 {
   if (!data->completed)
     {
       data->completed = TRUE;
-      /* request that the core OS / kernel rescans the device */
-      udisks_block_call_rescan (data->block,
-                                g_variant_new ("a{sv}", NULL), /* options */
-                                NULL, /* cancellable */
-                                (GAsyncReadyCallback) rescan_cb,
-                                restore_disk_image_data_ref (data));
       g_cancellable_cancel (data->cancellable);
-      restore_disk_image_data_unref (data);
+      gtk_widget_hide (data->dialog);
     }
+  dialog_data_unref (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+dialog_data_dismiss_and_unref (DialogData *data)
+{
+  gtk_widget_hide (data->dialog);
+  dialog_data_unref (data);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-restore_disk_image_update (RestoreDiskImageData *data)
+restore_disk_image_update (DialogData *data)
 {
   gboolean can_proceed = FALSE;
   gchar *restore_warning = NULL;
@@ -164,7 +213,7 @@ restore_disk_image_update (RestoreDiskImageData *data)
     goto out;
 
   /* Check if we have a file */
-  restore_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->source_file_fcbutton));
+  restore_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->image_fcbutton));
   if (restore_file != NULL)
     {
       GFileInfo *info;
@@ -191,7 +240,7 @@ restore_disk_image_update (RestoreDiskImageData *data)
                 {
                   s = udisks_client_get_size_for_display (gdu_window_get_client (data->window),
                                                           data->block_size - size, FALSE, FALSE);
-                  restore_warning = g_strdup_printf (_("The selected image is %s smaller than the device"), s);
+                  restore_warning = g_strdup_printf (_("The disk image is %s smaller than the target device"), s);
                   g_free (s);
                 }
               can_proceed = TRUE;
@@ -200,7 +249,7 @@ restore_disk_image_update (RestoreDiskImageData *data)
             {
               s = udisks_client_get_size_for_display (gdu_window_get_client (data->window),
                                                       size - data->block_size, FALSE, FALSE);
-              restore_error = g_strdup_printf (_("The selected image is %s bigger than the device"), s);
+              restore_error = g_strdup_printf (_("The disk image is %s bigger than the target device"), s);
               g_free (s);
             }
           else
@@ -244,8 +293,12 @@ static void
 on_file_set (GtkFileChooserButton   *button,
              gpointer                user_data)
 {
-  RestoreDiskImageData *data = user_data;
+  DialogData *data = user_data;
+  if (data->dialog == NULL)
+    goto out;
   restore_disk_image_update (data);
+ out:
+  ;
 }
 
 static void
@@ -253,68 +306,81 @@ on_notify (GObject    *object,
            GParamSpec *pspec,
            gpointer    user_data)
 {
-  RestoreDiskImageData *data = user_data;
+  DialogData *data = user_data;
+  if (data->dialog == NULL)
+    goto out;
   restore_disk_image_update (data);
+ out:
+  ;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-restore_disk_image_populate (RestoreDiskImageData *data)
+restore_disk_image_populate (DialogData *data)
 {
-  gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->source_file_fcbutton), TRUE);
-}
+  UDisksObjectInfo *info;
 
-/* ---------------------------------------------------------------------------------------------------- */
+  gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->image_fcbutton), TRUE);
 
-static void copy_more (RestoreDiskImageData *data);
+  /* Destination label */
+  info = udisks_client_get_object_info (gdu_window_get_client (data->window), data->object);
+  gtk_label_set_text (GTK_LABEL (data->destination_label), info->one_liner);
+  udisks_object_info_unref (info);
+}
 
-static void write_more (RestoreDiskImageData *data);
+/* ---------------------------------------------------------------------------------------------------- */
 
 static void
-write_cb (GOutputStream  *output_stream,
-          GAsyncResult  *res,
-          gpointer       user_data)
+update_gui (DialogData *data,
+            gboolean    done)
 {
-  RestoreDiskImageData *data = user_data;
-  GError *error;
-  gssize bytes_written;
-  guint64 bytes_per_sec;
-  guint64 usec_remaining;
+  guint64 bytes_completed = 0;
+  guint64 bytes_target = 1;
+  guint64 bytes_per_sec = 0;
+  guint64 usec_remaining = 1;
   gchar *s, *s2, *s3, *s4, *s5;
+  gdouble progress;
 
-  error = NULL;
-  bytes_written = g_output_stream_write_finish (output_stream, res, &error);
-  if (error != NULL)
+  g_mutex_lock (&data->copy_lock);
+  if (data->estimator != NULL)
     {
-      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
-        gdu_utils_show_error (GTK_WINDOW (data->dialog),
-                               _("Error writing to device"), error);
-      g_error_free (error);
-      restore_disk_image_data_complete (data);
-      goto out;
+      bytes_per_sec = gdu_estimator_get_bytes_per_sec (data->estimator);
+      usec_remaining = gdu_estimator_get_usec_remaining (data->estimator);
+      bytes_completed = gdu_estimator_get_completed_bytes (data->estimator);
+      bytes_target = gdu_estimator_get_target_bytes (data->estimator);
     }
+  data->update_id = 0;
+  g_mutex_unlock (&data->copy_lock);
 
-  data->buffer_bytes_written += bytes_written;
-  data->buffer_bytes_to_write -= bytes_written;
-
-  /* update progress bar and estimator */
-  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar),
-                                 ((gdouble) data->total_bytes_read) / ((gdouble) data->file_size));
-  gdu_estimator_add_sample (data->estimator, data->total_bytes_read);
-  bytes_per_sec = gdu_estimator_get_bytes_per_sec (data->estimator);
-  usec_remaining = gdu_estimator_get_usec_remaining (data->estimator);
-  if (bytes_per_sec > 0 && usec_remaining > 0)
+  if (done)
     {
-      s2 = g_format_size (data->total_bytes_read);
-      s3 = g_format_size (data->file_size);
+      gint64 duration_usec = data->end_time_usec - data->start_time_usec;
+      s2 = g_format_size (bytes_target);
+      s3 = gdu_utils_format_duration_usec (duration_usec, GDU_FORMAT_DURATION_FLAGS_SUBSECOND_PRECISION);
+      s4 = g_format_size (G_USEC_PER_SEC * bytes_target / duration_usec);
+      /* Translators: string used for conveying how long the copy took.
+       *              The first %s is the amount of bytes copied (ex. "650 MB").
+       *              The second %s is the time it took to copy (ex. "1 minute", or "Less than a minute").
+       *              The third %s is the average amount of bytes transfered per second (ex. "8.9 MB").
+       */
+      s = g_strdup_printf (_("%s copied in %s (%s/sec)"), s2, s3, s4);
+      g_free (s4);
+      g_free (s3);
+      g_free (s2);
+    }
+  else if (bytes_per_sec > 0 && usec_remaining > 0)
+    {
+      s2 = g_format_size (bytes_completed);
+      s3 = g_format_size (bytes_target);
       s4 = gdu_utils_format_duration_usec (usec_remaining,
                                            GDU_FORMAT_DURATION_FLAGS_NO_SECONDS);
       s5 = g_format_size (bytes_per_sec);
-      /* Translators: string used for conveying progress of copy operation.
-       * The first two %s are strings with the amount of bytes (ex. "3.4 MB" and "300 MB").
-       * The third %s is the estimated amount of time remaining (ex. "1 minute", "5 minutes" or "Less than a minute").
-       * The fourth %s is the average amount of bytes transfered per second (ex. "8.9 MB").
+      /* Translators: string used for conveying progress of copy operation when there are no errors.
+       *              The first %s is the amount of bytes copied (ex. "650 MB").
+       *              The second %s is the size of the device (ex. "8.5 GB").
+       *              The third %s is the estimated amount of time remaining (ex. "1 minute" or "5 minutes").
+       *              The fourth %s is the average amount of bytes transfered per second (ex. "8.9 MB").
        */
       s = g_strdup_printf (_("%s of %s copied â %s remaining (%s/sec)"), s2, s3, s4, s5);
       g_free (s5);
@@ -324,8 +390,8 @@ write_cb (GOutputStream  *output_stream,
     }
   else
     {
-      s2 = g_format_size (data->total_bytes_read);
-      s3 = g_format_size (data->file_size);
+      s2 = g_format_size (bytes_completed);
+      s3 = g_format_size (bytes_target);
       /* Translators: string used for convey progress of a copy operation where we don't know time remaining / speed.
        * The first two %s are strings with the amount of bytes (ex. "3.4 MB" and "300 MB").
        */
@@ -333,116 +399,333 @@ write_cb (GOutputStream  *output_stream,
       g_free (s2);
       g_free (s3);
     }
+
   s2 = g_strconcat ("<small>", s, "</small>", NULL);
-  gtk_label_set_markup (GTK_LABEL (data->copying_progress_label), s2);
+  gtk_label_set_markup (GTK_LABEL (data->copying_label), s2);
   g_free (s);
 
-  write_more (data);
-
- out:
-  restore_disk_image_data_unref (data);
+  if (done)
+    progress = 1.0;
+  else
+    progress = ((gdouble) bytes_completed) / ((gdouble) bytes_target);
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar), progress);
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
-write_more (RestoreDiskImageData *data)
+play_complete_sound (DialogData *data)
 {
-  if (data->buffer_bytes_to_write == 0)
+  const gchar *sound_message;
+  /* Translators: A descriptive string for the 'complete' sound, see CA_PROP_EVENT_DESCRIPTION */
+  sound_message = _("Disk image copying complete");
+  ca_gtk_play_for_widget (GTK_WIDGET (data->dialog), 0,
+                          CA_PROP_EVENT_ID, "complete",
+                          CA_PROP_EVENT_DESCRIPTION, sound_message,
+                          NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+on_update_ui (gpointer user_data)
+{
+  DialogData *data = user_data;
+  update_gui (data, FALSE);
+  dialog_data_unref (data);
+  return FALSE; /* remove source */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+on_show_error (gpointer user_data)
+{
+  DialogData *data = user_data;
+
+  play_complete_sound (data);
+
+  g_assert (data->copy_error != NULL);
+  gdu_utils_show_error (GTK_WINDOW (data->dialog),
+                        _("Error restoring disk image"),
+                        data->copy_error);
+  g_clear_error (&data->copy_error);
+
+  dialog_data_complete_and_unref (data);
+
+  dialog_data_unref (data);
+  return FALSE; /* remove source */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+on_success (gpointer user_data)
+{
+  DialogData *data = user_data;
+
+  update_gui (data, TRUE);
+
+  gtk_widget_hide (data->cancel_button);
+  gtk_widget_show (data->close_button);
+
+  play_complete_sound (data);
+
+  dialog_data_unref (data);
+  return FALSE; /* remove source */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gpointer
+copy_thread_func (gpointer user_data)
+{
+  DialogData *data = user_data;
+  guchar *buffer_unaligned = NULL;
+  guchar *buffer = NULL;
+  guint64 block_device_size = 0;
+  long page_size;
+  GError *error = NULL;
+  GError *error2 = NULL;
+  gint64 last_update_usec = -1;
+  gint fd = -1;
+  gint buffer_size;
+  guint64 num_bytes_completed = 0;
+
+  /* default to 1 MiB blocks */
+  buffer_size = (1 * 1024 * 1024);
+
+  /* Most OSes put ACLs for logged-in users on /dev/sr* nodes (this is
+   * so CD burning tools etc. work) so see if we can open the device
+   * file ourselves. If so, great, since this avoids a polkit dialog.
+   *
+   * As opposed to udisks' OpenForBackup() we also avoid O_EXCL since
+   * the disc is read-only by its very nature. As a side-effect this
+   * allows creating a disk image of a mounted disc.
+   */
+  if (g_str_has_prefix (udisks_block_get_device (data->block), "/dev/sr"))
     {
-      copy_more (data);
+      fd = open (udisks_block_get_device (data->block), O_RDONLY);
     }
-  else
+
+  /* Otherwise, request the fd from udisks */
+  if (fd == -1)
     {
-      g_output_stream_write_async (data->block_stream,
-                                   data->buffer + data->buffer_bytes_written,
-                                   data->buffer_bytes_to_write,
-                                   G_PRIORITY_DEFAULT,
-                                   data->cancellable,
-                                   (GAsyncReadyCallback) write_cb,
-                                   restore_disk_image_data_ref (data));
+      GUnixFDList *fd_list = NULL;
+      GVariant *fd_index = NULL;
+      if (!udisks_block_call_open_for_restore_sync (data->block,
+                                                    g_variant_new ("a{sv}", NULL), /* options */
+                                                    NULL, /* fd_list */
+                                                    &fd_index,
+                                                    &fd_list,
+                                                    NULL, /* cancellable */
+                                                    &error))
+        goto out;
+
+      fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_index), &error);
+      if (error != NULL)
+        {
+          g_prefix_error (&error,
+                          "Error extracing fd with handle %d from D-Bus message: ",
+                          g_variant_get_handle (fd_index));
+          goto out;
+        }
+      if (fd_index != NULL)
+        g_variant_unref (fd_index);
+      g_clear_object (&fd_list);
     }
-}
 
+  g_assert (fd != -1);
 
-static void
-read_cb (GInputStream  *input_stream,
-         GAsyncResult  *res,
-         gpointer       user_data)
-{
-  RestoreDiskImageData *data = user_data;
-  GError *error;
-  gssize bytes_read;
+  /* We can't use udisks_block_get_size() because the media may have
+   * changed and udisks may not have noticed. TODO: maybe have a
+   * Block.GetSize() method instead...
+   */
+  if (ioctl (fd, BLKGETSIZE64, &block_device_size) != 0)
+    {
+      error = g_error_new (G_IO_ERROR, g_io_error_from_errno (errno),
+                           "%s", strerror (errno));
+      g_prefix_error (&error, _("Error determining size of device: "));
+      goto out;
+    }
 
-  error = NULL;
-  bytes_read = g_input_stream_read_finish (input_stream, res, &error);
-  if (error != NULL)
+  if (block_device_size == 0)
     {
-      gchar *s;
-      s = g_strdup_printf (_("Error reading from offset %" G_GUINT64_FORMAT " of file"),
-                           (guint64) data->total_bytes_read);
-      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
-        gdu_utils_show_error (GTK_WINDOW (data->dialog), s, error);
-      g_free (s);
-      g_error_free (error);
-      restore_disk_image_data_complete (data);
+      error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+                           _("Device is size 0"));
       goto out;
     }
+  data->block_size = block_device_size;
+
+  page_size = sysconf (_SC_PAGESIZE);
+  buffer_unaligned = g_new0 (guchar, buffer_size + page_size);
+  buffer = (guchar*) (((gintptr) (buffer_unaligned + page_size)) & (~(page_size - 1)));
+
+  g_mutex_lock (&data->copy_lock);
+  data->estimator = gdu_estimator_new (data->file_size);
+  data->update_id = 0;
+  data->start_time_usec = g_get_real_time ();
+  g_mutex_unlock (&data->copy_lock);
 
-  data->total_bytes_read += bytes_read;
+  /* Read huge (e.g. 1 MiB) blocks and write it to the output
+   * device even if it was only partially read.
+   */
+  num_bytes_completed = 0;
+  while (num_bytes_completed < data->file_size)
+    {
+      gsize num_bytes_to_read;
+      gsize num_bytes_read;
+      ssize_t num_bytes_written;
+      gint64 now_usec;
+
+      num_bytes_to_read = buffer_size;
+      if (num_bytes_to_read + num_bytes_completed > data->file_size)
+        num_bytes_to_read = data->file_size - num_bytes_completed;
+
+      /* Update GUI - but only every 200 ms and only if last update isn't pending */
+      g_mutex_lock (&data->copy_lock);
+      now_usec = g_get_monotonic_time ();
+      if (now_usec - last_update_usec > 200 * G_USEC_PER_SEC / 1000 || last_update_usec < 0)
+        {
+          if (num_bytes_completed > 0)
+            gdu_estimator_add_sample (data->estimator, num_bytes_completed);
+          if (data->update_id == 0)
+            data->update_id = g_idle_add (on_update_ui, dialog_data_ref (data));
+          last_update_usec = now_usec;
+        }
+      g_mutex_unlock (&data->copy_lock);
+
+      if (!g_input_stream_read_all (G_INPUT_STREAM (data->file_input_stream),
+                                    buffer,
+                                    num_bytes_to_read,
+                                    &num_bytes_read,
+                                    data->cancellable,
+                                    &error))
+        {
+          g_prefix_error (&error,
+                          "Error reading %" G_GUINT64_FORMAT " bytes from offset %" G_GUINT64_FORMAT ": ",
+                          num_bytes_to_read,
+                          num_bytes_completed);
+          goto out;
+        }
+      if (num_bytes_read != num_bytes_to_read)
+        {
+          g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Requested %" G_GUINT64_FORMAT " bytes from offset %" G_GUINT64_FORMAT " but only read %" G_GUINT64_FORMAT " bytes",
+                       num_bytes_read,
+                       num_bytes_completed,
+                       num_bytes_to_read);
+          goto out;
+        }
 
-  data->buffer_bytes_written = 0;
-  data->buffer_bytes_to_write = bytes_read;
-  write_more (data);
+    copy_write_again:
+      num_bytes_written = write (fd, buffer, num_bytes_read);
+      if (num_bytes_written < 0)
+        {
+          if (errno == EAGAIN || errno == EINTR)
+            goto copy_write_again;
+
+          g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Error writing %" G_GUINT64_FORMAT " bytes to offset %" G_GUINT64_FORMAT ": %m",
+                       num_bytes_read,
+                       num_bytes_completed);
+          goto out;
+        }
+
+      /*g_print ("copied %" G_GUINT64_FORMAT " bytes at offset %" G_GUINT64_FORMAT "\n",
+               (guint64) num_bytes_written,
+               num_bytes_completed);*/
+
+      num_bytes_completed += num_bytes_written;
+    }
 
  out:
-  restore_disk_image_data_unref (data);
-}
+  data->end_time_usec = g_get_real_time ();
 
-static void
-copy_more (RestoreDiskImageData *data)
-{
-  guint64 bytes_to_read;
+  /* in either case, close the stream */
+  if (!g_input_stream_close (G_INPUT_STREAM (data->file_input_stream),
+                              NULL, /* cancellable */
+                              &error2))
+    {
+      g_warning ("Error closing file input stream: %s (%s, %d)",
+                 error2->message, g_quark_to_string (error2->domain), error2->code);
+      g_clear_error (&error2);
+    }
+  g_clear_object (&data->file_input_stream);
 
-  bytes_to_read = data->file_size - data->total_bytes_read;
-  if (bytes_to_read == 0)
+  if (fd != -1 )
     {
-      restore_disk_image_data_complete (data);
-      goto out;
+      if (close (fd) != 0)
+        g_warning ("Error closing fd: %m");
     }
-  if (bytes_to_read > BUFFER_SIZE)
-    bytes_to_read = BUFFER_SIZE;
-
-  g_input_stream_read_async (G_INPUT_STREAM (data->input_file_stream),
-                             data->buffer,
-                             bytes_to_read,
-                             G_PRIORITY_DEFAULT,
-                             data->cancellable,
-                             (GAsyncReadyCallback) read_cb,
-                             restore_disk_image_data_ref (data));
- out:
-  ;
+
+  if (error != NULL)
+    {
+      /* show error in GUI */
+      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
+        {
+          data->copy_error = error; error = NULL;
+          g_idle_add (on_show_error, dialog_data_ref (data));
+        }
+      g_clear_error (&error);
+
+      /* Wipe the device */
+      if (!udisks_block_call_format_sync (data->block,
+                                          "empty",
+                                          g_variant_new ("a{sv}", NULL), /* options */
+                                          NULL, /* cancellable */
+                                          &error2))
+        {
+          g_warning ("Error wiping device on error path: %s (%s, %d)",
+                     error2->message, g_quark_to_string (error2->domain), error2->code);
+          g_clear_error (&error2);
+        }
+    }
+  else
+    {
+      /* success */
+      g_idle_add (on_success, dialog_data_ref (data));
+    }
+
+  g_free (buffer_unaligned);
+
+  /* finally, request that the core OS / kernel rescans the device */
+  if (!udisks_block_call_rescan_sync (data->block,
+                                      g_variant_new ("a{sv}", NULL), /* options */
+                                      NULL, /* cancellable */
+                                      &error2))
+    {
+      g_warning ("Error rescanning device: %s (%s, %d)",
+                 error2->message, g_quark_to_string (error2->domain), error2->code);
+      g_clear_error (&error2);
+    }
+
+  dialog_data_unref_in_idle (data); /* unref on main thread */
+  return NULL;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gboolean
-start_copying (RestoreDiskImageData *data)
+start_copying (DialogData *data)
 {
   GFile *file = NULL;
   gboolean ret = FALSE;
   GFileInfo *info;
   GError *error;
+  gchar *uri = NULL;
 
   error = NULL;
-  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->source_file_fcbutton));
-  data->input_file_stream = g_file_read (file,
+  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->image_fcbutton));
+  data->file_input_stream = g_file_read (file,
                                          NULL,
                                          &error);
-  if (data->input_file_stream == NULL)
+  if (data->file_input_stream == NULL)
     {
       if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
         gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error opening file for reading"), error);
       g_error_free (error);
-      restore_disk_image_data_complete (data);
+      dialog_data_dismiss_and_unref (data);
       goto out;
     }
 
@@ -456,22 +739,23 @@ start_copying (RestoreDiskImageData *data)
     {
       gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error determing size of file"), error);
       g_error_free (error);
-      restore_disk_image_data_complete (data);
+      dialog_data_dismiss_and_unref (data);
       goto out;
     }
   data->file_size = g_file_info_get_size (info);
   g_object_unref (info);
 
-  /* Alright, time to start copying! */
-  data->cancellable = g_cancellable_new ();
-  data->buffer = g_new0 (guchar, BUFFER_SIZE);
-  data->estimator = gdu_estimator_new (data->file_size);
-  copy_more (data);
+  uri = gdu_utils_get_pretty_uri (file);
+  gtk_label_set_text (GTK_LABEL (data->source_label), uri);
 
+  g_thread_new ("copy-disk-image-thread",
+                copy_thread_func,
+                dialog_data_ref (data));
   ret = TRUE;
 
  out:
   g_clear_object (&file);
+  g_free (uri);
   return ret;
 }
 
@@ -480,63 +764,94 @@ on_dialog_response (GtkDialog     *dialog,
                     gint           response,
                     gpointer       user_data)
 {
-  RestoreDiskImageData *data = user_data;
-  if (response == GTK_RESPONSE_OK)
+  DialogData *data = user_data;
+
+  if (data->dialog == NULL)
+    goto out;
+
+  g_print ("response = %d\n", response);
+  switch (response)
     {
+    case GTK_RESPONSE_OK:
       if (!gdu_utils_show_confirmation (GTK_WINDOW (data->dialog),
                                         _("Are you sure you want to write the disk image to the device?"),
                                         _("All existing data will be lost"),
                                         _("_Restore"),
                                         NULL, NULL))
         {
-          restore_disk_image_data_complete (data);
+          dialog_data_dismiss_and_unref (data);
           goto out;
         }
 
       /* now that we know the user picked a folder, update file chooser settings */
-      gdu_utils_file_chooser_for_disk_images_update_settings (GTK_FILE_CHOOSER (data->source_file_fcbutton));
+      gdu_utils_file_chooser_for_disk_images_update_settings (GTK_FILE_CHOOSER (data->image_fcbutton));
 
-      gtk_label_set_markup (GTK_LABEL (data->copying_label), _("Copying data to device..."));
-
-      /* Advance to the progress page and hide infobars, if any */
-      gtk_notebook_set_current_page (GTK_NOTEBOOK (data->notebook), 1);
+      /* now that we advance to the "copy stage", hide/show some more widgets */
+      gtk_widget_hide (data->image_key_label);
+      gtk_widget_hide (data->image_fcbutton);
       gtk_widget_hide (data->start_copying_button);
+      gtk_widget_show (data->source_key_label);
+      gtk_widget_show (data->source_label);
+      gtk_widget_show (data->copying_key_label);
+      gtk_widget_show (data->copying_vbox);
       gtk_widget_hide (data->infobar_vbox);
 
       start_copying (data);
-    }
-  else
-    {
-      restore_disk_image_data_complete (data);
+      break;
+
+    case GTK_RESPONSE_CLOSE:
+      dialog_data_dismiss_and_unref (data);
+      break;
+
+    default: /* explicit fallthrough */
+    case GTK_RESPONSE_CANCEL:
+      dialog_data_complete_and_unref (data);
+      break;
     }
  out:
   ;
 }
 
-static void
-gdu_restore_disk_image_dialog_show2 (RestoreDiskImageData *data)
+void
+gdu_restore_disk_image_dialog_show (GduWindow    *window,
+                                    UDisksObject *object)
 {
-  gchar *s;
+  guint n;
+  DialogData *data;
+
+  data = g_new0 (DialogData, 1);
+  data->ref_count = 1;
+  g_mutex_init (&data->copy_lock);
+  data->window = g_object_ref (window);
+  data->object = g_object_ref (object);
+  data->block = udisks_object_get_block (object);
+  g_assert (data->block != NULL);
+  data->drive = udisks_client_get_drive_for_block (gdu_window_get_client (window), data->block);
+  data->cancellable = g_cancellable_new ();
+
+  /* TODO: use a method call for this so it works on e.g. floppy drives where e.g. we don't know the size */
+  data->block_size = udisks_block_get_size (data->block);
 
   data->dialog = GTK_WIDGET (gdu_application_new_widget (gdu_window_get_application (data->window),
                                                          "restore-disk-image-dialog.ui",
                                                          "restore-disk-image-dialog",
                                                          &data->builder));
-  data->notebook = GTK_WIDGET (gtk_builder_get_object (data->builder, "notebook"));
-  data->start_copying_button = GTK_WIDGET (gtk_builder_get_object (data->builder, "start_copying_button"));
-  data->source_file_fcbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, "source_file_fcbutton"));
-  g_signal_connect (data->source_file_fcbutton, "file-set",
-                    G_CALLBACK (on_file_set), data);
-  data->infobar_vbox = GTK_WIDGET (gtk_builder_get_object (data->builder, "infobar-vbox"));
+  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->image_fcbutton, "file-set", G_CALLBACK (on_file_set), data);
+
   data->warning_infobar = gdu_utils_create_info_bar (GTK_MESSAGE_INFO, "", &data->warning_label);
   gtk_box_pack_start (GTK_BOX (data->infobar_vbox), data->warning_infobar, TRUE, TRUE, 0);
   gtk_widget_set_no_show_all (data->warning_infobar, TRUE);
+  g_object_ref (data->warning_infobar);
+
   data->error_infobar = gdu_utils_create_info_bar (GTK_MESSAGE_ERROR, "", &data->error_label);
   gtk_box_pack_start (GTK_BOX (data->infobar_vbox), data->error_infobar, TRUE, TRUE, 0);
   gtk_widget_set_no_show_all (data->error_infobar, TRUE);
-  data->copying_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_label"));
-  data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
-  data->copying_progress_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progress_label"));
+  g_object_ref (data->error_infobar);
 
   restore_disk_image_populate (data);
   restore_disk_image_update (data);
@@ -544,96 +859,22 @@ gdu_restore_disk_image_dialog_show2 (RestoreDiskImageData *data)
   /* unfortunately, GtkFileChooserButton:file-set is not emitted when the user
    * unselects a file but we can work around that.. (TODO: file bug against gtk+)
    */
-  g_signal_connect (data->source_file_fcbutton, "notify",
+  g_signal_connect (data->image_fcbutton, "notify",
                     G_CALLBACK (on_notify), data);
 
-
-  /* Translators: This is the window title for the non-modal "Restore Disk Image" dialog. The %s is the device. */
-  s = g_strdup_printf (_("Restore Disk Image (%s)"),
-                       udisks_block_get_preferred_device (data->block));
-  gtk_window_set_title (GTK_WINDOW (data->dialog), s);
-  g_free (s);
-
-  data->response_signal_handler_id = g_signal_connect (data->dialog,
-                                                       "response",
-                                                       G_CALLBACK (on_dialog_response),
-                                                       data);
-  gtk_widget_show_all (data->dialog);
+  /* hide widgets not to be shown initially */
+  gtk_widget_hide (data->source_key_label);
+  gtk_widget_hide (data->source_label);
+  gtk_widget_hide (data->copying_key_label);
+  gtk_widget_hide (data->copying_vbox);
+  gtk_widget_hide (data->close_button);
+
+  g_signal_connect (data->dialog,
+                    "response",
+                    G_CALLBACK (on_dialog_response),
+                    data);
+  gtk_widget_show (data->dialog);
   gtk_window_present (GTK_WINDOW (data->dialog));
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
-
-static void
-open_cb (UDisksBlock  *block,
-         GAsyncResult *res,
-         gpointer      user_data)
-{
-  RestoreDiskImageData *data = user_data;
-  GError *error;
-  GUnixFDList *fd_list = NULL;
-  GVariant *fd_index = NULL;
-  int fd;
-
-  error = NULL;
-  if (!udisks_block_call_open_for_restore_finish (block,
-                                                  &fd_index,
-                                                  &fd_list,
-                                                  res,
-                                                  &error))
-    {
-      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
-        gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error opening device"), error);
-      g_error_free (error);
-      restore_disk_image_data_complete (data);
-      goto out;
-    }
-
-  fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_index), NULL);
-
-  /* We can't use udisks_block_get_size() because the media may have
-   * changed and udisks may not have noticed. TODO: maybe have a
-   * Block.GetSize() method instead...
-   */
-  if (ioctl (fd, BLKGETSIZE64, &data->block_size) != 0)
-    {
-      error = g_error_new (G_IO_ERROR, g_io_error_from_errno (errno), "%s", strerror (errno));
-      gdu_utils_show_error (GTK_WINDOW (data->dialog), _("Error determining size of device"), error);
-      g_error_free (error);
-      restore_disk_image_data_complete (data);
-      goto out;
-    }
-  data->block_stream = g_unix_output_stream_new (fd, TRUE);
-
-  /* OK, we can now show the dialog */
-  gdu_restore_disk_image_dialog_show2 (data);
-
- out:
-  if (fd_index != NULL)
-    g_variant_unref (fd_index);
-  g_clear_object (&fd_list);
-}
-
-
-void
-gdu_restore_disk_image_dialog_show (GduWindow    *window,
-                                   UDisksObject *object)
-{
-  RestoreDiskImageData *data;
-
-  data = g_new0 (RestoreDiskImageData, 1);
-  data->ref_count = 1;
-  data->window = g_object_ref (window);
-  data->object = g_object_ref (object);
-  data->block = udisks_object_get_block (object);
-  g_assert (data->block != NULL);
-  data->drive = udisks_client_get_drive_for_block (gdu_window_get_client (window), data->block);
-
-  /* first, open the device */
-  udisks_block_call_open_for_restore (data->block,
-                                      g_variant_new ("a{sv}", NULL), /* options */
-                                      NULL, /* fd_list */
-                                      NULL, /* cancellable */
-                                      (GAsyncReadyCallback) open_cb,
-                                      data);
-}
diff --git a/src/libgdu/gduutils.c b/src/libgdu/gduutils.c
index 22d7673..dd574b8 100644
--- a/src/libgdu/gduutils.c
+++ b/src/libgdu/gduutils.c
@@ -800,3 +800,37 @@ gdu_util_is_same_size (GList   *blocks,
     *out_min_size = min_size;
   return ret;
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gchar *
+gdu_utils_get_pretty_uri (GFile *file)
+{
+  gchar *ret;
+  gchar *s;
+
+  if (g_file_is_native (file))
+    {
+      const gchar *homedir;
+
+      ret = g_file_get_path (file);
+
+      homedir = g_get_home_dir ();
+      if (g_str_has_prefix (ret, homedir))
+        {
+          s = ret;
+          ret = g_strdup_printf ("~/%s", ret + strlen (homedir) + 1);
+          g_free (s);
+        }
+    }
+  else
+    {
+      s = g_file_get_uri (file);
+      ret = g_uri_unescape_string (s, NULL);
+      g_free (s);
+    }
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/libgdu/gduutils.h b/src/libgdu/gduutils.h
index 6fc6d75..7d4ef1c 100644
--- a/src/libgdu/gduutils.h
+++ b/src/libgdu/gduutils.h
@@ -66,6 +66,9 @@ gchar *gdu_utils_format_mdraid_level (const gchar *level,
 gboolean gdu_util_is_same_size (GList   *blocks,
                                 guint64 *out_min_size);
 
+gchar *gdu_utils_get_pretty_uri (GFile *file);
+
+
 G_END_DECLS
 
 #endif /* __GDU_UTILS_H__ */



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