# # 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. # from gi.repository import Gtk from . import uiutil from .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)