# # 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" # we only want to enable the accessibility stuff if requested for now... if flags.cmdline.has_key("dogtail"): os.environ["GTK_MODULES"] = "gail:atk-bridge" import string import time import isys import iutil import sys import shutil import gtk import gtk.glade import gobject from language import expandLangs from constants import * from product import * from network import hasActiveNetDev from installinterfacebase import InstallInterfaceBase import xutils import imputil import gettext _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("anaconda") isys.bind_textdomain_codeset("redhat-dist", "UTF-8") class StayOnScreen(Exception): pass mainWindow = None stepToClass = { # Welcome "language" : ("language_gui", "LanguageWindow"), "keyboard" : ("kbd_gui", "KeyboardWindow"), "welcome" : ("welcome_gui", "WelcomeWindow"), # Preparing the Disks "filtertype" : ("filter_type", "FilterTypeWindow"), "filter" : ("filter_gui", "FilterWindow"), "zfcpconfig" : ("zfcp_gui", "ZFCPWindow"), "partition" : ("partition_gui", "PartitionWindow"), "parttype" : ("autopart_type", "PartitionTypeWindow"), "cleardiskssel": ("cleardisks_gui", "ClearDisksWindow"), "findinstall" : ("examine_gui", "UpgradeExamineWindow"), "addswap" : ("upgrade_swap_gui", "UpgradeSwapWindow"), "upgrademigratefs" : ("upgrade_migratefs_gui", "UpgradeMigrateFSWindow"), # Installing the Gentoo Installation Files "makeconf" : ("makeconf_gui", "MakeconfWindow"), # Installing the Gentoo Base System "mirrorselect": ("mirrorselect_gui", "MirrorselectWindow"), "mirrorselect-sync": ("mirrorselect-sync_gui", "MirrorselectSyncWindow"), "profile": ("profile_gui", "ProfileWindow"), "use": ("use_gui", "UseWindow"), # Configuring the Kernel "timezone" : ("timezone_gui", "TimezoneWindow"), # Configuring your System "network" : ("network_gui", "NetworkWindow"), "accounts" : ("account_gui", "AccountWindow"), # Installing Necessary System Tools "systools" : ("systools_gui", "SystoolsWindow"), # Configuring the Bootloader "bootloader": ("bootloader_main_gui", "MainBootloaderWindow"), "upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"), # Finalizing your Gentoo Installation "useraccounts" : ("user_gui", "AccountWindow"), # Misc "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") # # Stuff for screenshots # screenshotDir = None screenshotIndex = 0 def copyScreenshots(): global screenshotIndex global screenshotDir # 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: window = MessageWindow("Error Saving Screenshot", _("An error occurred saving screenshots " "to disk."), type="warning") return # copy all png's over for f in os.listdir(screenshotDir): (path, fname) = os.path.split(f) (b, ext) = os.path.splitext(f) if ext == ".png": shutil.copyfile(screenshotDir + '/' + 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 global screenshotDir if screenshotDir is None: screenshotDir = "/tmp/anaconda-screenshots" if not os.access(screenshotDir, os.R_OK): try: os.mkdir(screenshotDir) except: screenshotDir = None 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 (1): sname = "screenshot-%04d.png" % ( screenshotIndex,) if not os.access(screenshotDir + '/' + sname, os.R_OK): break screenshotIndex = screenshotIndex + 1 if screenshotIndex > 9999: log.error("Too many screenshots!") return screenshot.save (screenshotDir + '/' + sname, "png") screenshotIndex = screenshotIndex + 1 window = MessageWindow(_("Saving Screenshot"), _("A screenshot named '%s' has been saved.") % (sname,) , type="ok") except: 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 handleShiftPrintScrnRelease (window, event): if (event.keyval == gtk.keysyms.Print and event.state & gtk.gdk.SHIFT_MASK): 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 titleBarMousePressCB(widget, event, data): if event.type & gtk.gdk.BUTTON_PRESS: (x, y) = data["window"].get_position() data["state"] = 1 data["button"] = event.button data["deltax"] = event.x_root - x data["deltay"] = event.y_root - y def titleBarMouseReleaseCB(widget, event, data): if data["state"] and event.button == data["button"]: data["state"] = 0 data["button"] = 0 data["deltax"] = 0 data["deltay"] = 0 def titleBarMotionEventCB(widget, event, data): if data["state"]: newx = event.x_root - data["deltax"] newy = event.y_root - data["deltay"] if newx < 0: newx = 0 if newy < 0: newy = 0 (w, h) = data["window"].get_size() if (newx+w) > gtk.gdk.screen_width(): newx = gtk.gdk.screen_width() - w if (newy+20) > (gtk.gdk.screen_height()): newy = gtk.gdk.screen_height() - 20 data["window"].move(int(newx), int(newy)) def addFrame(dialog, title=None): # make screen shots work dialog.connect ("key-release-event", handleShiftPrintScrnRelease) 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, msg: log.error("unable to read %s: %s" %(file, msg)) pixbuf = None return pixbuf def readImageFromFile(file, width = None, height = None, dither = None, image = None): pixbuf = getPixbuf(file) if pixbuf is None: log.warning("can't find pixmap %s" %(file,)) return None if (width is not None and height is not None and height != pixbuf.get_height() and width != pixbuf.get_width()): pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR) 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): # 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 InstallKeyWindow: def __init__(self, anaconda, key): (keyxml, self.win) = getGladeWidget("instkey.glade", "instkeyDialog") if anaconda.instClass.instkeydesc is not None: w = keyxml.get_widget("instkeyLabel") w.set_text(_(anaconda.instClass.instkeydesc)) if not anaconda.instClass.allowinstkeyskip: keyxml.get_widget("skipRadio").hide() keyName = _(anaconda.instClass.instkeyname) if anaconda.instClass.instkeyname is None: keyName = _("Installation Key") # set the install key name based on the installclass for l in ("instkeyLabel", "keyEntryLabel", "skipLabel"): w = keyxml.get_widget(l) t = w.get_text() w.set_text(t % {"instkey": keyName}) self.entry = keyxml.get_widget("keyEntry") self.entry.set_text(key) self.entry.set_sensitive(True) self.keyradio = keyxml.get_widget("keyRadio") self.skipradio = keyxml.get_widget("skipRadio") self.rc = 0 if anaconda.instClass.skipkey: self.skipradio.set_active(True) else: self.entry.grab_focus() self.win.connect("key-release-event", self.keyRelease) addFrame(self.win, title=keyName) def keyRelease(self, window, event): # XXX hack: remove this, too, when the accelerators work again if event.keyval == gtk.keysyms.F12: window.response(1) def run(self): self.win.show() self.rc = self.win.run() return self.rc def get_key(self): if self.skipradio.get_active(): return SKIP_KEY key = self.entry.get_text() key.strip() return key def destroy(self): self.win.destroy() 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") self.globalcheckbutton = xml.get_widget("globalcheckbutton") 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 isglobal = False if rc == gtk.RESPONSE_OK: passphrase = self.passphraseEntry.get_text() isglobal = self.globalcheckbutton.get_active() if busycursor: setCursorToBusy() self.rc = (passphrase, isglobal) 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)) 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: sys.exit(-1) try: # switch back isys.vtActivate (6) except SystemError: pass 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): 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") if parent: self.dialog.set_transient_for(parent) if custom_icon: img = gtk.Image() 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, 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 EntryWindow(MessageWindow): def __init__ (self, title, text, prompt, entrylength = None): mainWindow = None MessageWindow.__init__(self, title, text, type = "okcancel", custom_icon="question", run = False) self.entry = gtk.Entry() if entrylength: self.entry.set_width_chars(entrylength) self.entry.set_max_length(entrylength) # eww, eww, eww... but if we pack in the vbox, it goes to the right # place! self.dialog.child.pack_start(self.entry) def run(self): MessageWindow.run(self) if self.rc == 0: return None t = self.entry.get_text() t.strip() if len(t) == 0: return None return t def destroy(self): self.dialog.destroy() class InstallInterface(InstallInterfaceBase): def __init__ (self): InstallInterfaceBase.__init__(self) self.icw = None self.installProgress = None # figure out if we want to run interface at 800x600 or 640x480 if gtk.gdk.screen_width() >= 800: self.runres = "800x600" else: self.runres = "640x480" root = gtk.gdk.get_default_root_window() cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) root.set_cursor(cursor) self._initLabelAnswers = {} self._inconsistentLVMAnswers = {} def __del__ (self): pass def shutdown (self): pass def suspend(self): pass def resume(self): pass def enableNetwork(self): if len(self.anaconda.network.netdevices) == 0: return False from netconfig_dialog import NetworkConfigurator net = NetworkConfigurator(self.anaconda.network) ret = net.run() net.destroy() return ret not in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT] 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 createRepoWindow(self): from task_gui import RepoCreator dialog = RepoCreator(self.anaconda) dialog.createDialog() dialog.run() def editRepoWindow(self, repoObj): from task_gui import RepoEditor dialog = RepoEditor(self.anaconda, repoObj) dialog.createDialog() dialog.run() def methodstrRepoWindow(self, methodstr): from task_gui import RepoMethodstrEditor dialog = RepoMethodstrEditor(self.anaconda, methodstr) dialog.createDialog() return dialog.run() def entryWindow(self, title, text, type="ok", entrylength = None): d = EntryWindow(title, text, type, entrylength) rc = d.run() d.destroy() return rc def detailedMessageWindow(self, title, text, longText=None, type="ok", default=None, custom_buttons=None, custom_icon=None): 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).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 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 resetInitializeDiskQuestion(self): self._initLabelAnswers = {} def questionInitializeDisk(self, path, description, size, details=""): retVal = False # The less destructive default if not path: return retVal # we are caching answers so that we don't # ask in each storage.reset() again if path in self._initLabelAnswers: log.info("UI not asking about disk initialization, " "using cached answer: %s" % self._initLabelAnswers[path]) return self._initLabelAnswers[path] elif "all" in self._initLabelAnswers: log.info("UI not asking about disk initialization, " "using cached answer: %s" % self._initLabelAnswers["all"]) return self._initLabelAnswers["all"] rc = self.messageWindow(_("Warning"), _("Error processing drive:\n\n" "%(path)s\n%(size)-0.fMB\n%(description)s\n\n" "This device may need to be reinitialized.\n\n" "REINITIALIZING WILL CAUSE ALL DATA TO BE LOST!\n\n" "This action may also be applied to all other disks " "needing reinitialization.%(details)s") % {'path': path, 'size': size, 'description': description, 'details': details}, type="custom", custom_buttons = [ _("_Ignore"), _("Ignore _all"), _("_Re-initialize"), _("Re-ini_tialize all") ], custom_icon="question") if rc == 0: retVal = False elif rc == 1: path = "all" retVal = False elif rc == 2: retVal = True elif rc == 3: path = "all" retVal = True self._initLabelAnswers[path] = retVal return retVal def resetReinitInconsistentLVMQuestion(self): self._inconsistentLVMAnswers = {} def questionReinitInconsistentLVM(self, pv_names=None, lv_name=None, vg_name=None): retVal = False # The less destructive default allSet = frozenset(["all"]) if not pv_names or (lv_name is None and vg_name is None): return retVal # We are caching answers so that we don't ask for ignoring # in each storage.reset() again (note that reinitialization is # done right after confirmation in dialog, not as a planned # action). key = frozenset(pv_names) if key in self._inconsistentLVMAnswers: log.info("UI not asking about disk initialization, " "using cached answer: %s" % self._inconsistentLVMAnswers[key]) return self._inconsistentLVMAnswers[key] elif allSet in self._inconsistentLVMAnswers: log.info("UI not asking about disk initialization, " "using cached answer: %s" % self._inconsistentLVMAnswers[allSet]) return self._inconsistentLVMAnswers[allSet] if vg_name is not None: message = "Volume Group %s" % vg_name elif lv_name is not None: message = "Logical Volume %s" % lv_name na = {'msg': message, 'pvs': ", ".join(pv_names)} rc = self.messageWindow(_("Warning"), _("Error processing LVM.\n" "There is inconsistent LVM data on %(msg)s. You can " "reinitialize all related PVs (%(pvs)s) which will erase " "the LVM metadata, or ignore which will preserve the " "contents. This action may also be applied to all other " "PVs with inconsistent metadata.") % na, type="custom", custom_buttons = [ _("_Ignore"), _("Ignore _all"), _("_Re-initialize"), _("Re-ini_tialize all") ], custom_icon="question") if rc == 0: retVal = False elif rc == 1: key = allSet retVal = False elif rc == 2: retVal = True elif rc == 3: key = allSet retVal = True self._inconsistentLVMAnswers[key] = retVal return retVal 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 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 (self.runres) def setSteps(self, anaconda): pass 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.gotoPrev() self.setScreen () def nextClicked (self, *args): try: rc = self.currentWindow.getNext () except StayOnScreen: return self.anaconda.dispatch.gotoNext() self.setScreen () 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: 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 setScreen (self): (step, anaconda) = self.anaconda.dispatch.currentStep() if step is None: gtk.main_quit() return if not stepToClass[step]: if self.anaconda.dispatch.dir == DISPATCH_FORWARD: return self.nextClicked() else: return self.prevClicked() (file, className) = stepToClass[step] newScreenClass = None while True: try: found = imputil.imp.find_module(file) loaded = imputil.imp.load_module(className, found[0], found[1], found[2]) newScreenClass = loaded.__dict__[className] break except ImportError, e: print(e) 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 installer will now exit.") buttons = [_("_Exit")] MessageWindow(_("Exiting"), msg, type="custom", custom_icon="warning", custom_buttons=buttons) sys.exit(0) ics = InstallControlState (self) ics.setPrevEnabled(self.anaconda.dispatch.canGoBack()) self.destroyCurrentWindow() self.currentWindow = newScreenClass(ics) new_screen = self.currentWindow.getScreen(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: if self.anaconda.dispatch.dir == DISPATCH_FORWARD: self.anaconda.dispatch.gotoNext() else: self.anaconda.dispatch.gotoPrev() return self.setScreen() 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 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.reloadRcQueued = 0 self.currentWindow = None self.anaconda = anaconda self.handle = 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._doExit() # 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 and event.state & gtk.gdk.SHIFT_MASK): takeScreenShot() def _doReboot(self, *args): with open("/tmp/__anaconda_reboot__", "w") as reb_f: reb_f.flush() gtk.main_quit() os._exit(100) def _doExit (self, *args): gtk.main_quit() os._exit(0) def _doExitConfirm (self, win = None, *args): # FIXME: translate the string 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 self._doExit() def createWidgets (self): 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 (gtk.gdk.screen_height() < 600) or \ (gtk.gdk.screen_height() <= 675 and flags.livecdInstall) or \ getattr(self.anaconda, "fullScreen", False): i.hide() self.window.set_resizable(True) self.window.set_size_request(-1, -1) self.window.fullscreen() else: self.window.set_size_request(800, 600) # if we're running in the live mode and the dpi is something weird, # give ourselves as much space as we can. this gets things to fit # with a dpi of up to 147 if flags.livecdInstall: i.hide() else: self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) 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._doReboot, "on_closeButton_clicked": self._doExit, "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() self.loadGlade() self.window = self.mainxml.get_widget("mainWindow") self.createWidgets() self.connectSignals() self.setScreen() self.window.show() # calling present() will focus the window in the winodw manager so # the mnemonics work without additional clicking self.window.present() def busyCursorPush(self): rootPushBusyCursor() def busyCursorPop(self): rootPopBusyCursor() def run (self, runres): self.setup_window(False) 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 setScreenPrev (self): self.cw.prevClicked () def setScreenNext (self): self.cw.nextClicked () def setGrabNext (self, value): self.grabNext = value self.cw.update (self) def getGrabNext (self): return self.grabNext def getICW (self): return self.cw