[gnome-disk-utility] Disks: Add new --restore-disk-image option



commit 55ceb75042cb22a041847278aa3aaec9d044b729
Author: David Zeuthen <zeuthen gmail com>
Date:   Tue May 21 18:33:59 2013 -0700

    Disks: Add new --restore-disk-image option
    
    Signed-off-by: David Zeuthen <zeuthen gmail com>

 data/ui/restore-disk-image-dialog.ui  |  110 ++++++++++++++-
 doc/man/gnome-disks.xml               |   15 ++
 src/disks/gduapplication.c            |    8 +
 src/disks/gdudevicetreemodel.c        |  101 ++++++++++++--
 src/disks/gdudevicetreemodel.h        |    4 +-
 src/disks/gduenums.h                  |   10 ++
 src/disks/gdurestorediskimagedialog.c |  235 +++++++++++++++++++++++++++++----
 src/disks/gdurestorediskimagedialog.h |    3 +-
 src/disks/gduwindow.c                 |    8 +-
 9 files changed, 446 insertions(+), 48 deletions(-)
---
diff --git a/data/ui/restore-disk-image-dialog.ui b/data/ui/restore-disk-image-dialog.ui
index 6ba5e95..8657f93 100644
--- a/data/ui/restore-disk-image-dialog.ui
+++ b/data/ui/restore-disk-image-dialog.ui
@@ -36,26 +36,26 @@
             <property name="row_spacing">12</property>
             <property name="column_spacing">12</property>
             <child>
-              <object class="GtkLabel" id="image-key-label">
+              <object class="GtkLabel" id="selectable-image-label">
                 <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">image-fcbutton</property>
+                <property name="mnemonic_widget">selectable-image-fcbutton</property>
                 <style>
                   <class name="dim-label"/>
                 </style>
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">0</property>
+                <property name="top_attach">1</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
             </child>
             <child>
-              <object class="GtkFileChooserButton" id="image-fcbutton">
+              <object class="GtkFileChooserButton" id="selectable-image-fcbutton">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="orientation">vertical</property>
@@ -64,7 +64,7 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">0</property>
+                <property name="top_attach">1</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
@@ -81,7 +81,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">1</property>
+                <property name="top_attach">3</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
@@ -97,7 +97,103 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">1</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="selectable-destination-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">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="selectable-destination-combobox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="image-key-label">
+                <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>
+                <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="image-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">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="image-size-key-label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Image Size</property>
+                <property name="use_underline">True</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="image-size-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>
diff --git a/doc/man/gnome-disks.xml b/doc/man/gnome-disks.xml
index 35f183d..ceda33c 100644
--- a/doc/man/gnome-disks.xml
+++ b/doc/man/gnome-disks.xml
@@ -70,6 +70,21 @@
       </varlistentry>
 
       <varlistentry>
+        <term>
+          <option>--restore-disk-image <replaceable>FILE</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+            Shows the “Restore Disk Image” dialog for the file given
+            by <replaceable>FILE</replaceable> (for example,
+            <filename>/home/user/Downloads/SuperOS.iso</filename>) and
+            prompts the user to choose a disk to restore the image
+            unto.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>--help</option></term>
         <listitem>
           <para>
diff --git a/src/disks/gduapplication.c b/src/disks/gduapplication.c
index 1334d28..612e969 100644
--- a/src/disks/gduapplication.c
+++ b/src/disks/gduapplication.c
@@ -18,6 +18,7 @@
 
 #include "gduapplication.h"
 #include "gduformatvolumedialog.h"
+#include "gdurestorediskimagedialog.h"
 #include "gduwindow.h"
 #include "gdulocaljob.h"
 
