#
# gui.py - Graphical front end for anaconda
#
# Matt Wilson <[email protected]>
# Michael Fulbright <[email protected]>
#
# Copyright 1999-2003 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

import os
import iutil
import string
import isys
import sys
import parted
import gtk
import htmlbuffer
import rpm
import kudzu
from language import expandLangs
from splashscreen import splashScreenPop
from flags import flags
from constants import *

from rhpl.log import log
from rhpl.translate import _, N_

from product import *
rpm.addMacro("_i18ndomains", "redhat-dist")
isys.bind_textdomain_codeset("redhat-dist", "UTF-8")

StayOnScreen = "stayOnScreen"
mainWindow = None

stepToClass = {
   "language" : ("language_gui", "LanguageWindow"),
   "keyboard" : ("keyboard_gui", "KeyboardWindow"),
   "mouse" : ("mouse_gui", "MouseWindow"),
   "welcome" : ("welcome_gui", "WelcomeWindow"),
   "installtype" : ("installpath_gui", "InstallPathWindow"),
   "partitionmethod" : ("partmethod_gui", "PartitionMethodWindow"),
   "partition" : ("partition_gui", "PartitionWindow"),
   "autopartition" : ("partition_gui", "AutoPartitionWindow"),
   "findinstall" : ("examine_gui", "UpgradeExamineWindow"),
   "addswap" : ("upgrade_swap_gui", "UpgradeSwapWindow"),
   "upgrademigratefs" : ("upgrade_migratefs_gui", "UpgradeMigrateFSWindow"),
   "fdisk" : ("fdisk_gui", "FDiskWindow"),
   "bootloader": ("bootloader_main_gui", "MainBootloaderWindow"),
   "bootloaderadvanced": ("bootloader_advanced_gui", "AdvancedBootloaderWindow"),
   "upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"),
   "network" : ("network_gui", "NetworkWindow"),
   "firewall" : ("firewall_gui", "FirewallWindow"),
   "languagesupport" : ("language_support_gui", "LanguageSupportWindow"),
   "timezone" : ("timezone_gui", "TimezoneWindow"),
   "accounts" : ("account_gui", "AccountWindow"),
   "authentication" : ("auth_gui", "AuthWindow"),
   "desktopchoice": ("desktop_choice_gui", "DesktopChoiceWindow"),
   "package-selection" : ("package_gui", "PackageSelectionWindow"),
   "indivpackage" : ("package_gui", "IndividualPackageSelectionWindow"),
   "dependencies" : ("dependencies_gui", "UnresolvedDependenciesWindow"),
   "videocard" : ("xconfig_gui", "XConfigWindow"),
   "monitor" : ("xconfig_gui", "MonitorWindow"),
   "xcustom" : ("xconfig_gui", "XCustomWindow"),
   "confirminstall" : ("confirm_gui", "InstallConfirmWindow"),
   "confirmupgrade" : ("confirm_gui", "UpgradeConfirmWindow"),
   "finishxconfig" : None,
   "install" : ("progress_gui", "InstallProgressWindow"),
   "bootdisk" : ("bootdisk_gui", "BootdiskWindow"),
   "complete" : ("congrats_gui", "CongratulationWindow"),
}

if iutil.getArch() == 'sparc':
   stepToClass["bootloader"] = ("silo_gui", "SiloWindow")
