#!/usr/bin/python

# $Progeny: installer-druid,v 1.277 2002/04/19 19:54:55 branden Exp $

import re
import sys
import os
import parted
import stat
import string
from gtk import *
import gtk
import GdkImlib
from gnome.ui import *
from libglade import *

from popen2 import popen2

import _parted

import pgi.auto_partition
import pgi.base
import pgi.bootloader
import pgi.common

from pgi.common import EX_OK, EX_USERCANCEL, ROOT_MB, SWAP_MB, EFIBOOT_MB

LIVE = pgi.common.get_live()
LOGFILE = pgi.common.get_logfile()
TARGET = pgi.common.get_target()

ARCH = os.environ["ARCH"]
KERNEL_VERSION = os.environ["KERNEL_VERSION"]
MIRROR = os.environ["MIRROR"]
SUITE = os.environ["SUITE"]

GTKRC = LIVE + "/etc/gtk/gtkrc"

ARROW = LIVE + "/share/installer/arrow.xpm"
CHECK = LIVE + "/share/installer/check.xpm"
LOGO = LIVE + "/share/installer/logoicon.xpm"

# Set in on_druid_cancel when the user presses the Cancel button on
# the Druid.
USER_CANCEL = FALSE

# Used by partition_valid_state() to indicate valid input.
VALID = 42

setup_disks = []

# List of swap partitions to use. Currently only whole-disk and
# free-space use it, but Custom needs to use it as well. This is
# needed because we can't just use all swaps on all disks because
# of a bug in parted where it tells us that dirty ext2
# filesystems in primary partitions are swap devices :(.
setup_swaps = []

setup_format = {}
setup_mntpnt = {}
default_mntpnts = ("/",
                   "/home",
                   "/tmp",
                   "/usr",
                   "/usr/local",
                   "/var",
                   "/var/mail",
                   "/var/spool",
                   "/var/tmp",
                   "/boot/efi")

list = None
device = None
tree = None
part = None

def TRACE(message):
    sys.stdout.write(message)
    sys.stdout.write("\n")
    sys.stdout.flush()


def ERROR(message):
    TRACE(message)
    dialog = GnomeMessageBox(foldtext(message), MESSAGE_BOX_ERROR, STOCK_BUTTON_OK)
    dialog.set_position(WIN_POS_CENTER)
    dialog.run_and_close()


def foldtext(message):
    """
    Return text nicely line-wrapped at 65 characters, which should work
    well for GnomeMessageBox()es.
    """
    (outpipe, inpipe) = popen2("fmt -w 65")
    inpipe.write(message)
    inpipe.close()
    lines = outpipe.readlines()
    outpipe.close()
    folded_message = ""
    for line in lines:
        folded_message = folded_message + line
    # strip off the trailing newline
    return folded_message.rstrip()

def find_extended(disk):
    """Return the extended partition and its bounds.
    """

    TRACE("Looking for extended partition...")
    start = -1
    end = -1
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_EXTENDED:
            start = part.get_geom().get_start()
            end = part.get_geom().get_end()
            TRACE("Found.")
            break

    if start == -1:
        TRACE("Not found.")
        return (None, -1, -1)

    device = disk.get_dev()
    sector_size = device.get_sector_size()
    start_mb = setup_sectors_to_mb(start, sector_size)
    end_mb = setup_sectors_to_mb(end, sector_size)
    TRACE("Bounds are %d to %d." % (start_mb, end_mb))
    return (part, start, end)

def find_freespace_in_extended(need_size, disk, truncate=FALSE):
    """Find NEED_SIZE sectors of free space on the extended partition on DISK.

    Returns the PARTITION_FREESPACE Partition object for the freespace
    block or None on failure.

    If TRUNCATE is specified, return as close as possible to NEED_SIZE
    when there is less space available than NEED_SIZE.

    """

    (extended_part, extended_start, extended_end) = find_extended(disk)
    device = disk.get_dev()
    sector_size = device.get_sector_size()
    need_size_mb = setup_sectors_to_mb(need_size, sector_size)

    closest = None
    closest_geom = None

    TRACE("Searching for %d megabytes of free space in extended partition..."
          % (need_size_mb,))
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_FREESPACE:
            geom = part.get_geom()
            freespace = setup_sectors_to_mb(geom.get_length(),
                                            sector_size)
            start = geom.get_start()
            end = geom.get_end()
            size = geom.get_length()

            TRACE("Found some free space from %d to %d."
                  % (setup_sectors_to_mb(start, sector_size),
                     setup_sectors_to_mb(end, sector_size)))
            TRACE("I need it to be start:%d >= extended_start:%d and end:%d <= extended_end:%d and size:%d >= need_size:%d"
                  % (start, extended_start, end, extended_end, size, need_size))
            if (start >= extended_start
                and end <= extended_end
                and size >= need_size):
                return part

            if ((not closest)
                or closest_size < size):
                closest = part
                closest_size = size

    if (truncate and closest):
        TRACE("Returning closest match, which has size %d." % (closest_size,))
        return closest
    else:
        return None

def ensure_extended_partition(disk, start):
    """Maximizes the extended partition if there is one, creates one
    and maximizes if there isn't.
    """

    device = disk.get_dev()
    path = device.get_path()
    sector_size = device.get_sector_size()

    extended = find_extended(disk)[0]
    if not extended:
        # Create an extended partition covering the entire disk
        TRACE("Creating extended partition on %s..." % (path,))

        # It wants some kind of fstype even for an extended partition
        # where it doesn't make sense; Linux ext2 is a harmless thing
        # to give it.
        fstype = setup_return_fstype("Linux ext2")
        extended = parted.Partition(disk, parted.PARTITION_EXTENDED,
                                    fstype, start, device.get_length() - 1)
        if not extended:
            TRACE("Could not create extended partition.")
            return None

        TRACE("Done.")

        geom = extended.get_geom()
        start = geom.get_start()
        end = geom.get_end()
        start_mb = setup_sectors_to_mb(start, sector_size)
        end_mb = setup_sectors_to_mb(end, sector_size)
        TRACE("Bounds are %d to %d." % (start_mb, end_mb))

        extended.set_fs_type(fstype)
        TRACE("Adding extended partition to %s..." % (path,))
        disk.add_partition(extended)
        TRACE("Done.")

    setup_maximize_extended_partition(disk)

    return extended

def make_partition(disk, type, fstype_string, start, size,
                   mntpnt, format=TRUE):
    """Create a partition on DISK.

    TYPE is the paritition type (logical or primary, *not* extended).
    FSTYPE_STRING should be an fstype string as used throughtout this
    file (e.g. "EFI boot").  START and SIZE must be specified in
    sectors.

    FORMAT is a boolean specifying whether setup_prepare_system will
    format this partition.
    """

    global setup_format
    global setup_mntpnt

    if (type == parted.PARTITION_LOGICAL):
        type_string = "logical"
        if (not ensure_extended_partition(disk, start)):
            return None
    elif (type == parted.PARTITION_PRIMARY):
        type_string = "primary"
    else:
        TRACE("make_partition: Bogus partition type.")
        return None

    device = disk.get_dev()
    path = device.get_path()
    sector_size = device.get_sector_size()

    fstype = setup_return_fstype(fstype_string)
    size_mb = setup_sectors_to_mb(size, sector_size)
    start_mb = setup_sectors_to_mb(start, sector_size)

    TRACE("Creating %s partition of type %s size %d from %d to %d for %s..."
          % (type_string, fstype_string, size_mb, start_mb,
             start_mb + size_mb, mntpnt))
    part = parted.Partition(disk, type, fstype, start, start + size)
    if not part:
        ERROR("Could not create partition.")
        return None
    TRACE("Done.")

    TRACE("Adding partition to %s..." % (path,))
    if disk.add_partition(part) == 0:
        ERROR("Could not add partition to %s." % (path,))
        return None
    TRACE("Done.")

    part.set_fs_type(fstype)
    part.set_system(fstype)

    if fstype_string == "EFI boot":
        TRACE("Marking partition as EFI boot...")
        r = part.mark_efi()
        if r == -1:
            ERROR("EFI boot partition has type %s instead of EFI."
                  % (part.get_type().get_name(),))
            return None
        elif not r:
            ERROR("Could not mark EFI partition as EFI boot.")
            return None
        TRACE("Done.")

    key = gen_geometry_key(part.get_geom())
    if fstype_string == "Linux swap":
        setup_format[key] = FALSE
        setup_mntpnt[key] = ""
    else:
        setup_format[key] = format
        setup_mntpnt[key] = mntpnt

    return part

# Auto-partitioning helper functions

def auto_partition_find_free_space(partlist, sector_size):
    """Return the needed_parts list if enough space is found in any of
    the partitions in PARTLIST.
    """

    needed_parts = None

    for part in partlist:
        if part.get_type() & parted.PARTITION_FREESPACE:
            freespace = setup_sectors_to_mb(part.get_geom().get_length(),
                                            sector_size)
            start = part.get_geom().get_start()
            end = part.get_geom().get_end()
            start_mb = setup_sectors_to_mb(start, sector_size)
            end_mb = setup_sectors_to_mb(end, sector_size)

            TRACE("Found some free space from %d to %d." % (start_mb, end_mb))
            needed_parts = pgi.auto_partition.get_partitions(freespace)
            if needed_parts:
                break

    return needed_parts