@@ -172,12 +173,14 @@ gdu_application_command_line (GApplication            *_app,
   gchar *opt_block_device = NULL, *error_message = NULL;
   gboolean opt_help = FALSE;
   gboolean opt_format = FALSE;
+  gchar *opt_restore_disk_image = NULL;
   gint opt_xid = -1;
   GOptionEntry opt_entries[] =
   {
     {"block-device", 0, 0, G_OPTION_ARG_STRING, &opt_block_device, N_("Select device"), NULL },
     {"format-device", 0, 0, G_OPTION_ARG_NONE, &opt_format, N_("Format selected device"), NULL },
     {"xid", 0, 0, G_OPTION_ARG_INT, &opt_xid, N_("Parent window XID for the format dialog"), NULL },
+    {"restore-disk-image", 0, 0, G_OPTION_ARG_FILENAME, &opt_restore_disk_image, N_("Restore disk image"), 
NULL },
     {"help", '?', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_help, N_("Show help options"), NULL },
     {NULL}
   };
@@ -254,6 +257,10 @@ gdu_application_command_line (GApplication            *_app,
       gdu_format_volume_dialog_show_for_xid (app->client, opt_xid, object_to_select);
     }
 
+  if (opt_restore_disk_image != NULL)
+    {
+      gdu_restore_disk_image_dialog_show (app->window, NULL, opt_restore_disk_image);
+    }
 
   ret = 0;
 
@@ -261,6 +268,7 @@ gdu_application_command_line (GApplication            *_app,
   g_option_context_free (context);
   g_clear_object (&object_to_select);
   g_free (opt_block_device);
+  g_free (opt_restore_disk_image);
   g_strfreev (argv);
   return ret;
 }
diff --git a/src/disks/gdudevicetreemodel.c b/src/disks/gdudevicetreemodel.c
index 21e623a..a3e51b5 100644
--- a/src/disks/gdudevicetreemodel.c
+++ b/src/disks/gdudevicetreemodel.c
@@ -24,6 +24,8 @@ struct _GduDeviceTreeModel
   GduApplication *application;
   UDisksClient *client;
 
+  GduDeviceTreeModelFlags flags;
+
   GList *current_drives;
   GtkTreeIter drive_iter;
   gboolean drive_iter_valid;
@@ -52,7 +54,8 @@ typedef struct
 enum
 {
   PROP_0,
-  PROP_APPLICATION
+  PROP_APPLICATION,
+  PROP_FLAGS
 };
 
 G_DEFINE_TYPE (GduDeviceTreeModel, gdu_device_tree_model, GTK_TYPE_TREE_STORE);
@@ -80,7 +83,8 @@ gdu_device_tree_model_finalize (GObject *object)
 {
   GduDeviceTreeModel *model = GDU_DEVICE_TREE_MODEL (object);
 
-  g_source_remove (model->pefs_timeout_id);
+  if (model->pefs_timeout_id != 0)
+    g_source_remove (model->pefs_timeout_id);
 
   if (model->spinner_timeout != 0)
     g_source_remove (model->spinner_timeout);
@@ -95,7 +99,7 @@ gdu_device_tree_model_finalize (GObject *object)
   g_list_foreach (model->current_mdraids, (GFunc) g_object_unref, NULL);
   g_list_free (model->current_mdraids);
 
-  g_object_unref (model->client);
+  g_object_unref (model->application);
 
   G_OBJECT_CLASS (gdu_device_tree_model_parent_class)->finalize (object);
 }
@@ -119,6 +123,10 @@ gdu_device_tree_model_get_property (GObject    *object,
       g_value_set_object (value, gdu_device_tree_model_get_application (model));
       break;
 
+    case PROP_FLAGS:
+      g_value_set_flags (value, gdu_device_tree_model_get_flags (model));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -140,6 +148,10 @@ gdu_device_tree_model_set_property (GObject      *object,
       model->client = gdu_application_get_client (model->application);
       break;
 
+    case PROP_FLAGS:
+      model->flags = g_value_get_flags (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -480,8 +492,22 @@ gdu_device_tree_model_constructed (GObject *object)
                     model);
   coldplug (model);
 
-  model->pefs_timeout_id = g_timeout_add_seconds (5, on_pefs_timeout, model);
-  on_pefs_timeout (model);
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_POWER_STATE)
+    {
+      model->pefs_timeout_id = g_timeout_add_seconds (5, on_pefs_timeout, model);
+      on_pefs_timeout (model);
+    }
+
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_NONE_ITEM)
+    {
+      gtk_tree_store_insert_with_values (GTK_TREE_STORE (model),
+                                         &model->drive_iter,
+                                         NULL, /* GtkTreeIter *parent */
+                                         0,
+                                         GDU_DEVICE_TREE_MODEL_COLUMN_NAME, _("(None)"),
+                                         GDU_DEVICE_TREE_MODEL_COLUMN_SORT_KEY, "00_0select_device",
+                                         -1);
+    }
 
   if (G_OBJECT_CLASS (gdu_device_tree_model_parent_class)->constructed != NULL)
     G_OBJECT_CLASS (gdu_device_tree_model_parent_class)->constructed (object);
@@ -511,11 +537,27 @@ gdu_device_tree_model_class_init (GduDeviceTreeModelClass *klass)
                                                         G_PARAM_WRITABLE |
                                                         G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GduDeviceTreeModel:flags:
+   *
+   * The #GduApplication used by the #GduDeviceTreeModel instance.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_FLAGS,
+                                   g_param_spec_flags ("flags", NULL, NULL,
+                                                       GDU_TYPE_DEVICE_TREE_MODEL_FLAGS,
+                                                       GDU_DEVICE_TREE_MODEL_FLAGS_NONE,
+                                                       G_PARAM_READABLE |
+                                                       G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS));
 }
 
 /**
  * gdu_device_tree_model_new:
  * @application: A #GduApplication.
+ * @flags: Flags from #GduDeviceTreeModelFlags.
  *
  * Creates a new #GduDeviceTreeModel for viewing the devices belonging to
  * @application.
@@ -523,10 +565,12 @@ gdu_device_tree_model_class_init (GduDeviceTreeModelClass *klass)
  * Returns: A #GduDeviceTreeModel. Free with g_object_unref().
  */
 GduDeviceTreeModel *