elif iutil.getArch() == 's390':
   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 copying the "
                                    "screenshots over."), 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":
           iutil.copyFile(screenshotDir + '/' + f,
                          destDir + '/' + fname)

   window = MessageWindow(_("Screenshots Copied"),
                          _("The screenshots have been saved into 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/ramfs/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, gtk.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("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"] = gtk.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, gtk.TRUE, 0.5, 0.5)

   if id:
       gtk.idle_remove(id)

# setup globals
def processEvents():
   gtk.gdk.flush()
   while gtk.events_pending():
       gtk.main_iteration(gtk.FALSE)

def partedExceptionWindow(exc):
   # if our only option is to cancel, let us handle the exception
   # in our code and avoid popping up the exception window here.
   if exc.options == parted.EXCEPTION_CANCEL:
       return parted.EXCEPTION_UNHANDLED
   print exc.type_string
   print exc.message
   print exc.options
   win = gtk.Dialog(exc.type_string, mainWindow, gtk.DIALOG_MODAL)
   addFrame(win)
   win.set_position(gtk.WIN_POS_CENTER)
   label = WrappingLabel(exc.message)
   win.vbox.pack_start (label)
   numButtons = 0
   buttonToAction = {}

   exflags = ((parted.EXCEPTION_FIX, N_("Fix")),
            (parted.EXCEPTION_YES, N_("Yes")),
            (parted.EXCEPTION_NO, N_("No")),
            (parted.EXCEPTION_OK, N_("OK")),
            (parted.EXCEPTION_RETRY, N_("Retry")),
            (parted.EXCEPTION_IGNORE, N_("Ignore")),
            (parted.EXCEPTION_CANCEL, N_("Cancel")))
   for flag, string in exflags:
       if exc.options & flag:
           win.add_button(_(string), flag)
   win.show_all()
   rc = win.run()
   win.destroy()
   return rc

def widgetExpander(widget, growTo=None):
   widget.connect("size-allocate", growToParent, growTo)

def growToParent(widget, rect, growTo=None):
   return
   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=""):
       gtk.Label.__init__(self, "")
       self.set_text_with_mnemonic(text)

class WrappingLabel(gtk.Label):
   def __init__(self, label=""):
       gtk.Label.__init__(self, label)
       self.set_line_wrap(gtk.TRUE)
       self.ignoreEvents = 0
#        self.set_size_request(-1, 1)
       widgetExpander(self)

def titleBarMousePressCB(widget, event, data):
   if event.type & gtk.gdk.BUTTON_PRESS:
       data["state"] = 1
       data["button"] = event.button
       data["deltax"] = event.x
       data["deltay"] = event.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(newx, newy)

def addFrame(dialog, title=None):
   contents = dialog.get_children()[0]
   dialog.remove(contents)
   frame = gtk.Frame()
   frame.set_shadow_type(gtk.SHADOW_OUT)
   box = gtk.VBox()
   try:
       if title is None:
           title = dialog.get_title()

       if title:
           data = {}
           data["state"] = 0
           data["button"] = 0
           data["deltax"] = 0
           data["deltay"] = 0
           data["window"] = dialog
           eventBox = gtk.EventBox()
           eventBox.connect("button-press-event", titleBarMousePressCB, data)
           eventBox.connect("button-release-event", titleBarMouseReleaseCB, data)
           eventBox.connect("motion-notify-event", titleBarMotionEventCB,data)
           titleBox = gtk.HBox(gtk.FALSE, 5)
           eventBox.add(titleBox)
           eventBox.modify_bg(gtk.STATE_NORMAL,
                              eventBox.rc_get_style().bg[gtk.STATE_SELECTED])
           titlelbl = gtk.Label("")
           titlelbl.set_markup("<b>"+_(title)+"</b>")
           titlelbl.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse ("white"))
           titlelbl.set_property("ypad", 4)
           titleBox.pack_start(titlelbl)
           box.pack_start(eventBox, gtk.FALSE, gtk.FALSE)
   except:
       pass

   frame2=gtk.Frame()
   frame2.set_shadow_type(gtk.SHADOW_NONE)
   frame2.set_border_width(4)
   frame2.add(contents)
   box.pack_start(frame2, gtk.TRUE, gtk.TRUE, padding=5)
   frame.add(box)
   frame.show()
   dialog.add(frame)

   # make screen shots work
   dialog.connect ("key-release-event", handleShiftPrintScrnRelease)


class WaitWindow:
   def __init__(self, title, text):
       self.window = gtk.Window(gtk.WINDOW_POPUP)
       self.window.set_title(title)
       self.window.set_position(gtk.WIN_POS_CENTER)
       self.window.set_modal(gtk.TRUE)
       label = WrappingLabel(text)
       box = gtk.Frame()
       box.set_border_width(10)
       box.add(label)
       box.set_shadow_type(gtk.SHADOW_NONE)
       frame = gtk.Frame ()
       frame.set_shadow_type(gtk.SHADOW_OUT)
       frame.add (box)
       self.window.add(frame)
       self.window.show_all()
       rootPushBusyCursor()

   def pop(self):
       self.window.destroy()
       rootPopBusyCursor()

class ProgressWindow:
   def __init__(self, title, text, total):
       self.window = gtk.Window (gtk.WINDOW_POPUP)
       self.window.set_title (title)
       self.window.set_position (gtk.WIN_POS_CENTER)
       self.window.set_modal (gtk.TRUE)
       box = gtk.VBox (gtk.FALSE, 5)
       box.set_border_width (10)

       label = WrappingLabel (text)
       label.set_alignment (0.0, 0.5)
       box.pack_start (label, gtk.FALSE)

       self.total = total
       self.progress = gtk.ProgressBar ()
       box.pack_start (self.progress, gtk.TRUE)

       frame = gtk.Frame ()
       frame.set_shadow_type (gtk.SHADOW_OUT)
       frame.add (box)
       self.window.add (frame)
       self.window.show_all ()
       rootPushBusyCursor()

   def set (self, amount):
       # only update widget if we've changed by 5%
       curval = self.progress.get_fraction()
       newval = float (amount) / self.total
       if newval < 0.998:
           if (newval - curval) < 0.05 and newval > curval:
               return
       self.progress.set_fraction (newval)
       processEvents ()

   def pop(self):
       self.window.destroy ()
       rootPopBusyCursor()

class ExceptionWindow:
   def __init__ (self, text):
       try:
           floppyDevices = 0
           for dev in kudzu.probe(kudzu.CLASS_FLOPPY, kudzu.BUS_UNSPEC,
                                  kudzu.PROBE_ALL):
               if not dev.detached:
                   floppyDevices = floppyDevices + 1
       except:
           floppyDevices = 0

       win = gtk.Dialog("Exception Occured", mainWindow, gtk.DIALOG_MODAL)
       win.add_button("_Debug", 0)
       if floppyDevices > 0 or DEBUG:
           win.add_button("_Save to floppy", 1)
       win.add_button('gtk-ok', 2)
       buffer = gtk.TextBuffer(None)
       buffer.set_text(text)
       textbox = gtk.TextView()
       textbox.set_buffer(buffer)
       textbox.set_property("editable", gtk.FALSE)
       textbox.set_property("cursor_visible", gtk.FALSE)
       sw = gtk.ScrolledWindow ()
       sw.add (textbox)
       sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

       hbox = gtk.HBox (gtk.FALSE)
