# # gui.py - Graphical front end for anaconda # # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 # Red Hat, Inc. All rights reserved. # # 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, see . # # Author(s): Matt Wilson # Michael Fulbright # import os from flags import flags os.environ["GNOME_DISABLE_CRASH_DIALOG"] = "1" import string import time import traceback import isys import iutil import sys import shutil import gtk import gtk.glade import gobject from constants import * from product import * import network from installinterfacebase import InstallInterfaceBase import imp import iw import gettext _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("anaconda") stdout_log = logging.getLogger("anaconda.stdout") isys.bind_textdomain_codeset("redhat-dist", "UTF-8") iutil.setup_translations(gtk.glade) class StayOnScreen(Exception): pass mainWindow = None stepToClass = { "language" : ("language_gui", "LanguageWindow"), "keyboard" : ("kbd_gui", "KeyboardWindow"), "filtertype" : ("filter_type", "FilterTypeWindow"), "filter" : ("filter_gui", "FilterWindow"), "partition" : ("partition_gui", "PartitionWindow"), "parttype" : ("autopart_type", "PartitionTypeWindow"), "cleardiskssel": ("cleardisks_gui", "ClearDisksWindow"), "findinstall" : ("examine_gui", "UpgradeExamineWindow"), "upgrademigratefs" : ("upgrade_migratefs_gui", "UpgradeMigrateFSWindow"), "bootloader": ("bootloader_main_gui", "MainBootloaderWindow"), "upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"), "network" : ("network_gui", "NetworkWindow"), "timezone" : ("timezone_gui", "TimezoneWindow"), "accounts" : ("account_gui", "AccountWindow"), "tasksel": ("task_gui", "TaskWindow"), "group-selection": ("package_gui", "GroupSelectionWindow"), "install" : ("progress_gui", "InstallProgressWindow"), "complete" : ("congrats_gui", "CongratulationWindow"), } if iutil.isS390(): stepToClass["bootloader"] = ("zipl_gui", "ZiplWindow") def idle_gtk(func, *args, **kwargs): def return_false(func, *args, **kwargs): gtk.gdk.threads_enter() func(*args, **kwargs) gtk.gdk.threads_leave() return False gobject.idle_add(return_false, func, *args, **kwargs) # # Stuff for screenshots # screenshotDir = "/tmp/anaconda-screenshots" screenshotIndex = 0 def copyScreenshots(): # see if any screenshots taken if screenshotIndex == 0: return destDir = "/mnt/sysimage/root/anaconda-screenshots" if not os.access(destDir, os.R_OK): try: os.mkdir(destDir, 0750) except OSError: window = MessageWindow("Error Saving Screenshot", _("An error occurred saving screenshots " "to disk."), type="warning") return # Now copy all the PNGs over. Since some pictures could have been taken # under a root changed to /mnt/sysimage, we have to try to fetch files from # there as well. source_dirs = [screenshotDir, os.path.join("/mnt/sysimage", screenshotDir.lstrip('/'))] for source_dir in source_dirs: if not os.access(source_dir, os.X_OK): continue for f in os.listdir(source_dir): (path, fname) = os.path.split(f) (b, ext) = os.path.splitext(f) if ext == ".png": shutil.copyfile(source_dir + '/' + f, destDir + '/' + fname) window = MessageWindow(_("Screenshots Copied"), _("The screenshots have been saved in the " "directory:\n\n" "\t/root/anaconda-screenshots/\n\n" "You can access these when you reboot and " "login as root.")) def takeScreenShot(): global screenshotIndex if not os.access(screenshotDir, os.R_OK): try: os.mkdir(screenshotDir) except OSError as e: log.error("os.mkdir() failed for %s: %s" % (screenshotDir, e.strerror)) return try: screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, gtk.gdk.screen_width(), gtk.gdk.screen_height()) screenshot.get_from_drawable(gtk.gdk.get_default_root_window(), gtk.gdk.colormap_get_system(), 0, 0, 0, 0, gtk.gdk.screen_width(), gtk.gdk.screen_height()) if screenshot: while True: sname = "screenshot-%04d.png" % ( screenshotIndex,) if not os.access(screenshotDir + '/' + sname, os.R_OK): break screenshotIndex += 1 if screenshotIndex > 9999: log.error("Too many screenshots!") return screenshot.save (screenshotDir + '/' + sname, "png") screenshotIndex += 1 window = MessageWindow(_("Saving Screenshot"), _("A screenshot named '%s' has been saved.") % (sname,) , type="ok") except Exception: # FIXME: find out what exceptions gtk.gdk.Pixbuf might actually raise window = MessageWindow(_("Error Saving Screenshot"), _("An error occurred while saving " "the screenshot. If this occurred " "during package installation, you may need " "to try several times for it to succeed."), type="warning") def handlePrintScrnRelease (window, event): if event.keyval == gtk.keysyms.Print: takeScreenShot() # # HACK to make treeview work # def setupTreeViewFixupIdleHandler(view, store): id = {} id["id"] = gobject.idle_add(scrollToIdleHandler, (view, store, id)) def scrollToIdleHandler((view, store, iddict)): if not view or not store or not iddict: return try: id = iddict["id"] except: return selection = view.get_selection() if not selection: return model, iter = selection.get_selected() if not iter: return path = store.get_path(iter) col = view.get_column(0) view.scroll_to_cell(path, col, True, 0.5, 0.5) if id: gobject.source_remove(id) # setup globals def processEvents(): gtk.gdk.flush() while gtk.events_pending(): gtk.main_iteration(False) def widgetExpander(widget, growTo=None): widget.connect("size-allocate", growToParent, growTo) def growToParent(widget, rect, growTo=None): if not widget.parent: return ignore = widget.__dict__.get("ignoreEvents") if not ignore: if growTo: x, y, width, height = growTo.get_allocation() widget.set_size_request(width, -1) else: widget.set_size_request(rect.width, -1) widget.ignoreEvents = 1 else: widget.ignoreEvents = 0 _busyCursor = 0 def setCursorToBusy(process=1): root = gtk.gdk.get_default_root_window() cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) root.set_cursor(cursor) if process: processEvents() def setCursorToNormal(): root = gtk.gdk.get_default_root_window() cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) root.set_cursor(cursor) def rootPushBusyCursor(process=1): global _busyCursor _busyCursor += 1 if _busyCursor > 0: setCursorToBusy(process) def rootPopBusyCursor(): global _busyCursor _busyCursor -= 1 if _busyCursor <= 0: setCursorToNormal() def getBusyCursorStatus(): global _busyCursor return _busyCursor class MnemonicLabel(gtk.Label): def __init__(self, text="", alignment = None): gtk.Label.__init__(self, "") self.set_text_with_mnemonic(text) if alignment is not None: apply(self.set_alignment, alignment) class WrappingLabel(gtk.Label): def __init__(self, label=""): gtk.Label.__init__(self, label) self.set_line_wrap(True) self.ignoreEvents = 0 widgetExpander(self) def addFrame(dialog, title=None): # make screen shots work dialog.connect ("key-release-event", handlePrintScrnRelease) if title: dialog.set_title(title) def findGladeFile(file): path = os.environ.get("GLADEPATH", "./:ui/:/tmp/updates/:/tmp/updates/ui/") for dir in path.split(":"): fn = dir + file if os.access(fn, os.R_OK): return fn raise RuntimeError, "Unable to find glade file %s" % file def getGladeWidget(file, rootwidget, i18ndomain="anaconda"): f = findGladeFile(file) xml = gtk.glade.XML(f, root = rootwidget, domain = i18ndomain) w = xml.get_widget(rootwidget) if w is None: raise RuntimeError, "Unable to find root widget %s in %s" %(rootwidget, file) return (xml, w) def findPixmap(file): path = os.environ.get("PIXMAPPATH", "./:pixmaps/:/tmp/updates/:/tmp/updates/pixmaps/") for dir in path.split(":"): fn = dir + file if os.access(fn, os.R_OK): return fn return None def getPixbuf(file): fn = findPixmap(file) if not fn: log.error("unable to load %s" %(file,)) return None try: pixbuf = gtk.gdk.pixbuf_new_from_file(fn) except RuntimeError as msg: log.error("unable to read %s: %s" %(file, msg)) pixbuf = None return pixbuf def readImageFromFile(file, dither = False, image = None): pixbuf = getPixbuf(file) if pixbuf is None: log.warning("can't find pixmap %s" %(file,)) return None if image is None: p = gtk.Image() else: p = image if dither: (pixmap, mask) = pixbuf.render_pixmap_and_mask() pixmap.draw_pixbuf(gtk.gdk.GC(pixmap), pixbuf, 0, 0, 0, 0, pixbuf.get_width(), pixbuf.get_height(), gtk.gdk.RGB_DITHER_MAX, 0, 0) p = gtk.Image() p.set_from_pixmap(pixmap, mask) else: source = gtk.IconSource() source.set_pixbuf(pixbuf) source.set_size(gtk.ICON_SIZE_DIALOG) source.set_size_wildcarded(False) iconset = gtk.IconSet() iconset.add_source(source) p.set_from_icon_set(iconset, gtk.ICON_SIZE_DIALOG) return p class WaitWindow: def __init__(self, title, text, parent = None): if flags.livecdInstall: self.window = gtk.Window() if parent: self.window.set_transient_for(parent) else: self.window = gtk.Window() self.window.set_modal(True) self.window.set_type_hint (gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self.window.set_title(title) self.window.set_position(gtk.WIN_POS_CENTER) label = WrappingLabel(text) box = gtk.Frame() box.set_border_width(10) box.add(label) box.set_shadow_type(gtk.SHADOW_NONE) self.window.add(box) box.show_all() addFrame(self.window) # Displaying windows should not be done outside of the gtk # mainloop. With metacity this bites us and we have to do # window.show_now() AND refresh() to correctly display the window and # its contents: self.window.show_now() rootPushBusyCursor() self.refresh() def refresh(self): processEvents() def pop(self): self.window.destroy() rootPopBusyCursor() class ProgressWindow: def __init__(self, title, text, total, updpct = 0.05, updsecs=10, parent = None, pulse = False): if flags.livecdInstall: self.window = gtk.Window() if parent: self.window.set_transient_for(parent) else: self.window = gtk.Window() self.window.set_modal(True) self.window.set_type_hint (gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self.window.set_title (title) self.window.set_position (gtk.WIN_POS_CENTER) self.lastUpdate = time.time() self.updsecs = updsecs box = gtk.VBox (False, 5) box.set_border_width (10) label = WrappingLabel (text) label.set_alignment (0.0, 0.5) box.pack_start (label, False) self.total = total self.updpct = updpct self.progress = gtk.ProgressBar () box.pack_start (self.progress, True) box.show_all() self.window.add(box) addFrame(self.window) # see comment at WaitWindow.__init__(): self.window.show_now () rootPushBusyCursor() self.refresh() def refresh(self): processEvents() def pulse(self): then = self.lastUpdate now = time.time() delta = now-then if delta < 0.01: return self.progress.set_pulse_step(self.updpct) self.lastUpdate = now # if we've had a largish gap, some smoothing does actually help, # but don't go crazy if delta > 2: delta=2 while delta > 0: self.progress.pulse() processEvents() delta -= 0.05 def set (self, amount): amount = min(amount, self.total) # only update widget if we've changed by 5% or our timeout has # expired curval = self.progress.get_fraction() newval = float (amount) / self.total then = self.lastUpdate now = time.time() if newval < 0.998: if ((newval - curval) < self.updpct and (now-then) < self.updsecs): return self.lastUpdate = now self.progress.set_fraction (newval) processEvents () def pop(self): self.window.destroy () rootPopBusyCursor() class luksPassphraseWindow: def __init__(self, passphrase=None, preexist = False, parent = None): luksxml = gtk.glade.XML(findGladeFile("lukspassphrase.glade"), domain="anaconda", root="luksPassphraseDialog") self.passphraseEntry = luksxml.get_widget("passphraseEntry") self.passphraseEntry.set_visibility(False) self.confirmEntry = luksxml.get_widget("confirmEntry") self.confirmEntry.set_visibility(False) self.win = luksxml.get_widget("luksPassphraseDialog") self.okButton = luksxml.get_widget("okbutton1") self.globalcheckbutton = luksxml.get_widget("globalcheckbutton") self.isglobal = preexist if not preexist: self.globalcheckbutton.hide() else: self.globalcheckbutton.set_active(True) self.minimumLength = 8 # arbitrary; should probably be much larger if passphrase: self.initialPassphrase = passphrase self.passphraseEntry.set_text(passphrase) self.confirmEntry.set_text(passphrase) else: self.initialPassphrase = "" txt = _("Choose a passphrase for the encrypted devices. " "You will be prompted for this passphrase during system " "boot.") luksxml.get_widget("mainLabel").set_text(txt) if parent: self.win.set_transient_for(parent) addFrame(self.win) def run(self): self.win.show() while True: self.passphraseEntry.grab_focus() self.rc = self.win.run() if self.rc == gtk.RESPONSE_OK: passphrase = self.passphraseEntry.get_text() confirm = self.confirmEntry.get_text() if passphrase != confirm: MessageWindow(_("Error with passphrase"), _("The passphrases you entered were " "different. Please try again."), type = "ok", custom_icon = "error") self.confirmEntry.set_text("") continue if len(passphrase) < self.minimumLength: MessageWindow(_("Error with passphrase"), _("The passphrase must be at least " "eight characters long."), type = "ok", custom_icon = "error") self.passphraseEntry.set_text("") self.confirmEntry.set_text("") continue if self.isglobal: self.isglobal = self.globalcheckbutton.get_active() else: self.passphraseEntry.set_text(self.initialPassphrase) self.confirmEntry.set_text(self.initialPassphrase) return self.rc def getPassphrase(self): return self.passphraseEntry.get_text() def getGlobal(self): return self.isglobal def getrc(self): return self.rc def destroy(self): self.win.destroy() class PassphraseEntryWindow: def __init__(self, device, parent = None): def ok(*args): self.win.response(gtk.RESPONSE_OK) xml = gtk.glade.XML(findGladeFile("lukspassphrase.glade"), domain="anaconda", root="passphraseEntryDialog") self.txt = _("Device %s is encrypted. In order to " "access the device's contents during " "installation you must enter the device's " "passphrase below.") % (device,) self.win = xml.get_widget("passphraseEntryDialog") self.passphraseLabel = xml.get_widget("passphraseLabel") self.passphraseEntry = xml.get_widget("passphraseEntry2") if parent: self.win.set_transient_for(parent) self.passphraseEntry.connect('activate', ok) addFrame(self.win) def run(self): self.win.show() self.passphraseLabel.set_text(self.txt) self.passphraseEntry.grab_focus() busycursor = getBusyCursorStatus() setCursorToNormal() rc = self.win.run() passphrase = None if rc == gtk.RESPONSE_OK: passphrase = self.passphraseEntry.get_text() if busycursor: setCursorToBusy() self.rc = passphrase return self.rc def getrc(self): return self.rc def destroy(self): self.win.destroy() class MessageWindow: def getrc (self): return self.rc def __init__ (self, title, text, type="ok", default=None, custom_buttons=None, custom_icon=None, run = True, parent = None, destroyAfterRun = True): self.debugRid = None self.title = title if flags.autostep: self.rc = 1 return self.rc = None self.framed = False self.doCustom = False style = 0 if type == 'ok': buttons = gtk.BUTTONS_OK style = gtk.MESSAGE_INFO elif type == 'warning': buttons = gtk.BUTTONS_OK style = gtk.MESSAGE_WARNING elif type == 'okcancel': buttons = gtk.BUTTONS_OK_CANCEL style = gtk.MESSAGE_WARNING elif type == 'yesno': buttons = gtk.BUTTONS_YES_NO style = gtk.MESSAGE_QUESTION elif type == 'custom': self.doCustom = True buttons = gtk.BUTTONS_NONE style = gtk.MESSAGE_QUESTION if custom_icon == "warning": style = gtk.MESSAGE_WARNING elif custom_icon == "question": style = gtk.MESSAGE_QUESTION elif custom_icon == "error": style = gtk.MESSAGE_ERROR elif custom_icon == "info": style = gtk.MESSAGE_INFO self.dialog = gtk.MessageDialog(mainWindow, 0, style, buttons, str(text)) self.dialog.set_property("use-markup", True) if parent: self.dialog.set_transient_for(parent) if self.doCustom: rid=0 for button in custom_buttons: if button == _("Cancel"): tbutton = "gtk-cancel" else: tbutton = button widget = self.dialog.add_button(tbutton, rid) rid = rid + 1 if default is not None: defaultchoice = default else: defaultchoice = rid - 1 if flags.debug and not _("_Debug") in custom_buttons: widget = self.dialog.add_button(_("_Debug"), rid) self.debugRid = rid rid += 1 else: if default == "no": defaultchoice = 0 elif default == "yes" or default == "ok": defaultchoice = 1 else: defaultchoice = 0 self.dialog.set_position (gtk.WIN_POS_CENTER) self.dialog.set_default_response(defaultchoice) if run: self.run(destroyAfterRun) def run(self, destroy = False): if not self.framed: addFrame(self.dialog, title=self.title) self.framed = True self.dialog.show_all () # XXX - Messy - turn off busy cursor if necessary busycursor = getBusyCursorStatus() setCursorToNormal() self.rc = self.dialog.run() if not self.doCustom: if self.rc in [gtk.RESPONSE_OK, gtk.RESPONSE_YES]: self.rc = 1 elif self.rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_NO, gtk.RESPONSE_CLOSE, gtk.RESPONSE_DELETE_EVENT]: self.rc = 0 else: # generated by Esc key if self.rc == gtk.RESPONSE_DELETE_EVENT: self.rc = 0 if not self.debugRid is None and self.rc == self.debugRid: self.debugClicked(self) return self.run(destroy) if destroy: self.dialog.destroy() # restore busy cursor if busycursor: setCursorToBusy() def debugClicked (self, *args): try: # switch to VC1 so we can debug isys.vtActivate (1) except SystemError: pass import pdb try: pdb.set_trace() except Exception: # FIXME: what exceptions might pdb.set_trace raise? sys.exit(-1) try: # switch back isys.vtActivate (6) except SystemError: pass class ReinitializeWindow(MessageWindow): def __init__ (self, title, path, size, description, default=None, run=True, parent=None, destroyAfterRun=True): self.debugRid = None self.title = title if flags.autostep: self.rc = 1 return self.rc = None self.framed = False self.doCustom = False xml = gtk.glade.XML(findGladeFile("reinitialize-dialog.glade"), domain="anaconda") self.dialog = xml.get_widget("reinitializeDialog") self.apply_to_all = xml.get_widget("apply_to_all") self.label = xml.get_widget("disk_label") text = "%s\n%s MB\t%s" % (description, size, path) self.label.set_markup(text) if parent: self.dialog.set_transient_for(parent) self.dialog.set_position(gtk.WIN_POS_CENTER) if flags.debug: widget = self.dialog.add_button(_("_Debug"), 2) self.debugRid = 2 defaultchoice = 0 #no self.dialog.set_default_response(defaultchoice) if run: self.run(destroyAfterRun) def run(self, destroy=False): MessageWindow.run(self, destroy) apply_all = self.apply_to_all.get_active() # doCustom is false, so we will have self.rc set up as following: # if "Yes, discard" was clicked - self.rc = 1 # if "No, keep" was clicked - self.rc = 0 if self.rc == 1: #yes self.rc = 3 if apply_all else 2 elif self.rc == 0: #no self.rc = 1 if apply_all else 0 class DetailedMessageWindow(MessageWindow): def __init__(self, title, text, longText=None, type="ok", default=None, custom_buttons=None, custom_icon=None, run=True, parent=None, destroyAfterRun=True, expanded=False): self.title = title if flags.autostep: self.rc = 1 return self.debugRid = None self.rc = None self.framed = False self.doCustom = False if type == 'ok': buttons = ["gtk-ok"] elif type == 'warning': buttons = ["gtk-ok"] elif type == 'okcancel': buttons = ["gtk-ok", "gtk-cancel"] elif type == 'yesno': buttons = ["gtk-yes", "gtk-no"] elif type == 'custom': self.doCustom = True buttons = custom_buttons xml = gtk.glade.XML(findGladeFile("detailed-dialog.glade"), domain="anaconda") self.dialog = xml.get_widget("detailedDialog") self.mainVBox = xml.get_widget("mainVBox") self.hbox = xml.get_widget("hbox1") self.info = xml.get_widget("info") self.detailedExpander = xml.get_widget("detailedExpander") self.detailedView = xml.get_widget("detailedView") self.detailedExpander.set_expanded(expanded) if parent: self.dialog.set_transient_for(parent) if custom_icon: str_to_gtk_stock = { "warning" : gtk.STOCK_DIALOG_WARNING, "question": gtk.STOCK_DIALOG_QUESTION, "error" : gtk.STOCK_DIALOG_ERROR, "info" : gtk.STOCK_DIALOG_INFO } img = gtk.Image() stock = str_to_gtk_stock.get(custom_icon, None) if stock: img.set_from_stock(stock, gtk.ICON_SIZE_DIALOG) else: img.set_from_file(custom_icon) self.hbox.pack_start(img) self.hbox.reorder_child(img, 0) rid = 0 for button in buttons: self.dialog.add_button(button, rid) rid += 1 if self.doCustom: defaultchoice = rid-1 if flags.debug and not _("_Debug") in buttons: self.dialog.add_button(_("_Debug"), rid) self.debugRid = rid rid += 1 else: if default == "no": defaultchoice = 0 elif default == "yes" or default == "ok": defaultchoice = 1 else: defaultchoice = 0 self.info.set_text(text) if longText: textbuf = gtk.TextBuffer() iter = textbuf.get_start_iter() for line in longText: if __builtins__.get("type")(line) != unicode: try: line = unicode(line, encoding='utf-8') except UnicodeDecodeError as e: log.error("UnicodeDecodeException: line = %s" % (line,)) log.error("UnicodeDecodeException: %s" % (str(e),)) textbuf.insert(iter, line) self.detailedView.set_buffer(textbuf) else: self.mainVBox.remove(self.detailedExpander) self.dialog.set_position (gtk.WIN_POS_CENTER) self.dialog.set_default_response(defaultchoice) if run: self.run(destroyAfterRun) class InstallInterface(InstallInterfaceBase): def __init__ (self): InstallInterfaceBase.__init__(self) self.icw = None self.installProgress = None root = gtk.gdk.get_default_root_window() cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) root.set_cursor(cursor) def __del__ (self): pass def shutdown(self): if self.icw: self.icw.close() def suspend(self): pass def resume(self): pass # just_setup is used for [Configure Network] button def enableNetwork(self, just_setup=False): if len(self.anaconda.network.netdevices) == 0: return False from iw.network_gui import (runNMCE, selectInstallNetDeviceDialog, selectSSIDsDialog) networkEnabled = False while not networkEnabled: # We need to do it in each iteration because user can # delete ifcfg file in nm-c-e nm_controlled_devices = [devname for (devname, dev) in self.anaconda.network.netdevices.items() if not dev.usedByFCoE(self.anaconda)] if not just_setup and not nm_controlled_devices: return False if just_setup: install_device = None else: install_device = selectInstallNetDeviceDialog(self.anaconda.network, nm_controlled_devices) if not install_device: break # update ifcfg files for nm-c-e self.anaconda.network.setNMControlledDevices(nm_controlled_devices) # we might want to do this only once if self.anaconda.network.hasWirelessDev(): # NOTE: For wireless, we need supplicant to go to ready state, # that means to get the wireless device managed by NM self.anaconda.network.writeIfcfgFiles() w = self.anaconda.intf.waitWindow(_("Wireless setup"), _("Scanning access points for wireless devices")) # get available wireless APs dev_all_ssids = self.anaconda.network.getSSIDs() w.pop() # select wireless APs dev_ssids = selectSSIDsDialog(dev_all_ssids) or dev_all_ssids self.anaconda.network.writeSSIDifcfgs(dev_ssids) self.anaconda.network.writeIfcfgFiles() # Logging can race here with ifcfg-rh updating the file network.logIfcfgFiles(message="Dump before nm-c-e (can race " "with ifcfg updating). ") runNMCE(self.anaconda) network.logIfcfgFiles(message="Dump after nm-c-e. ") self.anaconda.network.update() if just_setup: waited_devs = self.anaconda.network.getOnbootControlledIfaces() else: waited_devs = [install_device] self.anaconda.network.updateActiveDevices([install_device]) self.anaconda.network.write() if waited_devs: w = WaitWindow(_("Waiting for NetworkManager"), _("Waiting for NetworkManager to activate " "these devices: %s") % ",".join(waited_devs)) failed_devs = self.anaconda.network.waitForDevicesActivation(waited_devs) w.pop() if just_setup: if failed_devs: self._handleDeviceActivationFail(failed_devs) if len(failed_devs) < len(waited_devs): # if any device was activated, remember to reset # resolver networkEnabled = True else: networkEnabled = install_device not in failed_devs if not networkEnabled: self._handleNetworkError(install_device) if just_setup: break if networkEnabled: network.resetResolver() return networkEnabled def _handleDeviceActivationFail(self, devices): d = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Failed to activate these " "network interfaces: %s" % ",".join(devices))) d.set_title(_("Network Configuration")) d.set_position(gtk.WIN_POS_CENTER) addFrame(d) d.run() d.destroy() def _handleNetworkError(self, field): d = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("An error occurred trying to bring up the " "%s network interface.") % (field,)) d.set_title(_("Error Enabling Network")) d.set_position(gtk.WIN_POS_CENTER) addFrame(d) d.run() d.destroy() def setInstallProgressClass(self, c): self.instProgress = c def setPackageProgressWindow (self, ppw): self.ppw = ppw def waitWindow (self, title, text): if self.icw: return WaitWindow (title, text, self.icw.window) else: return WaitWindow (title, text) def progressWindow (self, title, text, total, updpct = 0.05, pulse = False): if self.icw: return ProgressWindow (title, text, total, updpct, parent = self.icw.window, pulse = pulse) else: return ProgressWindow (title, text, total, updpct, pulse = pulse) def messageWindow(self, title, text, type="ok", default = None, custom_buttons=None, custom_icon=None): if self.icw: parent = self.icw.window else: parent = None rc = MessageWindow (title, text, type, default, custom_buttons, custom_icon, run=True, parent=parent).getrc() return rc def reinitializeWindow(self, title, path, size, description): if self.icw: parent = self.icw.window else: parent = None rc = ReinitializeWindow(title, path, size, description, parent=parent).getrc() return rc def editRepoWindow(self, repoObj): from iw.task_gui import RepoEditor dialog = RepoEditor(self.anaconda, repoObj) dialog.createDialog() dialog.run() def methodstrRepoWindow(self, methodstr, exception): from iw.task_gui import RepoMethodstrEditor self.messageWindow( _("Error Setting Up Repository"), _("The following error occurred while setting up the " "installation repository:\n\n%(e)s\n\nPlease provide the " "correct information for installing %(productName)s.") % {'e': exception, 'productName': productName}) dialog = RepoMethodstrEditor(self.anaconda, methodstr) dialog.createDialog() return dialog.run() def detailedMessageWindow(self, title, text, longText=None, type="ok", default=None, custom_buttons=None, custom_icon=None, expanded=False): if self.icw: parent = self.icw.window else: parent = None rc = DetailedMessageWindow (title, text, longText, type, default, custom_buttons, custom_icon, run=True, parent=parent, expanded=expanded).getrc() return rc def mainExceptionWindow(self, shortText, longTextFile): from meh.ui.gui import MainExceptionWindow log.critical(shortText) win = MainExceptionWindow (shortText, longTextFile) addFrame(win.dialog) return win def saveExceptionWindow(self, accountManager, signature): from meh.ui.gui import SaveExceptionWindow import urlgrabber if not network.hasActiveNetDev(): if self.messageWindow(_("Warning"), _("You do not have an active network connection. This is " "required by some exception saving methods. Would you " "like to configure your network now?"), type = "yesno"): if not self.enableNetwork(): self.messageWindow(_("No Network Available"), _("Remote exception saving methods will not work.")) else: urlgrabber.grabber.reset_curl_obj() win = SaveExceptionWindow (accountManager, signature) win.run() def exitWindow(self, title, text): if self.icw: parent = self.icw.window else: parent = None rc = MessageWindow (title, text, type="custom", custom_icon="info", parent=parent, custom_buttons=[_("_Exit installer")]).getrc() return rc def getLuksPassphrase(self, passphrase = "", preexist = False): if self.icw: parent = self.icw.window else: parent = None d = luksPassphraseWindow(passphrase, parent = parent, preexist = preexist) rc = d.run() passphrase = d.getPassphrase() isglobal = d.getGlobal() d.destroy() return (passphrase, isglobal) def passphraseEntryWindow(self, device): if self.icw: parent = self.icw.window else: parent = None d = PassphraseEntryWindow(device, parent = parent) rc = d.run() d.destroy() return rc def beep(self): gtk.gdk.beep() def kickstartErrorWindow(self, text): s = _("The following error was found while parsing the " "kickstart configuration file:\n\n%s") %(text,) return self.messageWindow(_("Error Parsing Kickstart Config"), s, type = "custom", custom_buttons = [_("_Exit installer")], custom_icon = "error") def display_step(self, step): return self.icw.display_step(step) def getBootdisk (self): return None def run(self, anaconda): self.anaconda = anaconda if anaconda.keyboard and not flags.livecdInstall: anaconda.keyboard.activate() self.icw = InstallControlWindow(self.anaconda) self.icw.run() class InstallControlWindow: def setLanguage (self): if not self.__dict__.has_key('window'): return self.reloadRcQueued = 1 self.setup_window(True) def setLtR(self): ltrrtl = gettext.dgettext("gtk20", "default:LTR") if ltrrtl == "default:RTL": gtk.widget_set_default_direction (gtk.TEXT_DIR_RTL) elif ltrrtl == "default:LTR": gtk.widget_set_default_direction (gtk.TEXT_DIR_LTR) else: log.error("someone didn't translate the ltr bits right: %s" %(ltrrtl,)) gtk.widget_set_default_direction (gtk.TEXT_DIR_LTR) def prevClicked (self, *args): try: self.currentWindow.getPrev () except StayOnScreen: return self.anaconda.dispatch.go_back() def nextClicked (self, *args): try: rc = self.currentWindow.getNext () except StayOnScreen: return self.anaconda.dispatch.go_forward() def debugClicked (self, *args): try: # switch to VC1 so we can debug isys.vtActivate (1) except SystemError: pass import pdb try: pdb.set_trace() except Exception: sys.exit(-1) try: # switch back isys.vtActivate (6) except SystemError: pass def handleRenderCallback(self): self.currentWindow.renderCallback() if flags.autostep: if flags.autoscreenshot: # let things settle down graphically processEvents() time.sleep(1) takeScreenShot() self.nextClicked() else: gobject.source_remove(self.handle) def display_step(self, step): (file, className) = stepToClass[step] newScreenClass = None while True: try: found = imp.find_module(file, iw.__path__) moduleName = 'pyanaconda.iw.%s' % file loaded = imp.load_module(moduleName, *found) newScreenClass = loaded.__dict__[className] break except ImportError as e: stdout_log.error("loading interface component %s" % className) stdout_log.error(traceback.format_exc()) win = MessageWindow(_("Error!"), _("An error occurred when attempting " "to load an installer interface " "component.\n\nclassName = %s") % (className,), type="custom", custom_icon="warning", custom_buttons=[_("_Exit"), _("_Retry")]) if not win.getrc(): msg = _("The system will now reboot.") buttons = [_("_Reboot")] MessageWindow(_("Exiting"), msg, type="custom", custom_icon="warning", custom_buttons=buttons) sys.exit(0) ics = InstallControlState (self) ics.setPrevEnabled(self.anaconda.dispatch.can_go_back()) self.destroyCurrentWindow() self.currentWindow = newScreenClass(ics) new_screen = self.currentWindow.getScreen(self.anaconda) # If the getScreen method returned None, that means the screen did not # want to be displayed for some reason and we should skip to the next # step. However, we do not want to remove the current step from the # list as later events may cause the screen to be displayed. if not new_screen: return DISPATCH_DEFAULT self.update (ics) self.installFrame.add(new_screen) self.installFrame.show_all() self.currentWindow.focus() self.handle = gobject.idle_add(self.handleRenderCallback) if self.reloadRcQueued: self.window.reset_rc_styles() self.reloadRcQueued = 0 # the screen is displayed, we wait for the user now return DISPATCH_WAITING def destroyCurrentWindow(self): children = self.installFrame.get_children () if children: child = children[0] self.installFrame.remove (child) child.destroy () self.currentWindow = None def update (self, ics): self.mainxml.get_widget("backButton").set_sensitive(ics.getPrevEnabled()) self.mainxml.get_widget("nextButton").set_sensitive(ics.getNextEnabled()) if ics.getGrabNext(): self.mainxml.get_widget("nextButton").grab_focus() self.mainxml.get_widget("nextButton").set_flags(gtk.HAS_DEFAULT) def __init__ (self, anaconda): self._main_loop_running = False self.reloadRcQueued = 0 self.currentWindow = None self.anaconda = anaconda self.handle = None self.window = None def keyRelease (self, window, event): if ((event.keyval == gtk.keysyms.KP_Delete or event.keyval == gtk.keysyms.Delete) and (event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK))): self.close() # XXX hack: remove me when the accelerators work again. elif (event.keyval == gtk.keysyms.F12 and self.currentWindow.getICS().getNextEnabled()): self.nextClicked() elif event.keyval == gtk.keysyms.Print: takeScreenShot() def close(self, *args): if self._main_loop_running: gtk.main_quit() self._main_loop_running = False def _doExitConfirm (self, win = None, *args): win = MessageWindow(_("Exit installer"), _("Are you sure you wish to exit the installer?"), type="custom", custom_icon="question", custom_buttons = [_("Cancel"), _("_Exit installer")], parent = win) if win.getrc() == 0: return True sys.exit(0) def createWidgets (self): """ Sets up the widgets in the main installler window. """ self.window.set_title(_("%s Installer") %(productName,)) i = self.mainxml.get_widget("headerImage") p = readImageFromFile("anaconda_header.png", dither = False, image = i) if p is None: print(_("Unable to load title bar")) if flags.livecdInstall: i.hide() self.window.set_resizable(True) self.window.maximize() elif flags.preexisting_x11: # Forwarded X11, don't take over their whole screen i.hide() self.window.set_resizable(True) else: # Normal install, full screen self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) if gtk.gdk.screen_height() < 600: i.hide() width = None height = None xrandr = iutil.execWithCapture("xrandr", ["-q"], stderr="/dev/tty5") lines = xrandr.splitlines() xrandr = filter(lambda x: "current" in x, lines) if xrandr and len(xrandr) == 1: fields = xrandr[0].split() pos = fields.index('current') if len(fields) > pos + 3: width = int(fields[pos + 1]) height = int(fields[pos + 3].replace(',', '')) if width and height: self.window.set_size_request(min(width, 800), min(height, 600)) self.window.show() if flags.debug: self.mainxml.get_widget("debugButton").show_now() self.installFrame = self.mainxml.get_widget("installFrame") def connectSignals(self): sigs = { "on_nextButton_clicked": self.nextClicked, "on_rebootButton_clicked": self.close, "on_closeButton_clicked": self.close, "on_backButton_clicked": self.prevClicked, "on_debugButton_clicked": self.debugClicked, "on_mainWindow_key_release_event": self.keyRelease, "on_mainWindow_delete_event": self._doExitConfirm, } self.mainxml.signal_autoconnect(sigs) def loadGlade(self): self.mainxml = gtk.glade.XML(findGladeFile("anaconda.glade"), domain="anaconda") def setup_window (self, window_reload): self.setLtR() if window_reload: self.window.destroy() elif flags.livecdInstall: pixbuf = getPixbuf("anaconda.png") gtk.window_set_default_icon(pixbuf) self.loadGlade() self.window = self.mainxml.get_widget("mainWindow") self.createWidgets() self.connectSignals() # 'Back and 'Next' is disabled by default icw = InstallControlState(self) icw.setPrevEnabled(False) icw.setNextEnabled(False) self.window.show() # calling present() will focus the window in the winodw manager so # the mnemonics work without additional clicking self.window.present() def run (self): self.setup_window(False) # start the dispatcher right after the main loop is started: idle_gtk(self.anaconda.dispatch.dispatch) self._main_loop_running = True gtk.main() class InstallControlState: def __init__ (self, cw): self.cw = cw self.prevEnabled = True self.nextEnabled = True self.title = _("Install Window") self.grabNext = True def setTitle (self, title): self.title = title self.cw.update (self) def getTitle (self): return self.title def setPrevEnabled (self, value): if value == self.prevEnabled: return self.prevEnabled = value self.cw.update (self) def getPrevEnabled (self): return self.prevEnabled def setNextEnabled (self, value): if value == self.nextEnabled: return self.nextEnabled = value self.cw.update (self) def getNextEnabled (self): return self.nextEnabled def setGrabNext (self, value): self.grabNext = value self.cw.update (self) def getGrabNext (self): return self.grabNext def getICW (self): return self.cw