-gdu_device_tree_model_new (GduApplication *application)
+gdu_device_tree_model_new (GduApplication          *application,
+                           GduDeviceTreeModelFlags  flags)
 {
   return GDU_DEVICE_TREE_MODEL (g_object_new (GDU_TYPE_DEVICE_TREE_MODEL,
                                               "application", application,
+                                              "flags", flags,
                                               NULL));
 }
 
@@ -546,6 +590,21 @@ gdu_device_tree_model_get_application (GduDeviceTreeModel *model)
   return model->application;
 }
 
+/**
+ * gdu_device_tree_model_get_flags:
+ * @model: A #GduDeviceTreeModel.
+ *
+ * Gets the #GduDeviceTreeModelFlags used by @model.
+ *
+ * Returns: The flags that @model was constructed with.
+ */
+GduDeviceTreeModelFlags
+gdu_device_tree_model_get_flags (GduDeviceTreeModel *model)
+{
+  g_return_val_if_fail (GDU_IS_DEVICE_TREE_MODEL (model), GDU_DEVICE_TREE_MODEL_FLAGS_NONE);
+  return model->flags;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static GtkTreeIter *
@@ -553,6 +612,9 @@ get_drive_header_iter (GduDeviceTreeModel *model)
 {
   gchar *s;
 
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_FLAT)
+    return NULL;
+
   if (model->drive_iter_valid)
     goto out;
 
@@ -626,6 +688,9 @@ get_mdraid_header_iter (GduDeviceTreeModel *model)
 {
   gchar *s;
 
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_FLAT)
+    return NULL;
+
   if (model->mdraid_iter_valid)
     goto out;
 
@@ -897,6 +962,7 @@ update_drive (GduDeviceTreeModel *model,
   UDisksObjectInfo *info = NULL;
   UDisksBlock *block = NULL;
   gchar *s = NULL;
+  gchar *included_device_name = NULL;
   gboolean warning = FALSE;
   gboolean jobs_running = FALSE;
   GtkTreeIter iter;
@@ -923,6 +989,9 @@ update_drive (GduDeviceTreeModel *model,
       g_free (s);
     }
 
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_DEVICE_NAME)
+    included_device_name = g_strdup_printf (" (%s)", udisks_block_get_preferred_device (block));
+
   info = udisks_client_get_object_info (model->client, object);
   if (warning)
     {
@@ -930,16 +999,18 @@ update_drive (GduDeviceTreeModel *model,
        * of hard-coding the color
        */
       s = g_strdup_printf ("<span foreground=\"#ff0000\">%s</span>\n"
-                           "<small><span foreground=\"#ff0000\">%s</span></small>",
+                           "<small><span foreground=\"#ff0000\">%s%s</span></small>",
                            udisks_object_info_get_description (info),
-                           udisks_object_info_get_name (info));
+                           udisks_object_info_get_name (info),
+                           included_device_name != NULL ? included_device_name : "");
     }
   else
     {
       s = g_strdup_printf ("%s\n"
-                           "<small>%s</small>",
+                           "<small>%s%s</small>",
                            udisks_object_info_get_description (info),
-                           udisks_object_info_get_name (info));
+                           udisks_object_info_get_name (info),
+                           included_device_name != NULL ? included_device_name : "");
     }
 
   jobs_running = drive_has_jobs (model, drive);