##         file = pixmap_file('gnome-warning.png')
##         if file:
##             hbox.pack_start (GnomePixmap (file), gtk.FALSE)

       if floppyDevices > 0:
           info = WrappingLabel(exceptionText)
       else:
           info = WrappingLabel(exceptionTextNoFloppy)

       info.set_size_request (400, -1)

       hbox.pack_start (sw, gtk.TRUE)
       win.vbox.pack_start (info, gtk.FALSE)
       win.vbox.pack_start (hbox, gtk.TRUE)
       win.set_size_request (500, 300)
       win.set_position (gtk.WIN_POS_CENTER)
       addFrame(win)
       win.show_all ()
       self.window = win
       self.rc = self.window.run ()
#        self.window.destroy()

   def getrc (self):
       # I did it this way for future expantion
       # 0 is debug
       if self.rc == 0:
           try:
               # switch to VC1 so we can debug
               isys.vtActivate (1)
           except SystemError:
               pass
           return 1
       # 1 is save
       if self.rc == 1:
           return 2
       # 2 is OK
       elif self.rc == 2:
           return 0

class MessageWindow:
   def getrc (self):
       return self.rc

   def __init__ (self, title, text, type="ok", default=None, custom_buttons=None, custom_icon=None):
       if flags.autostep:
           self.rc = 1
           return
       self.rc = None
       docustom = 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':
           docustom = 1
           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

       dialog = gtk.MessageDialog(mainWindow, 0, style, buttons, text)

       if docustom:
           rid=0
           for button in custom_buttons:
               if button == _("Cancel"):
                   tbutton = "gtk-cancel"
               else:
                   tbutton = button

               widget = dialog.add_button(tbutton, rid)
               rid = rid + 1

           defaultchoice = rid - 1
       else:
           if default == "no":
               defaultchoice = 0
           elif default == "yes" or default == "ok":
               defaultchoice = 1
           else:
               defaultchoice = 0

       addFrame(dialog, title=title)
       dialog.set_position (gtk.WIN_POS_CENTER)
       dialog.set_default_response(defaultchoice)
       dialog.show_all ()

       # XXX - Messy - turn off busy cursor if necessary
       busycursor = getBusyCursorStatus()
       setCursorToNormal()
       rc = dialog.run()

       if rc == gtk.RESPONSE_OK or rc == gtk.RESPONSE_YES:
           self.rc = 1
       elif (rc == gtk.RESPONSE_CANCEL or rc == gtk.RESPONSE_NO
           or rc == gtk.RESPONSE_CLOSE):
           self.rc = 0
       elif rc == gtk.RESPONSE_DELETE_EVENT:
           self.rc = 0
       else:
           self.rc = rc
       dialog.destroy()

       # restore busy cursor
       if busycursor:
           setCursorToBusy()

class InstallInterface:
   def __init__ (self):
       # 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"

   def __del__ (self):
       pass

   def shutdown (self):
       pass

   def setPackageProgressWindow (self, ppw):
       self.ppw = ppw

   def waitWindow (self, title, text):
       return WaitWindow (title, text)

   def progressWindow (self, title, text, total):
       return ProgressWindow (title, text, total)

   def packageProgressWindow (self, total, totalSize):
       self.ppw.setSizes (total, totalSize)
       return self.ppw

   def messageWindow(self, title, text, type="ok", default = None,
                    custom_buttons=None,  custom_icon=None):
       rc = MessageWindow (title, text, type, default,
                           custom_buttons, custom_icon).getrc()
       return rc

   def exceptionWindow(self, title, text):
       print text
       win = ExceptionWindow (text)
       return win.getrc ()

   def dumpWindow(self):
       window = MessageWindow("Save Crash Dump",
                              _("Please insert a floppy now. All contents "
                                "of the disk will be erased, so please "
                                "choose your diskette carefully."),
                              "okcancel")
       rc = window.getrc()
       return not rc

   def getBootdisk (self):
       return None

   def run(self, id, dispatch, configFileData):
##         from xkb import XKB
##         kb = XKB()

       self.dispatch = dispatch

       # XXX users complain when the keypad doesn't work for input.
##         if 0 and flags.setupFilesystems:
##             try:
##                 kb.setMouseKeys (1)
##             except SystemError:
##                 pass

       # XXX x_already_set is a hack
       if id.keyboard and not id.x_already_set:
           id.keyboard.activate()
##          info = id.keyboard.getXKB()
##          if info:
##                 (rules, model, layout, variant, options) = info
##                 kb.setRule (model, layout, variant, "complete")

       id.fsset.registerMessageWindow(self.messageWindow)
       id.fsset.registerProgressWindow(self.progressWindow)
       id.fsset.registerWaitWindow(self.waitWindow)
       parted.exception_set_handler(partedExceptionWindow)

       lang = id.instLanguage.getCurrent()
       lang = id.instLanguage.getLangNick(lang)
       self.icw = InstallControlWindow (self, self.dispatch, lang)
       self.icw.run (self.runres, configFileData)

