[gnome-boxes] Move UnattendedInstaller UI setup to a .ui file



commit dbcfe97234b1199b8ca77e856ddd53dd57a50c76
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Wed Dec 18 20:32:01 2013 +0000

    Move UnattendedInstaller UI setup to a .ui file
    
    Also the needed UI-related code goes into a separate class:
    UnattendedSetupBox.

 data/gnome-boxes.gresource.xml  |    1 +
 data/ui/unattended-setup-box.ui |  222 ++++++++++++++++++++++++++++
 po/POTFILES.in                  |    1 +
 po/POTFILES.skip                |    1 +
 src/Makefile.am                 |    1 +
 src/installer-media.vala        |    2 +-
 src/unattended-file.vala        |    2 +-
 src/unattended-installer.vala   |  312 +++++----------------------------------
 src/unattended-setup-box.vala   |  219 +++++++++++++++++++++++++++
 src/util-app.vala               |   10 ++
 src/vm-creator.vala             |    3 +-
 src/wizard.vala                 |   14 +-
 12 files changed, 500 insertions(+), 288 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 00d5144..9d07baa 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -5,5 +5,6 @@
     <file>icons/boxes-arrow.svg</file>
     <file>icons/boxes-dark.png</file>
     <file>icons/boxes-gray.png</file>
+    <file preprocess="xml-stripblanks">ui/unattended-setup-box.ui</file>
   </gresource>
 </gresources>
diff --git a/data/ui/unattended-setup-box.ui b/data/ui/unattended-setup-box.ui
new file mode 100644
index 0000000..17c690c
--- /dev/null
+++ b/data/ui/unattended-setup-box.ui
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.9 -->
+
+  <template class="BoxesUnattendedSetupBox" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">30</property>
+    <property name="halign">center</property>
+    <property name="valign">center</property>
+    <child>
+      <object class="GtkLabel" id="setup_label">
+        <property name="label" translatable="yes">Choose express install to automatically preconfigure the 
box with optimal settings.</property>
+        <property name="visible">True</property>
+        <property name="wrap">True</property>
+        <property name="halign">start</property>
+        <property name="width-chars">30</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="padding">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkBox" id="setup_hbox">
+        <property name="visible">True</property>
+        <property name="valign">start</property>
+        <property name="margin">24</property>
+        <property name="orientation">horizontal</property>
+        <property name="spacing">0</property>
+        <child>
+          <object class="GtkGrid" id="setup_grid">
+            <property name="visible">True</property>
+            <property name="column-homogeneous">False</property>
+            <property name="row-homogeneous">False</property>
+            <property name="column-spacing">0</property>
+            <property name="row-spacing">0</property>
+
+            <!-- First row -->
+            <child>
+              <object class="GtkLabel" id="express_label">
+                <!-- Translators: 'Express Install' means that the new box installation will be fully 
automated, the 
+                     user won't be asked anything while it's performed. -->
+                <property name="label" translatable="yes">Express Install</property>
+                <property name="visible">True</property>
+                <property name="halign">end</property>
+                <property name="valign">center</property>
+                <property name="margin-right">10</property>
+                <property name="margin-bottom">15</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+                <property name="width">2</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSwitch" id="express_toggle">
+                <property name="visible">True</property>
+                <property name="halign">start</property>
+                <property name="valign">center</property>
+                <property name="margin-bottom">15</property>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+
+            <!-- 2nd row (while user avatar spans over 2 rows) -->
+            <child>
+              <object class="GtkImage" id="user_avatar">
+                <property name="visible">True</property>
+                <property name="icon-name">avatar-default</property>
+                <property name="icon-size">0</property>
+                <property name="pixel-size">64</property>
+                <property name="margin-right">15</property>
+                <property name="valign">center</property>
+                <property name="halign">center</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+                <property name="width">1</property>
+                <property name="height">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="username_label">
+                <property name="label" translatable="yes">Username</property>
+                <property name="visible">True</property>
+                <property name="halign">end</property>
+                <property name="valign">baseline</property>
+                <property name="margin-right">10</property>
+                <property name="margin-bottom">10</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>
+            <child>
+              <object class="GtkEntry" id="username_entry">
+                <property name="visible">True</property>
+                <property name="visibility">True</property>
+                <property name="halign">fill</property>
+                <property name="valign">baseline</property>
+                <property name="margin-bottom">10</property>
+                <signal name="activate" handler="on_username_entry_activated"/>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+
+            <!-- 3rd row -->
+            <child>
+              <object class="GtkLabel" id="password_label">
+                <property name="label" translatable="yes">Password</property>
+                <property name="visible">True</property>
+                <property name="halign">end</property>
+                <property name="valign">baseline</property>
+                <property name="margin-right">10</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="GtkNotebook" id="password_notebook">
+                <property name="visible">True</property>
+                <property name="show-tabs">False</property>
+                <property name="show-border">False</property>
+                <property name="halign">fill</property>
+                <property name="valign">center</property>
+                <child>
+                  <object class="GtkButton" id="password_button">
+                    <property name="label" translatable="yes">_Add Password</property>
+                    <property name="visible">True</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">baseline</property>
+                    <signal name="clicked" handler="on_password_button_clicked"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="password_entry">
+                    <property name="text"></property>
+                    <property name="visible">True</property>
+                    <property name="visibility">False</property>
+                    <signal name="focus-out-event" handler="on_password_entry_focus_out"/>
+                    <signal name="activate" handler="on_password_entry_activated"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+
+            <!-- 4th row -->
+            <child>
+              <object class="GtkLabel" id="product_key_label">
+                <property name="visible">False</property>
+                <property name="label" translatable="yes">Product Key</property>
+                <property name="margin-top">15</property>
+                <property name="margin-right">10</property>
+                <property name="halign">end</property>
+                <property name="valign">center</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">3</property>
+                <property name="width">2</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="product_key_entry">
+                <property name="visible">False</property>
+                <property name="visibility">True</property>
+                <property name="halign">fill</property>
+                <property name="valign">center</property>
+                <property name="margin-top">15</property>
+                <style>
+                  <class name="boxes-product-key-entry"/>
+                </style>
+                <signal name="activate" handler="on_key_entry_activated"/>
+                <signal name="insert-text" handler="on_key_text_inserted"/>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="padding">0</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 081ef60..3a8700c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -34,3 +34,4 @@ src/vm-importer.vala
 src/vnc-display.vala
 src/wizard-source.vala
 src/wizard.vala
