#
# 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 <http://www.gnu.org/licenses/>.
#
# Author(s): Matt Wilson <msw@redhat.com>
#            Michael Fulbright <msf@redhat.com>
#

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 = {
    "language" : ("language_gui", "LanguageWindow"),
    "keyboard" : ("kbd_gui", "KeyboardWindow"),
    "welcome" : ("welcome_gui", "WelcomeWindow"),
    "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"),
    "bootloader": ("bootloader_main_gui", "MainBootloaderWindow"),
    "upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"),
    "network" : ("network_gui", "NetworkWindow"),
    "timezone" : ("timezone_gui", "TimezoneWindow"),
    "accounts" : ("account_gui", "AccountWindow"),
    "useraccounts" : ("user_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")

#
# 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