class TextViewBrowser(gtk.TextView):
   def __init__(self):
       self.hadj = None
       self.vadj = None

       gtk.TextView.__init__(self)
       self.set_property('editable', gtk.FALSE)
       self.set_property('cursor_visible', gtk.FALSE)
       self.set_left_margin(10)
       self.set_wrap_mode(gtk.WRAP_WORD)
       self.connect('move-cursor', self.moveCursor)
       self.connect('set-scroll-adjustments', self.cacheAdjustments)

   def swallowFocus(self, *args):
       self.emit_stop_by_name('focus-in-event')

   def cacheAdjustments(self, view, hadj, vadj):
       self.hadj = hadj
       self.vadj = vadj

   def moveCursor(self, view, step, count, extend_selection):
       if step == gtk.MOVEMENT_DISPLAY_LINES:
           if count == -1 and self.vadj != None:
               self.vadj.value = max(self.vadj.value - self.vadj.step_increment,
                                     self.vadj.lower)
               self.vadj.value_changed()
           elif count == 1 and self.vadj != None:
               self.vadj.value = min(self.vadj.value + self.vadj.step_increment - 1,
                                     self.vadj.upper - self.vadj.page_increment - 1)
               self.vadj.value_changed()
       elif step == gtk.MOVEMENT_PAGES:
           if count == -1 and self.vadj != None:
               self.vadj.value = max(self.vadj.value - self.vadj.page_increment,
                                     self.vadj.lower)
               self.vadj.value_changed()
           elif count == 1 and self.vadj != None:
               self.vadj.value = min(self.vadj.value + self.vadj.page_increment - 1,
                                     self.vadj.upper - self.vadj.page_increment - 1)
               self.vadj.value_changed()

       self.emit_stop_by_name ('move-cursor')


class InstallControlWindow:
   def setLanguage (self, locale):
       #gtk_set_locale ()
       #gtk_rc_init ()
       #gtk_rc_reparse_all ()

       self.langSearchPath = expandLangs(locale) + ['C']

##         found = 0
##         for l in self.langSearchPath:
##             if os.access ("/etc/gtk/gtkrc." + l, os.R_OK):
##                 rc_parse("/etc/gtk/gtkrc." + l)
##                 found = 1
##         if not found:
##             rc_parse("/etc/gtk/gtkrc")

##         #_gtk_nuke_rc_mtimes ()
##         gtk_rc_reparse_all ()

       if not self.__dict__.has_key('window'): return

       self.reloadRcQueued = 1

       self.updateStockButtons()
       self.helpFrame.set_label (_("Online Help"))
       self.installFrame.set_label (_("Language Selection"))
       self.loadReleaseNotes()
       self.refreshHelp(recreate = 1)

   def prevClicked (self, *args):
       try:
           self.currentWindow.getPrev ()
       except StayOnScreen:
           return

       self.dispatch.gotoPrev()
       self.dir = -1

       self.setScreen ()

   def nextClicked (self, *args):
       try:
           rc = self.currentWindow.getNext ()
       except StayOnScreen:
           return

       self.dispatch.gotoNext()
       self.dir = 1

       self.setScreen ()

   def helpClicked (self, widget, simulated=0):
       self.hbox.remove (widget)
       if widget == self.hideHelpButton:
           self.bin.remove (self.table)
           self.installFrame.reparent (self.bin)
           self.showHelpButton.show ()
           self.showHelpButton.set_state (gtk.STATE_NORMAL)
           self.hbox.pack_start (self.showHelpButton, gtk.FALSE)
           self.hbox.reorder_child (self.showHelpButton, 0)
           self.showHelpButton.grab_focus()
           self.displayHelp = gtk.FALSE
       else:
           self.bin.remove (self.installFrame)
           self.table.attach (self.installFrame, 1, 3, 0, 1,
                              gtk.FILL | gtk.EXPAND,
                              gtk.FILL | gtk.EXPAND)
           self.bin.add (self.table)
           self.refreshHelp()
           self.hideHelpButton.show ()
           self.showHelpButton.set_state (gtk.STATE_NORMAL)
           self.hbox.pack_start (self.hideHelpButton, gtk.FALSE)
           self.hbox.reorder_child (self.hideHelpButton, 0)
           self.hideHelpButton.grab_focus()
           self.displayHelp = gtk.TRUE

   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 (7)
       except SystemError:
           pass

   def refreshHelp(self, recreate = 0):
       buffer = htmlbuffer.HTMLBuffer()
       ics = self.currentWindow.getICS()
       buffer.feed(ics.getHTML(self.langSearchPath))
       textbuffer = buffer.get_buffer()
       if recreate == 0:
           self.help.set_buffer(textbuffer)
       else:
           self.help_sw.remove(self.help)
           self.help = TextViewBrowser()
           self.help_sw.add(self.help)
           self.help.set_buffer(textbuffer)
           self.help.show()
       # scroll to the top.  Do this with a mark so it's done in the idle loop
       iter = textbuffer.get_iter_at_offset(0)
       mark = textbuffer.create_mark("top", iter, gtk.FALSE)
       self.help.scroll_to_mark(mark, 0.0, gtk.FALSE, 0.0, 0.0)

   def relnotes_closed (self, *args):
       self.textWin.destroy()

