mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-05 09:17:57 +03:00
1f8afc8668
Have the UI push users more towards better defaults, by discouraging the 'generic' entry and offering the 'linuxXXXX' entries when their distro or OS version is not in the list. Signed-off-by: Cole Robinson <crobinso@redhat.com>
249 lines
7.1 KiB
Python
249 lines
7.1 KiB
Python
# 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.
|
|
|
|
from gi.repository import Gdk, Gtk
|
|
|
|
import virtinst
|
|
from virtinst import xmlutil
|
|
|
|
from .baseclass import vmmGObjectUI
|
|
|
|
|
|
def _always_show(osobj):
|
|
return bool(osobj.is_generic() or osobj.is_linux_generic())
|
|
|
|
|
|
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._selected_os = None
|
|
self.search_entry = self.widget("os-name")
|
|
self.search_entry.set_placeholder_text(_("Type to start searching..."))
|
|
self.eol_text = self.widget("eol-warn").get_text()
|
|
|
|
self.builder.connect_signals({
|
|
"on_include_eol_toggled": self._eol_toggled_cb,
|
|
|
|
"on_os_name_activate": self._entry_activate_cb,
|
|
"on_os_name_key_press_event": self._key_press_cb,
|
|
"on_os_name_search_changed": self._search_changed_cb,
|
|
"on_os_name_stop_search": self._stop_search_cb,
|
|
"on_os_list_row_activated": self._os_selected_cb,
|
|
})
|
|
|
|
self._init_state()
|
|
|
|
def _cleanup(self):
|
|
pass
|
|
|
|
|
|
###########
|
|
# UI init #
|
|
###########
|
|
|
|
def _init_state(self):
|
|
os_list = self.widget("os-list")
|
|
|
|
# (os object, label)
|
|
os_list_model = Gtk.ListStore(object, str)
|
|
|
|
all_os = virtinst.OSDB.list_os(sortkey="label")
|
|
# Always put the generic entries at the end of the list
|
|
all_os = list(sorted(all_os, key=_always_show))
|
|
|
|
for os in all_os:
|
|
os_list_model.append([os, "%s (%s)" % (os.label, os.name)])
|
|
|
|
model_filter = Gtk.TreeModelFilter(child_model=os_list_model)
|
|
model_filter.set_visible_func(self._filter_os_cb)
|
|
|
|
os_list.set_model(model_filter)
|
|
|
|
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)
|
|
|
|
markup = "<small>%s</small>" % xmlutil.xml_escape(
|
|
self.widget("eol-warn").get_text())
|
|
self.widget("eol-warn").set_markup(markup)
|
|
|
|
|
|
###################
|
|
# Private helpers #
|
|
###################
|
|
|
|
def _set_default_selection(self):
|
|
os_list = self.widget("os-list")
|
|
sel = os_list.get_selection()
|
|
if not self.is_visible():
|
|
return
|
|
if not len(os_list.get_model()):
|
|
return # pragma: no cover
|
|
sel.select_iter(os_list.get_model()[0].iter)
|
|
|
|
def _refilter(self):
|
|
os_list = self.widget("os-list")
|
|
sel = os_list.get_selection()
|
|
sel.unselect_all()
|
|
os_list.get_model().refilter()
|
|
self._set_default_selection()
|
|
|
|
def _filter_by_name(self, partial_name):
|
|
self._filter_name = partial_name.lower()
|
|
self._refilter()
|
|
|
|
def _clear_filter(self):
|
|
self._filter_by_name("")
|
|
self.widget("os-scroll").get_vadjustment().set_value(0)
|
|
|
|
def _sync_os_selection(self):
|
|
model, titer = self.widget("os-list").get_selection().get_selected()
|
|
self._selected_os = None
|
|
if titer:
|
|
self._selected_os = model[titer][0]
|
|
self.search_entry.set_text(self._selected_os.label)
|
|
|
|
self.emit("os-selected", self._selected_os)
|
|
|
|
def _show_popover(self):
|
|
# Match width to the search_entry width. Height is based on
|
|
# whatever we can fit into the hardcoded create wizard sizes
|
|
r = self.search_entry.get_allocation()
|
|
self.topwin.set_size_request(r.width, 350)
|
|
|
|
self.topwin.set_relative_to(self.search_entry)
|
|
self.topwin.popup()
|
|
self._set_default_selection()
|
|
|
|
|
|
################
|
|
# UI Callbacks #
|
|
################
|
|
|
|
def _entry_activate_cb(self, src):
|
|
os_list = self.widget("os-list")
|
|
if not os_list.is_visible():
|
|
return # pragma: no cover
|
|
|
|
sel = os_list.get_selection()
|
|
model, rows = sel.get_selected_rows()
|
|
if rows:
|
|
self.select_os(model[rows[0]][0])
|
|
|
|
def _key_press_cb(self, src, event):
|
|
if Gdk.keyval_name(event.keyval) != "Down":
|
|
return
|
|
self._show_popover()
|
|
self.widget("os-list").grab_focus()
|
|
|
|
def _eol_toggled_cb(self, src):
|
|
self._filter_eol = not src.get_active()
|
|
self._refilter()
|
|
|
|
def _search_changed_cb(self, src):
|
|
"""
|
|
Called text in search_entry is changed
|
|
"""
|
|
searchname = src.get_text().strip()
|
|
selected_label = None
|
|
if self._selected_os:
|
|
selected_label = self._selected_os.label
|
|
|
|
if (not src.get_sensitive() or
|
|
not searchname or
|
|
selected_label == searchname):
|
|
self.topwin.popdown()
|
|
self._clear_filter()
|
|
return
|
|
|
|
self._filter_by_name(searchname)
|
|
self._show_popover()
|
|
|
|
def _stop_search_cb(self, src):
|
|
"""
|
|
Called when the search window is closed, like with Escape key
|
|
"""
|
|
if self._selected_os:
|
|
self.search_entry.set_text(self._selected_os.label)
|
|
else:
|
|
self.search_entry.set_text("")
|
|
|
|
def _os_selected_cb(self, src, path, column):
|
|
self._sync_os_selection()
|
|
|
|
def _filter_os_cb(self, model, titer, ignore1):
|
|
osobj = model.get(titer, 0)[0]
|
|
if self._filter_eol:
|
|
if osobj.eol:
|
|
return False
|
|
|
|
if _always_show(osobj):
|
|
return True
|
|
|
|
if self._filter_name is not None and self._filter_name != "":
|
|
label = osobj.label.lower()
|
|
name = osobj.name.lower()
|
|
if (label.find(self._filter_name) == -1 and
|
|
name.find(self._filter_name) == -1):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
###############
|
|
# Public APIs #
|
|
###############
|
|
|
|
def reset_state(self):
|
|
self._selected_os = None
|
|
self.search_entry.set_text("")
|
|
self._clear_filter()
|
|
self._sync_os_selection()
|
|
|
|
def select_os(self, vmosobj):
|
|
self._clear_filter()
|
|
|
|
os_list = self.widget("os-list")
|
|
if vmosobj.eol and not self.widget("include-eol").get_active():
|
|
self.widget("include-eol").set_active(True)
|
|
|
|
for row in os_list.get_model():
|
|
osobj = row[0]
|
|
if osobj.name != vmosobj.name:
|
|
continue
|
|
|
|
os_list.get_selection().select_iter(row.iter)
|
|
self._sync_os_selection()
|
|
return
|
|
|
|
def get_selected_os(self):
|
|
return self._selected_os
|
|
|
|
def set_sensitive(self, sensitive):
|
|
if sensitive == self.search_entry.get_sensitive():
|
|
return
|
|
|
|
if not sensitive:
|
|
self.search_entry.set_sensitive(False)
|
|
self.reset_state()
|
|
else:
|
|
if self._selected_os:
|
|
self.select_os(self._selected_os)
|
|
else:
|
|
self.reset_state()
|
|
self.search_entry.set_sensitive(True)
|