+[type: gettext/glade]data/ui/unattended-setup-box.ui
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 06c1d44..6d0a432 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -24,6 +24,7 @@ src/sidebar.c
 src/spice-display.c
 src/topbar.c
 src/unattended-installer.c
+src/unattended-setup-box.c
 src/util-app.c
 src/util.c
 src/vm-configurator.c
diff --git a/src/Makefile.am b/src/Makefile.am
index e207e5c..8a402df 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -122,6 +122,7 @@ gnome_boxes_SOURCES =                               \
        ui.vala                                 \
        unattended-installer.vala               \
        unattended-file.vala                    \
+       unattended-setup-box.vala               \
        util-app.vala                           \
        vm-configurator.vala                    \
        vm-creator.vala                         \
diff --git a/src/installer-media.vala b/src/installer-media.vala
index af3b205..f638669 100644
--- a/src/installer-media.vala
+++ b/src/installer-media.vala
@@ -99,7 +99,7 @@ private class Boxes.InstallerMedia : GLib.Object {
         eject_cdrom_media (domain);
     }
 
-    public virtual void populate_setup_vbox (Gtk.Box setup_vbox) {}
+    public virtual void populate_setup_box (Gtk.Box setup_box) {}
 
     public virtual GLib.List<Pair<string,string>> get_vm_properties () {
         var properties = new GLib.List<Pair<string,string>> ();
diff --git a/src/unattended-file.vala b/src/unattended-file.vala
index 63f3dc6..b390a34 100644
--- a/src/unattended-file.vala
+++ b/src/unattended-file.vala
@@ -113,7 +113,7 @@ private class Boxes.UnattendedAvatarFile : GLib.Object, Boxes.UnattendedFile {
         if (pixbuf_format == null)
             throw new Boxes.Error.INVALID ("Failed to find suitable format to save user avatar file in.");
 
-        dest_name = installer.username + "." + pixbuf_format.get_extensions ()[0];
+        dest_name = installer.setup_box.username + "." + pixbuf_format.get_extensions ()[0];
     }
 
     ~UnattendedAvatarFile () {
diff --git a/src/unattended-installer.vala b/src/unattended-installer.vala
index d7abaac..070d13a 100644
--- a/src/unattended-installer.vala
+++ b/src/unattended-installer.vala
@@ -10,17 +10,9 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         }
     }
 