#
# XXX - disabling this behavior for now due to bug where if you pop up
#       release notes during package selection, then close it after
#       package selection is done and install has moved to next screen,
#       the stockButtons get their state screwed up
#
#        for (icon, name, text, func) in self.stockButtons:
#            if self.__dict__.has_key(name):
#               self.__dict__[name].set_sensitive(self.relnotes_buttonstate[name])
       return

   def releaseClicked (self, widget):
       self.textWin = gtk.Dialog(parent=mainWindow, flags=gtk.DIALOG_MODAL)


#
# XXX - disabling this behavior for now due to bug where if you pop up
#       release notes during package selection, then close it after
#       package selection is done and install has moved to next screen,
#       the stockButtons get their state screwed up
#       self.relnotes_buttonstate={}
#        for (icon, name, text, func) in self.stockButtons:
#            if self.__dict__.has_key(name):
#               self.relnotes_buttonstate[name] = self.__dict__[name].get_property("sensitive")
#                self.__dict__[name].set_sensitive(gtk.FALSE)

       table = gtk.Table(3, 3, gtk.FALSE)
       self.textWin.vbox.pack_start(table)
       self.textWin.add_button('gtk-close', gtk.RESPONSE_NONE)
       self.textWin.connect("response", self.relnotes_closed)
       vbox1 = gtk.VBox ()
       vbox1.set_border_width (10)
       frame = gtk.Frame (_("Release Notes"))
       frame.add(vbox1)
       frame.set_label_align (0.5, 0.5)
       frame.set_shadow_type (gtk.SHADOW_NONE)

       self.textWin.set_position (gtk.WIN_POS_CENTER)

       if self.releaseNotesBuffer:
           text = TextViewBrowser()
           text.set_buffer(self.releaseNotesBuffer)

           sw = gtk.ScrolledWindow()
           sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
           sw.set_shadow_type(gtk.SHADOW_IN)
           sw.add(text)
           vbox1.pack_start(sw)

           a = gtk.Alignment (0, 0, 1.0, 1.0)
           a.add (frame)

           self.textWin.set_default_size (635, 393)
           self.textWin.set_size_request (635, 393)
           self.textWin.set_position (gtk.WIN_POS_CENTER)

           table.attach (a, 1, 2, 1, 2,
                         gtk.FILL | gtk.EXPAND,
                         gtk.FILL | gtk.EXPAND, 5, 5)

           self.textWin.set_border_width(0)
           addFrame(self.textWin, _("Release Notes"))
           self.textWin.show_all()

       else:
           self.textWin.set_position (gtk.WIN_POS_CENTER)
           label = gtk.Label(_("Unable to load file!"))

           table.attach (label, 1, 2, 1, 2,
                         gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 5, 5)

           self.textWin.set_border_width(0)
           addFrame(self.textWin)
           self.textWin.show_all()

   def loadReleaseNotes(self):
       langList = self.langSearchPath + [ "" ]
       sourcepath = self.dispatch.method.getSourcePath()
       suffixList = []
       for lang in langList:
           if lang:
               suffixList.append("-%s.html" % (lang,))
               suffixList.append(".%s" % (lang,))
           else:
               suffixList.append(".html")
               suffixList.append("")

           for suffix in suffixList:
               fn = "%s/RELEASE-NOTES%s" % (sourcepath, suffix)
               if os.access(fn, os.R_OK):
                   file = open(fn, "r")
                   if suffix.endswith('.html'):
                       buffer = htmlbuffer.HTMLBuffer()
                       buffer.feed(file.read())
                       self.releaseNotesBuffer = buffer.get_buffer()
                   else:
                       buffer = gtk.TextBuffer(None)
                       buffer.set_text(file.read())
                       self.releaseNotesBuffer = buffer
                   file.close()
                   return

       buffer = gtk.TextBuffer(None)
       buffer.set_text(_("Release notes are missing.\n"))
       self.releaseNotesBuffer = buffer

   def handleRenderCallback(self):
       self.currentWindow.renderCallback()
       if flags.autostep:
           self.nextClicked()
       else:
           gtk.idle_remove(self.handle)

   def setScreen (self):
       (step, args) = self.dispatch.currentStep()
       if step is None:
           gtk.mainquit()
           return

       if not stepToClass[step]:
           if self.dir == 1:
               return self.nextClicked()
           else:
               return self.prevClicked()

       (file, className) = stepToClass[step]
       newScreenClass = None
       s = "from %s import %s; newScreenClass = %s" % (file, className,
                                                       className)
       while 1:
           try:
               exec s
               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():
                   MessageWindow(_("Rebooting System"),
                                 _("Your system will now be rebooted..."),
                                 type="custom",
                                 custom_icon="warning",
                                 custom_buttons=[_("_Reboot")])
                   sys.exit(0)

       ics = InstallControlState (self)
       ics.setPrevEnabled(self.dispatch.canGoBack())

       self.destroyCurrentWindow()
       self.currentWindow = newScreenClass(ics)

       new_screen = apply(self.currentWindow.getScreen, args)
       if not new_screen:
           return

       self.update (ics)

       self.installFrame.set_label(ics.getTitle ())
       self.installFrame.add(new_screen)
       self.installFrame.show_all()

       self.handle = gtk.idle_add(self.handleRenderCallback)

       if self.reloadRcQueued:
           self.window.reset_rc_styles()
           self.reloadRcQueued = 0

       if self.displayHelp:
           self.refreshHelp()

   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.installFrame.set_label (ics.getTitle ())

       prevButton = self.prevButtonStock
       nextButton = self.nextButtonStock

       if ics.getNextButton():
           (icon, text) = ics.getNextButton()
           button = gtk.Button()
           box = gtk.HBox(gtk.FALSE, 0)
           image = gtk.Image()
           image.set_from_stock(icon, gtk.ICON_SIZE_BUTTON)
           box.pack_start(image, gtk.FALSE, gtk.FALSE)
           label = gtk.Label(_(text))
           label.set_property("use-underline", gtk.TRUE)
           box.pack_start(label, gtk.TRUE, gtk.TRUE)
           button.add(box)
           button.connect("clicked", self.nextClicked)
           button.show_all()
           button.label = label
           nextButton = button

       children = self.buttonBox.get_children()

       if not nextButton in children and self.nextButtonStock in children:
           pos = children.index(self.nextButtonStock)
           self.buttonBox.remove(self.nextButtonStock)
           self.buttonBox.pack_end(nextButton)
           self.buttonBox.reorder_child(nextButton, pos)
           self.nextButtonStock = nextButton

       prevButton.set_sensitive (ics.getPrevEnabled ())
       nextButton.set_sensitive (ics.getNextEnabled ())
       self.hideHelpButton.set_sensitive (ics.getHelpButtonEnabled ())
       self.showHelpButton.set_sensitive (ics.getHelpButtonEnabled ())

       if ics.getHelpEnabled () == gtk.FALSE:
           if self.displayHelp:
               self.helpClicked (self.hideHelpButton, 1)
       elif ics.getHelpEnabled () == gtk.TRUE:
           if not self.displayHelp:
               self.helpClicked (self.showHelpButton, 1)

       if (ics.getGrabNext ()):
           nextButton.grab_focus ()

   def __init__ (self, ii, dispatch, locale):
       self.prevButtonStock = None
       self.nextButtonStock = None
       self.releaseButton = None
       self.showHelpButton = None
       self.hideHelpButton = None
       self.debugButton = None

       self.stockButtons = (('gtk-go-back', "prevButtonStock",
                             N_("_Back"), self.prevClicked),
                            ('gtk-go-forward', "nextButtonStock",
                             N_("_Next"), self.nextClicked),
                            ('gtk-new', "releaseButton",
                              N_("_Release Notes"), self.releaseClicked),
                            ('gtk-help', "showHelpButton",
                             N_("Show _Help"), self.helpClicked),
                            ('gtk-help', "hideHelpButton",
                             N_("Hide _Help"), self.helpClicked),
                            ('gtk-execute', 'debugButton',
                             N_("_Debug"), self.debugClicked))

       self.reloadRcQueued = 0
       self.ii = ii
       self.dispatch = dispatch
       self.setLanguage(locale)
       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))):
           gtk.mainquit()
           os._exit(0)
       # 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 buildStockButtons(self):
       for (icon, item, text, action) in self.stockButtons:
           button = gtk.Button()
           box = gtk.HBox(gtk.FALSE, 0)
           image = gtk.Image()
           image.set_from_stock(icon, gtk.ICON_SIZE_BUTTON)
           box.pack_start(image, gtk.FALSE, gtk.FALSE)
           label = gtk.Label(_(text))
           label.set_property("use-underline", gtk.TRUE)
           box.pack_start(label, gtk.TRUE, gtk.TRUE)
           button.add(box)
           button.connect("clicked", action)
           button.show_all()
           button.label = label
           self.__dict__[item] = button

   def updateStockButtons(self):
       for (icon, item, text, action) in self.stockButtons:
           button = self.__dict__[item]

           for child in button.get_children():
               button.remove(child)

           # FIXME: this is cut and pasted from above; make a nicer
           # function that knows how to replace the contents in the
           # button for a future release
           box = gtk.HBox(gtk.FALSE, 0)
           image = gtk.Image()
           image.set_from_stock(icon, gtk.ICON_SIZE_BUTTON)
           box.pack_start(image, gtk.FALSE, gtk.FALSE)
           label = gtk.Label(_(text))
           label.set_property("use-underline", gtk.TRUE)
           box.pack_start(label, gtk.TRUE, gtk.TRUE)
           button.add(box)
           button.show_all()
           button.label = label
           button.queue_resize()

   def findPixmap(self, file):
       for path in ("/mnt/source/" + productSite + "/RHupdates/pixmaps/",
                    "/mnt/source/" + productSite + "/RHupdates/",
                    "/tmp/updates/pixmaps/", "/tmp/updates/",
                    "/mnt/source/RHupdates/pixmaps/",
                    "/mnt/source/RHupdates/",
                    "/tmp/product/pixmaps/", "/tmp/product/",
                    "/usr/share/anaconda/pixmaps/", "pixmaps/",
                    "/usr/share/pixmaps/",
                    "/usr/share/anaconda/", ""):
           fn = path + file
           if os.access(fn, os.R_OK):
               return fn
       return None

   def setup_window (self, runres):
       self.window = gtk.Window ()
       global mainWindow
       mainWindow = self.window
       self.window.set_events (gtk.gdk.KEY_RELEASE_MASK)

       if runres == '640x480':
           self.window.set_default_size (640, 480)
           self.window.set_size_request (640, 480)
       else:
           self.window.set_default_size (800, 600)
           self.window.set_size_request (800, 600)

       self.window.set_border_width (10)

       title = _("%s Installer") % (productName,)
       if os.environ["DISPLAY"][:1] != ':':
           # from gnome.zvt import *
           # zvtwin = gtk.Window ()
