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)
|