def auto_partition_check_free_space(device):
    """Returns TRUE if there is enough free space on device for the
    default partition scheme for this architecture.
    """

    disk = device.disk_open()
    (extended_part, extended_start, extended_end) = find_extended(disk)

    TRACE("Searching for free space...")
    needed_parts = auto_partition_find_free_space(disk.get_part_list(),
                                                  device.get_sector_size())

    if needed_parts:
        TRACE("I'm happy with this free space.")
        return TRUE
    else:
        TRACE("Not enough free space?  Screw you guys, i'm going home.")
        return FALSE

def auto_partition_check_disk_size(device):
    """Returns TRUE if the device is large enough for the default
    partition scheme for this architecture.  """

    freespace = setup_sectors_to_mb(device.get_length(),
                                    device.get_sector_size())
    if pgi.auto_partition.get_partitions(freespace):
        return TRUE
    else:
        return FALSE

def auto_partition_wipe_disk(device):
    """Wipe the device in preparation for automatic partitioning.
    """

    path = device.get_path()

    # Replace whatever partition table is there with a new, empty one
    TRACE("Creating partition table on %s..." % (path,))
    disk = device.disk_create(parted.disk_type_get("msdos"))
    if not disk:
        ERROR("Could not create partition table on %s." % path)
        return FALSE
    disk.write()
    disk.close()
    TRACE("Done.")

    return TRUE

def auto_partition_go(disk):
    """Partition the disk according to the default scheme for this
    architecture.
    """

    device = disk.get_dev()
    path = device.get_path()
    sector_size = device.get_sector_size()

    TRACE("Searching for contiguous free space...")
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_FREESPACE:
            geom = part.get_geom()
            freespace = setup_sectors_to_mb(geom.get_length(),
                                            sector_size)
            start = geom.get_start()
            end = geom.get_end()

            TRACE("Found some free space from %d to %d."
                  % (setup_sectors_to_mb(start, sector_size),
                     setup_sectors_to_mb(end, sector_size)))

            needed_parts = pgi.auto_partition.get_partitions(freespace)
            if needed_parts:
                break

    if not needed_parts:
        ERROR("""\
This disk does not contain enough contiguous free space.  Please go to the
manual partitioning screen.""")
        return FALSE

    disk_size = device.get_length()
    disk_size_mb = setup_sectors_to_mb(disk_size, sector_size)

    next = start
    for i in needed_parts:
        mntpnt = i[0]
        next_mb = setup_sectors_to_mb(next, sector_size)
        fstype_string = i[1]
        size_mb = i[2]
        size = setup_mb_to_sectors(size_mb, sector_size)
        must_be_primary = i[3]

        if next + size > disk_size:
            if i == needed_parts[-1]:
                # Only perform this check on the last partition, where
                # we can do something about it.  If earlier partitions
                # blow past the disk we're just plain fucked so we let
                # parted throw an exception.
                TRACE("Last partition wants to blow past the end of the disk.")
                TRACE("Wanted to be size %d starting at %d ending at %d."
                      % (size_mb, next_mb, next_mb + size_mb))
                TRACE("But the disk is only size %d." % (disk_size_mb,))
                size = disk_size - next - 1
                size_mb = setup_sectors_to_mb(size, sector_size)
                TRACE("Truncating to size %d." % (size_mb,))

        if must_be_primary:
            TRACE("Partition for %s wants to be primary." % (mntpnt,))
            TRACE("This had better be the last in the list, else we're "
                  "in trouble.")
            part = make_partition(disk, parted.PARTITION_PRIMARY,
                                  fstype_string, next, size, mntpnt)
        else:
            part = make_partition(disk, parted.PARTITION_LOGICAL,
                                  fstype_string, next, size, mntpnt)
        if not part:
            return FALSE

        geom = part.get_geom()
        next = geom.get_end() + 1

    return TRUE

def gen_geometry_key(geometry):
    # Use strings for geom start and end to avoid long int
    # conversion problems.
    return "%s-%s-%s" % (geometry.get_disk().get_dev().get_path(),
                         geometry.get_start(), geometry.get_end())


def setup_exception_handler(message, type, options):
    if type == parted.EXCEPTION_INFORMATION:
        message_box_type = MESSAGE_BOX_INFO
    elif type == parted.EXCEPTION_WARNING:
        message_box_type = MESSAGE_BOX_WARNING
    else:
        message_box_type = MESSAGE_BOX_ERROR

    message_box_button_label = {} # XXX?
    message_box_button_value = {} # XXX?

    index = 0

    if options & parted.EXCEPTION_YES:
        message_box_button_label[index] = STOCK_BUTTON_YES
        message_box_button_value[index] = parted.EXCEPTION_YES
        index = index + 1
    if options & parted.EXCEPTION_NO:
        message_box_button_label[index] = STOCK_BUTTON_NO
        message_box_button_value[index] = parted.EXCEPTION_NO
        index = index + 1
    if options & parted.EXCEPTION_OK:
        message_box_button_label[index] = STOCK_BUTTON_OK
        message_box_button_value[index] = parted.EXCEPTION_OK
        index = index + 1
    if options & parted.EXCEPTION_RETRY:
        message_box_button_label[index] = "Retry"
        message_box_button_value[index] = parted.EXCEPTION_RETRY
        index = index + 1
    if options & parted.EXCEPTION_IGNORE:
        message_box_button_label[index] = "Ignore"
        message_box_button_value[index] = parted.EXCEPTION_IGNORE
        index = index + 1
    if options & parted.EXCEPTION_CANCEL:
        message_box_button_label[index] = STOCK_BUTTON_CANCEL
        message_box_button_value[index] = parted.EXCEPTION_CANCEL
        index = index + 1

    # XXX: C has gnome_message_box_newv, which takes a vector argument,
    # but the Python interface doesn't seem to have this, so we have to
    # do it this way.
    if index == 0:
        dialog = GnomeMessageBox(foldtext(message), message_box_type)
    elif index == 1:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0])
    elif index == 2:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0],
                                 message_box_button_label[1])
    elif index == 3:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0],
                                 message_box_button_label[1],
                                 message_box_button_label[2])
    elif index == 4:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0],
                                 message_box_button_label[1],
                                 message_box_button_label[2],
                                 message_box_button_label[3])
    elif index == 5:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0],
                                 message_box_button_label[1],
                                 message_box_button_label[2],
                                 message_box_button_label[3],
                                 message_box_button_label[4])
    elif index == 6:
        dialog = GnomeMessageBox(foldtext(message), message_box_type,
                                 message_box_button_label[0],
                                 message_box_button_label[1],
                                 message_box_button_label[2],
                                 message_box_button_label[3],
                                 message_box_button_label[4],
                                 message_box_button_label[5])

    dialog.set_position(WIN_POS_CENTER)

    reply = dialog.run_and_close()
    if reply == -1:
        value = parted.EXCEPTION_UNHANDLED
    else:
        value = message_box_button_value[reply]

    return value


def setup_null_exception_handler(message, type, options):
    return parted.EXCEPTION_UNHANDLED


def setup_probe_nonstandard_devices():
    """Probe for nonstandard disk controllers and the like that don't
       use the standard disk device files, such as the Compaq SmartArray
       controllers."""

    # Load the module list
    if not os.path.exists("/proc/modules"):
        return

    modules = []
    mod_file = open("/proc/modules")
    for line in mod_file.readlines():
        fields = string.split(line)
        modules.append(fields[0])
    mod_file.close()

    # Switch the exception handler so that exceptions are not displayed
    # to the user.  This prevents spurious exceptions when trying to
    # detect the presence of drives that may not exist.
    _parted.exception_set_handler(setup_null_exception_handler)

    # Go over the modules, doing whatever needs to be done to load
    # information about the module into the disk list.
    for module in modules:
        if module[:8] == "cpqarray":
            # Compaq SmartArray controllers have a weird device layout
            # and aren't detected by parted because of it.  The devices
            # don't normally fit on the initrd, so link them from the
            # CD.
            os.symlink("%s/dev/ida" % (LIVE,), "/dev/ida")

            # Now iterate over a list of possible devices and try to
            # open them with parted; this will cause devices that exist
            # to be added to the devices table.
            for cpq_file in os.listdir("/dev/ida"):
                if cpq_file[0] != "c":
                    continue
                if string.find(os.path.basename(cpq_file), "p") != -1:
                    continue
                dev = parted.device_get("/dev/ida/%s" % (cpq_file,))

    # Now set the exception handler back.
    _parted.exception_set_handler(setup_exception_handler)


def setup_sectors_to_mb(sectors, sector_size):
    "Convert sectors to megabytes (MB)"
    return float(sectors * sector_size) / 1024 / 1024


def setup_sectors_to_gb(sectors, sector_size):
    "Convert sectors to gigabytes (GB)"
    return float(sectors * sector_size) / 1024 / 1024 / 1024


def setup_mb_to_sectors(mb, sector_size):
    "Convert megabytes (MB) to sectors"
    return long((mb * 1024 * 1024) / sector_size)


def setup_gb_to_sectors(gb, sector_size):
    "Convert gigabytes (GB) to sectors"
    return long((gb * 1024 * 1024 * 1024) / sector_size)


def setup_return_fstype_string(part):
    """Get the string corresponding to the filesystem type object.
    """

    if not part:
        return ""
    fstype = part.get_fs_type()
    if not fstype:
        return ""

    if fstype.get_name() == "ext2":
        return "Linux ext2"
    elif fstype.get_name() == "linux-swap":
        return "Linux swap"
    elif fstype.get_name() == "FAT":
        if part.is_efi():
            return "EFI boot"
        else:
            return "FAT"
    else:
        return ""