#           shtitle = _("Red Hat Linux Install Shell")
           try:
               f = open ("/tmp/netinfo", "r")
           except:
               pass
           else:
               lines = f.readlines ()
               f.close ()
               for line in lines:
                   netinf = string.splitfields (line, '=')
                   if netinf[0] == "HOSTNAME":
                       title = _("%s Installer on %s") % (productName, string.strip (netinf[1]))
#                       shtitle = _("Red Hat Linux Install Shell on %s") % string.strip (netinf[1])
                       break

           # zvtwin.set_title (shtitle)
           # zvt = ZvtTerm (80, 24)
           # if zvt.forkpty() == 0:
           #     os.execv ("/bin/sh", [ "/bin/sh" ])
           # zvt.show ()
           # zvtwin.add (zvt)
           # zvtwin.show_all ()

       self.window.set_title (title)
       self.window.set_position (gtk.WIN_POS_CENTER)
       self.window.set_border_width(0)
       vbox = gtk.VBox (gtk.FALSE, 10)

       image = self.configFileData["TitleBar"]

       pixbuf = None
       # Create header at the top of the installer
       if runres != '640x480':
           fn = self.findPixmap(image)
           if not fn:
               log("unable to load %s", image)
           else :
               pixbuf = gtk.gdk.pixbuf_new_from_file(fn)

           if pixbuf:
               p = gtk.Image()
               p.set_from_pixbuf(pixbuf)
               a = gtk.Alignment()
               a.set(0.5, 0.5, 1.0, 1.0)
               a.add(p)
               vbox.pack_start(a, gtk.FALSE, gtk.TRUE, 0)
           else:
               print _("Unable to load title bar")


       self.loadReleaseNotes()

       vbox.set_spacing(0)

       self.buttonBox = gtk.HButtonBox ()
       self.buttonBox.set_layout (gtk.BUTTONBOX_END)
       self.buttonBox.set_spacing (30)

       self.buildStockButtons()

       group = gtk.AccelGroup()
       self.window.add_accel_group(group)
       self.nextButtonStock.add_accelerator('clicked', group,
                                            gtk.keysyms.F12,
                                            gtk.gdk.RELEASE_MASK, 0);

       # set up ctrl+alt+delete handler
       self.window.connect ("key-release-event", self.keyRelease)

       if DEBUG:
           self.buttonBox.add (self.debugButton)

       self.buttonBox.add (self.prevButtonStock)
       self.buttonBox.add (self.nextButtonStock)

       self.hbox = gtk.HBox ()
       self.hbox.set_border_width(5)
       self.hbox.pack_start (self.hideHelpButton, gtk.FALSE)
       self.hbox.set_spacing (25)
       self.hbox.pack_start (self.releaseButton, gtk.FALSE)
       self.hbox.pack_start (self.buttonBox)

       vbox.pack_end (self.hbox, gtk.FALSE)

       self.help = TextViewBrowser()

       self.displayHelp = gtk.TRUE
       self.helpState = gtk.TRUE

       self.helpFrame = gtk.Frame (_("Online Help"))
       self.box = gtk.VBox (gtk.FALSE, 0)
       self.box.set_spacing(0)

       self.help_sw = gtk.ScrolledWindow()
       self.help_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
       self.help_sw.set_shadow_type(gtk.SHADOW_IN)
       self.help_sw.add(self.help)
       self.box.pack_start(self.help_sw, gtk.TRUE)

       self.helpFrame.add (self.box)

       table = gtk.Table (1, 3, gtk.TRUE)
       table.attach (self.helpFrame, 0, 1, 0, 1,
                     gtk.FILL | gtk.EXPAND,
                     gtk.FILL | gtk.EXPAND)

       self.installFrame = gtk.Frame ()

       self.windowList = []

       #self.setStateList (self.steps, 0)
       self.setScreen ()

       table.attach (self.installFrame, 1, 3, 0, 1,
                     gtk.FILL | gtk.EXPAND,
                     gtk.FILL | gtk.EXPAND)
       table.set_col_spacing (0, 5)

       self.bin = gtk.Frame ()
       self.bin.set_shadow_type (gtk.SHADOW_NONE)
       self.bin.add (table)
       vbox.pack_end (self.bin, gtk.TRUE, gtk.TRUE)
       self.table = table

       self.window.add (vbox)

       # Popup the ICW and wait for it to wake us back up
       self.window.show_all ()

       splashScreenPop()

   def busyCursorPush(self):
       rootPushBusyCursor()

   def busyCursorPop(self):
       rootPopBusyCursor()

   def run (self, runres, configFileData):
       self.configFileData = configFileData
       self.setup_window(runres)
       gtk.main()