-    public bool ready_for_express {
-        get {
-            return username != "" &&
-                   (product_key_format == null ||
-                    key_entry.text_length == product_key_format.length);
-        }
-    }
-
     public override bool ready_to_create {
         get {
-            return !express_toggle.active || ready_for_express;
+            return setup_box.ready_to_create;
         }
     }
 
@@ -28,37 +20,14 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         owned get {
             var devices = base.supported_devices;
 
-            if (express_install)
+            if (setup_box.express_install)
                 return (devices as Osinfo.List).new_union (additional_devices) as Osinfo.DeviceList;
             else
                 return devices;
         }
     }
 
-    public bool express_install {
-        get { return express_toggle.active; }
-    }
-
-    public string username {
-        get { return username_entry.text; }
-    }
-
-    public string password {
-        owned get { return password_entry.text; }
-    }
-
-    public string hidden_password {
-        owned get {
-            if (password_entry.text.length > 0) {
-                var str = "";
-                for (var i = 0; i < password_entry.text_length; i++)
-                    str += password_entry.get_invisible_char ().to_string ();
-
-                return str;
-            } else
-                return _("no password");
-        }
-    }
+    public UnattendedSetupBox setup_box;
 
     public File? disk_file;           // Used for installer scripts, user avatar and pre-installation drivers
     public File? secondary_disk_file; // Used for post-installation drivers that won't fit on 1.44M primary 
disk
@@ -73,16 +42,6 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
     private GLib.List<UnattendedFile> secondary_unattended_files;
     private UnattendedAvatarFile avatar_file;
 
-    private Gtk.Grid setup_grid;
-    private int setup_grid_n_rows;
-
-    private Gtk.Label setup_label;
-    private Gtk.Box setup_hbox;
-    private Gtk.Switch express_toggle;
-    private Gtk.Entry username_entry;
-    private Gtk.Entry password_entry;
-    private Gtk.Entry key_entry;
-
     private string? timezone;
     private string lang;
     private string hostname;
@@ -92,8 +51,6 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
     // Devices made available by device drivers added through express installation (only).
     private Osinfo.DeviceList additional_devices;
 
-    private ulong key_inserted_id; // ID of key_entry.insert_text signal handler
-
     private static Fdo.Accounts? accounts;
 
     private static string escape_mkisofs_path (string path) {
@@ -141,7 +98,15 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         kbd = lang;
         product_key_format = get_product_key_format ();
 
-        setup_ui ();
+        setup_box = new UnattendedSetupBox (os_media.live, product_key_format);
+        setup_box.notify["ready-to-create"].connect (() => {
+            notify_property ("ready-to-create");
+        });
+        setup_box.user_wants_to_create.connect (() => {
+            user_wants_to_create ();
+        });
+
+        fetch_user_avatar.begin ();
     }
 
     public override void prepare_to_continue_installation (string vm_name) {
@@ -163,7 +128,7 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
     }
 
     public override async void prepare_for_installation (string vm_name, Cancellable? cancellable) throws 
GLib.Error {
-        if (!express_toggle.active) {
+        if (!setup_box.express_install) {
             debug ("Unattended installation disabled.");
 
             return;
@@ -204,7 +169,7 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
             // An error occurred when trying to setup unattended installation, but it's likely that a 
non-unattended
             // installation will work. When this happens, just disable unattended installs, and let the 
caller decide
             // if it wants to retry a non-automatic install or to just abort the box creation..
-            express_toggle.active = false;
+            setup_box.express_install = false;
 
             throw error;
         }
@@ -213,7 +178,7 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
     public override void setup_domain_config (Domain domain) {
         base.setup_domain_config (domain);
 
-        if (!express_toggle.active)
+        if (!setup_box.express_install)
             return;
 
         return_if_fail (disk_file != null);
@@ -226,16 +191,16 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
     }
 
     public void configure_install_script (InstallScript script) {
-        if (password != null) {
-            config.set_user_password (password);
-            config.set_admin_password (password);
+        if (setup_box.password != null) {
+            config.set_user_password (setup_box.password);
+            config.set_admin_password (setup_box.password);
         }
-        if (username != null) {
-            config.set_user_login (username);
-            config.set_user_realname (username);
+        if (setup_box.username != null) {
+            config.set_user_login (setup_box.username);
+            config.set_user_realname (setup_box.username);
         }
-        if (key_entry != null && key_entry.text != null)
-            config.set_reg_product_key (key_entry.text);
+        if (setup_box.product_key != null)
+            config.set_reg_product_key (setup_box.product_key);
         if (timezone != null)
             config.set_l10n_timezone (timezone);
         config.set_l10n_language (lang);
@@ -282,21 +247,20 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         base.setup_post_install_domain_config (domain);
     }
 
-    public override void populate_setup_vbox (Gtk.Box setup_vbox) {
-        foreach (var child in setup_vbox.get_children ())
-            setup_vbox.remove (child);
+    public override void populate_setup_box (Gtk.Box setup_box) {
+        foreach (var child in setup_box.get_children ())
+            setup_box.remove (child);
 
-        setup_vbox.pack_start (setup_label, false, false);
-        setup_vbox.pack_start (setup_hbox, false, false);
-        setup_vbox.show_all ();
+        setup_box.add (this.setup_box);
+        setup_box.show ();
     }
 
     public override GLib.List<Pair> get_vm_properties () {
         var properties = base.get_vm_properties ();
 
-        if (express_install) {
-            properties.append (new Pair<string,string> (_("Username"), username));
-            properties.append (new Pair<string,string> (_("Password"), hidden_password));
+        if (setup_box.express_install) {
+            properties.append (new Pair<string,string> (_("Username"), setup_box.username));
+            properties.append (new Pair<string,string> (_("Password"), setup_box.hidden_password));
         }
 
         return properties;
@@ -361,199 +325,6 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         yield setup_drivers (progress, cancellable);
     }
 
-    private void setup_ui () {
-        setup_label = new Gtk.Label (_("Choose express install to automatically preconfigure the box with 
optimal settings."));
-        setup_label.wrap = true;
-        setup_label.width_chars = 30;
-        setup_label.halign = Gtk.Align.START;
-        setup_hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
-        setup_hbox.valign = Gtk.Align.START;
-        setup_hbox.margin = 24;
-
-        setup_grid = new Gtk.Grid ();
-        setup_hbox.pack_start (setup_grid, false, false);
-        setup_grid.column_spacing = 0;
-        setup_grid.column_homogeneous = false;
-        setup_grid.row_spacing = 0;
-        setup_grid.row_homogeneous = true;
-
-        // First row
-        // Translators: 'Express Install' means that the new box installation will be fully automated, the 
user
-        // won't be asked anything while it's performed.
-        var express_label = new Gtk.Label (_("Express Install"));
-        express_label.margin_right = 10;
-        express_label.margin_bottom = 15;
-        express_label.halign = Gtk.Align.END;
-        express_label.valign = Gtk.Align.CENTER;
-        setup_grid.attach (express_label, 0, 0, 2, 1);
-
-        express_toggle = new Gtk.Switch ();
-        express_toggle.active = !os_media.live;
-        express_toggle.margin_bottom = 15;
-        express_toggle.halign = Gtk.Align.START;
-        express_toggle.valign = Gtk.Align.CENTER;
-        express_toggle.notify["active"].connect (() => { notify_property ("ready-to-create"); });
-        setup_grid.attach (express_toggle, 2, 0, 1, 1);
-        setup_grid_n_rows++;
-
-        // 2nd row (while user avatar spans over 2 rows)
-        var avatar = new Gtk.Image.from_icon_name ("avatar-default", 0);
-        avatar.pixel_size = 64;
-        avatar.margin_right = 15;
-        avatar.valign = Gtk.Align.CENTER;
-        avatar.halign = Gtk.Align.CENTER;
-        setup_grid.attach (avatar, 0, 1, 1, 2);
-        avatar.show_all ();
-        fetch_user_avatar.begin (avatar);
-
-        var label = new Gtk.Label (_("Username"));
-        label.margin_right = 10;
-        label.margin_bottom  = 10;
-        label.halign = Gtk.Align.END;
-        label.valign = Gtk.Align.BASELINE;
-        setup_grid.attach (label, 1, 1, 1, 1);
-        username_entry = create_input_entry (Environment.get_user_name ());
-        username_entry.margin_bottom  = 10;
-        username_entry.halign = Gtk.Align.FILL;
-        username_entry.valign = Gtk.Align.BASELINE;
-        username_entry.activate.connect (() => {
-            if (ready_for_express)
-                user_wants_to_create ();
-            else if (username != "" && product_key_format != null)
-                key_entry.grab_focus (); // If username is provided, must be product key thats still not 
provided
-        });
-
-        setup_grid.attach (username_entry, 2, 1, 1, 1);
-        setup_grid_n_rows++;
-
-        // 3rd row
-        label = new Gtk.Label (_("Password"));
-        label.margin_right = 10;
-        label.halign = Gtk.Align.END;
-        label.valign = Gtk.Align.BASELINE;
-        setup_grid.attach (label, 1, 2, 1, 1);
-
-        var notebook = new Gtk.Notebook ();
-        notebook.show_tabs = false;
-        notebook.show_border = false;
-        notebook.halign = Gtk.Align.FILL;
-        notebook.valign = Gtk.Align.CENTER;
-        var button = new Gtk.Button.with_mnemonic (_("_Add Password"));
-        button.visible = true;
-        button.valign = Gtk.Align.BASELINE;
-        notebook.append_page (button);
-        password_entry = create_input_entry ("", false, false);
-        notebook.append_page (password_entry);
-        button.clicked.connect (() => {
-            notebook.next_page ();
-            password_entry.grab_focus ();
-        });
-        password_entry.focus_out_event.connect (() => {
-            if (password_entry.text_length == 0)
-                notebook.prev_page ();
-            return false;
-        });
-        password_entry.activate.connect (() => {
-            if (ready_for_express)
-                user_wants_to_create ();
-            else if (username == "")
-                username_entry.grab_focus ();
-            else if (product_key_format != null)
-                key_entry.grab_focus ();
-        });
-        setup_grid.attach (notebook, 2, 2, 1, 1);
-        setup_grid_n_rows++;
-
-        foreach (var child in setup_grid.get_children ())
-            if ((child != express_label) && (child != express_toggle))
-                express_toggle.bind_property ("active", child, "sensitive", BindingFlags.SYNC_CREATE);
-
-        if (product_key_format == null)
-            return;
-
-        label = new Gtk.Label (_("Product Key"));
-        label.margin_top = 15;
-        label.margin_right = 10;
-        label.halign = Gtk.Align.END;
-        label.valign = Gtk.Align.CENTER;
-        setup_grid.attach (label, 0, setup_grid_n_rows, 2, 1);
-        express_toggle.bind_property ("active", label, "sensitive", 0);
-
-        key_entry = create_input_entry ("");
-        key_entry.width_chars = product_key_format.length;
-        key_entry.max_length =  product_key_format.length;
-        key_entry.margin_top = 15;
-        key_entry.halign = Gtk.Align.FILL;
-        key_entry.valign = Gtk.Align.CENTER;
-        key_entry.get_style_context ().add_class ("boxes-product-key-entry");
-        setup_grid.attach (key_entry, 2, setup_grid_n_rows, 1, 1);
-        express_toggle.bind_property ("active", key_entry, "sensitive", 0);
-        key_entry.activate.connect (() => {
-            if (ready_for_express)
-                user_wants_to_create ();
-            else if (key_entry.text_length == product_key_format.length)
-                username_entry.grab_focus (); // If product key is provided, must be username thats still 
not provided.
-        });
-        setup_grid_n_rows++;
-
-        key_inserted_id = key_entry.insert_text.connect (on_key_text_inserted);
-    }
-
-    private void on_key_text_inserted (string text, int text_length, ref int position) {
-        var result = "";
-
-        for (uint i = 0, j = position; i < text_length && j < product_key_format.length; ) {
-            var character = text.get (i);
-            var allowed_char = product_key_format.get (j);
-
-            var skip_input_char = false;
-            switch (allowed_char) {
-            case '@': // Any character
-                break;
-
-            case '%': // Alphabet
-                if (!character.isalpha ())
-                    skip_input_char = true;
-                break;
-
-            case '#': // Numeric
-                if (!character.isdigit ())
-                    skip_input_char = true;
-                break;
-
-            case '$': // Alphnumeric
-                if (!character.isalnum ())
-                    skip_input_char = true;
-                break;
-
-            default: // Hardcoded character required
-                if (character != allowed_char) {
-                    result += allowed_char.to_string ();
-                    j++;
-
-                    continue;
-                }
-
-                break;
-            }
-
-            i++;
-            if (skip_input_char)
-                continue;
-
-            result += character.to_string ();
-            j++;
-        }
-
-        if (result != "") {
-            SignalHandler.block (key_entry, key_inserted_id);
-            key_entry.insert_text (result.up (), result.length, ref position);
-            SignalHandler.unblock (key_entry, key_inserted_id);
-        }
-
-        Signal.stop_emission_by_name (key_entry, "insert-text");
-    }
-
     private DomainDisk? get_unattended_disk_config (PathFormat path_format = PathFormat.UNIX) {
         var disk = new DomainDisk ();
         disk.set_type (DomainDiskType.FILE);
@@ -604,20 +375,6 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         secondary_unattended_files.append (file);
     }
 
-    private Gtk.Entry create_input_entry (string text, bool mandatory = true, bool visibility = true) {
-        var entry = new Gtk.Entry ();
-        entry.visibility = visibility;
-        entry.visible = true;
-        entry.text = text;
-
-        if (mandatory)
-            entry.notify["text"].connect (() => {
-                notify_property ("ready-to-create");
-            });
-
-        return entry;
-    }
-
     private async void create_disk_image (Cancellable? cancellable) throws GLib.Error {
         var template_path = get_unattended ("disk.img");
         var template_file = File.new_for_path (template_path);
@@ -627,7 +384,7 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
         debug ("Floppy image for unattended installation created at '%s'", disk_file.get_path ());
     }
 
-    private async void fetch_user_avatar (Gtk.Image avatar) {
+    private async void fetch_user_avatar () {
         if (accounts == null)
             return;
 
@@ -642,10 +399,9 @@ private class Boxes.UnattendedInstaller: InstallerMedia {
             warning ("Failed to retrieve information about user '%s': %s", username, error.message);
         }
 
-        try {
-            var pixbuf = new Gdk.Pixbuf.from_file_at_scale (avatar_path, 64, 64, true);
-            avatar.pixbuf = pixbuf;
+        setup_box.avatar_path = avatar_path;
 
+        try {
             AvatarFormat avatar_format = null;
             foreach (var s in scripts.get_elements ()) {
                 var script = s as InstallScript;
diff --git a/src/unattended-setup-box.vala b/src/unattended-setup-box.vala
new file mode 100644
index 0000000..1938a25
--- /dev/null
+++ b/src/unattended-setup-box.vala
@@ -0,0 +1,219 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/unattended-setup-box.ui")]
+private class Boxes.UnattendedSetupBox : Gtk.Box {
+    public bool ready_for_express {
+        get {
+            return username != "" &&
+                   (product_key_format == null ||
+                    product_key_entry.text_length == product_key_format.length);
+        }
+    }
+
+    public bool ready_to_create {
+        get {
+            return !express_toggle.active || ready_for_express;
+        }
+    }
+
+    public bool express_install {
+        get {
+            return express_toggle.active;
+        }
+
+        set {
+            express_toggle.active = value;
+        }
+    }
+
+    public string username {
+        get { return username_entry.text; }
+    }
+
+    public string password {
+        owned get { return password_entry.text; }
+    }
+
+    public string hidden_password {
+        owned get {
+            if (password_entry.text.length > 0) {
+                var str = "";
+                for (var i = 0; i < password_entry.text_length; i++)
+                    str += password_entry.get_invisible_char ().to_string ();
+
+                return str;
+            } else
+                return _("no password");
+        }
+    }
+
+    public string product_key {
+        owned get {
+            return (product_key_entry != null)? product_key_entry.text : null;
+        }
+    }
+
+    public string avatar_path {
+        set {
+            try {
+                var pixbuf = new Gdk.Pixbuf.from_file_at_scale (value, 64, 64, true);
+                user_avatar.pixbuf = pixbuf;
+            } catch (GLib.Error error) {
+                debug ("Failed to load user avatar file '%s': %s", value, error.message);
+            }
+        }
+    }
+
+    public signal void user_wants_to_create (); // User wants to already create the VM
+
+    [GtkChild]
+    private Gtk.Grid setup_grid;
+    [GtkChild]
+    private Gtk.Label express_label;
+    [GtkChild]
+    private Gtk.Switch express_toggle;
+    [GtkChild]
+    private Gtk.Image user_avatar;
+    [GtkChild]
+    private Gtk.Entry username_entry;
+    [GtkChild]
+    private Gtk.Notebook password_notebook;
+    [GtkChild]
+    private Gtk.Entry password_entry;
+    [GtkChild]
+    private Gtk.Label product_key_label;
+    [GtkChild]
+    private Gtk.Entry product_key_entry;
+
+    string? product_key_format;
+
+    public UnattendedSetupBox (bool live, string? product_key_format) {
+        this.product_key_format = product_key_format;
+        express_toggle.active = !live;
+        username_entry.text = Environment.get_user_name ();
+
+        if (product_key_format != null) {
+            product_key_label.visible = true;
+
+            product_key_entry.visible = true;
+            product_key_entry.width_chars = product_key_format.length;
+            product_key_entry.max_length = product_key_format.length;
+        }
+
+        foreach (var child in setup_grid.get_children ())
+            if (child != express_label && child != express_toggle)
+                express_toggle.bind_property ("active", child, "sensitive", BindingFlags.SYNC_CREATE);
+
+        // FIXME: We should do this from .ui file once this bug is fixed:
+        //        https://bugzilla.gnome.org/show_bug.cgi?id=720825
+        express_toggle.notify["active"].connect (on_mandatory_input_changed);
+        username_entry.notify["text"].connect (on_mandatory_input_changed);
+        if (product_key_format != null)
+            product_key_entry.notify["text"].connect (on_mandatory_input_changed);
+    }
+
+    private void on_mandatory_input_changed () {
+        notify_property ("ready-to-create");
+    }
+
+    [GtkCallback]
+    private void on_username_entry_activated () {
+        if (ready_for_express)
+            user_wants_to_create ();
+        else if (username != "" && product_key_format != null)
+            product_key_entry.grab_focus (); // If username is provided, must be product key thats still not 
provided
+    }
+
+    [GtkCallback]
+    private void on_password_button_clicked () {
+        password_notebook.next_page ();
+        password_entry.grab_focus ();
+    }
+
+    [GtkCallback]
+    private bool on_password_entry_focus_out () {
+        if (password_entry.text_length == 0)
+            password_notebook.prev_page ();
+        return false;
+    }
+
+    [GtkCallback]
+    private void on_password_entry_activated () {
+        if (ready_for_express)
+            user_wants_to_create ();
+        else if (username == "")
+            username_entry.grab_focus ();
+        else if (product_key_format != null)
+            product_key_entry.grab_focus ();
+    }
+
+    [GtkCallback]
+    private void on_key_entry_activated () {
+        if (ready_for_express)
+            user_wants_to_create ();
+        else if (product_key_entry.text_length == product_key_format.length)
+            username_entry.grab_focus (); // If product key is provided, must be username thats still not 
provided.
+    }
+
+    bool we_inserted_text;
+
+    [GtkCallback]
+    private void on_key_text_inserted (string text, int text_length, ref int position) {
+        if (we_inserted_text)
+            return;
+
+        var result = "";
+
+        for (uint i = 0, j = position; i < text_length && j < product_key_format.length; ) {
+            var character = text.get (i);
+            var allowed_char = product_key_format.get (j);
+
+            var skip_input_char = false;
+            switch (allowed_char) {
+            case '@': // Any character
+                break;
+
+            case '%': // Alphabet
+                if (!character.isalpha ())
+                    skip_input_char = true;
+                break;
+
+            case '#': // Numeric
+                if (!character.isdigit ())
+                    skip_input_char = true;
+                break;
+
+            case '$': // Alphnumeric
+                if (!character.isalnum ())
+                    skip_input_char = true;
+                break;
+
+            default: // Hardcoded character required
+                if (character != allowed_char) {
+                    result += allowed_char.to_string ();
+                    j++;
+
+                    continue;
+                }
+
+                break;
+            }
+
+            i++;
+            if (skip_input_char)
+                continue;
+
+            result += character.to_string ();
+            j++;
+        }
+
+        if (result != "") {
+            we_inserted_text = true;
+            product_key_entry.insert_text (result.up (), result.length, ref position);
+            we_inserted_text = false;
+        }
+
+        Signal.stop_emission_by_name (product_key_entry, "insert-text");
+    }
+}
+
diff --git a/src/util-app.vala b/src/util-app.vala
index 2c33f1d..1be7111 100644
--- a/src/util-app.vala
+++ b/src/util-app.vala
@@ -20,6 +20,16 @@ namespace Boxes {
         return new Gdk.Pixbuf.from_resource ("/org/gnome/Boxes/icons/" + asset);
     }
 
+    public Gtk.Builder load_ui (string ui) {
+        var builder = new Gtk.Builder ();
+        try {
+            builder.add_from_resource ("/org/gnome/Boxes/ui/".concat (ui, null));
+        } catch (GLib.Error e) {
+            error ("Failed to load UI file '%s': %s", ui, e.message);
+        }
+        return builder;
+    }
+
     public Clutter.Color gdk_rgba_to_clutter_color (Gdk.RGBA gdk_rgba) {
         Clutter.Color color = {
             (uint8) (gdk_rgba.red * 255).clamp (0, 255),
diff --git a/src/vm-creator.vala b/src/vm-creator.vala
index dbd5dbc..c2a1de4 100644
--- a/src/vm-creator.vala
+++ b/src/vm-creator.vala
@@ -63,7 +63,8 @@ private class Boxes.VMCreator {
     }
 
     public virtual void launch_vm (LibvirtMachine machine) throws GLib.Error {
-        if (!(install_media is UnattendedInstaller) || !(install_media as 
UnattendedInstaller).express_install) {
+        if (!(install_media is UnattendedInstaller) ||
+            !(install_media as UnattendedInstaller).setup_box.express_install) {
             ulong signal_id = 0;
 
             signal_id = App.app.notify["ui-state"].connect (() => {
diff --git a/src/wizard.vala b/src/wizard.vala
index 90aedf2..30d7fec 100644
--- a/src/wizard.vala
+++ b/src/wizard.vala
@@ -28,7 +28,7 @@ private class Boxes.Wizard: Boxes.UI {
     private Gtk.ProgressBar prep_progress;
     private Gtk.Label prep_media_label;
     private Gtk.Label prep_status_label;
-    private Gtk.Box setup_vbox;
+    private Gtk.Box setup_box;
     private Gtk.Label review_label;
     private Gtk.Box nokvm_box;
     private Gtk.Image installer_image;
@@ -348,7 +348,7 @@ private class Boxes.Wizard: Boxes.UI {
         vm_creator.install_media.bind_property ("ready-to-create",
                                                 continue_button, "sensitive",
                                                 BindingFlags.SYNC_CREATE);
-        vm_creator.install_media.populate_setup_vbox (setup_vbox);
+        vm_creator.install_media.populate_setup_box (setup_box);
         vm_creator.install_media.user_wants_to_create.connect (() => {
             if (vm_creator.install_media.ready_to_create)
                 page = page + 1;
@@ -624,11 +624,11 @@ private class Boxes.Wizard: Boxes.UI {
         vbox.show_all ();
 
         /* Setup */
-        setup_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 30);
-        setup_vbox.valign = Gtk.Align.CENTER;
-        setup_vbox.halign = Gtk.Align.CENTER;
-        add_step (setup_vbox, _("Setup"), WizardPage.SETUP);
-        setup_vbox.show_all ();
+        setup_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+        setup_box.valign = Gtk.Align.CENTER;
+        setup_box.halign = Gtk.Align.CENTER;
+        add_step (setup_box, _("Setup"), WizardPage.SETUP);
+        setup_box.show_all ();
 
         /* Review */
         vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);



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