def setup_return_fstype(fstype_string):
    """Get the parted object corresponding to the chosen filesystem type.
    """

    if fstype_string == "Linux ext2":
        return parted.file_system_type_get("ext2")
    elif fstype_string == "Linux swap":
        return parted.file_system_type_get("linux-swap")
    elif fstype_string == "EFI boot" or \
         fstype_string == "FAT":
        return parted.file_system_type_get("FAT")
    else:
        return None


def setup_return_size_string(sectors, sector_size):
    "Convert sectors to the appropriate size unit (MB or GB)"
    mb = setup_sectors_to_mb(sectors, sector_size)
    if (mb < 1024):
        return "%.2f" % (mb,) + " MB"
    gb = setup_sectors_to_gb(sectors, sector_size)
    return "%.2f" % (gb,) + " GB"


def setup_get_part_list(disk):
    "Return a list of partitions on the disk DISK."
    parts = []
    for part in disk.get_part_list():
        parts.append(part)
    return parts


def setup_return_part_device_file(part):
    "Return the device file that corresponds to the partition on the disk."
    dev_file = part.get_geom().get_disk().get_dev().get_path()
    part_num = part.get_num()
    if dev_file[:8] == "/dev/ida":
        # Compaq SmartArray disk device
        part_file = dev_file + "p" + str(part_num)
    else:
        # Normal disk device
        part_file = dev_file + str(part_num)

    return part_file


def setup_open_all_disks():
    "Open all disks in the system and add them to SETUP_DISKS list."
    global setup_disks
    global setup_format
    global setup_mntpnt

    setup_close_all_disks()

    TRACE("Opening all disks.")

    for device in parted.get_devices():
        path = device.get_path()

        TRACE("\tOpening %s." % (path,))

        disk = device.disk_open()
        if disk == None:
            TRACE("\t\tCould not open %s." % (path,))

            dialog = GnomeMessageBox(foldtext("Could not read partition table "
                                     "from %s. Create it?" % (path,)),
                                     MESSAGE_BOX_QUESTION,
                                     STOCK_BUTTON_YES,
                                     STOCK_BUTTON_NO)
            dialog.set_position(WIN_POS_CENTER)

            reply = dialog.run_and_close()
            if reply != YES:
                continue

            TRACE("\t\tCreating partition table on %s..." % (path,))
            disk = device.disk_create(parted.disk_type_get("msdos"))
            TRACE("\t\tDone.")
            if disk == None:
                ERROR("Could not create partition table on %s." % (path,))
                continue
            disk.close()
            TRACE("\t\tReopening %s..." % (path,))
            disk = device.disk_open()
            if disk == None:
                ERROR("Could not reopen %s." % (path,))
                continue
            TRACE("\t\tDone.")

        TRACE("\tDone.")

        for part in disk.get_part_list():
            minor = part.get_num()
            type = part.get_type()
            if (minor != -1
                and (type & parted.PARTITION_PRIMARY
                     or type == parted.PARTITION_PRIMARY
                     or type & parted.PARTITION_LOGICAL)):
                # Initialize SETUP_FORMAT and SETUP_MNTPNT dictionaries:
                key = gen_geometry_key(part.get_geom())
                setup_format[key] = FALSE
                setup_mntpnt[key] = ""
        setup_disks.append(disk)
    TRACE("Done.")


def setup_reopen_all_disks():
    "Reopen all the disks in SETUP_DISKS list."
    TRACE("Reopening all disks...")
    setup_open_all_disks()
    TRACE("Done.")


def setup_close_all_disks():
    "Close all disks in SETUP_DISKS list and clear it."
    global setup_disks
    global setup_format
    global setup_mntpnt
    TRACE("Closing all disks...")
    for disk in setup_disks:
        path = disk.get_dev().get_path()
        TRACE("\tClosing %s..." % (path,))
        if disk.close() == 0:
            ERROR("Could not close %s." % (path,))
        TRACE("\tDone.")
    TRACE("Done.")
    setup_disks = []
    setup_format = {}
    setup_mntpnt = {}

def setup_has_extended_partition(disk):
    exists = FALSE
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_EXTENDED:
            exists = TRUE
    return exists


def setup_is_within_extended_partition(disk, geom):
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_EXTENDED:
            if geom.get_start() >= part.get_geom().get_start() \
               and geom.get_end() <= part.get_geom().get_end():
                return TRUE
    return FALSE


def setup_has_logical_partitions(disk):
    exists = FALSE
    for part in disk.get_part_list():
        if part.get_type() & parted.PARTITION_LOGICAL:
            exists = TRUE
    return exists


def setup_num_primary_partitions(disk):
    path = disk.get_dev().get_path()
    count = 0

    TRACE("Counting primary partitions...")
    for part in disk.get_part_list():
        minor = part.get_num()
        type = part.get_type()
        if (minor != -1
            and (type & parted.PARTITION_PRIMARY
                 or type == parted.PARTITION_PRIMARY)):
            count = count + 1
            TRACE("Found %d %s%d" % (count, path, minor))

    TRACE("Found %d primary partitions." % (count,))
    return count

def setup_maximize_extended_partition(disk):
    if setup_has_extended_partition(disk):
        extended = disk.get_extended_partition()
        TRACE("Maximizing extended partition...")
        disk.maximize_partition(extended)
        TRACE("Done.")

#
# Functions for manipulating the Edit Partition dialog:
#

size_current_maximum = 0
size_current_maximum_sectors = 0
size_current_minimum = 0
size_units_current_state = 0

# Called when the user changes the units of partition size from MB to
# GB or vice versa.  Used to convert the size input box dynamically.
def on_size_unit_changed(widget, size_units_widget, size_widget):
    # The user changed size units from MB to GB or vice versa, so
    # adjust the size value appropriately:
    global size_current_maximum
    global size_current_minimum
    global size_units_current_state

    selection = setup_option_menu_get_history(size_units_widget)
    if selection == 0 and size_units_current_state == 1: # GB to MB
        step_increment = 50
        page_increment = 500
        size_current_minimum = size_current_minimum * 1024
        size_current_maximum = size_current_maximum * 1024
        size_units_current_state = 0
    else:
        step_increment = 1
        page_increment = 10
        size_current_minimum = size_current_minimum / 1024
        size_current_maximum = size_current_maximum / 1024
        size_units_current_state = 1
    adjustment = GtkAdjustment(size_widget.get_value(),
                               size_current_minimum,
                               size_current_maximum,
                               step_increment,
                               page_increment,
                               page_increment)
    size_widget.set_adjustment(adjustment)

# Called when the user changes the file system type.  Used to grey
# out the partition flags and mount point options when "Linux swap"
# is selected, and to grey out the mount point option when "FAT" is
# selected.
def on_fstype_changed(widget, fstype_widget, bootable_widget, mntpnt_widget):
    fstype = setup_option_menu_get_history(fstype_widget)

    if fstype != 2 and mntpnt_widget.get_text() == "/boot/efi":
        mntpnt_widget.set_text("/")

    if fstype == 0: # Linux ext2
        bootable_widget.set_sensitive(TRUE)
        mntpnt_widget.set_sensitive(TRUE)
    elif fstype == 1: # Linux swap
        bootable_widget.set_sensitive(FALSE)
        mntpnt_widget.set_sensitive(FALSE)
    elif fstype == 2: # EFI boot
        bootable_widget.set_sensitive(TRUE)
        mntpnt_widget.set_sensitive(TRUE)
        mntpnt_widget.set_text("/boot/efi")
    elif fstype == 3: # FAT
        bootable_widget.set_sensitive(TRUE)
        mntpnt_widget.set_sensitive(FALSE)