class InstallControlState:
   def __init__ (self, cw):
       self.searchPath = ("/mnt/source/RHupdates",
                          "./", "/usr/share/anaconda/")
       self.cw = cw
       self.prevEnabled = 1
       self.nextEnabled = 1
       self.nextButtonInfo = None
       self.helpButtonEnabled = gtk.TRUE
       self.title = _("Install Window")
       self.html = ""
       self.htmlFile = None
       self.nextButton = 'gtk-next'
       self.prevButton = 'gtk-prev'
       self.nextButtonLabel = None
       self.prevButtonLabel = None
       # Values other than gtk.TRUE or gtk.FALSE don't change the help setting
       self.helpEnabled = 3
       self.grabNext = 0

   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:
           self.nextEnabled = value
           self.cw.update (self)

   def getNextEnabled (self):
       return self.nextEnabled

   def setHelpButtonEnabled (self, value):
       if value == self.helpButtonEnabled: return
       self.helpButtonEnabled = value
       self.cw.update (self)

   def getHelpButtonEnabled (self):
       return self.helpButtonEnabled

   def findPixmap(self, file):
       for path in ("/mnt/source/" + productSite + "/RHupdates/pixmaps/",
                    "/mnt/source/" + productSite + "/RHupdates/",
                    "/tmp/updates/pixmaps/", "/tmp/updates/",
                    "/mnt/source/RHupdates/pixmaps/",
                    "/mnt/source/RHupdates/",
                    "/tmp/product/pixmaps/", "/tmp/product/",
                    "/usr/share/anaconda/pixmaps/", "pixmaps/",
                    "/usr/share/pixmaps/",
                    "/usr/share/anaconda/", ""):
           fn = path + file
           if os.access(fn, os.R_OK):
               return fn
       return None

   def readPixmap (self, file, height = None, width = None):
       fn = self.findPixmap(file)
       if not fn:
           log("unable to load %s", file)
           return None
       try:
           pixbuf = gtk.gdk.pixbuf_new_from_file(fn)
       except RuntimeError, msg:
           log("unable to read %s: %s", file, msg)
           return None
       if (height is not None and width is not None
           and height != pixbuf.get_height()
           and width != pixbuf.get_width()):
           sclpix = pixbuf.scale_simple(height, width,
                                        gtk.gdk.INTERP_BILINEAR)
           p = gtk.Image()
           p.set_from_pixbuf(sclpix)
       else:
           source = gtk.IconSource()
           source.set_pixbuf(pixbuf)
           source.set_size(gtk.ICON_SIZE_DIALOG)
           source.set_size_wildcarded(gtk.FALSE)
           iconset = gtk.IconSet()
           iconset.add_source(source)
           p = gtk.image_new_from_icon_set(iconset, gtk.ICON_SIZE_DIALOG)
       return p

   def readPixmapDithered(self, file, height = None, width = None):
       fn = self.findPixmap(file)
       if not fn:
           log("unable to load %s", file)
           return None
       try:
           pixbuf = gtk.gdk.pixbuf_new_from_file(fn)
       except RuntimeError, msg:
           log("unable to read %s: %s", file, msg)
           return None
       if (height is not None and width is not None
           and height != pixbuf.get_height()
           and width != pixbuf.get_width()):
           pixbuf = pixbuf.scale_simple(height, width,
                                        gtk.gdk.INTERP_BILINEAR)

       (pixmap, mask) = pixbuf.render_pixmap_and_mask()
       pixbuf.render_to_drawable(pixmap, gtk.gdk.GC(pixmap), 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)
       return p

   def readHTML (self, file):
       self.htmlFile = file

   def setHTML (self, text):
       self.html = text
       self.cw.update (self)

   def getHTML (self, langPath):
       text = None
       if self.htmlFile:
           file = self.htmlFile

           if self.cw.configFileData.has_key("helptag"):
               helpTag = "-%s" % (self.cw.configFileData["helptag"],)
           else:
               helpTag = ""

           arch = "-%s" % (iutil.getArch(),)
           tags = [ "%s%s" % (helpTag, arch), "%s" % (helpTag,),
                    "%s" % (arch,), "" ]

           found = 0
           for path in self.searchPath:
               if found:
                   break
               for lang in langPath + ['C']:
                   if found:
                       break
                   for tag in tags:
                       try:
                           text = open("%s/help/%s/s1-help-screens-%s%s.html"
                                       % (path, lang, file, tag)).read ()
                           found = 1
                           break
                       except IOError:
                           continue
               if text:
                   break

           if text:
               text = text.replace("@RHL@", productName)
               text = text.replace("@RHLVER@", productVersion)
               return text

           print "Unable to read %s help text" % (file,)

       return self.html

   def setScreenPrev (self):
       self.cw.prevClicked ()

   def setScreenNext (self):
       self.cw.nextClicked ()

   def setHelpEnabled (self, value):
       self.helpEnabled = value
       self.cw.update (self)

   def getHelpEnabled (self):
       return self.helpEnabled

   def setGrabNext (self, value):
       self.grabNext = value
       self.cw.update (self)

   def getGrabNext (self):
       return self.grabNext

   def getICW (self):
       return self.cw

   def setNextButton(self, icon, text):
       self.nextButtonInfo = (icon, text)

   def getNextButton(self):
       return self.nextButtonInfo