[virt-tools-list] [virt-manager PATCH v2] Redesign OS distro selection UI to be faster to use

Radostin Stoyanov rstoyanov1 at gmail.com
Sat Apr 28 23:47:56 UTC 2018


Hi Daniel,

I have a few comments. I hope they will be helpful.

1. I noticed that when uncheck "install-detect-os" (Automatically detect
from the installation media / source) and then press "Back" followed by
"Forward" the inactive state of the "install-detect-os" checkbox will be
preserved, but the input field for "install-os-name" SearchEntry will
remain disabled/not sensitive (even after providing media/source). This
can be solved with the following diff:

diff --git a/virtManager/create.py b/virtManager/create.py
index ac252dda..3a1c3666 100644
--- a/virtManager/create.py
+++ b/virtManager/create.py
@@ -2029,8 +2029,11 @@ class vmmCreate(vmmGObjectUI):
         if check_install_page and not is_install_page:
             return
         if not media:
+            autodetect = self.widget("install-detect-os").get_active()
             name = self.widget("install-os-name")
-            if not name.get_sensitive():
+            if not autodetect:
+                name.set_sensitive(True)
+            else:
                 name.set_text(_("Waiting for install media / source"))
             return
         if not self._is_os_detect_active():


2. In my opinion, when reading the content of "install-os-name"
SearchEntry we should apply .strip() to remove trailing/leading
white-space. Otherwise no results will be shown.

diff --git a/virtManager/create.py b/virtManager/create.py
index ac252dda..15143551 100644
--- a/virtManager/create.py
+++ b/virtManager/create.py
@@ -1265,7 +1265,7 @@ class vmmCreate(vmmGObjectUI):
             self._start_detect_os_if_needed()
 
     def _os_name_search_changed(self, src):
-        searchname = src.get_text()
+        searchname = src.get_text().strip()
         if self._os is not None:
             if self._os.label != searchname:
                 self._os = None


3. I think it would be better to set the vertical scroll policy of
GtkScrolledWindow (contains the OS instances) to Automatic. In this way,
vertical scroll bar will be show only when needed.

diff --git a/ui/oslist.ui b/ui/oslist.ui
index cdf84462..4d58c769 100644
--- a/ui/oslist.ui
+++ b/ui/oslist.ui
@@ -59,7 +59,6 @@ or use the "Generic" entry.</property>
           <object class="GtkScrolledWindow">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="vscrollbar_policy">always</property>
             <property name="shadow_type">in</property>
             <child>
               <object class="GtkTreeView" id="os-list">

4. Simplify _os_name_search_changed() ?

diff --git a/virtManager/create.py b/virtManager/create.py
index ac252dda..318d9e87 100644
--- a/virtManager/create.py
+++ b/virtManager/create.py
@@ -1266,16 +1266,11 @@ class vmmCreate(vmmGObjectUI):
 
     def _os_name_search_changed(self, src):
         searchname = src.get_text()
-        if self._os is not None:
-            if self._os.label != searchname:
-                self._os = None
-
-        if self._os is None:
-            if src.get_sensitive() and src.get_text() != "":
-                self._os_list.filter_name(src.get_text())
-                self._os_list.show(src)
-            else:
-                self._os_list.hide()
+        if self._os is not None and self._os.label != searchname:
+            self._os = None
+        elif self._os is None and src.get_sensitive() and
src.get_text() != "":
+            self._os_list.filter_name(searchname)
+            self._os_list.show(src)
         else:
             self._os_list.hide()

5. What do you think about adding horizontal grid lines?

diff --git a/ui/oslist.ui b/ui/oslist.ui
index cdf84462..85c4c6ff 100644
--- a/ui/oslist.ui
+++ b/ui/oslist.ui
@@ -67,6 +67,7 @@ or use the "Generic" entry.</property>
                 <property name="can_focus">True</property>
                 <property name="headers_visible">False</property>
                 <property name="enable_search">False</property>
+                <property name="enable_grid_lines">horizontal</property>
                 <child internal-child="selection">
                   <object class="GtkTreeSelection"/>
                 </child>

6. Instead of connecting to the "changed" signal of
Gtk|||TreeSelection|, we can use "row_activated" for GtkTreeView. This
will enable the hover functionality for rows, IMHO looks better.

diff --git a/ui/oslist.ui b/ui/oslist.ui
index cdf84462..f64f96db 100644
--- a/ui/oslist.ui
+++ b/ui/oslist.ui
@@ -67,6 +67,8 @@ or use the "Generic" entry.</property>
                 <property name="can_focus">True</property>
                 <property name="headers_visible">False</property>
                 <property name="enable_search">False</property>
+                <property name="hover_selection">True</property>
+                <property name="activate_on_single_click">True</property>
                 <child internal-child="selection">
                   <object class="GtkTreeSelection"/>
                 </child>
diff --git a/virtManager/oslist.py b/virtManager/oslist.py
index fe31a085..fc60f346 100644
--- a/virtManager/oslist.py
+++ b/virtManager/oslist.py
@@ -56,15 +56,14 @@ class vmmOSList(vmmGObjectUI):
         nameCol.add_attribute(text, 'text', 1)
         os_list.append_column(nameCol)
 
-        sel = os_list.get_selection()
-        sel.connect("changed", self._os_selected)
+        os_list.connect("row_activated", self._os_selected)
 
     def _eol_toggled(self, src):
         self._filter_eol = not src.get_active()
         self._refilter()
 
-    def _os_selected(self, sel):
-        model, titer = sel.get_selected()
+    def _os_selected(self, tree_view, path, column):
+        model, titer = tree_view.get_selection().get_selected()
         if titer is None:
             self.emit("os-selected", None)
         else:



On 24/04/18 19:10, Daniel P. Berrangé wrote:
> The current OS distro selection UI is fairly cumbersome to use. First
> you need to decide on a variant, then decide a distro and then look for
> the version you want. The list is filtered by default so only a subset
> of OS are displayed. So for less common distros you'll then need to
> start again and tell it to show all OS to try to find the one you want.
>
> The core problem is that we have an incredibly large list and want to
> make it easy for the user to find a specific entry. The modern UI
> paradigm for this problem is to provide interactive search with
> live updated results. The current UI does provide an interactive search
> facility on the OS version results, but you still have to first select a
> variant to be able to use the search which is unhelpful.
>
> This patch attempts to better apply the search UI design to the OS selection
> problem. We get rid of the notion of variants, distros and version, and
> provide a single text entry box in which the user can type a few letters
> of the OS name. As they type, a popover displays the matching results
> filtered on OS name. By default end of life OS will be hidden, so in
> general there will only be a small handful of results left after just
> typing a few characters. This makes it very quick to find and select the
> desired OS, without needing to provide a mutli-step navigation hierarchy.
>
> https://bugzilla.redhat.com/show_bug.cgi?id=1464306
>
> Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
> ---
>
> Changed in v2:
>
>  - Redesigned UI to have just a text entry, no separate label
>  - Match on short-id and label when searching
>  - Give hint for when no matches are found
>
>  tests/osdict.py       |  26 ----
>  ui/create.ui          | 205 +++++++-----------------
>  ui/oslist.ui          | 101 ++++++++++++
>  virtManager/create.py | 421 +++++++++-----------------------------------------
>  virtManager/oslist.py | 107 +++++++++++++
>  virtinst/osdict.py    |  88 +++--------
>  6 files changed, 356 insertions(+), 592 deletions(-)
>  create mode 100644 ui/oslist.ui
>  create mode 100644 virtManager/oslist.py
>
> diff --git a/tests/osdict.py b/tests/osdict.py
> index 62d3ca7b..4c299962 100644
> --- a/tests/osdict.py
> +++ b/tests/osdict.py
> @@ -45,29 +45,3 @@ class TestOSDB(unittest.TestCase):
>          guest.type = "qemu"
>          res = OSDB.lookup_os("fedora21").get_recommended_resources(guest)
>          assert res["n-cpus"] == 1
> -
> -    def test_list_os(self):
> -        full_list = OSDB.list_os()
> -        pref_list = OSDB.list_os(typename="linux", sortpref=["fedora", "rhel"])
> -        support_list = OSDB.list_os(only_supported=True)
> -
> -        assert full_list[0] is not pref_list[0]
> -        assert len(full_list) > len(support_list)
> -        assert len(OSDB.list_os(typename="generic")) == 1
> -
> -        # Verify that sort order actually worked
> -        found_fedora = False
> -        found_rhel = False
> -        for idx, osobj in enumerate(pref_list[:]):
> -            if osobj.name.startswith("fedora"):
> -                found_fedora = True
> -                continue
> -
> -            for osobj2 in pref_list[idx:]:
> -                if osobj2.name.startswith("rhel"):
> -                    found_rhel = True
> -                    continue
> -                break
> -            break
> -
> -        assert found_fedora and found_rhel
> diff --git a/ui/create.ui b/ui/create.ui
> index 532c0331..58312d56 100644
> --- a/ui/create.ui
> +++ b/ui/create.ui
> @@ -1,5 +1,5 @@
>  <?xml version="1.0" encoding="UTF-8"?>
> -<!-- Generated with glade 3.20.3 -->
> +<!-- Generated with glade 3.20.4 -->
>  <interface>
>    <requires lib="gtk+" version="3.14"/>
>    <object class="GtkAdjustment" id="adjustment2">
> @@ -2027,184 +2027,89 @@ connections is not yet supported.</small></property>
>                            </packing>
>                          </child>
>                          <child>
> -                          <object class="GtkBox" id="install-os-distro-box">
> +                          <object class="GtkFrame" id="install-os-distro-box">
>                              <property name="visible">True</property>
>                              <property name="can_focus">False</property>
> -                            <property name="orientation">vertical</property>
> -                            <property name="spacing">6</property>
> -                            <child>
> -                              <object class="GtkBox" id="install-detect-os-box">
> -                                <property name="visible">True</property>
> -                                <property name="can_focus">False</property>
> -                                <property name="orientation">vertical</property>
> -                                <signal name="hide" handler="on_install_detect_os_box_hide" swapped="no"/>
> -                                <signal name="show" handler="on_install_detect_os_box_show" swapped="no"/>
> -                                <child>
> -                                  <object class="GtkCheckButton" id="install-detect-os">
> -                                    <property name="label" translatable="yes">A_utomatically detect operating system based on install media</property>
> -                                    <property name="visible">True</property>
> -                                    <property name="can_focus">True</property>
> -                                    <property name="receives_default">False</property>
> -                                    <property name="use_underline">True</property>
> -                                    <property name="active">True</property>
> -                                    <property name="draw_indicator">True</property>
> -                                    <signal name="toggled" handler="on_install_detect_os_toggled" swapped="no"/>
> -                                  </object>
> -                                  <packing>
> -                                    <property name="expand">False</property>
> -                                    <property name="fill">True</property>
> -                                    <property name="position">0</property>
> -                                  </packing>
> -                                </child>
> -                              </object>
> -                              <packing>
> -                                <property name="expand">False</property>
> -                                <property name="fill">False</property>
> -                                <property name="position">0</property>
> -                              </packing>
> -                            </child>
> -                            <child>
> -                              <object class="GtkLabel" id="install-nodetect-label">
> -                                <property name="can_focus">False</property>
> -                                <property name="halign">start</property>
> -                                <property name="label" translatable="yes">Choose an operating system type and version</property>
> -                              </object>
> -                              <packing>
> -                                <property name="expand">False</property>
> -                                <property name="fill">False</property>
> -                                <property name="position">1</property>
> -                              </packing>
> -                            </child>
> +                            <property name="label_xalign">0</property>
> +                            <property name="shadow_type">none</property>
>                              <child>
> -                              <object class="GtkAlignment" id="alignment9">
> +                              <object class="GtkAlignment">
>                                  <property name="visible">True</property>
>                                  <property name="can_focus">False</property>
> -                                <property name="left_padding">15</property>
> +                                <property name="top_padding">6</property>
> +                                <property name="bottom_padding">6</property>
> +                                <property name="left_padding">12</property>
>                                  <child>
> -                                  <object class="GtkGrid" id="table1">
> +                                  <object class="GtkBox">
>                                      <property name="visible">True</property>
>                                      <property name="can_focus">False</property>
> -                                    <property name="row_spacing">4</property>
> -                                    <property name="column_spacing">6</property>
> -                                    <child>
> -                                      <object class="GtkLabel" id="install-os-version-label">
> -                                        <property name="visible">True</property>
> -                                        <property name="can_focus">False</property>
> -                                        <property name="halign">start</property>
> -                                        <property name="valign">center</property>
> -                                        <property name="label">-</property>
> -                                        <child internal-child="accessible">
> -                                          <object class="AtkObject" id="install-os-version-label-atkobject">
> -                                            <property name="AtkObject::accessible-name">install-os-version-label</property>
> -                                          </object>
> -                                        </child>
> -                                      </object>
> -                                      <packing>
> -                                        <property name="left_attach">2</property>
> -                                        <property name="top_attach">1</property>
> -                                      </packing>
> -                                    </child>
> -                                    <child>
> -                                      <object class="GtkLabel" id="install-os-type-label">
> -                                        <property name="visible">True</property>
> -                                        <property name="can_focus">False</property>
> -                                        <property name="halign">start</property>
> -                                        <property name="valign">center</property>
> -                                        <property name="label">-</property>
> -                                        <child internal-child="accessible">
> -                                          <object class="AtkObject" id="install-os-type-label-atkobject">
> -                                            <property name="AtkObject::accessible-name">install-os-type-label</property>
> -                                          </object>
> -                                        </child>
> -                                      </object>
> -                                      <packing>
> -                                        <property name="left_attach">2</property>
> -                                        <property name="top_attach">0</property>
> -                                      </packing>
> -                                    </child>
> -                                    <child>
> -                                      <object class="GtkComboBox" id="install-os-type">
> -                                        <property name="visible">True</property>
> -                                        <property name="can_focus">False</property>
> -                                        <property name="valign">center</property>
> -                                        <signal name="changed" handler="on_install_os_type_changed" swapped="no"/>
> -                                        <child internal-child="accessible">
> -                                          <object class="AtkObject" id="install-os-type-atkobject">
> -                                            <property name="AtkObject::accessible-name">install-os-type</property>
> -                                          </object>
> -                                        </child>
> -                                      </object>
> -                                      <packing>
> -                                        <property name="left_attach">1</property>
> -                                        <property name="top_attach">0</property>
> -                                      </packing>
> -                                    </child>
> -                                    <child>
> -                                      <object class="GtkLabel" id="label17">
> -                                        <property name="visible">True</property>
> -                                        <property name="can_focus">False</property>
> -                                        <property name="halign">start</property>
> -                                        <property name="valign">center</property>
> -                                        <property name="label" translatable="yes">_Version:</property>
> -                                        <property name="use_underline">True</property>
> -                                        <property name="mnemonic_widget">install-os-version</property>
> -                                      </object>
> -                                      <packing>
> -                                        <property name="left_attach">0</property>
> -                                        <property name="top_attach">1</property>
> -                                      </packing>
> -                                    </child>
> +                                    <property name="orientation">vertical</property>
> +                                    <property name="spacing">6</property>
>                                      <child>
> -                                      <object class="GtkLabel" id="label16">
> +                                      <object class="GtkSearchEntry" id="install-os-name">
>                                          <property name="visible">True</property>
> -                                        <property name="can_focus">False</property>
> -                                        <property name="halign">end</property>
> -                                        <property name="valign">center</property>
> -                                        <property name="label" translatable="yes">OS _type:</property>
> -                                        <property name="use_underline">True</property>
> -                                        <property name="mnemonic_widget">install-os-type</property>
> +                                        <property name="can_focus">True</property>
> +                                        <property name="primary_icon_name">edit-find-symbolic</property>
> +                                        <property name="primary_icon_activatable">False</property>
> +                                        <property name="primary_icon_sensitive">False</property>
> +                                        <signal name="search-changed" handler="on_install_os_name_search_changed" swapped="no"/>
> +                                        <signal name="stop-search" handler="on_install_os_name_stop_search" swapped="no"/>
>                                        </object>
>                                        <packing>
> -                                        <property name="left_attach">0</property>
> -                                        <property name="top_attach">0</property>
> +                                        <property name="expand">False</property>
> +                                        <property name="fill">True</property>
> +                                        <property name="position">0</property>
>                                        </packing>
>                                      </child>
>                                      <child>
> -                                      <object class="GtkComboBox" id="install-os-version">
> +                                      <object class="GtkBox" id="install-detect-os-box">
>                                          <property name="visible">True</property>
>                                          <property name="can_focus">False</property>
> -                                        <property name="valign">center</property>
> -                                        <property name="has_entry">True</property>
> -                                        <signal name="changed" handler="on_install_os_version_changed" swapped="no"/>
> -                                        <child internal-child="entry">
> -                                          <object class="GtkEntry" id="install-os-version-entry">
> +                                        <child>
> +                                          <object class="GtkCheckButton" id="install-detect-os">
> +                                            <property name="label" translatable="yes">A_utomatically detect from the installation media / source</property>
> +                                            <property name="visible">True</property>
>                                              <property name="can_focus">True</property>
> -                                            <child internal-child="accessible">
> -                                              <object class="AtkObject" id="install-os-version-entry-atkobject">
> -                                                <property name="AtkObject::accessible-name">install-os-version-entry</property>
> -                                              </object>
> -                                            </child>
> +                                            <property name="receives_default">False</property>
> +                                            <property name="use_underline">True</property>
> +                                            <property name="active">True</property>
> +                                            <property name="draw_indicator">True</property>
> +                                            <signal name="toggled" handler="on_install_detect_os_toggled" swapped="no"/>
>                                            </object>
> +                                          <packing>
> +                                            <property name="expand">False</property>
> +                                            <property name="fill">True</property>
> +                                            <property name="position">0</property>
> +                                          </packing>
>                                          </child>
> -                                        <child internal-child="accessible">
> -                                          <object class="AtkObject" id="install-os-version-atkobject">
> -                                            <property name="AtkObject::accessible-name">install-os-version</property>
> +                                        <child>
> +                                          <object class="GtkSpinner" id="install-detect-os-spinner">
> +                                            <property name="visible">True</property>
> +                                            <property name="can_focus">False</property>
>                                            </object>
> +                                          <packing>
> +                                            <property name="expand">False</property>
> +                                            <property name="fill">True</property>
> +                                            <property name="position">1</property>
> +                                          </packing>
>                                          </child>
>                                        </object>
>                                        <packing>
> -                                        <property name="left_attach">1</property>
> -                                        <property name="top_attach">1</property>
> +                                        <property name="expand">False</property>
> +                                        <property name="fill">True</property>
> +                                        <property name="position">1</property>
>                                        </packing>
>                                      </child>
>                                    </object>
>                                  </child>
>                                </object>
> -                              <packing>
> -                                <property name="expand">False</property>
> -                                <property name="fill">False</property>
> -                                <property name="position">2</property>
> -                              </packing>
> +                            </child>
> +                            <child type="label">
> +                              <object class="GtkLabel">
> +                                <property name="visible">True</property>
> +                                <property name="can_focus">False</property>
> +                                <property name="label" translatable="yes">Operating system distribution:</property>
> +                              </object>
>                              </child>
>                            </object>
>                            <packing>
> diff --git a/ui/oslist.ui b/ui/oslist.ui
> new file mode 100644
> index 00000000..cdf84462
> --- /dev/null
> +++ b/ui/oslist.ui
> @@ -0,0 +1,101 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<!-- Generated with glade 3.20.4 -->
> +<interface>
> +  <requires lib="gtk+" version="3.14"/>
> +  <object class="GtkPopover" id="vmm-oslist">
> +    <property name="width_request">400</property>
> +    <property name="height_request">300</property>
> +    <property name="can_focus">False</property>
> +    <child>
> +      <object class="GtkBox">
> +        <property name="visible">True</property>
> +        <property name="can_focus">False</property>
> +        <property name="margin_left">6</property>
> +        <property name="margin_right">6</property>
> +        <property name="margin_top">6</property>
> +        <property name="margin_bottom">6</property>
> +        <property name="orientation">vertical</property>
> +        <property name="spacing">6</property>
> +        <child>
> +          <object class="GtkBox">
> +            <property name="visible">True</property>
> +            <property name="can_focus">False</property>
> +            <property name="spacing">6</property>
> +            <child>
> +              <object class="GtkImage">
> +                <property name="visible">True</property>
> +                <property name="can_focus">False</property>
> +                <property name="stock">gtk-info</property>
> +                <property name="icon_size">3</property>
> +              </object>
> +              <packing>
> +                <property name="expand">False</property>
> +                <property name="fill">True</property>
> +                <property name="position">0</property>
> +              </packing>
> +            </child>
> +            <child>
> +              <object class="GtkLabel">
> +                <property name="visible">True</property>
> +                <property name="can_focus">False</property>
> +                <property name="label" translatable="yes">Can't find the operating system you are looking for ?
> +Try selecting the next most recent version displayed,
> +or use the "Generic" entry.</property>
> +              </object>
> +              <packing>
> +                <property name="expand">True</property>
> +                <property name="fill">True</property>
> +                <property name="position">1</property>
> +              </packing>
> +            </child>
> +          </object>
> +          <packing>
> +            <property name="expand">False</property>
> +            <property name="fill">True</property>
> +            <property name="position">0</property>
> +          </packing>
> +        </child>
> +        <child>
> +          <object class="GtkScrolledWindow">
> +            <property name="visible">True</property>
> +            <property name="can_focus">True</property>
> +            <property name="vscrollbar_policy">always</property>
> +            <property name="shadow_type">in</property>
> +            <child>
> +              <object class="GtkTreeView" id="os-list">
> +                <property name="visible">True</property>
> +                <property name="can_focus">True</property>
> +                <property name="headers_visible">False</property>
> +                <property name="enable_search">False</property>
> +                <child internal-child="selection">
> +                  <object class="GtkTreeSelection"/>
> +                </child>
> +              </object>
> +            </child>
> +          </object>
> +          <packing>
> +            <property name="expand">True</property>
> +            <property name="fill">True</property>
> +            <property name="position">1</property>
> +          </packing>
> +        </child>
> +        <child>
> +          <object class="GtkCheckButton" id="include-eol">
> +            <property name="label" translatable="yes">Include end of life operating systems</property>
> +            <property name="visible">True</property>
> +            <property name="can_focus">True</property>
> +            <property name="receives_default">False</property>
> +            <property name="halign">start</property>
> +            <property name="draw_indicator">True</property>
> +            <signal name="toggled" handler="on_include_eol_toggled" swapped="no"/>
> +          </object>
> +          <packing>
> +            <property name="expand">False</property>
> +            <property name="fill">True</property>
> +            <property name="position">2</property>
> +          </packing>
> +        </child>
> +      </object>
> +    </child>
> +  </object>
> +</interface>
> diff --git a/virtManager/create.py b/virtManager/create.py
> index 5afe0afe..ac252dda 100644
> --- a/virtManager/create.py
> +++ b/virtManager/create.py
> @@ -29,6 +29,7 @@ from .engine import vmmEngine
>  from .mediacombo import vmmMediaCombo
>  from .netlist import vmmNetworkList
>  from .storagebrowse import vmmStorageBrowser
> +from .oslist import vmmOSList
>  
>  # Number of seconds to wait for media detection
>  DETECT_TIMEOUT = 20
> @@ -124,6 +125,7 @@ class vmmCreate(vmmGObjectUI):
>  
>          self._guest = None
>          self._failed_guest = None
> +        self._os = None
>  
>          # Distro detection state variables
>          self._detect_os_in_progress = False
> @@ -171,10 +173,6 @@ class vmmCreate(vmmGObjectUI):
>              "on_install_container_source_toggle": self._container_source_toggle,
>  
>              "on_install_detect_os_toggled": self._toggle_detect_os,
> -            "on_install_os_type_changed": self._change_os_type,
> -            "on_install_os_version_changed": self._change_os_version,
> -            "on_install_detect_os_box_show": self._os_detect_visibility_changed,
> -            "on_install_detect_os_box_hide": self._os_detect_visibility_changed,
>  
>              "on_kernel_browse_clicked": self._browse_kernel,
>              "on_initrd_browse_clicked": self._browse_initrd,
> @@ -183,6 +181,9 @@ class vmmCreate(vmmGObjectUI):
>              "on_enable_storage_toggled": self._toggle_enable_storage,
>  
>              "on_create_vm_name_changed": self._name_changed,
> +
> +            "on_install_os_name_search_changed": self._os_name_search_changed,
> +            "on_install_os_name_stop_search": self._os_name_stop_search,
>          })
>          self.bind_escape_key_close()
>  
> @@ -294,46 +295,6 @@ class vmmCreate(vmmGObjectUI):
>          # Lists for OS container bootstrap
>          set_model_list("install-oscontainer-source-url-combo")
>  
> -        def sep_func(model, it, combo):
> -            ignore = combo
> -            return model[it][OS_COL_IS_SEP]
> -
> -        def make_os_model():
> -            # [os value, os label, is seperator, is 'show all']
> -            cols = []
> -            cols.insert(OS_COL_ID, str)
> -            cols.insert(OS_COL_LABEL, str)
> -            cols.insert(OS_COL_IS_SEP, bool)
> -            cols.insert(OS_COL_IS_SHOW_ALL, bool)
> -            return Gtk.TreeStore(*cols)
> -
> -        def make_completion_model():
> -            # [os value, os label]
> -            cols = []
> -            cols.insert(OS_COL_ID, str)
> -            cols.insert(OS_COL_LABEL, str)
> -            return Gtk.ListStore(*cols)
> -
> -        # Lists for distro type + variant
> -        os_type_list = self.widget("install-os-type")
> -        os_type_model = make_os_model()
> -        os_type_list.set_model(os_type_model)
> -        uiutil.init_combo_text_column(os_type_list, 1)
> -        os_type_list.set_row_separator_func(sep_func, os_type_list)
> -
> -        os_variant_list = self.widget("install-os-version")
> -        os_variant_model = make_os_model()
> -        os_variant_list.set_model(os_variant_model)
> -        uiutil.init_combo_text_column(os_variant_list, 1)
> -        os_variant_list.set_row_separator_func(sep_func, os_variant_list)
> -
> -        entry = self.widget("install-os-version-entry")
> -        completion = Gtk.EntryCompletion()
> -        entry.set_completion(completion)
> -        completion.set_text_column(1)
> -        completion.set_inline_completion(True)
> -        completion.set_model(make_completion_model())
> -
>          # Archtecture
>          archList = self.widget("arch")
>          # [label, guest.os.arch value]
> @@ -365,6 +326,16 @@ class vmmCreate(vmmGObjectUI):
>          lst.set_model(model)
>          uiutil.init_combo_text_column(lst, 0)
>  
> +        self._os_list = vmmOSList()
> +        self._os_list.connect("os-selected", self._os_name_selected)
> +
> +    def _os_name_selected(self, ignore, osobj):
> +        name = self.widget("install-os-name")
> +        self._os = osobj
> +        self._os_list.hide()
> +
> +        if self._os is not None:
> +            name.set_text(self._os.label)
>  
>      def _reset_state(self, urihint=None):
>          """ @@ -395,8 +366,6 @@ class vmmCreate(vmmGObjectUI): # Distro/Variant
> self._toggle_detect_os(self.widget("install-detect-os"))
> -        self._populate_os_type_model()
> -        self.widget("install-os-type").set_active(0)
>  
>          def _populate_media_model(media_model, urls):
>              media_model.clear()
> @@ -415,7 +384,6 @@ class vmmCreate(vmmGObjectUI):
>          self.widget("install-url-options").set_expanded(False)
>          urlmodel = self.widget("install-url-combo").get_model()
>          _populate_media_model(urlmodel, self.config.get_media_urls())
> -        self._set_distro_labels("-", "-")
>  
>          # Install import
>          self.widget("install-import-entry").set_text("")
> @@ -970,183 +938,6 @@ class vmmCreate(vmmGObjectUI):
>      # Helpers for populating OS type/variant UI #
>      #############################################
>  
> -    def _add_os_row(self, model, name="", label="",
> -                      sep=False, action=False, parent=None):
> -        """
> -        Helper for building an os type/version row and adding it to
> -        the list model if necessary
> -        """
> -
> -        row = []
> -        row.insert(OS_COL_ID, name)
> -        row.insert(OS_COL_LABEL, label)
> -        row.insert(OS_COL_IS_SEP, sep)
> -        row.insert(OS_COL_IS_SHOW_ALL, action)
> -
> -        return model.append(parent, row)
> -
> -    def _add_completion_row(self, model, name, label):
> -        row = []
> -        row.insert(OS_COL_ID, name)
> -        row.insert(OS_COL_LABEL, label)
> -        model.append(row)
> -
> -    def _populate_os_type_model(self):
> -        widget = self.widget("install-os-type")
> -        model = widget.get_model()
> -        model.clear()
> -
> -        # Kind of a hack, just show linux + windows by default since
> -        # that's all 98% of people care about
> -        supported = {"generic", "linux", "windows"}
> -
> -        # Move 'generic' to the front of the list
> -        types = virtinst.OSDB.list_types()
> -        types.remove("generic")
> -        types.insert(0, "generic")
> -
> -        # Pretty names for OSes.  If a new OS is not found here,
> -        # its capitalized name is used.
> -        oses = {
> -            "bsd": _("BSD"),
> -            "generic": _("Generic"),
> -            "linux": _("Linux"),
> -            "macos": _("macOS"),
> -            "other": _("Others"),
> -            "solaris": _("Solaris"),
> -            "windows": _("Windows"),
> -        }
> -
> -        # When only the "supported" types are requested,
> -        # filter them.
> -        if not self._show_all_os_was_selected:
> -            types = [t for t in types if t in supported]
> -
> -        for typename in types:
> -            try:
> -                typelabel = oses[typename]
> -            except KeyError:
> -                typelabel = typename.capitalize()
> -
> -            self._add_os_row(model, typename, typelabel)
> -
> -        if not self._show_all_os_was_selected:
> -            self._add_os_row(model, sep=True)
> -            self._add_os_row(model, label=_("Show all OS options"), action=True)
> -
> -        # Select 'generic' by default
> -        widget.set_active(0)
> -
> -    def _populate_os_variant_model(self, _type):
> -        widget = self.widget("install-os-version")
> -        model = widget.get_model()
> -        model.clear()
> -
> -        completion_model = self.widget("install-os-version-entry").get_completion().get_model()
> -        completion_model.clear()
> -
> -        preferred = self.config.preferred_distros
> -
> -        # All the subgroups for top-level types.  Distributions not
> -        # belonging to these groups will be shown either in a "Others"
> -        # group, or top-level if there are no other groups.
> -        groups = {
> -            "altlinux": _("ALT Linux"),
> -            "centos": _("CentOS"),
> -            "debian": _("Debian"),
> -            "fedora": _("Fedora"),
> -            "freebsd": _("FreeBSD"),
> -            "mageia": _("Mageia"),
> -            "netbsd": _("NetBSD"),
> -            "openbsd": _("OpenBSD"),
> -            "opensuse": _("openSUSE"),
> -            "rhel": _("Red Hat Enterprise Linux"),
> -            "sled": _("SUSE Linux Enterprise Desktop"),
> -            "sles": _("SUSE Linux Enterprise Server"),
> -            "ubuntu": _("Ubuntu"),
> -        }
> -
> -        if self._show_all_os_was_selected:
> -            # List all the OSes, and determine which OSes have groups,
> -            # and which do not.
> -            variants = virtinst.OSDB.list_os(typename=_type,
> -                sortpref=preferred)
> -            all_distros = set([_os.distro for _os in variants])
> -            distros = [_os for _os in all_distros if _os in groups]
> -            distros.sort()
> -            other_distros = [_os for _os in all_distros if _os not in groups]
> -            parents = dict()
> -            if len(distros) > 0:
> -                # We have groups for the OSes, so create them.
> -                for d in distros:
> -                    parents[d] = self._add_os_row(model, "", groups[d])
> -                # Create the "Others" group at the end, for the OSes
> -                # without a group.
> -                if len(other_distros):
> -                    others_parent = self._add_os_row(model, "", _('Others'))
> -                    for d in other_distros:
> -                        parents[d] = others_parent
> -            else:
> -                # No groups, so assume the top-level will be the parent
> -                # all the OSes.
> -                for d in other_distros:
> -                    parents[d] = None
> -            for v in variants:
> -                self._add_os_row(model, v.name, v.label,
> -                    parent=parents[v.distro])
> -                self._add_completion_row(completion_model, v.name, v.label)
> -        else:
> -            # We are showing only the supported systems, so query them,
> -            # and add them directly to their type.
> -            variants = virtinst.OSDB.list_os(typename=_type,
> -                sortpref=preferred, only_supported=True)
> -            for v in variants:
> -                self._add_os_row(model, v.name, v.label)
> -                self._add_completion_row(completion_model, v.name, v.label)
> -
> -            # Add the menu entries to show all the OSes
> -            self._add_os_row(model, sep=True)
> -            self._add_os_row(model, label=_("Show all OS options"), action=True)
> -
> -        widget.set_active(0)
> -
> -    def _set_distro_labels(self, distro, ver):
> -        self.widget("install-os-type-label").set_text(distro)
> -        self.widget("install-os-version-label").set_text(ver)
> -
> -    def _set_os_id_in_ui(self, os_widget, os_id):
> -        """
> -        Helper method to set the os type/version widgets to the passed
> -        OS ID value
> -        """
> -        model = os_widget.get_model()
> -        def find_row():
> -            def cmp_func(model, path, it, user_data):
> -                ignore = path
> -                if model.get_value(it, OS_COL_ID) == os_id:
> -                    os_widget.set_active_iter(it)
> -                    user_data[0] = model.get_value(it, OS_COL_LABEL)
> -                    return True
> -                return False
> -            data = [None]
> -            model.foreach(cmp_func, data)
> -            label = data[0]
> -            if not label:
> -                os_widget.set_active(0)
> -            return label
> -
> -        label = None
> -        if os_id:
> -            label = find_row()
> -
> -            if not label and not self._show_all_os_was_selected:
> -                # We didn't find the OS in the variant UI, but we are only
> -                # showing the reduced OS list. Trigger the _show_all_os option,
> -                # and try again.
> -                os_widget.set_active(len(model) - 1)
> -                label = find_row()
> -        return label or _("Unknown")
> -
>      def _set_distro_selection(self, variant):
>          """
>          Update the UI with the distro that was detected from the detection
> @@ -1157,18 +948,16 @@ class vmmCreate(vmmGObjectUI):
>              # update the UI
>              return
>  
> -        distro_type = None
> -        distro_var = None
> +        name = self.widget("install-os-name")
>          if variant:
> -            osclass = virtinst.OSDB.lookup_os(variant)
> -            distro_type = osclass.get_typename()
> -            distro_var = osclass.name
> +            self._os = virtinst.OSDB.lookup_os(variant)
> +        else:
> +            self._os = None
>  
> -        dl = self._set_os_id_in_ui(
> -            self.widget("install-os-type"), distro_type)
> -        vl = self._set_os_id_in_ui(
> -            self.widget("install-os-version"), distro_var)
> -        self._set_distro_labels(dl, vl)
> +        if self._os is None:
> +            name.set_text(_("None detected"))
> +        else:
> +            name.set_text(self._os.label)
>  
>  
>      ###############################
> @@ -1200,7 +989,6 @@ class vmmCreate(vmmGObjectUI):
>          self.widget("summary-storage-path").set_markup(storagepath)
>  
>      def _populate_summary(self):
> -        distro, version, ignore1, dlabel, vlabel = self._get_config_os_info()
>          mem = _pretty_memory(int(self._guest.memory))
>          cpu = str(int(self._guest.vcpus))
>  
> @@ -1221,21 +1009,8 @@ class vmmCreate(vmmGObjectUI):
>          elif instmethod == INSTALL_PAGE_VZ_TEMPLATE:
>              install = _("Virtuozzo container")
>  
> -        osstr = ""
> -        have_os = True
> -        if self._guest.os.is_container():
> -            osstr = _("Linux")
> -        elif not distro:
> -            osstr = _("Generic")
> -            have_os = False
> -        elif not version:
> -            osstr = _("Generic") + " " + dlabel
> -            have_os = False
> -        else:
> -            osstr = vlabel
> -
> -        self.widget("finish-warn-os").set_visible(not have_os)
> -        self.widget("summary-os").set_text(osstr)
> +        self.widget("finish-warn-os").set_visible(self._os is None)
> +        self.widget("summary-os").set_text(self._os and self._os.label or _("Unknown"))
>          self.widget("summary-install").set_text(install)
>          self.widget("summary-mem").set_text(mem)
>          self.widget("summary-cpu").set_text(cpu) @@ -1317,31 +1092,6 @@ class vmmCreate(vmmGObjectUI):
> INSTALL_PAGE_CONTAINER_OS, INSTALL_PAGE_VZ_TEMPLATE] - def
> _get_config_os_info(self): - drow =
> uiutil.get_list_selected_row(self.widget("install-os-type"))
> -        distro = None
> -        dlabel = None
> -        variant = None
> -        entry = self.widget("install-os-version-entry")
> -        vlabel = entry.get_text()
> -
> -        for row in entry.get_completion().get_model():
> -            if row[OS_COL_LABEL] == vlabel:
> -                variant = row[OS_COL_ID]
> -                break
> -
> -        if not variant:
> -            return (None, None, False, None, None)
> -
> -        if drow:
> -            distro = drow[OS_COL_ID]
> -            dlabel = drow[OS_COL_LABEL]
> -
> -        return (distro and str(distro),
> -                str(variant),
> -                True,
> -                str(dlabel), str(vlabel))
> -
>      def _get_config_local_media(self, store_media=False):
>          if self.widget("install-cdrom-radio").get_active():
>              return self._mediacombo.get_path()
> @@ -1506,56 +1256,32 @@ class vmmCreate(vmmGObjectUI):
>      def _toggle_detect_os(self, src):
>          dodetect = src.get_active()
>  
> -        self.widget("install-os-type-label").set_visible(dodetect)
> -        self.widget("install-os-version-label").set_visible(dodetect)
> -        self.widget("install-os-type").set_visible(not dodetect)
> -        self.widget("install-os-version").set_visible(not dodetect)
> +        self.widget("install-os-name").set_sensitive(not dodetect)
> +        self.widget("install-os-name").set_text("")
> +        self._os = None
>  
>          if dodetect:
> -            self.widget("install-os-version-entry").set_text("")
>              self._os_already_detected_for_media = False
>              self._start_detect_os_if_needed()
>  
> -    def _selected_os_row(self):
> -        return uiutil.get_list_selected_row(self.widget("install-os-type"))
> -
> -    def _change_os_type(self, box):
> -        ignore = box
> -        row = self._selected_os_row()
> -        if not row:
> -            return
> +    def _os_name_search_changed(self, src):
> +        searchname = src.get_text()
> +        if self._os is not None:
> +            if self._os.label != searchname:
> +                self._os = None
>  
> -        _type = row[OS_COL_ID]
> -        self._populate_os_variant_model(_type)
> -        if not row[OS_COL_IS_SHOW_ALL]:
> -            return
> -
> -        self._show_all_os_was_selected = True
> -        self._populate_os_type_model()
> -
> -    def _change_os_version(self, box):
> -        show_all = uiutil.get_list_selection(box,
> -            column=OS_COL_IS_SHOW_ALL, check_entry=False)
> -        if not show_all:
> -            return
> -
> -        # 'show all OS' was clicked
> -        # Get previous type to reselect it later
> -        type_row = self._selected_os_row()
> -        if not type_row:
> -            return
> -        old_type = type_row[OS_COL_ID]
> -
> -        self._show_all_os_was_selected = True
> -        self._populate_os_type_model()
> +        if self._os is None:
> +            if src.get_sensitive() and src.get_text() != "":
> +                self._os_list.filter_name(src.get_text())
> +                self._os_list.show(src)
> +            else:
> +                self._os_list.hide()
> +        else:
> +            self._os_list.hide()
>  
> -        # Reselect previous type row
> -        os_type_list = self.widget("install-os-type")
> -        os_type_model = os_type_list.get_model()
> -        for idx, row in enumerate(os_type_model):
> -            if row[OS_COL_ID] == old_type:
> -                os_type_list.set_active(idx)
> -                break
> +    def _os_name_stop_search(self, src):
> +        src.set_text("")
> +        self._os_list.hide()
>  
>      def _local_media_toggled(self, src):
>          usecdrom = src.get_active()
> @@ -1568,19 +1294,6 @@ class vmmCreate(vmmGObjectUI):
>          else:
>              self._iso_changed(self.widget("install-iso-entry"))
>  
> -    def _os_detect_visibility_changed(self, src, ignore=None):
> -        is_visible = src.get_visible()
> -        detect_chkbox = self.widget("install-detect-os")
> -        nodetect_label = self.widget("install-nodetect-label")
> -
> -        detect_chkbox.set_active(is_visible)
> -        detect_chkbox.toggled()
> -
> -        if is_visible:
> -            nodetect_label.hide()
> -        else:
> -            nodetect_label.show()
> -
>      def _browse_oscontainer(self, ignore):
>          self._browse_file("install-oscontainer-fs", is_dir=True)
>      def _browse_app(self, ignore):
> @@ -1666,7 +1379,7 @@ class vmmCreate(vmmGObjectUI):
>                  fs_dir = [os.environ['HOME'],
>                            '.local/share/libvirt/filesystems/']
>  
> -            fs = fs_dir + [self._generate_default_name(None, None)]
> +            fs = fs_dir + [self._generate_default_name(None)]
>              self.widget("install-oscontainer-fs").set_text(os.path.join(*fs))
>  
>  
> @@ -1738,6 +1451,7 @@ class vmmCreate(vmmGObjectUI):
>          instnotebook = self.widget("install-method-pages")
>          detectbox = self.widget("install-detect-os-box")
>          osbox = self.widget("install-os-distro-box")
> +        name = self.widget("install-os-name")
>          instpage = self._get_config_install_page()
>  
>          # Setting OS value for a container guest doesn't really matter
> @@ -1752,6 +1466,12 @@ class vmmCreate(vmmGObjectUI):
>                          self._get_config_install_page() == INSTALL_PAGE_URL)
>  
>          detectbox.set_visible(enabledetect)
> +        name.set_sensitive(not enabledetect)
> +        if enabledetect:
> +            self._os = None
> +        else:
> +            if self._os is None:
> +                name.set_text("")
>  
>          if instpage == INSTALL_PAGE_PXE:
>              # Hide the install notebook for pxe, since there isn't anything
> @@ -1915,18 +1635,16 @@ class vmmCreate(vmmGObjectUI):
>              return False
>          return True
>  
> -    def _generate_default_name(self, distro, variant):
> +    def _generate_default_name(self, osobj):
>          force_num = False
>          if self._guest.os.is_container():
>              basename = "container"
>              force_num = True
> -        elif not distro:
> +        elif not osobj or not osobj.distro:
>              basename = "vm"
>              force_num = True
> -        elif not variant:
> -            basename = distro
>          else:
> -            basename = variant
> +            basename = osobj.distro
>  
>          if self._guest.os.arch != self.conn.caps.host.cpu.arch:
>              basename += "-%s" % _pretty_arch(self._guest.os.arch)
> @@ -1949,9 +1667,8 @@ class vmmCreate(vmmGObjectUI):
>          init = None
>          fs = None
>          template = None
> -        distro, variant, valid, ignore1, ignore2 = self._get_config_os_info()
>  
> -        if not valid:
> +        if self._os is None:
>              return self.err.val_err(_("Please specify a valid OS variant.")) if instmethod == INSTALL_PAGE_ISO: @@ -2050,7 +1767,7 @@ class
> vmmCreate(vmmGObjectUI): try: # Overwrite the guest installer =
> instclass(self.conn.get_backend()) - self._guest =
> self._build_guest(variant or distro) + self._guest =
> self._build_guest(self._os.name) if not self._guest: return False
> self._guest.installer = installer @@ -2114,7 +1831,7 @@ class
> vmmCreate(vmmGObjectUI): self._capsinfo.arch) try: - name =
> self._generate_default_name(distro, variant) + name =
> self._generate_default_name(self._os) self.widget("create-vm-name").set_text(name)
>              self._guest.name = name
>          except Exception as e:
> @@ -2138,11 +1855,10 @@ class vmmCreate(vmmGObjectUI):
>                  self, self.conn, path)
>  
>          res = None
> -        osobj = virtinst.OSDB.lookup_os(variant)
> -        if osobj:
> -            res = osobj.get_recommended_resources(self._guest)
> -            logging.debug("Recommended resources for variant=%s: %s",
> -                variant, res)
> +        if self._os is not None:
> +            res = self._os.get_recommended_resources(self._guest)
> +            logging.debug("Recommended resources for os=%s: %s",
> +                self._os.label, res)
>  
>          # Change the default values suggested to the user.
>          ram_size = DEFAULT_MEM
> @@ -2313,6 +2029,9 @@ class vmmCreate(vmmGObjectUI):
>          if check_install_page and not is_install_page:
>              return
>          if not media:
> +            name = self.widget("install-os-name")
> +            if not name.get_sensitive():
> +                name.set_text(_("Waiting for install media / source"))
>              return
>          if not self._is_os_detect_active():
>              return
> @@ -2357,6 +2076,9 @@ class vmmCreate(vmmGObjectUI):
>          detectThread.setDaemon(True)
>          detectThread.start()
>  
> +        spin = self.widget("install-detect-os-spinner")
> +        spin.start()
> +
>          self._report_detect_os_progress(0, thread_results,
>                  forward_after_finish)
>  
> @@ -2385,15 +2107,10 @@ class vmmCreate(vmmGObjectUI):
>          chance of the detection hanging (like slow URL lookup)
>          """
>          try:
> -            base = _("Detecting")
> -
>              if (thread_results.in_progress() and
>                  (idx < (DETECT_TIMEOUT * 2))):
>                  # Thread is still going and we haven't hit the timeout yet,
>                  # so update the UI labels and reschedule this function
> -                detect_str = base + ("." * ((idx % 3) + 1))
> -                self._set_distro_labels(detect_str, detect_str)
> -
>                  self.timeout_add(500, self._report_detect_os_progress,
>                      idx + 1, thread_results, forward_after_finish)
>                  return
> @@ -2403,6 +2120,8 @@ class vmmCreate(vmmGObjectUI):
>              distro = None
>              logging.exception("Error in distro detect timeout")
>  
> +        spin = self.widget("install-detect-os-spinner")
> +        spin.stop()
>          logging.debug("Finished UI OS detection.")
>  
>          self.widget("create-forward").set_sensitive(True)
> diff --git a/virtManager/oslist.py b/virtManager/oslist.py
> new file mode 100644
> index 00000000..fe31a085
> --- /dev/null
> +++ b/virtManager/oslist.py
> @@ -0,0 +1,107 @@
> +# Copyright (C) 2018 Red Hat, Inc.
> +#
> +# This work is licensed under the GNU GPLv2 or later.
> +# See the COPYING file in the top-level directory.
> +
> +import logging
> +
> +from gi.repository import Gtk
> +
> +import virtinst
> +
> +from .baseclass import vmmGObjectUI
> +
> +
> +class vmmOSList(vmmGObjectUI):
> +    __gsignals__ = {
> +        "os-selected": (vmmGObjectUI.RUN_FIRST, None, [object])
> +    }
> +
> +    def __init__(self):
> +        vmmGObjectUI.__init__(self, "oslist.ui", "vmm-oslist")
> +        self._cleanup_on_app_close()
> +
> +        self._filter_name = None
> +        self._filter_eol = True
> +
> +        self.builder.connect_signals({
> +            "on_include_eol_toggled": self._eol_toggled,
> +        })
> +
> +        self._init_state()
> +
> +    def _init_state(self):
> +
> +        self.topwin.set_modal(False)
> +        os_list = self.widget("os-list")
> +
> +        # (os object, label)
> +        os_list_model = Gtk.ListStore(object, str)
> +
> +        all_os = virtinst.OSDB.list_os()
> +
> +        for os in all_os:
> +            os_list_model.append([os, "%s (%s)" % (os.label, os.name)])
> +
> +        self._os_list_model = Gtk.TreeModelFilter(child_model=os_list_model)
> +        self._os_list_model.set_visible_func(self._filter_os)
> +
> +        os_list.set_model(self._os_list_model)
> +
> +        nameCol = Gtk.TreeViewColumn(_("Name"))
> +        nameCol.set_spacing(6)
> +
> +        text = Gtk.CellRendererText()
> +        nameCol.pack_start(text, True)
> +        nameCol.add_attribute(text, 'text', 1)
> +        os_list.append_column(nameCol)
> +
> +        sel = os_list.get_selection()
> +        sel.connect("changed", self._os_selected)
> +
> +    def _eol_toggled(self, src):
> +        self._filter_eol = not src.get_active()
> +        self._refilter()
> +
> +    def _os_selected(self, sel):
> +        model, titer = sel.get_selected()
> +        if titer is None:
> +            self.emit("os-selected", None)
> +        else:
> +            self.emit("os-selected", model[titer][0])
> +
> +    def _filter_os(self, model, titer, ignore1):
> +        os = model.get(titer, 0)[0]
> +        if self._filter_eol:
> +            if os.eol:
> +                return False
> +
> +        if self._filter_name is not None and self._filter_name != "":
> +            label = os.label.lower()
> +            name = os.name.lower()
> +            if (label.find(self._filter_name) == -1 and
> +                name.find(self._filter_name) == -1):
> +                return False
> +
> +        return True
> +
> +    def _refilter(self):
> +        os_list = self.widget("os-list")
> +        sel = os_list.get_selection()
> +        sel.unselect_all()
> +        self._os_list_model.refilter()
> +
> +    def filter_name(self, partial_name):
> +        self._filter_name = partial_name.lower()
> +        self._refilter()
> +
> +    def show(self, parent):
> +        logging.debug("Showing oslist")
> +        self.topwin.set_relative_to(parent)
> +        self.topwin.popup()
> +
> +    def hide(self):
> +        self.topwin.popdown()
> +
> +    def _cleanup(self):
> +        pass
> diff --git a/virtinst/osdict.py b/virtinst/osdict.py
> index 425557c8..ebeefef2 100644
> --- a/virtinst/osdict.py
> +++ b/virtinst/osdict.py
> @@ -10,62 +10,22 @@
>  import datetime
>  import logging
>  import re
> +import time
>  
>  import gi
>  gi.require_version('Libosinfo', '1.0')
>  from gi.repository import Libosinfo as libosinfo
> +from gi.repository import GLib
>  
>  
>  ###################
>  # Sorting helpers #
>  ###################
>  
> -def _remove_older_point_releases(distro_list):
> -    ret = distro_list[:]
> -
> -    def _get_minor_version(osobj):
> -        return int(osobj.name.rsplit(".", 1)[-1])
> -
> -    def _find_latest(prefix):
> -        """
> -        Given a prefix like 'rhel4', find the latest 'rhel4.X',
> -        and remove the rest from the os list
> -        """
> -        latest_os = None
> -        first_id = None
> -        for osobj in ret[:]:
> -            if not re.match("%s\.\d+" % prefix, osobj.name):
> -                continue
> -
> -            if first_id is None:
> -                first_id = ret.index(osobj)
> -            ret.remove(osobj)
> -
> -            if (latest_os and
> -                _get_minor_version(latest_os) > _get_minor_version(osobj)):
> -                continue
> -            latest_os = osobj
> -
> -        if latest_os:
> -            ret.insert(first_id, latest_os)
> -
> -    _find_latest("rhel4")
> -    _find_latest("rhel5")
> -    _find_latest("rhel6")
> -    _find_latest("rhel7")
> -    _find_latest("freebsd9")
> -    _find_latest("freebsd10")
> -    _find_latest("freebsd11")
> -    _find_latest("centos6")
> -    _find_latest("centos7")
> -    return ret
> -
> -
> -def _sort(tosort, sortpref=None, limit_point_releases=False):
> +def _sort(tosort):
>      sortby_mappings = {}
>      distro_mappings = {}
>      retlist = []
> -    sortpref = sortpref or []
>  
>      for key, osinfo in tosort.items():
>          # Libosinfo has some duplicate version numbers here, so append .1
> @@ -90,15 +50,8 @@ def _sort(tosort, sortpref=None, limit_point_releases=False):
>          distro_list.sort()
>          distro_list.reverse()
>  
> -    # Move the sortpref values to the front of the list
>      sorted_distro_list = list(distro_mappings.keys())
>      sorted_distro_list.sort()
> -    sortpref.reverse()
> -    for prefer in sortpref:
> -        if prefer not in sorted_distro_list:
> -            continue
> -        sorted_distro_list.remove(prefer)
> -        sorted_distro_list.insert(0, prefer)
>  
>      # Build the final list of sorted os objects
>      for distro in sorted_distro_list:
> @@ -107,10 +60,6 @@ def _sort(tosort, sortpref=None, limit_point_releases=False):
>              orig_key = sortby_mappings[key]
>              retlist.append(tosort[orig_key])
>  
> -    # Filter out older point releases
> -    if limit_point_releases:
> -        retlist = _remove_older_point_releases(retlist)
> -
>      return retlist
>  
>  
> @@ -236,25 +185,16 @@ class _OSDB(object):
>              "solaris", "other", "generic"]
>          return approved_types
>  
> -    def list_os(self, typename=None, only_supported=False, sortpref=None):
> +    def list_os(self):
>          """
>          List all OSes in the DB
> -
> -        :param typename: Only list OSes of this type
> -        :param only_supported: Only list OSses where self.supported == True
> -        :param sortpref: Sort these OSes at the front of the list
>          """
>          sortmap = {}
>  
>          for name, osobj in self._all_variants.items():
> -            if typename and typename != osobj.get_typename():
> -                continue
> -            if only_supported and not osobj.get_supported():
> -                continue
>              sortmap[name] = osobj
>  
> -        return _sort(sortmap, sortpref=sortpref,
> -            limit_point_releases=only_supported)
> +        return _sort(sortmap)
>  
>      def latest_regex(self, regex):
>          """ @@ -282,6 +222,24 @@ class _OsVariant(object): self.label = self._os
> and self._os.get_name() or "Generic"
>          self.codename = self._os and self._os.get_codename() or ""
>          self.distro = self._os and self._os.get_distro() or ""
> +        self.eol = False
> +
> +        eol = self._os and self._os.get_eol_date() or None
> +        rel = self._os and self._os.get_release_date() or None
> +
> +        # End of life if an EOL date is present and has past,
> +        # or if the release date is present and was 5 years or more
> +        if eol is not None:
> +            now = GLib.Date()
> +            now.set_time_t(time.time())
> +            if eol.compare(now) < 0:
> +                self.eol = True
> +        elif rel is not None:
> +            then = GLib.Date()
> +            then.set_time_t(time.time())
> +            then.subtract_years(5)
> +            if rel.compare(then) < 0:
> +                self.eol = True
>  
>          self.sortby = self._get_sortby()
>          self.urldistro = self._get_urldistro()

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/virt-tools-list/attachments/20180429/a8eebf33/attachment.htm>


More information about the virt-tools-list mailing list