@@ -966,7 +1037,7 @@ update_drive (GduDeviceTreeModel *model,
                       -1);
 
   /* update spinner, if jobs are running */
-  if (jobs_running)
+  if (jobs_running && (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_PULSE))
     {
       if (model->spinner_timeout == 0)
         {
@@ -978,6 +1049,7 @@ update_drive (GduDeviceTreeModel *model,
   g_clear_object (&block);
   g_clear_object (&info);
   g_free (s);
+  g_free (included_device_name);
   return jobs_running;
 }
 
@@ -1170,7 +1242,7 @@ update_mdraid (GduDeviceTreeModel *model,
                       -1);
 
   /* update spinner, if jobs are running */
-  if (jobs_running)
+  if (jobs_running && (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_PULSE))
     {
       if (model->spinner_timeout == 0)
         {
@@ -1261,6 +1333,9 @@ get_block_header_iter (GduDeviceTreeModel *model)
 {
   gchar *s;
 
+  if (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_FLAT)
+    return NULL;
+
   if (model->block_iter_valid)
     goto out;
 
@@ -1402,7 +1477,7 @@ update_block (GduDeviceTreeModel  *model,
                       -1);
 
   /* update spinner, if jobs are running */
-  if (jobs_running)
+  if (jobs_running && (model->flags & GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_PULSE))
     {
       if (model->spinner_timeout == 0)
         {
diff --git a/src/disks/gdudevicetreemodel.h b/src/disks/gdudevicetreemodel.h
index 021b567..079ae98 100644
--- a/src/disks/gdudevicetreemodel.h
+++ b/src/disks/gdudevicetreemodel.h
@@ -38,8 +38,10 @@ enum
 };
 
 GType               gdu_device_tree_model_get_type            (void) G_GNUC_CONST;
-GduDeviceTreeModel *gdu_device_tree_model_new                 (GduApplication     *application);
+GduDeviceTreeModel *gdu_device_tree_model_new                 (GduApplication          *application,
+                                                               GduDeviceTreeModelFlags  flags);
 GduApplication     *gdu_device_tree_model_get_application     (GduDeviceTreeModel *model);
+GduDeviceTreeModelFlags gdu_device_tree_model_get_flags       (GduDeviceTreeModel *model);
 gboolean            gdu_device_tree_model_get_iter_for_object (GduDeviceTreeModel *model,
                                                                UDisksObject       *object,
                                                                GtkTreeIter        *iter);
diff --git a/src/disks/gduenums.h b/src/disks/gduenums.h
index 1dcbeda..b520be8 100644
--- a/src/disks/gduenums.h
+++ b/src/disks/gduenums.h
@@ -30,6 +30,16 @@ typedef enum
   GDU_POWER_STATE_FLAGS_FAILED            = (1<<2)
 } GduPowerStateFlags;
 
+typedef enum
+{
+  GDU_DEVICE_TREE_MODEL_FLAGS_NONE                = 0,
+  GDU_DEVICE_TREE_MODEL_FLAGS_FLAT                = (1<<0),
+  GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_POWER_STATE  = (1<<1),
+  GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_PULSE        = (1<<2),
+  GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_DEVICE_NAME = (1<<3),
+  GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_NONE_ITEM   = (1<<4),
+} GduDeviceTreeModelFlags;
+
 G_END_DECLS
 
 #endif /* __GDU_ENUMS_H__ */
diff --git a/src/disks/gdurestorediskimagedialog.c b/src/disks/gdurestorediskimagedialog.c
index abfc4f5..181cbf0 100644
--- a/src/disks/gdurestorediskimagedialog.c
+++ b/src/disks/gdurestorediskimagedialog.c
@@ -25,6 +25,7 @@
 #include "gduvolumegrid.h"
 #include "gduestimator.h"
 #include "gdulocaljob.h"
+#include "gdudevicetreemodel.h"
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -34,6 +35,9 @@ typedef struct
 
   GduWindow *window;
   UDisksObject *object;
+  gchar *disk_image_filename;
+  gboolean switch_to_object;
+
   UDisksBlock *block;
   UDisksDrive *drive;
 
@@ -47,10 +51,17 @@ typedef struct
   GtkWidget *error_label;
 
   GtkWidget *image_key_label;
-  GtkWidget *image_fcbutton;
+  GtkWidget *image_label;
+  GtkWidget *selectable_image_label;
+  GtkWidget *selectable_image_fcbutton;
+
+  GtkWidget *image_size_key_label;
+  GtkWidget *image_size_label;
 
   GtkWidget *destination_key_label;
   GtkWidget *destination_label;
+  GtkWidget *selectable_destination_label;
+  GtkWidget *selectable_destination_combobox;
 
   GtkWidget *start_copying_button;
   GtkWidget *cancel_button;
@@ -91,9 +102,17 @@ static const struct {
   {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, image_label), "image-label"},
+  {G_STRUCT_OFFSET (DialogData, selectable_image_label), "selectable-image-label"},
+  {G_STRUCT_OFFSET (DialogData, selectable_image_fcbutton), "selectable-image-fcbutton"},
+
+  {G_STRUCT_OFFSET (DialogData, image_size_key_label), "image-size-key-label"},
+  {G_STRUCT_OFFSET (DialogData, image_size_label), "image-size-label"},
+
   {G_STRUCT_OFFSET (DialogData, destination_key_label), "destination-key-label"},
   {G_STRUCT_OFFSET (DialogData, destination_label), "destination-label"},
+  {G_STRUCT_OFFSET (DialogData, selectable_destination_label), "selectable-destination-label"},
+  {G_STRUCT_OFFSET (DialogData, selectable_destination_combobox), "selectable-destination-combobox"},
 
   {G_STRUCT_OFFSET (DialogData, start_copying_button), "start-copying-button"},
   {G_STRUCT_OFFSET (DialogData, cancel_button), "cancel-button"},
@@ -158,9 +177,10 @@ dialog_data_unref (DialogData *data)
       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);
+      g_clear_object (&data->object);
+      g_clear_object (&data->block);
       g_clear_object (&data->drive);
+      g_free (data->disk_image_filename);
       if (data->builder != NULL)
         g_object_unref (data->builder);
       g_free (data->buffer);
@@ -213,6 +233,7 @@ restore_disk_image_update (DialogData *data)
   gboolean can_proceed = FALSE;
   gchar *restore_warning = NULL;
   gchar *restore_error = NULL;
+  gchar *image_size_str = NULL;
   GFile *restore_file = NULL;
 
   if (data->dialog == NULL)
@@ -223,7 +244,11 @@ restore_disk_image_update (DialogData *data)
     goto out;
 
   /* Check if we have a file */
-  restore_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->image_fcbutton));
+  if (data->disk_image_filename != NULL)
+    restore_file = g_file_new_for_commandline_arg (data->disk_image_filename);
+  else
+    restore_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->selectable_image_fcbutton));
+
   if (restore_file != NULL)
     {
       GFileInfo *info;
@@ -237,6 +262,8 @@ restore_disk_image_update (DialogData *data)
       size = g_file_info_get_size (info);
       g_object_unref (info);
 
+      image_size_str = udisks_client_get_size_for_display (gdu_window_get_client (data->window), size, 
FALSE, TRUE);
+
       if (data->block_size > 0)
         {
           if (size == 0)
@@ -289,9 +316,12 @@ restore_disk_image_update (DialogData *data)
       gtk_widget_hide (data->error_infobar);
     }
 
+  gtk_label_set_text (GTK_LABEL (data->image_size_label), image_size_str != NULL ? image_size_str : "—");
+
   g_free (restore_warning);
   g_free (restore_error);
   g_clear_object (&restore_file);
+  g_free (image_size_str);
 
   gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, can_proceed);
 
@@ -327,16 +357,167 @@ on_notify (GObject    *object,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+destination_combobox_sensitive_cb (GtkCellLayout   *cell_layout,
+                                   GtkCellRenderer *renderer,
+                                   GtkTreeModel    *model,
+                                   GtkTreeIter     *iter,
+                                   gpointer         user_data)
+{
+  /* DialogData *data = user_data; */
+  gboolean sensitive = FALSE;
+  UDisksBlock *block = NULL;
+
+  gtk_tree_model_get (model, iter,
+                      GDU_DEVICE_TREE_MODEL_COLUMN_BLOCK, &block,
+                      -1);
+
+  if (block == NULL)
+    sensitive = TRUE;
+
+  if (block != NULL &&
+      udisks_block_get_size (block) > 0 &&
+      !udisks_block_get_read_only (block))
+    sensitive = TRUE;
+
+  gtk_cell_renderer_set_sensitive (renderer, sensitive);
+
+  g_clear_object (&block);
+}
+
+static void
+set_destination_object (DialogData *data,
+                        UDisksObject *object)
+{
+  if (data->object != object)
+    {
+      g_clear_object (&data->object);
+      g_clear_object (&data->block);
+      g_clear_object (&data->drive);
+      data->block_size = 0;
+      if (object != NULL)
+        {
+          data->object = g_object_ref (object);
+          data->block = udisks_object_get_block (data->object);
+          g_assert (data->block != NULL);
+          data->drive = udisks_client_get_drive_for_block (gdu_window_get_client (data->window), 
data->block);
+          /* 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);
+        }
+    }
+}
+
+static void
+on_destination_combobox_notify_active (GObject    *gobject,
+                                       GParamSpec *pspec,
+                                       gpointer    user_data)
+{
+  DialogData *data = user_data;
+  UDisksObject *object = NULL;
+  GtkTreeIter iter;
+  GtkComboBox *combobox;
+
+  combobox = GTK_COMBO_BOX (data->selectable_destination_combobox);
+  if (gtk_combo_box_get_active_iter (combobox, &iter))
+    {
+      UDisksBlock *block = NULL;
+      gtk_tree_model_get (gtk_combo_box_get_model (combobox),
+                          &iter,
+                          GDU_DEVICE_TREE_MODEL_COLUMN_BLOCK, &block,
+                          -1);
+      if (block != NULL)
+        object = (UDisksObject *) g_dbus_interface_dup_object (G_DBUS_INTERFACE (block));
+      g_clear_object (&block);
+    }
+  set_destination_object (data, object);
+  restore_disk_image_update (data);
+  g_clear_object (&object);
+}
+
+static void
+populate_destination_combobox (DialogData *data)
+{
+  GduDeviceTreeModel *model;
+  GtkComboBox *combobox;
+  GtkCellRenderer *renderer;
+
+  combobox = GTK_COMBO_BOX (data->selectable_destination_combobox);
+  model = gdu_device_tree_model_new (gdu_window_get_application (data->window),
+                                     GDU_DEVICE_TREE_MODEL_FLAGS_FLAT |
+                                     GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_DEVICE_NAME |
+                                     GDU_DEVICE_TREE_MODEL_FLAGS_INCLUDE_NONE_ITEM);
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+                                        GDU_DEVICE_TREE_MODEL_COLUMN_SORT_KEY,
+                                        GTK_SORT_ASCENDING);
+  gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (model));
+  g_object_unref (model);
+
+  renderer = gtk_cell_renderer_pixbuf_new ();
+  g_object_set (G_OBJECT (renderer),
+                "stock-size", GTK_ICON_SIZE_DND,
+                NULL);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
+                                  "gicon", GDU_DEVICE_TREE_MODEL_COLUMN_ICON,
+                                  NULL);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combobox), renderer,
+                                      destination_combobox_sensitive_cb, data, NULL);
+
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
+                                  "markup", GDU_DEVICE_TREE_MODEL_COLUMN_NAME,
+                                  NULL);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combobox), renderer,
+                                      destination_combobox_sensitive_cb, data, NULL);
+
+  g_signal_connect (combobox, "notify::active", G_CALLBACK (on_destination_combobox_notify_active), data);
+
+  /* Select (None) item */
+  gtk_combo_box_set_active (combobox, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 restore_disk_image_populate (DialogData *data)
 {
-  UDisksObjectInfo *info;
+  gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->selectable_image_fcbutton), 
TRUE);
+
+  /* Image: Show label if image is known, otherwise show a filechooser button */
+  if (data->disk_image_filename != NULL)
+    {
+      gchar *s;
+      s = gdu_utils_unfuse_path (data->disk_image_filename);
+      gtk_label_set_text (GTK_LABEL (data->image_label), s);
+      g_free (s);
+
+      gtk_widget_hide (data->selectable_image_label);
+      gtk_widget_hide (data->selectable_image_fcbutton);
+    }
+  else
+    {
+      gtk_widget_hide (data->image_key_label);
+      gtk_widget_hide (data->image_label);
+    }
 
-  gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->image_fcbutton), TRUE);
+  /* Destination: Show label if device is known, otherwise show a combobox */
+  if (data->object != NULL)
+    {
+      UDisksObjectInfo *info;
+      info = udisks_client_get_object_info (gdu_window_get_client (data->window), data->object);
+      gtk_label_set_text (GTK_LABEL (data->destination_label), udisks_object_info_get_one_liner (info));
+      g_clear_object (&info);
 
-  /* 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), udisks_object_info_get_one_liner (info));
-  g_clear_object (&info);
+      gtk_widget_hide (data->selectable_destination_label);
+      gtk_widget_hide (data->selectable_destination_combobox);
+    }
+  else
+    {
+      gtk_widget_hide (data->destination_key_label);
+      gtk_widget_hide (data->destination_label);
+
+      populate_destination_combobox (data);
+    }
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -712,7 +893,11 @@ start_copying (DialogData *data)
   GError *error;
 
   error = NULL;
-  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->image_fcbutton));
+  if (data->disk_image_filename != NULL)
+    file = g_file_new_for_commandline_arg (data->disk_image_filename);
+  else
+    file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->selectable_image_fcbutton));
+
   data->file_input_stream = g_file_read (file,
                                          NULL,
                                          &error);
@@ -761,6 +946,9 @@ start_copying (DialogData *data)
 
   dialog_data_hide (data);
 
+  if (data->switch_to_object)
+    gdu_window_select_object (data->window, data->object);
+
   g_thread_new ("copy-disk-image-thread",
                 copy_thread_func,
                 dialog_data_ref (data));
@@ -815,7 +1003,7 @@ on_dialog_response (GtkDialog     *dialog,
         }
 
       /* 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->image_fcbutton));
+      gdu_utils_file_chooser_for_disk_images_update_settings (GTK_FILE_CHOOSER 
(data->selectable_image_fcbutton));
 
       /* ensure the device is unused (e.g. unmounted) before copying data to it... */
       gdu_window_ensure_unused (data->window,
@@ -836,7 +1024,8 @@ on_dialog_response (GtkDialog     *dialog,
 
 void
 gdu_restore_disk_image_dialog_show (GduWindow    *window,
-                                    UDisksObject *object)
+                                    UDisksObject *object,
+                                    const gchar  *disk_image_filename)
 {
   guint n;
   DialogData *data;
@@ -845,15 +1034,12 @@ gdu_restore_disk_image_dialog_show (GduWindow    *window,
   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);
+  set_destination_object (data, object);
+  if (object == NULL)
+    data->switch_to_object = TRUE;
+  data->disk_image_filename = g_strdup (disk_image_filename);
   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",
@@ -863,7 +1049,7 @@ gdu_restore_disk_image_dialog_show (GduWindow    *window,
       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);
+  g_signal_connect (data->selectable_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);
@@ -881,7 +1067,7 @@ gdu_restore_disk_image_dialog_show (GduWindow    *window,
   /* 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->image_fcbutton, "notify",
+  g_signal_connect (data->selectable_image_fcbutton, "notify",
                     G_CALLBACK (on_notify), data);
 
   data->response_signal_handler_id = g_signal_connect (data->dialog,
@@ -891,6 +1077,9 @@ gdu_restore_disk_image_dialog_show (GduWindow    *window,
 
   gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
   gtk_window_present (GTK_WINDOW (data->dialog));
+
+  gtk_widget_realize (data->selectable_destination_combobox);
+  gtk_widget_grab_focus (data->selectable_destination_combobox);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/disks/gdurestorediskimagedialog.h b/src/disks/gdurestorediskimagedialog.h
index 496084c..261bc0e 100644
--- a/src/disks/gdurestorediskimagedialog.h
+++ b/src/disks/gdurestorediskimagedialog.h
@@ -16,7 +16,8 @@
 G_BEGIN_DECLS
 
 void     gdu_restore_disk_image_dialog_show (GduWindow    *window,
-                                             UDisksObject *object);
+                                             UDisksObject *object,
+                                             const gchar  *disk_image_filename);
 
 G_END_DECLS
 
diff --git a/src/disks/gduwindow.c b/src/disks/gduwindow.c
index 17a1c26..e5218ff 100644
--- a/src/disks/gduwindow.c
+++ b/src/disks/gduwindow.c
@@ -1310,7 +1310,9 @@ gdu_window_constructed (GObject *object)
   //gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
 
-  window->model = gdu_device_tree_model_new (window->application);
+  window->model = gdu_device_tree_model_new (window->application,
+                                             GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_POWER_STATE |
+                                             GDU_DEVICE_TREE_MODEL_FLAGS_UPDATE_PULSE);
 
   gtk_tree_view_set_model (GTK_TREE_VIEW (window->device_tree_treeview), GTK_TREE_MODEL (window->model));
   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->model),
@@ -3774,7 +3776,7 @@ on_generic_drive_menu_item_restore_disk_image (GtkMenuItem *menu_item,
 
   object = gdu_volume_grid_get_block_object (GDU_VOLUME_GRID (window->volume_grid));
   g_assert (object != NULL);
-  gdu_restore_disk_image_dialog_show (window, object);
+  gdu_restore_disk_image_dialog_show (window, object, NULL);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -3816,7 +3818,7 @@ on_generic_menu_item_restore_volume_image (GtkMenuItem *menu_item,
 
   object = gdu_volume_grid_get_selected_device (GDU_VOLUME_GRID (window->volume_grid));
   g_assert (object != NULL);
-  gdu_restore_disk_image_dialog_show (window, object);
+  gdu_restore_disk_image_dialog_show (window, object, NULL);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */



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