def partition_valid_state(state, new):
    """Returns -1 or 1 if it's invalid and the user doesn't want
    to try again. Returns 0 if she does want to try again.
    Returns VALID if it's valid.
    """

    global part

    geom = part.get_geom()
    disk = geom.get_disk()
    # There can be no more than four primary partitions.
    if (new):
        if ((state["type"] & parted.PARTITION_PRIMARY
             or state["type"] == parted.PARTITION_PRIMARY)
            and setup_num_primary_partitions(disk) == 3
            and state["sectors"] < size_current_maximum_sectors):

            dialog = GnomeMessageBox(foldtext("""\
You have chosen to create a fourth primary partition that will
leave free space on the disk which cannot be allocated, since
there can only be four primary partitions on a disk. Instead, you
should create a logical partition, since there is no limitation
of how many of those you may have.

Try again?"""),
                                     MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                                     STOCK_BUTTON_NO)
            dialog.set_position(WIN_POS_CENTER)
            return dialog.run_and_close()

    # Everything below here is related to mount points, so just
    # return VALID if it's not an ext2 or EFI partition.
    if state["fstype"] != "Linux ext2" and state["fstype"] != "EFI boot":
        state["mntpnt"] = ""
        return VALID

    # Mount points are mandatory for real filesystems (not swap space).
    # TODO: Accept a blank mount point so that the user can deliberately ignore
    # a filesystem.
    if state["fstype"] == "Linux ext2" and not state["mntpnt"]:
        dialog = GnomeMessageBox(foldtext("""\
You must specifiy a mount point for ext2 partitions.

Try again?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        return dialog.run_and_close()

    state["mntpnt"] = string.strip(state["mntpnt"])
    mnt = state["mntpnt"]

    # Mount points must be absolute.
    if mnt and mnt[0] != "/":
        dialog = GnomeMessageBox(foldtext("""\
The mount point must be a fully qualified path (e.g., '/mnt'),
not a relative path (e.g., 'mnt').

Try again?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        return dialog.run_and_close()

    # Don't let the user force us to write loony things to /etc/fstab.
    if not re.match(r"[a-zA-Z0-9/._-]+$", mnt):
        dialog = GnomeMessageBox(foldtext("""\
The specified mount point contains invalid characters. You may
only use the alphanumeric characters as well as the '/', '.',
'_', and '-' characters.

Try again?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        return dialog.run_and_close()

    # Warn the user if they're creating a /boot partition.
    if mnt == "/boot":
        dialog = GnomeMessageBox(foldtext("""\
Historically, on some platforms, with some boot loaders,
a separate /boot partition was required to boot the system.

The boot loader used by this installer does not have such a
requirement.  /boot may be an ordinary subdirectory of the
root partition.  If you require a separate /boot partition for an
alternative boot loader, it will likely require configuration
after the system is installed.

Are you sure you want to create a /boot partition?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        result = dialog.run_and_close()
        if result:
            return 0

    # Check to make sure no other partitions are mounted here.
    mykey = gen_geometry_key(part.get_geom())
    other_mnt = None
    devices = {}
    for disk in setup_disks:
        for partc in disk.get_part_list():
            if not partc.is_active():
                continue

            path = setup_return_part_device_file(partc)
            key = gen_geometry_key(partc.get_geom())
            if key == mykey:
                continue
            try:
                if setup_mntpnt[key]:
                    other_mnt = setup_mntpnt[key]
                    break
            except KeyError:
                # Skip missing free space "partitions"
                pass

    if other_mnt == mnt:
        dialog = GnomeMessageBox(foldtext("""\
The specified mount point is already being used by %s.

Try again?""" % (path,)),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        return dialog.run_and_close()

    # For now, EFI boot partitions are only supported on ia64.
    if mnt == "/boot/efi" and ARCH != "ia64":
        dialog = GnomeMessageBox(foldtext("""\
The /boot/efi partition path is generally only necessary on
Itanium (ia64) systems.

Are you sure you want to create a /boot/efi partition?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        result = dialog.run_and_close()
        if result:
            return 0

    # EFI partitions should be either unmounted or mounted on /boot/efi.
    if mnt and state["fstype"] == "EFI boot" and mnt != "/boot/efi":
        dialog = GnomeMessageBox(foldtext("""\
EFI partitions should be mounted to /boot/efi for the boot loader
software to work properly.

Try again?"""),
                               MESSAGE_BOX_ERROR, STOCK_BUTTON_YES,
                               STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        return dialog.run_and_close()

    return VALID

# XXX: Bugs/suggestions:
# - If this is a Create dialog, and the user unchecks format, mntpnt
#   should be made insensitive

def setup_edit_dialog(state, allow_edit_size, new=FALSE):
    global part
    global size_current_maximum
    global size_current_maximum_sectors
    global size_current_minimum
    global size_units_current_state

    # Get the widgets from glade:
    wtree_dialog_edit = GladeXML(GLADE, "dialog-edit")
    dialog = wtree_dialog_edit.get_widget("dialog-edit")
    type_widget = wtree_dialog_edit.get_widget("dialog-edit-type")
    bootable_widget = wtree_dialog_edit.get_widget("dialog-edit-bootable")
    size_widget = wtree_dialog_edit.get_widget("dialog-edit-size")
    size_units_widget = wtree_dialog_edit.get_widget("dialog-edit-size-units")
    fstype_widget = wtree_dialog_edit.get_widget("dialog-edit-fstype")
    format_widget = wtree_dialog_edit.get_widget("dialog-edit-format")
    mntpnt_widget = wtree_dialog_edit.get_widget("dialog-edit-mntpnt")
    mntpnt_combo = wtree_dialog_edit.get_widget("dialog-edit-mntpnt-combo")

    # Hmm, this is just as broken as what Jeff originally had here.  I
    # think the only way we can do this is if we can get the GtkMenu
    # out of the option menu; the menu is *not* one of its children.
    # If we were in C we could probably poke into the GtkOptionMenu
    # struct and get it; in Python we're fucked.  I don't know what we
    # can do about this.
    #if ARCH != "ia64":
    #    label = fstype_widget.children()[0]
    #    if label.get() == "EFI boot":
    #        item.set_sensitive(FALSE)

    # Extract the values from the current partition state:
    type = state["type"]
    bootable = state["bootable"]
    sectors = state["sectors"]
    sector_size = state["sector_size"]
    fstype = state["fstype"]
    format = state["format"]
    mntpnt = state["mntpnt"]

    new_state = {}
    old_state = state

    if not new:
        # Use current partition type as default:
        if type & parted.PARTITION_PRIMARY or type == parted.PARTITION_PRIMARY:
            type_widget.set_history(0)
        elif type & parted.PARTITION_LOGICAL:
            type_widget.set_history(1)

    # If the extended partition exists the user can only create
    # logical partitions.
    if setup_has_extended_partition(part.get_geom().get_disk()):
        type_widget.set_sensitive(FALSE)

    # Use current value of bootable flag as default:
    if bootable:
        bootable_widget.set_active(TRUE)
    else:
        bootable_widget.set_active(FALSE)

    size_current_maximum_sectors = sectors

    # Use current partition size as default:
    mb = setup_sectors_to_mb(sectors, sector_size)
    gb = setup_sectors_to_gb(sectors, sector_size)
    if mb < 1024:
        size = mb
        step_increment = 50
        page_increment = 500
        size_current_minimum = 0
        size_current_maximum = mb
        size_units_current_state = 0
    else:
        size = gb
        step_increment = 1
        page_increment = 10
        size_current_minimum = 0
        size_current_maximum = gb
        size_units_current_state = 1
    adjustment = GtkAdjustment(size,
                               size_current_minimum,
                               size_current_maximum,
                               step_increment,
                               page_increment,
                               page_increment)
    size_widget.set_adjustment(adjustment)
    size_widget.set_digits(2)
    size_widget.set_value(size)
    size_units_widget.set_history(size_units_current_state)
    if not allow_edit_size:
        size_widget.set_sensitive(FALSE)
        size_units_widget.set_sensitive(FALSE)

    # Use current file system type as default:
    if fstype == "Linux ext2":
        fstype_widget.set_history(0)
        bootable_widget.set_sensitive(TRUE)
        mntpnt_combo.set_sensitive(TRUE)
    elif fstype == "Linux swap":
        fstype_widget.set_history(1)
        bootable_widget.set_sensitive(FALSE)
        mntpnt_combo.set_sensitive(FALSE)
    elif fstype == "EFI boot":
        fstype_widget.set_history(2)
        bootable_widget.set_sensitive(TRUE)
        mntpnt_combo.set_sensitive(TRUE)
        if not mntpnt:
            mntpnt = "/boot/efi"
    elif fstype == "FAT":
        fstype_widget.set_history(3)
        bootable_widget.set_sensitive(TRUE)
        mntpnt_combo.set_sensitive(FALSE)

    # Format the file system?
    format_widget.set_active(format)

    # Put all the default_mntpnts into the mntpnt_combo except
    # those which have already been used.
    mntpnts = []
    for i in default_mntpnts:
        if i not in setup_mntpnt.values():
            mntpnts.append(i)
    mntpnt_combo.set_popdown_strings(mntpnts)

    # Use current mount point as default:
    if mntpnt:
        mntpnt_widget.set_text(mntpnt)

    # Attach a callback to the "deactivate" signal of the size
    # units option menu, so we can adjust the current value of
    # size appropriately:
    size_units_widget.get_menu().connect("deactivate", on_size_unit_changed,
                                         size_units_widget, size_widget)

    # Attach a callback to the "deactivate" signal of the file system
    # type option menu, so we can set do various things when the user
    # changes file system type.
    fstype_widget.get_menu().connect("deactivate", on_fstype_changed,
                                     fstype_widget, bootable_widget,
                                     mntpnt_widget)

    # Run the Edit dialog until the user gives valid input or Cancels.
    while 1:
        # don't use run_and_close(), because we need to read the state of this
        # dialog's widgets after the user dismisses it
        result = dialog.run()
        if result == 0:
            # OK

            # Get specified partition type:
            selection = setup_option_menu_get_history(type_widget)
            if selection == 0:
                new_state["type"] = parted.PARTITION_PRIMARY
            elif selection == 1:
                new_state["type"] = parted.PARTITION_LOGICAL

            # Get specified bootable flag:
            if bootable_widget.get_active():
                new_state["bootable"] = 1
            else:
                new_state["bootable"] = 0

            # Get specified partition size:
            sector_size = old_state["sector_size"]
            value = size_widget.get_value()
            selection = setup_option_menu_get_history(size_units_widget)
            if selection == 0:
                sectors = setup_mb_to_sectors(value, sector_size)
            elif selection == 1:
                sectors = setup_gb_to_sectors(value, sector_size)
            new_state["sectors"] = sectors
            new_state["sector_size"] = sector_size

            # Get specified file system type:
            selection = setup_option_menu_get_history(fstype_widget)
            if selection == 0:
                fstype = "Linux ext2"
            elif selection == 1:
                fstype = "Linux swap"
            elif selection == 2:
                fstype = "EFI boot"
            elif selection == 3:
                fstype = "FAT"
            new_state["fstype"] = fstype

            # Determine whether or not file system should be formatted:
            if format_widget.get_active():
                new_state["format"] = TRUE
            else:
                new_state["format"] = FALSE

            # Get specified mount point:
            new_state["mntpnt"] = mntpnt_widget.get_text()

            valid = partition_valid_state(new_state, new)
            if valid == VALID:
                break
            elif valid == 1 or valid == -1:
                # Invalid, cancel
                dialog.close()
                return {}
            else:
                # Invalid, retry
                continue

        elif result == 1 or result == -1:
            # Cancel, window deleted, or escape pressed

            dialog.close()

            return {}

        else:
            # This should never happen
            assert 0

    dialog.close()

    # Check to see if the state really changed. If it didn't,
    # return OLD_STATE, so the caller knows nothing actually
    # changed.
    # (XXX: The caller should probably do this when appropriate.)
    if new_state["type"] == old_state["type"] \
       and new_state["bootable"] == old_state["bootable"] \
       and new_state["sectors"] == old_state["sectors"] \
       and new_state["sector_size"] == old_state["sector_size"] \
       and new_state["fstype"] == old_state["fstype"] \
       and new_state["format"] == old_state["format"] \
       and new_state["mntpnt"] == old_state["mntpnt"]:
        return old_state
    return new_state


def setup_option_menu_get_history(option_menu):
    menu = option_menu.get_menu()
    children = menu.children()
    item = menu.get_active()
    for i in range(len(children)):
        if children[i] == item:
            break
    return i


def setup_load_pixmap(pixmap_name, hbox_name):
    hbox = wtree.get_widget(hbox_name)
    hbox.children()[0].destroy()
    p = create_pixmap_from_xpm(hbox, None, pixmap_name)
    pixmap = GtkPixmap(p[0], p[1])
    pixmap.show()
    hbox.add(pixmap)
    hbox.reorder_child(pixmap, 0)


def setup_redraw(progress_text=None):
    progress = wtree.get_widget("prepare-progress")
    progress_label = wtree.get_widget("prepare-label")

    if progress_text:
        progress_label.set_text(progress_text)

    while (events_pending()):
        mainiteration()

def update_status(line, data = None):

    if data:
        progress = data[0]
    else:
        progress = wtree.get_widget("prepare-progress")

    max_line_length = 70

    # don't print blank lines
    if (re.match(r"^\s+$", line)):
        line = None

    # XXX: This code ought to be:
    #adj = progress.get_adjustment()
    #val = adj.value + 1
    #if val > adj.uppper:
    #    val = adj.lower
    # But that won't work because the Python bindings suck and we
    # have no way to access the adjustment.
    val = progress.get_value() + 1

    # put a floor and ceiling on the value
    if val < 0:
        val = 0
    if val > 30:
        val = 1

    progress.set_value(val)

    # don't let message lines get too long
    if line:
        # debootstrap has output like this:
        #I: Retrieving http://archive.progeny.com/debian/dists/woody/Release
        #I: Retrieving http://archive.progeny.com/debian/dists/woody/main/binary-i386/Packages.gz
        #I: Retrieving http://archive.progeny.com/debian/pool/main/b/base-passwd/base-passwd_3.2.1_i386.deb
        #...
        #I: Extracting /var/cache/apt/archives/sysvinit_2.83-2_i386.deb...
        if (re.match("^I: .*", line)):
            # strip off the leading "I: "
            line = re.sub(r'^I: ', '', line)

        # strip off any trailing newlines
        while line[-1] == "\n":
            # import chomp ;-)
            line = line[0:-1]

        # is the line too long?
        if len(line) > max_line_length:
            # see if there's a long path we can shorten
            (words, lastword) = re.split(r'\s', line, 1)
            line = re.sub(re.escape(lastword), os.path.basename(lastword), line)
        # still too long?
        if len(line) > max_line_length:
            # take the excess out of the middle
            center = len(line) / 2
            excess = abs(max_line_length - len(line))
            line = line[:(center - (excess / 2))] + "..." + line[(center + (excess / 2)):]

    setup_redraw(line)

def setup_prepare_system():
    global setup_swaps
    global USER_CANCEL

    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("prepare"))
    setup_redraw()

    # Show the watch cursor
    gtk._root_window().set_cursor(cursor_new(GDK.WATCH))

    # Don't allow the user to go back, next, or cancel until the
    # system is fully prepared.
    druid.set_buttons_sensitive(FALSE, FALSE, FALSE)

    progress_count = 0
    progress_total = 1

    # Count the number of file systems to format and partition tables
    # to write, so we can upgrade the progress bar appropriately:
    for disk in setup_disks:
        for part in disk.get_part_list():
            if not part.is_active():
                continue
            minor = part.get_num()
            type = part.get_type()
            if (minor != -1
                and (type & parted.PARTITION_PRIMARY
                     or type == parted.PARTITION_PRIMARY
                     or type & parted.PARTITION_LOGICAL)):
                key = gen_geometry_key(part.get_geom())
                if part.get_fs_type() != None:
                    if part.get_fs_type().get_name() == "ext2":
                        if setup_format[key]:
                            progress_total = progress_total + 1
                    elif part.get_fs_type().get_name() == "linux-swap":
                        progress_total = progress_total + 1
                    elif part.get_fs_type().get_name() == "FAT":
                        if setup_format[key]:
                            progress_total = progress_total + 1

        progress_total = progress_total + 1

    # Configure the initial state of the progress bar appropriately:
    progress = wtree.get_widget("prepare-progress")
    progress.set_adjustment(GtkAdjustment(0, 0, progress_total, 1, 10, 10))

    setup_load_pixmap(ARROW, "formatting-hbox")

    setup_redraw()

    #
    # Write out the partition table:
    #

    ext2devs = []
    swapdevs = []
    fatdevs = []
    win_boot_dev = ""

    device = {}

    for disk in setup_disks:
        path = disk.get_dev().get_path()

        # Collect some information about the partitions for use during
        # the formatting and mounting phases.
        for part in disk.get_part_list():
            if not part.is_active():
                continue

            type = part.get_type()
            if (type & parted.PARTITION_EXTENDED
                or type & parted.PARTITION_FREESPACE
                or type & parted.PARTITION_METADATA):
                continue

            path = setup_return_part_device_file(part)

            key = gen_geometry_key(part.get_geom())

            if part.get_fs_type() != None:
                # Build a list of ext2 partitions to format in EXT2DEVS.
                if part.get_fs_type().get_name() == "ext2":
                    fs_type_name = "ext2"
                    if setup_format[key]:
                        ext2devs.append(path)

                # Build a list of swap partitions to format in SWAPDEVS.
                elif part.get_fs_type().get_name() == "linux-swap":
                    swapdevs.append(path)

                # Build a list of FAT partitions to format in FATDEVS.
                elif part.get_fs_type().get_name() == "FAT":
                    fs_type_name = "vfat"
                    if setup_format[key]:
                        fatdevs.append(path)

                    # See if there's a bootable Windows partition for
                    # setting up a boot stanza line in GRUB.
                    if part.get_bootable() and \
                       not part.is_efi() and \
                       not win_boot_dev:
                        win_boot_dev = path

            # Set up a dictionary that maps mount points to device
            # names and filesystem types, for use during the mounting
            # phase.
            if setup_mntpnt[key]:
                device[setup_mntpnt[key]] = [path, fs_type_name]

        path = disk.get_dev().get_path()

        update_status("Writing partition table to %s..." % (path,))
        TRACE("Writing partition table to %s..." % (path,))
        if disk.write() == 0:
            ERROR("Could not write partition table to %s." % (path,))
        TRACE("Done.")

        TRACE("Closing %s..." % (path,))
        if disk.close() == 0:
            ERROR("Could not close %s." % (path,))
        TRACE("Done.")

        progress_count = progress_count + 1
        progress.set_value(progress_count)
        setup_redraw()

    TRACE("Calling parted.done()...")
    parted.done()
    TRACE("Done.")

    # Use only the swap devices we've created
    if setup_swaps:
        swapdevs = setup_swaps

    #
    # Format and activate swap:
    #

    for swapdev in swapdevs:
        update_status("Formatting swap partition %s..." % (swapdev,))
        TRACE("Formatting swap partition %s..." % (swapdev,))
        os.system("mkswap %s" % (swapdev,))
        TRACE("Done.")

        update_status("Activating swap on %s..." % (swapdev,))
        TRACE("Activating swap on %s..." % (swapdev,))
        os.system("swapon %s" % (swapdev,))
        TRACE("Done.")

        progress_count = progress_count + 1
        progress.set_value(progress_count)
        setup_redraw()

    #
    # Format file systems:
    #

    for ext2dev in ext2devs:
        update_status("Formatting ext2 file system %s..." % (ext2dev,))
        TRACE("Formatting ext2 file system %s..." % (ext2dev,))
        os.system("mke2fs %s" % (ext2dev,))
        TRACE("Done.")

        progress_count = progress_count + 1
        progress.set_value(progress_count)
        setup_redraw()

    for fatdev in fatdevs:
        update_status("Formatting FAT file system %s..." % (fatdev,))
        TRACE("Formatting FAT file system %s..." % (fatdev,))
        os.system("mkdosfs %s" % (fatdev,))
        TRACE("Done.")

        progress_count = progress_count + 1
        progress.set_value(progress_count)
        setup_redraw()

    pgi.base.mount(device, TARGET)

    # Set the value of the progress bar to PROGRESS_TOTAL, so we
    # always end at 100%
    progress.set_value(progress_total)
    setup_redraw()

    #
    # Run debootstrap to extract a base system
    #

    # Set the adjustment and other UI elements properly.
    update_status("Running debootstrap...")
    progress.set_activity_mode(TRUE)
    progress.set_activity_step(5)
    progress.set_adjustment(GtkAdjustment(0, 1, 30, 0, 0, 0))
    setup_load_pixmap(CHECK, "formatting-hbox")
    setup_load_pixmap(ARROW, "extracting-hbox")
    setup_redraw()

    fn = "%s/share/installer/extra_packages" % (LIVE,)
    if not os.path.exists(fn):
        fn = None

    r = pgi.base.install_packages(SUITE, MIRROR, TARGET, fn,
                                  update_status, (progress,))

    if not r:
        TRACE("ERROR: debootstrap failed!")
        dialog = GnomeMessageBox(foldtext("""\
Installation failed because debootstrap experienced a problem.  Debootstrap
failures are often caused by out-of-sync mirrors, or because your machine needs
to use an HTTP proxy to access the Web, and there is no proxy configured, or
there is a problem contacting the proxy.  Failures can also be caused by, for
example, scratched or dirty CD-ROMs.

You can find more information about what caused debootstrap to fail in the log
file, %s.  You may want to report this information to your installer vendor.

Press OK to cancel the installation.  If the problem is something you are able
to rectify, such as a dirty CD-ROM or specification of an HTTP proxy, you may
re-try the installation at any time.
""" % (LOGFILE,)),
                                 MESSAGE_BOX_ERROR,
                                 STOCK_BUTTON_OK)
        dialog.set_position(WIN_POS_CENTER)
        dialog.run_and_close()
        USER_CANCEL = TRUE
        gtk.mainquit()

    TRACE("Done.")

    # Set the value of the progress bar to PROGRESS_TOTAL, so we
    # always end at 100%.
    progress.set_activity_mode(FALSE)
    progress.set_value(progress_total)
    setup_redraw()

    #
    # Do some final setup tasks:
    #

    progress_count = 0
    # NOTE: If you add more things to do below, *UPDATE THIS NUMBER*!
    progress_total = 3
    for swapdev in swapdevs:
        progress_total += 1

    progress.set_adjustment(GtkAdjustment(0, 0, progress_total, 1, 10, 10))

    setup_load_pixmap(CHECK, "extracting-hbox")
    setup_load_pixmap(ARROW, "preparing-hbox")

    setup_redraw()

    # Generate /etc/fstab:
    update_status("Generating /etc/fstab...")
    TRACE("Generating /etc/fstab...")
    pgi.base.mkfstab(device, swapdevs, TARGET)

    progress_count = progress_count + 1
    progress.set_value(progress_count)
    setup_redraw()

    # Install the kernel.
    update_status("Installing the kernel...")
    TRACE("Installing the kernel...")
    pgi.base.install_kernel(TARGET)

    progress_count = progress_count + 1
    progress.set_value(progress_count)
    setup_redraw()

    TRACE("Done.")

    # Set up the boot loader (if desired).
    # XXX: Arch testing doesn't belong in here.
    if ARCH == "ia64":
        instdevice = device["/boot/efi"][0]
        do_bootloader = TRUE
    else:
        if wtree.get_widget("startup-partition").get_active():
            instdevice = device["/"][0]
        else:
            instdevice = re.split("[0-9]+", device["/"][0])[0]

        do_bootloader = not wtree.get_widget("startup-none").get_active()

    if do_bootloader:
        update_status("Installing GRUB to %s with root %s" % (instdevice, device["/"][0]))
        TRACE("Installing GRUB to %s with root %s" % (instdevice, device["/"][0]))
        pgi.bootloader.install(instdevice, device["/"][0], TARGET)

    # Deactivate the swap partitions.
    for swapdev in swapdevs:
        update_status("Deactivating swap on %s..." % (swapdev,))
        TRACE("Deactivating swap on %s..." % (swapdev,))
        os.system("swapoff %s" % (swapdev,))
        TRACE("Done.")

        progress_count = progress_count + 1
        progress.set_value(progress_count)
        setup_redraw()

    progress_count = progress_count + 1
    progress.set_value(progress_count)
    update_status("Installation complete.")
    setup_redraw()

    TRACE("Installation complete.")

    # Set the value of the progress bar to PROGRESS_TOTAL, so we
    # always end at 100%.
    progress.set_value(progress_total)

    setup_load_pixmap(CHECK, "preparing-hbox")

    setup_redraw()

    # Replace watch cursor with normal arrow.
    gtk._root_window().set_cursor(cursor_new(GDK.LEFT_PTR))

    # The user may proceed now:
    druid.set_buttons_sensitive(FALSE, TRUE, FALSE)

def setup_print_state(state):
    if (state["type"] & parted.PARTITION_PRIMARY
        or state["type"] == parted.PARTITION_PRIMARY):
        string = "(type=Primary, "
    elif (state["type"] & parted.PARTITION_LOGICAL):
        string = "(type=Logical, "
    elif (state["type"] & parted.PARTITION_EXTENDED):
        string = "(type=Extended, "
    else:
        string = "(type=Unknown, "

    if state["bootable"]:
        string = string + "bootable, "

    sectors = state["sectors"]
    sector_size = state["sector_size"]
    size = setup_return_size_string(sectors, sector_size)
    string = string + "size=" + size + ", "

    fstype = state["fstype"]
    string = string + "fstype=" + fstype + ", "

    if state["format"]:
        string = string + "format, "

    string = string + "mntpnt=" + state["mntpnt"] + ")"

    return string


def setup_update_disk_list(list):
    global setup_disks
    druid = wtree.get_widget("druid")

    # Use this dictionary to map from "hda" to "IDE Disk 1".
    charmap = {"a":  1,
               "b":  2,
               "c":  3,
               "d":  4,
               "e":  5,
               "f":  6,
               "g":  7,
               "h":  8,
               "i":  9,
               "j": 10,
               "k": 11,
               "l": 12,
               "m": 13,
               "n": 14,
               "o": 15,
               "p": 16,
               "q": 17,
               "r": 18,
               "s": 19,
               "t": 20,
               "u": 21,
               "v": 22,
               "w": 23,
               "x": 24,
               "y": 25,
               "z": 26}

    list.freeze()
    list.clear()
    row = 0
    for disk in setup_disks:
        device = disk.get_dev()
        sectors = device.get_length()
        sector_size = device.get_sector_size()

        devpath = device.get_path()

        # devpath now looks like "/dev/hda"
        (_, _, s) = string.split(devpath, "/")
        # s now looks like "hda"
        (type, num) = string.split(s, "d")
        # type now looks like "h" or "s" and num like "a"
        if type == "h":
            path = "%s (IDE Disk %d)" % (devpath, charmap[num[0]])
        else:
            path = "%s (SCSI Disk %d)" % (devpath, charmap[num[0]])

        size = setup_return_size_string(sectors, sector_size)
        model = device.get_model()
        list.append([ path, size, model ])
        list.set_row_data(row, devpath)
        row = row + 1
    list.select_row(0, 0)
    list.thaw()

def setup_update_part_tree(tree):
    global setup_format
    global setup_mntpnt

    TRACE("Populating part-tree...")
    tree.freeze()
    tree.clear()
    for disk in setup_disks:
        setup_maximize_extended_partition(disk)
        device = disk.get_dev()
        path = device.get_path()
        parent = tree.insert_node(None, None, [ path, "", "", "", "" ],
                                  is_leaf = FALSE, expanded = TRUE)
        parts = setup_get_part_list(disk)
        # tree.insert_node prepends, so we want to iterate over the
        # list in reverse.
        parts.reverse()
        child = None
        for part in parts:
            geom = part.get_geom()
            disk = geom.get_disk()
            minor = part.get_num()
            type = part.get_type()
            sectors = geom.get_length()
            sector_size = device.get_sector_size()

            if (minor != -1
                and (type & parted.PARTITION_PRIMARY
                     or type == parted.PARTITION_PRIMARY
                     or type & parted.PARTITION_LOGICAL)):
                path = setup_return_part_device_file(part)
                key = gen_geometry_key(part.get_geom())

                TRACE("Adding %s (key %s) (type %d)." % (path,
                                               gen_geometry_key(geom),
                                               type))

                if part.get_fs_type() != None:
                    fstype = setup_return_fstype_string(part)
                else:
                    fstype = ""
                mntpnt = setup_mntpnt[key]
                if setup_format[key]:
                    status = "To be formatted"
                elif setup_mntpnt[key]:
                    status = "To be mounted"
                elif part.get_bootable():
                    status = "Bootable"
                else:
                    status = ""

            elif (type & parted.PARTITION_FREESPACE):
                TRACE("Adding free space (key %s) (type %d)."
                      % (gen_geometry_key(geom), type))
                path = ""
                fstype = "Free Space"
                mntpnt = ""
                status = ""

            else:
                TRACE("Not adding %s (type %d)."
                      % (gen_geometry_key(geom), type))
                continue

            size = setup_return_size_string(sectors, sector_size)
            child = tree.insert_node(parent, child, [ path, size, fstype,
                                                      mntpnt, status ])
            tree.node_set_row_data(child, part)
    tree.thaw()
    TRACE("Done.")

def setup_are_mounts_valid():
    global setup_mntpnt

    mntpnts = setup_mntpnt.values()

    for part in pgi.auto_partition.get_standard_partitions():
        # XXX: Do we really want to skip checking for swap?
        if part[0] != "swap" and part[0] not in mntpnts:
            TRACE("INVALID: Partition %s not mounted yet" % (part[0],))
            return FALSE

    TRACE("VALID: All required mounts found.")
    return TRUE

def setup_do_boot():
    if ARCH == "ia64":
        setup_prepare_system()
    else:
        wtree.get_widget("druid").set_page(wtree.get_widget("boot"))

def on_target_back(widget, ignored):
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("start"))
    return TRUE

def on_target_next(widget, ignored):
    druid = wtree.get_widget("druid")
    setup_open_all_disks()

    found_space = FALSE
    wholedisk = wtree.get_widget("target-whole").get_active()
    if wholedisk or wtree.get_widget("target-free").get_active():
        found_space = FALSE
        for disk in setup_disks:
            device = disk.get_dev()
            if wholedisk:
                if not auto_partition_check_disk_size(device):
                    continue
                found_space = TRUE
            else:
                if not auto_partition_check_free_space(device):
                    continue
                found_space = TRUE

        if found_space:
            druid.set_page(wtree.get_widget("disk"))
        else:
            if wholedisk:
                message = ("No disks were found that are large enough to "
                           "install Debian. Please go to the custom "
                           "partitioning screen and partition manually.")
            else:
                message = ("No disks were found with enough free space to "
                           "install Debian. Please go to the custom "
                           "partitioning screen and partition manually.")
            dialog = GnomeMessageBox(foldtext(message),
                                     MESSAGE_BOX_ERROR,
                                     STOCK_BUTTON_OK)
            dialog.set_position(WIN_POS_CENTER)
            dialog.run_and_close()

    elif wtree.get_widget("target-custom").get_active():
        druid.set_page(wtree.get_widget("parted"))

    return TRUE

def on_disk_prepare(widget, ignored):
    global list
    list = wtree.get_widget("disk-list")
    # XXX: This should probably be done using Glade, but I'm not sure
    # how to do it.
    list.column_titles_passive()
    list.set_column_auto_resize(0, TRUE)
    list.set_column_auto_resize(1, TRUE)
    list.set_column_auto_resize(2, TRUE)
    list.set_column_justification(0, JUSTIFY_CENTER)
    list.set_column_justification(1, JUSTIFY_CENTER)
    list.set_column_justification(2, JUSTIFY_CENTER)
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(TRUE, FALSE, TRUE)
    setup_update_disk_list(list)

def on_disk_select(list, row, column, event):
    global device
    device = list.get_row_data(row)
    TRACE("Device row: %s" % (`row`,))
    TRACE("Device selected: %s" % (device,))
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(TRUE, TRUE, TRUE)

def on_disk_unselect(_, row, column, event):
    global device
    global list
    device = None
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(TRUE, FALSE, TRUE)

def on_disk_back(widget, ignored):
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("target"))
    return TRUE

def on_disk_next(widget, ignored):
    global device
    global setup_disks
    global setup_format
    global setup_mntpnt
    global setup_swaps

    if not device:
        return TRUE

    for disk in setup_disks:
        realdev = disk.get_dev()
        if realdev.get_path() == device:
            break

    if wtree.get_widget("target-whole").get_active():
        dialog = GnomeMessageBox(foldtext("Are you absolutely sure?  All data "
                                 "on this disk will be erased to make room for "
                                 "Debian GNU/Linux."),
                                 MESSAGE_BOX_QUESTION,
                                 STOCK_BUTTON_YES,
                                 STOCK_BUTTON_NO)
        dialog.set_position(WIN_POS_CENTER)
        reply = dialog.run_and_close()
        if reply != YES:
            return TRUE

        setup_close_all_disks()
        if not auto_partition_wipe_disk(realdev):
            return TRUE
        setup_open_all_disks()

        # Of course we need a fresh disk object now.
        for disk in setup_disks:
            realdev = disk.get_dev()
            if realdev.get_path() == device:
                break

    if not auto_partition_go(disk):
        return TRUE

    setup_do_boot()

    return TRUE

def on_boot_prepare(widget, ignored):
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(FALSE, TRUE, TRUE)

def on_boot_next(widget, ignored):
    setup_prepare_system()

    return TRUE

def on_parted_prepare(widget, ignored):
    global tree
    global setup_format
    global setup_mntpnt
    tree = wtree.get_widget("part-tree")
    # XXX: This should probably be done using Glade, but I'm not sure
    # how to do it.
    tree.column_titles_passive()
    tree.set_column_justification(0, JUSTIFY_CENTER)
    tree.set_column_justification(1, JUSTIFY_CENTER)
    tree.set_column_justification(2, JUSTIFY_CENTER)
    tree.set_column_justification(3, JUSTIFY_CENTER)
    tree.set_column_justification(4, JUSTIFY_CENTER)
    setup_open_all_disks()
    setup_update_part_tree(tree)
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(TRUE, FALSE, TRUE)
    create = wtree.get_widget("part-create")
    create.set_sensitive(FALSE)
    delete = wtree.get_widget("part-delete")
    delete.set_sensitive(FALSE)
    edit = wtree.get_widget("part-edit")
    edit.set_sensitive(FALSE)
    undo = wtree.get_widget("part-undo")
    undo.set_sensitive(FALSE)


def on_parted_select(tree, node, column):
    if not tree.node_get_row_data(node):
        return

    global part
    part = tree.node_get_row_data(node)
    geom = part.get_geom()
    disk = geom.get_disk()
    minor = part.get_num()

    TRACE("Selected %s" % (gen_geometry_key(geom),))

    if minor > 0:
        wtree.get_widget("part-delete").set_sensitive(TRUE)
        wtree.get_widget("part-edit").set_sensitive(TRUE)
    else:
        create = wtree.get_widget("part-create")
        # If there are no free primary partitions, and there is no
        # extended partition, we can't create a partition, so grey
        # out the Create button.
        if setup_num_primary_partitions(disk) < 4 \
           or setup_has_extended_partition(disk):
            create.set_sensitive(TRUE)
        else:
            create.set_sensitive(FALSE)


def on_parted_unselect(tree, node, column):
    global part
    part = None
    create = wtree.get_widget("part-create")
    create.set_sensitive(FALSE)
    delete = wtree.get_widget("part-delete")
    delete.set_sensitive(FALSE)
    edit = wtree.get_widget("part-edit")
    edit.set_sensitive(FALSE)


def on_parted_create(widget):
    global part

    if part == None:
        return

    extended = None
    geom = part.get_geom()
    disk = geom.get_disk()
    device = disk.get_dev()

    state = {}

    # Initialize defaults values for the Edit partition dialog:

    state["type"] = parted.PARTITION_PRIMARY
    state["bootable"] = FALSE
    state["sectors"] = geom.get_length()
    state["sector_size"] = device.get_sector_size()
    state["fstype"] = "Linux ext2"
    state["format"] = TRUE
    state["mntpnt"] = ""

    # Pop up the Edit partition dialog and get values from the user:
    state = setup_edit_dialog(state, TRUE, TRUE)
    if state == {}:
        return

    # Apply changes:

    # Get the parted object corresponding to the chosen filesystem type.
    parted_fstype = setup_return_fstype(state["fstype"])

    part = make_partition(disk, state["type"], state["fstype"],
                          geom.get_start(),
                          min(geom.get_end(), state["sectors"] - 1),
                          state["mntpnt"], state["format"])
    if not part:
        return

    setup_update_part_tree(tree)

    # If the root file system has been defined, the user may proceed.
    # Also requires /boot/efi to be defined as an EFI partition on ia64.
    if setup_are_mounts_valid():
        TRACE("Mounts are now valid; enabling Next button")
        druid = wtree.get_widget("druid")
        druid.set_buttons_sensitive(TRUE, TRUE, TRUE)
    create = wtree.get_widget("part-create")
    create.set_sensitive(FALSE)
    delete = wtree.get_widget("part-delete")
    delete.set_sensitive(FALSE)
    edit = wtree.get_widget("part-edit")
    edit.set_sensitive(FALSE)
    undo = wtree.get_widget("part-undo")
    undo.set_sensitive(TRUE)

def on_parted_delete(widget):
    global part
    global setup_format
    global setup_mntpnt

    if part == None:
        return

    geom = part.get_geom()
    disk = geom.get_disk()
    device = disk.get_dev()

    path = setup_return_part_device_file(part)

    TRACE("Deleting partition %s..." % (path,))
    if disk.delete_partition(part) == 0:
        ERROR("Could not delete partition %s." % (path,))
        return
    TRACE("Done.")

    key = gen_geometry_key(geom)
    TRACE("part is %s, key is %s" % (path, key))

    try:
        mntpnt = setup_mntpnt[key]
        del setup_format[key]
        del setup_mntpnt[key]
    except KeyError:
        mntpnt = ""

    if not setup_has_logical_partitions(disk):
        if setup_has_extended_partition(disk):
            TRACE("Deleting extended partition...")
            if disk.delete_partition(disk.get_extended_partition()) == 0:
                ERROR("Could not delete extended partition.")
                return
            TRACE("Done.")
    else:
        TRACE("Minimizing extended partition...")
        disk.minimize_extended_partition()
        TRACE("Done.")

    setup_update_part_tree(tree)

    # If the root file system was on this partition, the user may no
    # longer proceed.  Also disallows proceeding when removing the EFI
    # partition.
    if not setup_are_mounts_valid():
        TRACE("Mounts are no longer valid; disabling Next button.")
        druid = wtree.get_widget("druid")
        druid.set_buttons_sensitive(TRUE, FALSE, TRUE)

    create = wtree.get_widget("part-delete")
    create.set_sensitive(FALSE)
    delete = wtree.get_widget("part-delete")
    delete.set_sensitive(FALSE)
    edit = wtree.get_widget("part-edit")
    edit.set_sensitive(FALSE)
    undo = wtree.get_widget("part-undo")
    undo.set_sensitive(TRUE)


def on_parted_edit(widget):
    global part

    if part == None:
        return

    geom = part.get_geom()
    disk = geom.get_disk()
    device = disk.get_dev()

    path = setup_return_part_device_file(part)

    key = gen_geometry_key(geom)

    # Use the current values as defaults:
    old_state = {}
    old_state["type"] = part.get_type()
    old_state["bootable"] = part.get_bootable()
    old_state["sectors"] = geom.get_length()
    old_state["sector_size"] = device.get_sector_size()
    old_state["format"] = setup_format[key]
    old_state["mntpnt"] = setup_mntpnt[key]

    part_fs_type = setup_return_fstype_string(part)

    old_state["fstype"] = part_fs_type

    # Pop up the Edit partition dialog and get values from the user:
    TRACE("Editing partition %s..." % (path,))
    new_state = setup_edit_dialog(old_state, FALSE)
    TRACE("Done.")

    if new_state == {} or new_state == old_state:
        # No changes were made.
        return

    TRACE("Partition %s changed from %s to %s." % (path, setup_print_state(old_state), setup_print_state(new_state)))

    # Apply changes:
    if new_state["bootable"] != old_state["bootable"]:
        part.set_bootable(new_state["bootable"])
    if new_state["format"] != old_state["format"]:
        setup_format[key] = new_state["format"]
    if new_state["mntpnt"] != old_state["mntpnt"]:
        setup_mntpnt[key] = new_state["mntpnt"]
    if new_state["fstype"] != old_state["fstype"]:
        parted_fs_type = setup_return_fstype(new_state["fstype"])

        # This method sets the partition type as far as parted
        # cares.
        part.set_fs_type(parted_fs_type)

        # And this one puts the partition into the partition
        # table for other, less intelligent systems.
        part.set_system(parted_fs_type)

        # EFI is still a hack; make sure not to lose EFI information.
        if new_state["fstype"] == "EFI boot":
            part.mark_efi()

    setup_update_part_tree(tree)

    # If the root file system has been defined, the user may proceed.
    # Also requires EFI partition on ia64.
    if setup_are_mounts_valid():
        TRACE("Mounts are valid; enabling Next button.")
        druid = wtree.get_widget("druid")
        druid.set_buttons_sensitive(TRUE, TRUE, TRUE)
    else:
        TRACE("Mounts are invalid; disabling Next button.")
        druid = wtree.get_widget("druid")
        druid.set_buttons_sensitive(TRUE, FALSE, TRUE)

    # XXX: If changes were made, turn the line red to indicate that
    # the partition has been changed.

    create = wtree.get_widget("part-create")
    create.set_sensitive(FALSE)
    delete = wtree.get_widget("part-delete")
    delete.set_sensitive(FALSE)
    edit = wtree.get_widget("part-edit")
    edit.set_sensitive(FALSE)
    undo = wtree.get_widget("part-undo")
    undo.set_sensitive(TRUE)


def on_parted_undo(widget):
    undo = wtree.get_widget("part-undo")
    undo.set_sensitive(FALSE)
    # Throw out all changes:
    TRACE("Undoing all changes...")
    setup_reopen_all_disks()
    TRACE("Done.")
    setup_update_part_tree(tree)


def on_parted_back(widget, ignored):
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("target"))
    return TRUE


def on_parted_next(widget, ignored):
    dialog = GnomeMessageBox(foldtext("Are you absolutely sure?  All data on "
                             "partitions scheduled to be formatted, deleted, "
                             "or used as swap space will be lost."),
                             MESSAGE_BOX_QUESTION,
                             STOCK_BUTTON_YES,
                             STOCK_BUTTON_NO)
    dialog.set_position(WIN_POS_CENTER)

    reply = dialog.run_and_close()
    if reply == YES:
        # Make it so:
        setup_do_boot()
    return TRUE


def on_druid_cancel(widget):
    global USER_CANCEL

    dialog = GnomeMessageBox(foldtext("Cancel installation of Debian GNU/Linux "
                             "and reboot?"),
                             MESSAGE_BOX_QUESTION,
                             STOCK_BUTTON_YES,
                             STOCK_BUTTON_NO)
    dialog.set_position(WIN_POS_CENTER)

    reply = dialog.run_and_close()
    if reply == YES:
        USER_CANCEL = TRUE
        gtk.mainquit()

def on_start_next(widget, ignored):
    # Skip the first two screens for Beta 2:
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("target"))
    return TRUE


def on_finish_prepare(widget, ignored):
    druid = wtree.get_widget("druid")
    druid.set_buttons_sensitive(FALSE, TRUE, FALSE)


def on_finish_finish(widget, ignored):
    mainquit()
    return TRUE


# This function is necessary because there are places where Glade
# won't allow you to put icons in buttons.
def button_to_gnome_button(button, type, info, text = None):
    button.remove(button.children()[0])

    if type == APP_PIXMAP_STOCK:
        pixmap = GnomeStock(info)
    elif type == APP_PIXMAP_FILENAME:
        pixmap = GnomePixmap(info)
    else:
        return

    pixmap.show()

    if text:
        hbox1 = GtkHBox(FALSE, 0)
        hbox1.show()

        hbox2 = GtkHBox(FALSE, 0)
        hbox2.show()
        hbox1.pack_start(hbox2, TRUE, FALSE, 7)
        hbox2.pack_start(pixmap, FALSE, FALSE, 0)

        label = GtkLabel(text)
        label.show()
        hbox2.pack_end(label, FALSE, FALSE, 7)

        button.add(hbox1)
    else:
        button.add(pixmap)

    return button

dic = { "on_installer_delete_event": mainquit,
        "on_installer_destroy":      mainquit,
        "on_start_next":             on_start_next,
        "on_druid_cancel":           on_druid_cancel,
        "on_target_back":            on_target_back,
        "on_target_next":            on_target_next,
        "on_disk_prepare":           on_disk_prepare,
        "on_disk_select":            on_disk_select,
        "on_disk_unselect":          on_disk_unselect,
        "on_disk_back":              on_disk_back,
        "on_disk_next":              on_disk_next,
        "on_parted_prepare":         on_parted_prepare,
        "on_parted_select":          on_parted_select,
        "on_parted_unselect":        on_parted_unselect,
        "on_parted_create":          on_parted_create,
        "on_parted_delete":          on_parted_delete,
        "on_parted_edit":            on_parted_edit,
        "on_parted_undo":            on_parted_undo,
        "on_parted_back":            on_parted_back,
        "on_parted_next":            on_parted_next,
        "on_boot_prepare":           on_boot_prepare,
        "on_boot_next":              on_boot_next,
        "on_finish_prepare":         on_finish_prepare,
        "on_finish_finish":          on_finish_finish }

if __name__ == "__main__":

    if len(sys.argv) != 2:
        sys.stderr.write("Usage: %s GLADE-FILE-PATH\n" % (sys.argv[0],))
        sys.exit(1)

    GLADE = sys.argv[1]

    # Change from watch cursor to arrow.
    gtk._root_window().set_cursor(cursor_new(GDK.LEFT_PTR))

    # Load the PGI theme:
    rc_parse(GTKRC)

    # Set up the druid.
    wtree = GladeXML(GLADE, "installer")

    logoimage = GdkImlib.Image(LOGO)
    for page in ("start", "target", "disk", "parted", "boot", "prepare",
                 "finish"):
        wtree.get_widget(page).set_logo(logoimage)

    installer = wtree.get_widget("installer")

    wtree.signal_autoconnect(dic)

    button_to_gnome_button(wtree.get_widget("part-create"),
                           APP_PIXMAP_STOCK, STOCK_PIXMAP_NEW, "New")
    button_to_gnome_button(wtree.get_widget("part-edit"),
                           APP_PIXMAP_STOCK, STOCK_PIXMAP_PROPERTIES,
                           "Edit")
    button_to_gnome_button(wtree.get_widget("part-delete"),
                           APP_PIXMAP_STOCK, STOCK_PIXMAP_TRASH, "Delete")
    button_to_gnome_button(wtree.get_widget("part-undo"),
                           APP_PIXMAP_STOCK, STOCK_PIXMAP_UNDO, "Undo All")

    # Load the configured text if it's available.
    for message_type in ("start", "finish"):
        message_str = pgi.base.get_messages(message_type)
        if message_str:
            TRACE("Setting message for %s..." % (message_type,))
            wtree.get_widget(message_type).set_text(message_str)

    # Set up parted.
    TRACE("Calling _parted.exception_set_handler()...")
    _parted.exception_set_handler(setup_exception_handler)
    TRACE("Done.")

    TRACE("Calling parted.init()...")
    parted.init()
    TRACE("Done.")

    TRACE("Calling parted.device_probe_all()...")
    parted.device_probe_all()
    TRACE("Done.")

    TRACE("Calling setup_probe_nonstandard_devices()...")
    setup_probe_nonstandard_devices()
    TRACE("Done.")

    # Show the druid and get things rolling.
    wtree.get_widget("installer").show()
    mainloop()

    # We're done - exit with the appropriate return value.
    if USER_CANCEL:
        sys.exit(EX_USERCANCEL)
    else:
        sys.exit(EX_OK)

# vim:ai:et:sts=4:sw=4:tw=0:
