[gnome-boxes/boxes-newbox-assistant-306] WIP



commit acde5211b57e6fb0e6a8579a9cfb31b2f7213acb
Author: Felipe Borges <felipeborges gnome org>
Date:   Mon Nov 4 10:00:33 2019 +0100

    WIP

 data/gnome-boxes.gresource.xml              |   5 +
 data/gtk-style.css                          |   4 +
 data/ui/assistant/pages/index-page.ui       | 241 +++++++++++++++++++
 data/ui/assistant/pages/preparation-page.ui |  82 +++++++
 data/ui/assistant/pages/review-page.ui      |  92 +++++++
 data/ui/assistant/pages/setup-page.ui       |  20 ++
 data/ui/assistant/vm-assistant.ui           |  65 +++++
 data/ui/collection-toolbar.ui               |   7 +
 src/app-window.vala                         |   4 +
 src/assistant/vm-assistant.vala             | 355 ++++++++++++++++++++++++++++
 src/collection-toolbar.vala                 |   5 +
 src/meson.build                             |   1 +
 src/vm-creator.vala                         |   2 +-
 13 files changed, 882 insertions(+), 1 deletion(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index c7ba7de1..470606c6 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -48,5 +48,10 @@
     <file preprocess="xml-stripblanks">ui/wizard-web-view.ui</file>
     <file preprocess="xml-stripblanks">ui/wizard-window.ui</file>
     <file preprocess="xml-stripblanks">ui/assistant/remote-connection.ui</file>
+    <file preprocess="xml-stripblanks">ui/assistant/vm-assistant.ui</file>
+    <file preprocess="xml-stripblanks">ui/assistant/pages/index-page.ui</file>
+    <file preprocess="xml-stripblanks">ui/assistant/pages/preparation-page.ui</file>
+    <file preprocess="xml-stripblanks">ui/assistant/pages/setup-page.ui</file>
+    <file preprocess="xml-stripblanks">ui/assistant/pages/review-page.ui</file>
   </gresource>
 </gresources>
diff --git a/data/gtk-style.css b/data/gtk-style.css
index 40f0391d..72e2f215 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -43,6 +43,10 @@
     border: 1px solid @theme_bg_color;
 }
 
+.bold-label {
+    font-weight: bold;
+}
+
 /******************
  * New Box Wizard *
  ******************/
diff --git a/data/ui/assistant/pages/index-page.ui b/data/ui/assistant/pages/index-page.ui
new file mode 100644
index 00000000..a3cc0652
--- /dev/null
+++ b/data/ui/assistant/pages/index-page.ui
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="BoxesAssistantIndexPage" parent="BoxesAssistantPage">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">True</property>
+        <property name="expand">True</property>
+        <property name="hscrollbar-policy">never</property>
+
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">30</property>
+            <property name="halign">center</property>
+            <property name="border-width">20</property>
+            <property name="margin-left">30</property>
+            <property name="margin-right">30</property>
+
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="wrap">True</property>
+                <property name="max-width-chars">60</property>
+                <property name="xalign">0</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">A new virtual machine will be created and an 
operating system installed into it. Select an operating system source to begin.</property>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="spacing">10</property>
+                <property name="orientation">vertical</property>
+
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Detected Sources</property>
+                    <style>
+                      <class name="bold-label"/>
+                    </style>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkListBox" id="source_medias">
+                    <property name="visible">True</property>
+                    <signal name="row-activated" handler="on_source_media_selected"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="spacing">10</property>
+                <property name="orientation">vertical</property>
+
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Featured Downloads</property>
+                    <style>
+                      <class name="bold-label"/>
+                    </style>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">You will be notified when the download has 
completed.</property>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkListBox" id="featured_medias">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="spacing">10</property>
+                <property name="orientation">vertical</property>
+
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Select an OS Source</property>
+                    <style>
+                      <class name="bold-label"/>
+                    </style>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkListBox">
+                    <property name="visible">True</property>
+
+                    <child>
+                      <object class="GtkButton">
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="boxes-menu-row"/>
+                        </style>
+
+                        <child>
+                          <object class="GtkGrid">
+                            <property name="visible">True</property>
+
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="hexpand">True</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">Operating System 
Download</property>
+                                <style>
+                                  <class name="bold-label"/>
+                                </style>
+                              </object>
+                              <packing>
+                                <property name="left-attach">0</property>
+                                <property name="top-attach">0</property>
+                              </packing>
+                            </child>
+
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="hexpand">True</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">Browse and search for operating 
systems to install.</property>
+                                <style>
+                                  <class name="dim-label"/>
+                                </style>
+                              </object>
+                              <packing>
+                                <property name="left-attach">0</property>
+                                <property name="top-attach">1</property>
+                              </packing>
+                            </child>
+
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="icon-name">go-next-symbolic</property>
+                              </object>
+                              <packing>
+                                <property name="left-attach">1</property>
+                                <property name="top-attach">0</property>
+                                <property name="height">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+
+                      </object>
+                    </child>
+
+                    <child>
+                      <object class="GtkButton">
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="boxes-menu-row"/>
+                        </style>
+
+                        <child>
+                          <object class="GtkGrid">
+                            <property name="visible">True</property>
+
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="hexpand">True</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">Operating System Image 
File</property>
+                                <style>
+                                  <class name="bold-label"/>
+                                </style>
+                              </object>
+                              <packing>
+                                <property name="left-attach">0</property>
+                                <property name="top-attach">0</property>
+                              </packing>
+                            </child>
+
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="hexpand">True</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">Select an .iso file to install a 
virtual machine.</property>
+                                <style>
+                                  <class name="dim-label"/>
+                                </style>
+                              </object>
+                              <packing>
+                                <property name="left-attach">0</property>
+                                <property name="top-attach">1</property>
+                              </packing>
+                            </child>
+
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="icon-name">go-next-symbolic</property>
+                              </object>
+                              <packing>
+                                <property name="left-attach">1</property>
+                                <property name="top-attach">0</property>
+                                <property name="height">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+
+  </template>
+</interface>
diff --git a/data/ui/assistant/pages/preparation-page.ui b/data/ui/assistant/pages/preparation-page.ui
new file mode 100644
index 00000000..bd45c976
--- /dev/null
+++ b/data/ui/assistant/pages/preparation-page.ui
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="BoxesAssistantPreparationPage" parent="BoxesAssistantPage">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="expand">True</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="column-spacing">10</property>
+
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="wrap">True</property>
+            <property name="halign">start</property>
+            <property name="label" translatable="yes">Preparing to create a new box</property>
+            <property name="margin-bottom">20</property>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">0</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkImage" id="installer_image">
+            <property name="visible">True</property>
+            <property name="icon-size">0</property>
+            <property name="pixel-size">128</property>
+            <property name="icon-name">media-optical</property>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">1</property>
+            <property name="height">3</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkLabel" id="media_label">
+            <property name="visible">True</property>
+            <property name="halign">start</property>
+            <property name="valign">end</property>
+            <style>
+              <class name="boxes-wizard-media-os-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">1</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkLabel" id="status_label">
+            <property name="visible">True</property>
+            <property name="halign">start</property>
+            <property name="valign">center</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">2</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkProgressBar" id="progress_bar">
+            <property name="visible">True</property>
+            <property name="valign">start</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/assistant/pages/review-page.ui b/data/ui/assistant/pages/review-page.ui
new file mode 100644
index 00000000..3e84d7df
--- /dev/null
+++ b/data/ui/assistant/pages/review-page.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="BoxesAssistantReviewPage" parent="BoxesAssistantPage">
+    <property name="visible">True</property>
+    <property name="expand">True</property>
+
+    <child>
+      <object class="GtkInfoBar" id="nokvm_infobar">
+        <property name="visible">True</property>
+        <property name="halign">fill</property>
+        <property name="spacing">0</property>
+        <property name="message-type">warning</property>
+
+        <child internal-child="content_area">
+          <object class="GtkContainer" id="nokvm_container">
+            <property name="visible">True</property>
+
+            <child>
+              <object class="GtkImage" id="nokvm_image">
+                <property name="visible">True</property>
+                <property name="icon-name">dialog-warning</property>
+                <property name="icon-size">3</property>
+                <property name="pixel-size">48</property>
+              </object>
+            </child>
+
+              <child>
+                <object class="GtkLabel" id="nokvm_label">
+                  <property name="visible">True</property>
+                  <property name="label" translatable="yes">Virtualization extensions are unavailable on 
your system.
+Check your BIOS settings to enable them.</property>
+                  <property name="wrap">True</property>
+                  <property name="halign">start</property>
+                  <property name="hexpand">True</property>
+                </object>
+              </child>
+            </object>
+          </child>
+        </object>
+
+        <packing>
+          <property name="expand">False</property>
+          <property name="fill">False</property>
+        </packing>
+      </child>
+
+      <child>
+        <object class="GtkBox" id="review_vbox">
+          <property name="visible">True</property>
+          <property name="orientation">vertical</property>
+          <property name="spacing">30</property>
+          <property name="valign">center</property>
+          <property name="halign">center</property>
+
+          <child>
+            <object class="GtkLabel" id="review_label">
+              <property name="visible">True</property>
+              <property name="halign">start</property>
+              <property name="wrap">True</property>
+              <property name="width-chars">30</property>
+            </object>
+            <packing>
+            <property name="expand">False</property>
+              <property name="fill">False</property>
+            </packing>
+          </child>
+
+          <child>
+            <object class="BoxesWizardSummary" id="summary"/>
+            <packing>
+              <property name="expand">True</property>
+              <property name="fill">True</property>
+            </packing>
+          </child>
+
+          <!--<child>
+            <object class="GtkButton">
+              <property name="visible">True</property>
+              <property name="label">Create</property>
+              <signal name="clicked" handler="next"/>
+            </object>
+          </child>-->
+        </object>
+
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">False</property>
+        </packing>
+      </child>
+
+  </template>
+</interface>
diff --git a/data/ui/assistant/pages/setup-page.ui b/data/ui/assistant/pages/setup-page.ui
new file mode 100644
index 00000000..c5fd4b50
--- /dev/null
+++ b/data/ui/assistant/pages/setup-page.ui
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="BoxesAssistantSetupPage" parent="BoxesAssistantPage">
+    <property name="visible">True</property>
+    <property name="expand">True</property>
+
+    <child>
+      <object class="GtkBox" id="setup_box">
+        <property name="visible">True</property>
+        <property name="expand">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">0</property>
+        <property name="margin-start">10</property>
+        <property name="margin-end">10</property>
+        <property name="valign">center</property>
+      </object>
+    </child>
+
+  </template>
+</interface>
diff --git a/data/ui/assistant/vm-assistant.ui b/data/ui/assistant/vm-assistant.ui
new file mode 100644
index 00000000..fba260f2
--- /dev/null
+++ b/data/ui/assistant/vm-assistant.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="BoxesVMAssistant" parent="GtkDialog">
+    <property name="modal">True</property>
+    <property name="type-hint">dialog</property>
+    <property name="title" translatable="yes">Create a Virtual Machine</property>
+    <property name="width-request">724</property>
+    <property name="height-request">568</property>
+
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+
+        <child>
+          <object class="GtkStack" id="pages">
+            <property name="visible">True</property>
+            <signal name="notify::visible-child" handler="update_titlebar_buttons"/>
+            <child>
+              <object class="BoxesAssistantIndexPage" id="index_page">
+                <signal name="done" handler="do_preparation"/>
+              </object>
+            </child>
+            <child>
+              <object class="BoxesAssistantPreparationPage" id="preparation_page">
+                <signal name="done" handler="do_setup"/>
+              </object>
+            </child>
+            <child>
+              <object class="BoxesAssistantSetupPage" id="setup_page">
+                <signal name="done" handler="do_review"/>
+              </object>
+            </child>
+            <child>
+              <object class="BoxesAssistantReviewPage" id="review_page">
+                <signal name="done" handler="do_create"/>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+
+    <child type="action">
+      <object class="GtkButton" id="previous_button">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">Cancel</property>
+        <signal name="clicked" handler="on_previous_button_clicked"/>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="next_button">
+        <property name="visible">False</property>
+        <property name="label" translatable="yes">Next</property>
+        <signal name="clicked" handler="on_next_button_clicked"/>
+      </object>
+    </child>
+
+    <action-widgets>
+      <action-widget response="cancel">previous_button</action-widget>
+      <action-widget response="ok">next_button</action-widget>
+    </action-widgets>
+
+  </template>
+</interface>
diff --git a/data/ui/collection-toolbar.ui b/data/ui/collection-toolbar.ui
index a9810b6e..ad117123 100644
--- a/data/ui/collection-toolbar.ui
+++ b/data/ui/collection-toolbar.ui
@@ -213,6 +213,13 @@
             <signal name="clicked" handler="on_new_vm_btn_clicked"/>
           </object>
         </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="text" translatable="yes">Create a Virtual Machineā€¦</property>
+            <signal name="clicked" handler="on_create_vm_btn_clicked"/>
+          </object>
+        </child>
         <child>
           <object class="GtkModelButton">
             <property name="visible">True</property>
diff --git a/src/app-window.vala b/src/app-window.vala
index 82a9f834..51556afc 100644
--- a/src/app-window.vala
+++ b/src/app-window.vala
@@ -303,6 +303,10 @@ public void show_remote_connection_assistant () {
         new Boxes.RemoteConnectionAssistant (this).run ();
     }
 
+    public void show_vm_assistant () {
+        new Boxes.VMAssistant (this).run ();
+    }
+
     public void show_properties () {
         if (current_item != null) {
             if (ui_state == UIState.COLLECTION && selection_mode)
diff --git a/src/assistant/vm-assistant.vala b/src/assistant/vm-assistant.vala
new file mode 100644
index 00000000..39ef10e3
--- /dev/null
+++ b/src/assistant/vm-assistant.vala
@@ -0,0 +1,355 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+private abstract class Boxes.AssistantPage : Gtk.Box {
+    protected Object? artifact;
+    public bool skip = false;
+    protected signal void done (Object artifact);
+
+    public async virtual void next () {
+        done (artifact);
+    }
+
+    public abstract void cleanup ();
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/assistant/vm-assistant.ui")]
+private class Boxes.VMAssistant : Gtk.Dialog {
+    [GtkChild]
+    private Stack pages;
+    [GtkChild]
+    private AssistantIndexPage index_page;
+    [GtkChild]
+    private AssistantPreparationPage preparation_page;
+    [GtkChild]
+    private AssistantSetupPage setup_page;
+    [GtkChild]
+    private AssistantReviewPage review_page;
+
+    [GtkChild]
+    private Button previous_button;
+    [GtkChild]
+    private Button next_button;
+
+    private AssistantPage visible_page {
+        get {
+            return pages.get_visible_child () as AssistantPage;
+        }
+    }
+
+    private AssistantPage? previous_page {
+        get {
+            var current_page_index = pages.get_children ().index (visible_page);
+            return pages.get_children ().nth_data (current_page_index - 1) as AssistantPage;
+        }
+    }
+
+    construct {
+        use_header_bar = 1;
+    }
+
+    public VMAssistant (AppWindow app_window) {
+        set_transient_for (app_window);
+    }
+
+    [GtkCallback]
+    private void update_titlebar_buttons () {
+        var is_index = (pages.visible_child == index_page);
+        var is_last = (pages.visible_child == review_page);
+
+        next_button.visible = !is_index;
+        next_button.label = is_last ? _("Create") : _("Next");
+        previous_button.label = is_index ? _("Cancel") : _("Previous");
+    }
+
+    [GtkCallback]
+    private void on_previous_button_clicked () {
+        if (visible_page == index_page)
+            close ();
+        else
+            go_back ();
+    }
+
+    private void go_back () {
+        visible_page.cleanup ();
+
+        pages.set_visible_child (previous_page);
+        if (visible_page.skip)
+            go_back ();
+    }
+
+    [GtkCallback]
+    private void on_next_button_clicked () {
+        visible_page.next ();
+    }
+
+    [GtkCallback]
+    private void do_preparation (Object object) {
+        pages.set_visible_child (preparation_page);
+
+        preparation_page.setup (object as InstallerMedia);
+    }
+
+    [GtkCallback]
+    private void do_setup (Object object) {
+        pages.set_visible_child (setup_page);
+
+        var vm_creator = object as VMCreator;
+        vm_creator.install_media.bind_property ("ready-to-create",
+                                                next_button, "sensitive",
+                                                BindingFlags.SYNC_CREATE);
+
+        setup_page.setup (vm_creator);
+    }
+
+    [GtkCallback]
+    private async void do_review (Object object) {
+        pages.set_visible_child (review_page);
+
+        review_page.setup (object as VMCreator);
+    }
+
+    [GtkCallback]
+    private async void do_create (Object object) {
+        var machine = object as LibvirtMachine;
+
+        var vm_creator = machine.vm_creator;
+        try {
+            vm_creator.launch_vm (machine);
+        } catch (GLib.Error error) {
+            warning ("Failed to create machine: %s", error.message);
+        }
+
+        vm_creator.install_media.clean_up_preparation_cache ();
+
+        //App.app.main_window.select_item (machine);
+        machine.domain.start (0);
+        close ();
+    }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/assistant/pages/review-page.ui")]
+private class Boxes.AssistantReviewPage : AssistantPage {
+
+    [GtkChild]
+    private WizardSummary summary;
+    [GtkChild]
+    private InfoBar nokvm_infobar;
+
+    public async void setup (VMCreator vm_creator) {
+        try {
+            artifact = yield vm_creator.create_vm (null); // TODO: make it cancellable
+        } catch (IOError.CANCELLED cancel_error) { // We did this, so ignore!
+        } catch (GLib.Error error) {
+            warning ("Box setup failed: %s", error.message);
+        }
+
+        yield populate (artifact as LibvirtMachine);
+    }
+
+    public async void populate (LibvirtMachine machine) {
+        var vm_creator = machine.vm_creator;
+        foreach (var property in vm_creator.install_media.get_vm_properties ())
+            summary.add_property (property.first, property.second);
+
+        try {
+            var config = null as GVirConfig.Domain;
+            yield App.app.async_launcher.launch (() => {
+                config = machine.domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+            });
+
+            var memory = format_size (config.memory * Osinfo.KIBIBYTES, FormatSizeFlags.IEC_UNITS);
+            summary.add_property (_("Memory"), memory);
+        } catch (GLib.Error error) {
+            warning ("Failed to get configuration for machine '%s': %s", machine.name, error.message);
+        }
+
+        if (!machine.importing && machine.storage_volume != null) {
+            try {
+                var volume_info = machine.storage_volume.get_info ();
+                var capacity = format_size (volume_info.capacity);
+                summary.add_property (_("Disk"),
+                                      // Translators: This is disk size. E.g "1 GB maximum".
+                                      _("%s maximum").printf (capacity));
+            } catch (GLib.Error error) {
+                warning ("Failed to get information on volume '%s': %s",
+                         machine.storage_volume.get_name (),
+                         error.message);
+            }
+
+            nokvm_infobar.visible = (machine.domain_config.get_virt_type () != 
GVirConfig.DomainVirtType.KVM);
+
+            // TODO: show customize button
+
+        }
+        print ("REview page setup done!\n");
+    }
+
+    public override void cleanup () {
+        summary.clear ();
+        nokvm_infobar.hide ();
+
+        if (artifact != null) {
+            App.app.delete_machine (artifact as Machine);
+        }
+    }
+
+    public override async void next () {
+        if (artifact == null) {
+            var wait = notify["artifact"].connect (() => {
+                next.callback ();
+            });
+            yield;
+            disconnect (wait);
+        }
+
+        done (artifact);
+    }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/assistant/pages/setup-page.ui")]
+private class Boxes.AssistantSetupPage : AssistantPage {
+    [GtkChild]
+    private Gtk.Box setup_box;
+
+    public async void setup (VMCreator vm_creator) {
+        this.artifact = vm_creator;
+
+        vm_creator.install_media.populate_setup_box (setup_box);
+        if (!vm_creator.install_media.need_user_input_for_vm_creation && 
vm_creator.install_media.ready_to_create)
+            done (vm_creator);
+
+        skip = !vm_creator.install_media.need_user_input_for_vm_creation;
+    }
+
+    public override async void next () {
+        var vm_creator = artifact as VMCreator;
+        if (vm_creator.install_media.ready_to_create) {
+            done (vm_creator);
+        }
+    }
+
+    public override void cleanup () {
+        if (skip) {
+            foreach (var child in setup_box.get_children ())
+                child.destroy ();
+        }
+    }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/assistant/pages/preparation-page.ui")]
+private class Boxes.AssistantPreparationPage : AssistantPage {
+    [GtkChild]
+    private Gtk.Label media_label;
+    [GtkChild]
+    private Gtk.Label status_label;
+    [GtkChild]
+    private Gtk.Image installer_image;
+    [GtkChild]
+    private Gtk.ProgressBar progress_bar;
+
+    private InstallerMedia _media;
+    public InstallerMedia media {
+        get { return _media; }
+        set {
+            _media = value;
+
+            if (_media.os != null) {
+                media_label.label = _media.os.name;
+                Downloader.fetch_os_logo.begin (installer_image, _media.os, 128);
+            }
+        }
+    }
+
+    public void setup (InstallerMedia media) {
+        try {
+            var media_manager = MediaManager.get_instance ();
+            media = media_manager.create_installer_media_from_media (media);
+        } catch (GLib.Error error) {
+            warning ("Failed to setup installation media '%s': %s", media.device_file, error.message);
+        }
+
+        prepare (media);
+
+        skip = true;
+    }
+
+    public async void prepare (InstallerMedia media) {
+        var progress = create_preparation_progress ();
+        if (!yield media.prepare (progress, null)) // add cancellable
+            return;
+
+        progress_bar.fraction = 1.0;
+
+        done (media.get_vm_creator ());
+    }
+
+    private ActivityProgress create_preparation_progress () {
+        var progress = new ActivityProgress ();
+
+        progress.notify["progress"].connect (() => {
+            if (progress.progress - progress_bar.fraction >= 0.01)
+                progress_bar.fraction = progress.progress;
+        });
+        progress_bar.fraction = progress.progress = 0;
+
+        progress.bind_property ("info", status_label, "label");
+
+        return progress;
+    }
+
+    public override void cleanup () {
+        // reset cancellation singleton
+    }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/assistant/pages/index-page.ui")]
+private class Boxes.AssistantIndexPage : AssistantPage {
+    GLib.ListStore source_model = new GLib.ListStore (typeof (InstallerMedia));
+    GLib.ListStore featured_model = new GLib.ListStore (typeof (Osinfo.Media));
+
+    private const int MAX_MEDIA_ENTRIES = 3;
+
+    [GtkChild]
+    private Gtk.ListBox source_medias;
+    [GtkChild]
+    private Gtk.ListBox featured_medias;
+
+    construct {
+        populate_media_lists.begin ();
+
+        source_medias.bind_model (source_model, add_media_entry);    
+        featured_medias.bind_model (featured_model, add_featured_media_entry);
+    }
+
+    private async void populate_media_lists () {
+        var media_manager = MediaManager.get_instance ();
+
+        var source_medias = yield media_manager.list_installer_medias ();
+        //for (var i = 0; i < MAX_MEDIA_ENTRIES; i++) {
+        foreach (var media in source_medias) {
+            source_model.append (media); 
+        }
+
+        var recommended_downloads = yield get_recommended_downloads ();
+        for (var i = 0; i < MAX_MEDIA_ENTRIES; i++) {
+            featured_model.append (recommended_downloads.nth (i).data);
+        }
+    }
+
+    private Gtk.Widget add_media_entry (GLib.Object object) {
+        return new WizardMediaEntry (object as InstallerMedia);
+    }
+
+    private Gtk.Widget add_featured_media_entry (GLib.Object object) {
+        return new WizardDownloadableEntry (object as Osinfo.Media);
+    }
+
+    [GtkCallback]
+    private void on_source_media_selected (Gtk.ListBoxRow row) {
+        done ((row as WizardMediaEntry).media);
+    }
+
+    public override void cleanup () {
+    }
+}
diff --git a/src/collection-toolbar.vala b/src/collection-toolbar.vala
index 10fd5b94..2e337148 100644
--- a/src/collection-toolbar.vala
+++ b/src/collection-toolbar.vala
@@ -70,6 +70,11 @@ private void on_connect_to_remote_btn_clicked () {
         window.show_remote_connection_assistant ();
     }
 
+    [GtkCallback]
+    private void on_create_vm_btn_clicked () {
+        window.show_vm_assistant ();
+    }
+
     [GtkCallback]
     private void on_back_btn_clicked () {
         window.set_state (UIState.COLLECTION);
diff --git a/src/meson.build b/src/meson.build
index 5b59c3b8..9e0bfec8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -118,6 +118,7 @@ vala_sources = [
   'snapshot-list-row.vala',
   'snapshots-property.vala',
   'assistant/remote-connection.vala',
+  'assistant/vm-assistant.vala',  
 ]
 
 dependencies = [
diff --git a/src/vm-creator.vala b/src/vm-creator.vala
index 8851bd4b..7fe0d7f6 100644
--- a/src/vm-creator.vala
+++ b/src/vm-creator.vala
@@ -3,7 +3,7 @@
 using Osinfo;
 using GVir;
 
-private class Boxes.VMCreator {
+private class Boxes.VMCreator : Object {
     // Seems installers aren't very consistent about exact number of bytes written so we ought to leave some 
margin
     // of error. It's better to report '100%' done while it's not exactly 100% than reporting '99%' done 
forever..
     private const int INSTALL_COMPLETE_PERCENT = 99;


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