virt-manager/virtManager/oslist.py

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)