From 8bb9853ec8b26acd7fd7cf69b8a17240c2b13a16 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 28 Jan 2014 13:51:59 -0500 Subject: [PATCH] Move shared cdrom media UI to mediacombo.py --- ui/choosecd.ui | 33 ++---- ui/create.ui | 32 +----- virtManager/baseclass.py | 4 + virtManager/choosecd.py | 71 +++++------- virtManager/create.py | 56 +++++----- virtManager/mediacombo.py | 223 ++++++++++++++++++++++++++++++++++++++ virtManager/sharedui.py | 142 ------------------------ 7 files changed, 287 insertions(+), 274 deletions(-) create mode 100644 virtManager/mediacombo.py diff --git a/ui/choosecd.ui b/ui/choosecd.ui index fef35005..6b0725bc 100644 --- a/ui/choosecd.ui +++ b/ui/choosecd.ui @@ -1,7 +1,7 @@ - + - + True True @@ -216,33 +216,13 @@ - + True False - 6 + False + False - - True - False - - - - True - True - 0 - - - - - True - False - gtk-dialog-warning - - - False - True - 1 - + @@ -250,6 +230,7 @@ 2 3 4 + GTK_FILL GTK_FILL diff --git a/ui/create.ui b/ui/create.ui index ca614c3b..31761424 100644 --- a/ui/create.ui +++ b/ui/create.ui @@ -687,40 +687,12 @@ bar - + True False 20 - - True - False - 6 - - - True - False - - - - False - False - 0 - - - - - True - False - gtk-dialog-warning - - - False - True - 1 - - - + diff --git a/virtManager/baseclass.py b/virtManager/baseclass.py index 22f07e16..2d752d06 100644 --- a/virtManager/baseclass.py +++ b/virtManager/baseclass.py @@ -93,6 +93,10 @@ class vmmGObject(GObject.GObject): ret = GObject.GObject.disconnect(self, handle) self._gobject_handles.remove(handle) return ret + def disconnect_by_func(self, *args, **kwargs): + handle = GObject.GObject.disconnect_by_func(*args, **kwargs) + self._gobject_handles.remove(handle) + return handle def add_gconf_handle(self, handle): self._gconf_handles.append(handle) diff --git a/virtManager/choosecd.py b/virtManager/choosecd.py index dee79c05..6129671d 100644 --- a/virtManager/choosecd.py +++ b/virtManager/choosecd.py @@ -24,10 +24,10 @@ import logging from gi.repository import GObject # pylint: enable=E0611 -from virtManager import uiutil from virtManager import sharedui from virtManager.baseclass import vmmGObjectUI from virtManager.mediadev import MEDIA_FLOPPY +from virtManager.mediacombo import vmmMediaCombo from virtManager.storagebrowse import vmmStorageBrowser @@ -47,18 +47,20 @@ class vmmChooseCD(vmmGObjectUI): self.disk = disk self.media_type = disk.device + self.mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin, + self.media_type) + self.widget("media-combo-align").add(self.mediacombo.top_box) + self.builder.connect_signals({ + "on_vmm_choose_cd_delete_event": self.close, + "on_media_toggled": self.media_toggled, "on_fv_iso_location_browse_clicked": self.browse_fv_iso_location, - "on_cd_path_changed": self.change_cd_path, + "on_ok_clicked": self.ok, - "on_vmm_choose_cd_delete_event": self.close, "on_cancel_clicked": self.close, }) - self.widget("iso-image").set_active(True) - - self.initialize_opt_media() self.reset_state() def close(self, ignore1=None, ignore2=None): @@ -84,23 +86,27 @@ class vmmChooseCD(vmmGObjectUI): if self.storage_browser: self.storage_browser.cleanup() self.storage_browser = None + if self.mediacombo: + self.mediacombo.cleanup() + self.mediacombo = None + + def _init_ui(self): + if self.media_type == MEDIA_FLOPPY: + self.widget("physical-media").set_label(_("Floppy D_rive")) + self.widget("iso-image").set_label(_("Floppy _Image")) def reset_state(self): - cd_path = self.widget("cd-path") - use_cdrom = (cd_path.get_active() > -1) + self.mediacombo.reset_state() + use_cdrom = (self.mediacombo.has_media()) - if use_cdrom: - self.widget("physical-media").set_active(True) - else: - self.widget("iso-image").set_active(True) + self.widget("physical-media").set_active(use_cdrom) + self.widget("iso-image").set_active(not use_cdrom) def ok(self, ignore1=None, ignore2=None): if self.widget("iso-image").get_active(): path = self.widget("iso-path").get_text() else: - path = uiutil.get_list_selection(self.widget("cd-path"), - sharedui.OPTICAL_DEV_PATH) - + path = self.mediacombo.get_path() if path == "" or path is None: return self.err.val_err(_("Invalid Media Path"), _("A media path must be specified.")) @@ -125,41 +131,14 @@ class vmmChooseCD(vmmGObjectUI): self.close() def media_toggled(self, ignore1=None, ignore2=None): - if self.widget("physical-media").get_active(): - self.widget("cd-path").set_sensitive(True) - self.widget("iso-path").set_sensitive(False) - self.widget("iso-file-chooser").set_sensitive(False) - else: - self.widget("cd-path").set_sensitive(False) - self.widget("iso-path").set_sensitive(True) - self.widget("iso-file-chooser").set_sensitive(True) - - def change_cd_path(self, ignore1=None, ignore2=None): - pass + is_phys = bool(self.widget("physical-media").get_active()) + self.mediacombo.combo.set_sensitive(is_phys) + self.widget("iso-path").set_sensitive(not is_phys) + self.widget("iso-file-chooser").set_sensitive(not is_phys) def browse_fv_iso_location(self, ignore1=None, ignore2=None): self._browse_file() - def initialize_opt_media(self): - widget = self.widget("cd-path") - warn = self.widget("cd-path-warn") - - error = self.conn.mediadev_error - sharedui.build_mediadev_combo(widget) - sharedui.populate_mediadev_combo(self.conn, widget, self.media_type) - - if error: - warn.show() - warn.set_tooltip_text(error) - else: - warn.hide() - - self.widget("physical-media").set_sensitive(not bool(error)) - - if self.media_type == MEDIA_FLOPPY: - self.widget("physical-media").set_label(_("Floppy D_rive")) - self.widget("iso-image").set_label(_("Floppy _Image")) - def set_storage_path(self, src_ignore, path): self.widget("iso-path").set_text(path) diff --git a/virtManager/create.py b/virtManager/create.py index 346ffc82..0ad4adfa 100644 --- a/virtManager/create.py +++ b/virtManager/create.py @@ -40,6 +40,7 @@ from virtManager.storagebrowse import vmmStorageBrowser from virtManager.details import vmmDetails from virtManager.domain import vmmDomainVirtinst from virtManager.netlist import vmmNetworkList +from virtManager.mediacombo import vmmMediaCombo # Number of seconds to wait for media detection DETECT_TIMEOUT = 20 @@ -109,6 +110,7 @@ class vmmCreate(vmmGObjectUI): self.config_window_signals = [] self.netlist = None + self.mediacombo = None self.builder.connect_signals({ "on_vmm_newcreate_delete_event" : self.close, @@ -199,6 +201,13 @@ class vmmCreate(vmmGObjectUI): self.netlist.cleanup() self.netlist = None + if self.netlist: + self.netlist.cleanup() + self.netlist = None + if self.mediacombo: + self.mediacombo.cleanup() + self.mediacombo = None + def remove_conn(self): if not self.conn: return @@ -288,11 +297,6 @@ class vmmCreate(vmmGObjectUI): uiutil.set_combo_text_column(os_variant_list, 1) os_variant_list.set_row_separator_func(sep_func, os_variant_list) - - # Physical CD-ROM model - cd_list = self.widget("install-local-cdrom-combo") - sharedui.build_mediadev_combo(cd_list) - # Archtecture # [value, label] archList = self.widget("config-arch") @@ -550,26 +554,22 @@ class vmmCreate(vmmGObjectUI): # Install local iso_option = self.widget("install-local-iso") cdrom_option = self.widget("install-local-cdrom") - cdrom_list = self.widget("install-local-cdrom-combo") - cdrom_warn = self.widget("install-local-cdrom-warn") - sigs = sharedui.populate_mediadev_combo(self.conn, cdrom_list, - MEDIA_CDROM) - self.conn_signals.extend(sigs) + if self.mediacombo: + self.widget("install-local-cdrom-align").remove( + self.mediacombo.top_box) + self.mediacombo.cleanup() + self.mediacombo = None - if self.conn.mediadev_error: - cdrom_warn.show() - cdrom_option.set_sensitive(False) - cdrom_warn.set_tooltip_text(self.conn.mediadev_error) - else: - cdrom_warn.hide() + self.mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin, + MEDIA_CDROM) + self.mediacombo.reset_state() + self.widget("install-local-cdrom-align").add( + self.mediacombo.top_box) # Don't select physical CDROM if no valid media is present - use_cd = (cdrom_list.get_active() >= 0) - if use_cd: - cdrom_option.set_active(True) - else: - iso_option.set_active(True) + cdrom_option.set_active(self.mediacombo.has_media()) + iso_option.set_active(not self.mediacombo.has_media()) # Only allow ISO option for remote VM is_local = not self.conn.is_remote() @@ -1012,9 +1012,7 @@ class vmmCreate(vmmGObjectUI): def get_config_local_media(self, store_media=False): if self.widget("install-local-cdrom").get_active(): - return uiutil.get_list_selection( - self.widget("install-local-cdrom-combo"), - sharedui.OPTICAL_DEV_PATH) + return self.mediacombo.get_path() else: ret = self.widget("install-local-box").get_child().get_text() if ret and store_media: @@ -1229,14 +1227,12 @@ class vmmCreate(vmmGObjectUI): break def toggle_local_cdrom(self, src): - combo = self.widget("install-local-cdrom-combo") is_active = src.get_active() - if is_active: - if combo.get_active() != -1: - # Local CDROM was selected with media preset, detect distro - self.detect_media_os() + if is_active and self.mediacombo.get_path(): + # Local CDROM was selected with media preset, detect distro + self.detect_media_os() - self.widget("install-local-cdrom-combo").set_sensitive(is_active) + self.widget("install-local-cdrom-align").set_sensitive(is_active) def toggle_local_iso(self, src): uselocal = src.get_active() diff --git a/virtManager/mediacombo.py b/virtManager/mediacombo.py new file mode 100644 index 00000000..f74505d0 --- /dev/null +++ b/virtManager/mediacombo.py @@ -0,0 +1,223 @@ +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA. +# + +# pylint: disable=E0611 +from gi.repository import Gtk +# pylint: enable=E0611 + +from virtManager import uiutil +from virtManager.baseclass import vmmGObjectUI + + +class vmmMediaCombo(vmmGObjectUI): + OPTICAL_FIELDS = 4 + (OPTICAL_DEV_PATH, + OPTICAL_LABEL, + OPTICAL_HAS_MEDIA, + OPTICAL_DEV_KEY) = range(OPTICAL_FIELDS) + + def __init__(self, conn, builder, topwin, media_type): + vmmGObjectUI.__init__(self, None, None, builder=builder, topwin=topwin) + self.conn = conn + self.media_type = media_type + + self.top_box = None + self.combo = None + self._warn_icon = None + self._populated = False + self._init_ui() + + def _cleanup(self): + try: + self.conn.disconnect_by_func(self._mediadev_added) + self.conn.disconnect_by_func(self._mediadev_removed) + except: + pass + + self.conn = None + self.top_box.destroy() + self.top_box = None + + ########################## + # Initialization methods # + ########################## + + def _init_ui(self): + self.top_box = Gtk.Box() + self.top_box.set_spacing(6) + self.top_box.set_orientation(Gtk.Orientation.HORIZONTAL) + self._warn_icon = Gtk.Image() + self._warn_icon.set_from_stock( + Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.MENU) + self.combo = Gtk.ComboBox() + self.top_box.add(self.combo) + self.top_box.add(self._warn_icon) + self.top_box.show_all() + + # [Device path, pretty label, has_media?, device key, media key, + # vmmMediaDevice, is valid device] + fields = [] + fields.insert(self.OPTICAL_DEV_PATH, str) + fields.insert(self.OPTICAL_LABEL, str) + fields.insert(self.OPTICAL_HAS_MEDIA, bool) + fields.insert(self.OPTICAL_DEV_KEY, str) + self.combo.set_model(Gtk.ListStore(*fields)) + + text = Gtk.CellRendererText() + self.combo.pack_start(text, True) + self.combo.add_attribute(text, 'text', self.OPTICAL_LABEL) + + error = self.conn.mediadev_error + self._warn_icon.set_visible(bool(error)) + self._warn_icon.set_tooltip_text(error) + + + def _set_mediadev_default(self): + model = self.combo.get_model() + if len(model) != 0: + return + + row = [None] * self.OPTICAL_FIELDS + row[self.OPTICAL_DEV_PATH] = None + row[self.OPTICAL_LABEL] = _("No device present") + row[self.OPTICAL_HAS_MEDIA] = False + row[self.OPTICAL_DEV_KEY] = None + model.append(row) + + def _set_mediadev_row_from_object(self, row, obj): + row[self.OPTICAL_DEV_PATH] = obj.get_path() + row[self.OPTICAL_LABEL] = obj.pretty_label() + row[self.OPTICAL_HAS_MEDIA] = obj.has_media() + row[self.OPTICAL_DEV_KEY] = obj.get_key() + + def _mediadev_set_default_selection(self): + # Set the first active cdrom device as selected, otherwise none + widget = self.combo + model = widget.get_model() + idx = 0 + active = widget.get_active() + + if active != -1: + # already a selection, don't change it + return + + for row in model: + if row[self.OPTICAL_HAS_MEDIA] is True: + widget.set_active(idx) + return + idx += 1 + + widget.set_active(0) + + def _mediadev_media_changed(self, newobj): + widget = self.combo + model = widget.get_model() + active = widget.get_active() + idx = 0 + + # Search for the row with matching device node and + # fill in info about inserted media. If model has no current + # selection, select the new media. + for row in model: + if row[self.OPTICAL_DEV_PATH] == newobj.get_path(): + self._set_mediadev_row_from_object(row, newobj) + has_media = row[self.OPTICAL_HAS_MEDIA] + + if has_media and active == -1: + widget.set_active(idx) + elif not has_media and active == idx: + widget.set_active(-1) + + idx = idx + 1 + + self._mediadev_set_default_selection() + + def _mediadev_added(self, ignore, newobj): + widget = self.combo + model = widget.get_model() + + if newobj.get_media_type() != self.media_type: + return + if model is None: + return + + if len(model) == 1 and model[0][self.OPTICAL_DEV_PATH] is None: + # Only entry is the 'No device' entry + model.clear() + + newobj.connect("media-added", self._mediadev_media_changed) + newobj.connect("media-removed", self._mediadev_media_changed) + + # Brand new device + row = [None] * self.OPTICAL_FIELDS + self._set_mediadev_row_from_object(row, newobj) + model.append(row) + + self._mediadev_set_default_selection() + + def _mediadev_removed(self, ignore, key): + widget = self.combo + model = widget.get_model() + active = widget.get_active() + idx = 0 + + for row in model: + if row[self.OPTICAL_DEV_KEY] == key: + # Whole device removed + del(model[idx]) + + if idx > active and active != -1: + widget.set_active(active - 1) + elif idx == active: + widget.set_active(-1) + + idx += 1 + + self._set_mediadev_default() + self._mediadev_set_default_selection() + + def _populate_media(self): + if self._populated: + return + + widget = self.combo + model = widget.get_model() + model.clear() + self._set_mediadev_default() + + self.conn.connect("mediadev-added", self._mediadev_added) + self.conn.connect("mediadev-removed", self._mediadev_removed) + + widget.set_active(-1) + self._mediadev_set_default_selection() + self._populated = True + + + ############## + # Public API # + ############## + + def reset_state(self): + self._populate_media() + + def get_path(self): + return uiutil.get_list_selection(self.combo, self.OPTICAL_DEV_PATH) + + def has_media(self): + return uiutil.get_list_selection(self.combo, self.OPTICAL_HAS_MEDIA) diff --git a/virtManager/sharedui.py b/virtManager/sharedui.py index 3c539061..286e8296 100644 --- a/virtManager/sharedui.py +++ b/virtManager/sharedui.py @@ -223,148 +223,6 @@ def check_path_search_for_qemu(err, conn, path): config.running_config.add_perms_fix_ignore(errors.keys()) -############################################ -# Populate media widget (choosecd, create) # -############################################ - -OPTICAL_DEV_PATH = 0 -OPTICAL_LABEL = 1 -OPTICAL_IS_MEDIA_PRESENT = 2 -OPTICAL_DEV_KEY = 3 -OPTICAL_MEDIA_KEY = 4 -OPTICAL_IS_VALID = 5 - - -def _set_mediadev_default(model): - if len(model) == 0: - model.append([None, _("No device present"), False, None, None, False]) - - -def _set_mediadev_row_from_object(row, obj): - row[OPTICAL_DEV_PATH] = obj.get_path() - row[OPTICAL_LABEL] = obj.pretty_label() - row[OPTICAL_IS_MEDIA_PRESENT] = obj.has_media() - row[OPTICAL_DEV_KEY] = obj.get_key() - row[OPTICAL_MEDIA_KEY] = obj.get_media_key() - row[OPTICAL_IS_VALID] = True - - -def _mediadev_set_default_selection(widget): - # Set the first active cdrom device as selected, otherwise none - model = widget.get_model() - idx = 0 - active = widget.get_active() - - if active != -1: - # already a selection, don't change it - return - - for row in model: - if row[OPTICAL_IS_MEDIA_PRESENT] is True: - widget.set_active(idx) - return - idx += 1 - - widget.set_active(-1) - - -def _mediadev_media_changed(newobj, widget): - model = widget.get_model() - active = widget.get_active() - idx = 0 - - # Search for the row with matching device node and - # fill in info about inserted media. If model has no current - # selection, select the new media. - for row in model: - if row[OPTICAL_DEV_PATH] == newobj.get_path(): - _set_mediadev_row_from_object(row, newobj) - has_media = row[OPTICAL_IS_MEDIA_PRESENT] - - if has_media and active == -1: - widget.set_active(idx) - elif not has_media and active == idx: - widget.set_active(-1) - - idx = idx + 1 - - _mediadev_set_default_selection(widget) - - -def _mediadev_added(ignore_helper, newobj, widget, devtype): - model = widget.get_model() - - if newobj.get_media_type() != devtype: - return - if model is None: - return - - if len(model) == 1 and model[0][OPTICAL_IS_VALID] is False: - # Only entry is the 'No device' entry - model.clear() - - newobj.connect("media-added", _mediadev_media_changed, widget) - newobj.connect("media-removed", _mediadev_media_changed, widget) - - # Brand new device - row = [None, None, None, None, None, None] - _set_mediadev_row_from_object(row, newobj) - model.append(row) - - _mediadev_set_default_selection(widget) - - -def _mediadev_removed(ignore_helper, key, widget): - model = widget.get_model() - active = widget.get_active() - idx = 0 - - for row in model: - if row[OPTICAL_DEV_KEY] == key: - # Whole device removed - del(model[idx]) - - if idx > active and active != -1: - widget.set_active(active - 1) - elif idx == active: - widget.set_active(-1) - - idx += 1 - - _set_mediadev_default(model) - _mediadev_set_default_selection(widget) - - -def build_mediadev_combo(widget): - # [Device path, pretty label, has_media?, device key, media key, - # vmmMediaDevice, is valid device] - model = Gtk.ListStore(str, str, bool, str, str, bool) - widget.set_model(model) - model.clear() - - text = Gtk.CellRendererText() - widget.pack_start(text, True) - widget.add_attribute(text, 'text', OPTICAL_LABEL) - widget.add_attribute(text, 'sensitive', OPTICAL_IS_VALID) - - -def populate_mediadev_combo(conn, widget, devtype): - sigs = [] - - model = widget.get_model() - model.clear() - _set_mediadev_default(model) - - sigs.append(conn.connect("mediadev-added", - _mediadev_added, widget, devtype)) - sigs.append(conn.connect("mediadev-removed", _mediadev_removed, widget)) - - widget.set_active(-1) - _mediadev_set_default_selection(widget) - - return sigs - - #################################################################### # Build toolbar shutdown button menu (manager and details toolbar) # ####################################################################