#! /usr/bin/python

# XXX: This script needs more comments and better error handling.

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

#GLADE = "./installer.glade"
#GLADE = "/installer.glade"
#GLADE = "/usr/share/glade/installer/installer.glade"
GLADE = sys.argv[1]

#GTKRC = "/etc/gtk/gtkrc"
GTKRC = "/cdrom/live/etc/gtk/gtkrc"

MINIMAL = "/cdrom/dists/progeny/main/install-i386/minimal.tar.gz"

TARGET = "/target"

# Load the Progeny theme:
rc_parse(GTKRC)

wtree = GladeXML(GLADE, "installer")

parted.init()
parted.device_probe_all()

##setup_method = "Undefined"
##setup_source = "Undefined"
##setup_target = "Undefined"

setup_disks = []

setup_format = {}
setup_mntpnt = {}

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


def TRACE(message):
    print message


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(fstype):
    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":
        return "FAT"
    else:
        return ""


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_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()

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

        TRACE("Attempting to open %s..." % path)

        disk = device.disk_open()
        if disk == None:
            TRACE("Could not open %s." % path)
            wtree_dialog_create = GladeXML(GLADE, "dialog-create")
            dialog = wtree_dialog_create.get_widget("dialog-create")
            result = dialog.run_and_close()
            if result != OK:
                continue
            TRACE("Creating partition table on %s..." % path)
            disk = device.disk_create(parted.disk_type_get("msdos"))
            TRACE("Done.")
            # XXX: disk seems to be invalid (disk.get_dev() returns a
            # value different from device) and the program eventually
            # crashes.
            if disk == None:
                # XXX: Error..
                continue
        for part in disk.get_part_list():
            minor = part.get_num()
            if minor != -1:
                # Initialize SETUP_FORMAT and SETUP_MNTPNT dictionaries:
                path = device.get_path() + str(minor)
                setup_format[path] = FALSE
                setup_mntpnt[path] = ""
        setup_disks.append(disk)


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


def setup_close_all_disks():
    "Close all disks in SETUP_DISKS list and clear it."
    global setup_disks
    global setup_format
    global setup_mntpnt
    for disk in setup_disks:
        disk.close()
    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.PED_PARTITION_EXTENDED:
            exists = TRUE
    return exists


def setup_num_primary_partitions(disk):
    count = 0
    for part in disk.get_part_list():
        if part.get_num() >= 1 and part.get_num() <= 4:
            count = count + 1
    return count


size_current_maximum = 0
size_current_minimum = 0
size_units_current_state = 0
initial_fstype = 0
current_format = FALSE

def on_fstype_changed(widget, fstype_widget, format_widget):
    # The user changed the file system type, so toggle the format
    # checkbox appropriately: If the file system type has been
    # changed from its original value, activate the
    # format checkbox and make it insensitive (because if the
    # user changes the type of an existing partition, it has to
    # be reformatted); and if the file system type is changed
    # back to its original value, return the format checkbox to
    # its original value as well (to allow undo).
    #
    # XXX: This doesn't work quite right. I think the FORMAT_CHANGED
    # callback gets triggered when set_active is called.
    fstype = setup_option_menu_get_history(fstype_widget)
    if fstype == initial_fstype:
        format_widget.set_active(current_format)
        format_widget.set_sensitive(TRUE)
    else:
        format_widget.set_active(TRUE)
        format_widget.set_sensitive(FALSE)

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
    size = size_widget.get_value()
    selection = setup_option_menu_get_history(size_units_widget)
    if selection == 0 and size_units_current_state == 1:
        size = size * 1024
        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
    elif selection == 1 and size_units_current_state == 0:
        size = size / 1024
        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,
                               size_current_minimum,
                               size_current_maximum,
                               step_increment,
                               page_increment,
                               page_increment)
    size_widget.set_adjustment(adjustment)
    size_widget.set_value(size)

def on_format_changed(widget, format_widget):
    if format_widget.get_active():
        current_format = TRUE
    else:
        current_format = FALSE

# XXX: Bugs/suggestions:
# - If this is a Create dialog, and the user unchecks format, mntpnt
#   should be made insensitive
# - If users changes fstype to swap, mount point should made insensitive.

def setup_edit_dialog(state, allow_edit_type, allow_edit_size):
    global size_current_maximum
    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")

    # XXX: Blech.  This works around an apparent bug in glade, where
    # the option menus are off center until there value has changed.
    type_widget.set_history(1)
    type_widget.set_history(0)
    size_units_widget.set_history(1)
    size_units_widget.set_history(0)
    fstype_widget.set_history(1)
    fstype_widget.set_history(0)

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

    new_state = {}
    old_state = state

    # Use current partition type as default:
    if type == parted.PED_PARTITION_PRIMARY:
        type_widget.set_history(0)
    elif type == parted.PED_PARTITION_LOGICAL:
        type_widget.set_history(1)
    if not allow_edit_type:
        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)

    # 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":
        initial_fstype = 0
    elif fstype == "Linux swap":
        initial_fstype = 1
    elif fstype == "FAT":
        initial_fstype = 2

    fstype_widget.set_history(initial_fstype)

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

    current_format = format

    # Use current mount point as default:
    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 the Format checkbox to "yes" if
    # the user changes file system type.
    fstype_widget.get_menu().connect("deactivate", on_fstype_changed,
                                     fstype_widget, format_widget)

    # Attach a callback to the "toggled" signal of the format checkbox,
    # so we can update CURRENT_FORMAT when the user changes that.
    format_widget.connect("toggled", on_format_changed, format_widget)

    # Pop up the "Edit partition" dialog:
    result = dialog.run()
    if result == OK:
        # Get specified partition type:
        selection = setup_option_menu_get_history(type_widget)
        if selection == 0:
            new_state["type"] = parted.PED_PARTITION_PRIMARY
        elif selection == 1:
            new_state["type"] = parted.PED_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 = parted.file_system_type_get("ext2")
        elif selection == 1:
            fstype = parted.file_system_type_get("linux-swap")
        elif selection == 2:
            fstype = parted.file_system_type_get("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()

        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 setup_return_fstype_string(new_state["fstype"]) == \
               setup_return_fstype_string(old_state["fstype"]) \
           and new_state["format"] == old_state["format"] \
           and new_state["mntpnt"] == old_state["mntpnt"]:
            return old_state
        return new_state
    else:
        dialog.close()

        return old_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_prepare_system():
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("prepare"))
    while (events_pending()):
        mainiteration()

    # Don't allow the user to go back, next, or cancel until the
    # system is fully prepared.
    # XXX: We should probably allow for Cancel here.
    druid.set_buttons_sensitive(FALSE, FALSE, FALSE)

    #
    # Format file systems:
    #

    format_count = 0
    format_total = 0

    # Count the number of file systems to format and partition tables
    # to write:
    for disk in setup_disks:
        for part in disk.get_part_list():
            if not part.is_active():
                continue
            path = disk.get_dev().get_path() + str(part.get_num())
            if setup_format[path]:
                format_total = format_total + 1

        format_total = format_total + 1

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

    # Format file systems and write partition tables:
    for disk in setup_disks:
        for part in disk.get_part_list():
            if not part.is_active():
                continue
            path = disk.get_dev().get_path() + str(part.get_num())
            if setup_format[path]:
                progress.set_format_string("Formatting %s..." % path)
                while (events_pending()):
                    mainiteration()

                fs = parted.FileSystem(part.get_geom(), part.get_fs_type())
                fs.close()

                format_count = format_count + 1

                progress.set_value(format_count)
                while (events_pending()):
                    mainiteration()

        path = disk.get_dev().get_path()

        progress.set_format_string("Writing partition table to %s..." % path)
        while (events_pending()):
            mainiteration()

        disk.write()
        disk.close()

        format_count = format_count + 1

        progress.set_value(format_count)
        while (events_pending()):
            mainiteration()

    parted.done()

    progress.set_format_string("Done.")
    while (events_pending()):
        mainiteration()

    #
    # Mount file systems:
    #

    # XXX: Activate swap?

    device = {}

    # Set up a dictionary that maps mount points to device names
    # (essentially, this is the inverse of SETUP_MNTPNT:
    for path in setup_mntpnt.keys():
        if setup_mntpnt[path] != "":
            device[setup_mntpnt[path]] = path

    # Sort the mount points, so we mount the file systems in the
    # appropriate order:
    mntpnts = device.keys()
    mntpnts.sort()

    # Mount the file systems:
    for mntpnt in mntpnts:
        if mntpnt == "/":
            os.system("mkdir " + TARGET)
            os.system("mount " + device[mntpnt] + " " + TARGET)
        else:
            os.system("mkdir " + TARGET + mntpnt)
            os.system("mount " + device[mntpnt] + " " + TARGET + mntpnt)

    #
    # XXX: Autoinstaller integration.
    # This is a sample of how to control the autoinstaller to do what
    # you want.  This implements the user's choices of CD-ROM or
    # network installs.
    #

##    installer_cfgfile = open("/etc/interactive.cfg", "a")

##    if setup_source == "CD-ROM":
##        installer_cfgfile.write(
##            "baseurl cdrom:/dists/progeny/main/install-i386/minimal.tar.gz\n")
##        installer_cfgfile.write("cdinst")
##    elif setup_source == "Network":
##        installer_cfgfile.write(
##            "baseurl http://www.indy.progeny.com/~jlicquia/minimal.tar.gz\n")

##        installer_srclist = open("/etc/sources.lst")
##        installer_srclist.write(
##            "deb http://www.indy.progeny.com/progeny progeny main contrib non-free\n")
##        installer_srclist.close()
##    else:
##        print "Error!"

##    installer_cfgfile.close()


    #
    # Extract minimal system:
    #

    progress = wtree.get_widget("prepare-installing")
    progress.set_format_string("Preparing to install...")
    progress.set_value(0)
    while (events_pending()):
        mainiteration()

    install_count = 0
    install_total = 0

    # Count the number of files that are to be extracted:
    pipe = os.popen("( cd %s && gunzip -c %s | tar tvf - )"
                    % (TARGET, MINIMAL), "r")
    while pipe.readline():
        install_total = install_total + 1

    # Configure the initial state of the progress bar appropriately:
    progress.set_adjustment(GtkAdjustment(0, 0, install_total, 1, 10, 10))
    progress.set_format_string("Installing (%P%%)")
    while (events_pending()):
        mainiteration()

    # Extract the minimal system:
    pipe = os.popen("( cd %s && gunzip -c %s | tar xvf - )"
                    % (TARGET, MINIMAL), "r")
    while pipe.readline():
        install_count = install_count + 1
        progress.set_value(install_count)
        while (events_pending()):
            mainiteration()

    progress.set_format_string("Done.")
    while (events_pending()):
        mainiteration()

    # Write out package set selections:
    pkgsel = open("/etc/pkgsel.cfg", "a")
    pkgsel.write("minimal\n")
    pkgsel.write("standard\n")
    pkgsel.write("gnome\n")
    pkgsel.write("netscape\n")
    pkgsel.write("x11\n")
    pkgsel.close()

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


def setup_print_state(state):
    if state["type"] == parted.PED_PARTITION_PRIMARY:
        print "Type: Primary"
    elif state["type"] == parted.PED_PARTITION_LOGICAL:
        print "Type: Logical"
    else:
        print "Type: Unknown"

    if state["bootable"]:
        print "Bootable"

    sectors = state["sectors"]
    sector_size = state["sector_size"]
    size = setup_return_size_string(sectors, sector_size)
    print "Size: " + size

    fstype = setup_return_fstype_string(state["fstype"])
    print "File System Type: " + fstype

    if state["format"]:
        print "Format"

    print "Mount Point: " + state["mntpnt"]


def setup_update_disk_list(list):
    list.freeze()
    list.clear()
    row = 0
    for device in parted.get_devices():
        sectors = device.get_length()
        sector_size = device.get_sector_size()
        path = device.get_path()
        size = setup_return_size_string(sectors, sector_size)
        model = device.get_model()
        list.append([ path, size, model ])
        list.set_row_data(row, device)
        row = row + 1
    list.thaw()


def setup_update_part_tree(tree):
    global setup_format
    global setup_mntpnt
    tree.freeze()
    tree.clear()
    for disk in setup_disks:
        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
        first = TRUE
        for part in parts:
            geom = part.get_geom()
            disk = geom.get_disk()
            minor = part.get_num()
            sectors = geom.get_length()
            sector_size = device.get_sector_size()
            if part.get_type() & parted.PED_PARTITION_METADATA:
                continue
            # Don't display extended partitions:
            # XXX: We display them for now, so we can make sure
            # the right thing happens.
            #if part.get_type() & parted.PED_PARTITION_EXTENDED:
            #    continue

            # If there are partitions allocated, parted always seems
            # to say there is a small extent of free space at the end
            # of the disk, even when there isn't any free space there.
            # To avoid confusion, we assume that if the last partition
            # is free space, and it's less than 8MB in size, then it
            # shouldn't be displayed, because there isn't really
            # any free space there.  Since we reversed PARTS earlier,
            # the last partition is the first inserted.
            if first:
                first = FALSE
                if part.get_type() & parted.PED_PARTITION_FREESPACE \
                   and setup_sectors_to_mb(sectors, sector_size) < 8:
                    continue
            if minor != -1:
                path = device.get_path() + str(minor)
                if part.get_fs_type() != None:
                    fstype = setup_return_fstype_string(part.get_fs_type())
                # XXX: Eventually, we won't display extended partitions.
                # When that happens, the next 2 lines should be removed.
                elif part.get_type() == parted.PED_PARTITION_EXTENDED:
                    fstype = "Extended"
                else:
                    fstype = ""
                mntpnt = setup_mntpnt[path]
                if setup_format[path]:
                    status = "To be formatted"
                elif setup_mntpnt[path] != "":
                    status = "To be mounted"
                elif part.get_bootable():
                    status = "Bootable"
                else:
                    status = ""
            else:
                path = ""
                fstype = "Free Space"
                mntpnt = ""
                status = ""
            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()


##def on_method_next(widget, ignored):
##    global setup_method
##    if wtree.get_widget("method-standard").get_active():
##        setup_method = "Standard"
##    elif wtree.get_widget("method-custom").get_active():
##        setup_method = "Custom"
##    return FALSE


##def on_source_next(widget, ignored):
##    global setup_source
##    if wtree.get_widget("source-cdrom").get_active():
##        setup_source="CD-ROM"
##    elif wtree.get_widget("source-disk").get_active():
##        setup_source="Hard Disk"
##    elif wtree.get_widget("source-network").get_active():
##        setup_source="Network"
##    return FALSE


def on_target_next(widget, ignored):
    druid = wtree.get_widget("druid")
    if wtree.get_widget("target-whole").get_active():
        druid.set_page(wtree.get_widget("disk"))
    elif wtree.get_widget("target-free").get_active():
        druid.set_page(wtree.get_widget("finish"))
    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.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(_, row, column, event):
    global device
    global list
    device = list.get_row_data(row)
    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):
    druid = wtree.get_widget("druid")
    druid.set_page(wtree.get_widget("confirm"))
    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.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):
    global part
    part = tree.node_get_row_data(node)
    geom = part.get_geom()
    disk = geom.get_disk()
    minor = part.get_num()
    if (minor > 0):
        delete = wtree.get_widget("part-delete")
        delete.set_sensitive(TRUE)
        edit = wtree.get_widget("part-edit")
        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)


# XXX: Bugs/suggestions:
# - Pick reasonable mount point as default

def on_parted_create(widget):
    global part

    if part == None:
        return

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

    state = {}

    # Initialize defaults values for the Edit partition dialog:

    # Check to see if there is a primary partition slot available, and
    # if there is, default to primary partition. If not, check to
    # see if an extended partition exists, and if one does, default to
    # logical partition.
    if setup_num_primary_partitions(disk) < 4:
        state["type"] = parted.PED_PARTITION_PRIMARY
    elif setup_has_extended_partition(disk):
        state["type"] = parted.PED_PARTITION_LOGICAL
    #else:
    #   XXX: error (should never get here, but we should check anyway
    #   and pop up an error dialog if we do, because it's a bug)
    state["bootable"] = 0
    state["sectors"] = geom.get_length()
    state["sector_size"] = device.get_sector_size()
    state["fstype"] = parted.file_system_type_get("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)

    # Apply changes:

    # If the user requests to create a logical partition, make sure
    # an extended partition exists, and if one doesn't, create one.

    # XXX: Maximizing the extended partition prior to creating the
    # partition then minimizing it afterward doesn't work--the call
    # to disk.maximize_partition causes python to crash, and I'm
    # not sure why yet. The idea was to make the extended partition
    # as large as possible, giving the user maximum flexibility in
    # creating logical partitions, then making the extended
    # partition as small as possible, giving the user maximum
    # flexibility in creating primary partitions the next time.  As it
    # is now, the user can create only one logical partition. There
    # are probably other problems with this. At worst, we'll simply
    # make the user have to explicitly create extended partitions
    # again, but it's worth a bit more effort to try to get this to
    # work.
    if state["type"] == parted.PED_PARTITION_LOGICAL:
        if not setup_has_extended_partition(disk):
            extended = parted.Partition(disk, parted.PED_PARTITION_EXTENDED,
                                        None, geom.get_start(),
                                        geom.get_start() + state["sectors"]
                                        - 1)
            disk.add_partition(extended)
        #disk.maximize_partition(extended)

    # Create the partition, as per the user:
    part = parted.Partition(disk, state["type"], state["fstype"],
                            geom.get_start(),
                            geom.get_start() + state["sectors"] - 1)
    disk.add_partition(part)
    part.set_fs_type(state["fstype"])

    path = device.get_path() + str(part.get_num())
    setup_format[path] = state["format"]
    setup_mntpnt[path] = state["mntpnt"]

    #disk.minimize_extended_partition()

    setup_update_part_tree(tree)

    # If the root file system has been defined, the user may proceed.
    if state["mntpnt"] == "/":
        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 = device.get_path() + str(part.get_num())

    disk.delete_partition(part)

    mntpnt = setup_mntpnt[path]

    del setup_format[path]
    del setup_mntpnt[path]

    setup_update_part_tree(tree)

    # If the root file system was on this partition, the user may no
    # longer proceed.
    if mntpnt == "/":
        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 = device.get_path() + str(part.get_num())

    # 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["fstype"] = part.get_fs_type()
    old_state["format"] = setup_format[path]
    old_state["mntpnt"] = setup_mntpnt[path]

    # Pop up the Edit partition dialog and get values from the user:
    new_state = setup_edit_dialog(old_state, FALSE, FALSE)

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

    #print "OLD STATE:"
    #setup_print_state(old_state)
    #print "NEW 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[path] = new_state["format"]
    if new_state["mntpnt"] != old_state["mntpnt"]:
        setup_mntpnt[path] = new_state["mntpnt"]

    setup_update_part_tree(tree)

    # If the root file system has been defined, the user may proceed.
    if new_state["mntpnt"] == "/":
        druid = wtree.get_widget("druid")
        druid.set_buttons_sensitive(TRUE, TRUE, TRUE)
    # If the root file system was on this partition, and a new mount
    # point was selected, the user may no longer proceed.
    if old_state["mntpnt"] == "/" and new_state["mntpnt"] != "/":
        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:
    setup_reopen_all_disks()
    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):
##    global setup_target
##    setup_target = "Custom"
##    druid = wtree.get_widget("druid")
##    druid.set_page(wtree.get_widget("confirm"))
    wtree_dialog_proceed = GladeXML(GLADE, "dialog-proceed")
    dialog = wtree_dialog_proceed.get_widget("dialog-proceed")
    result = dialog.run_and_close()
    if result == OK:
        setup_prepare_system()
    return TRUE


##def on_confirm_prepare(widget, ignored):
##    confirm_method = wtree.get_widget("confirm-method")
##    confirm_method.set_text(setup_method)
##    confirm_source = wtree.get_widget("confirm-source")
##    confirm_source.set_text(setup_source)
##    confirm_target = wtree.get_widget("confirm-target")
##    confirm_target.set_text(setup_target)


### XXX: Make sure this preserves previous answers/configuration..
##def on_confirm_back(widget, ignored):
##    druid = wtree.get_widget("druid")
##    druid.set_page(wtree.get_widget("method"))
##    return TRUE


##def on_confirm_next(widget, ignored):
##    setup_prepare_system()
##    return TRUE


def on_druid_cancel(widget):
    wtree_dialog_reboot = GladeXML(GLADE, "dialog-reboot")
    dialog = wtree_dialog_reboot.get_widget("dialog-reboot")
    result = dialog.run_and_close()
    if result == OK:
        os.system("reboot")


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_finish(widget, ignored):
    mainquit()
    return TRUE



dic = { "on_installer_delete_event": mainquit,
        "on_installer_destroy":      mainquit,
        "on_start_next":             on_start_next,
        "on_druid_cancel":           on_druid_cancel,
##        "on_method_next":            on_method_next,
##        "on_source_next":            on_source_next,
        "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_confirm_prepare":        on_confirm_prepare,
##        "on_confirm_back":           on_confirm_back,
##        "on_confirm_next":           on_confirm_next,
        "on_finish_finish":          on_finish_finish }

wtree.signal_autoconnect(dic)

installer = wtree.get_widget("installer")

# XXX: These are not currently supported, so grey them out:
source_disk = wtree.get_widget("source-disk")
source_disk.set_sensitive(FALSE)
source_network = wtree.get_widget("source-network")
source_network.set_sensitive(FALSE)
target_whole = wtree.get_widget("target-whole")
target_whole.set_sensitive(FALSE)
target_free = wtree.get_widget("target-free")
target_free.set_sensitive(FALSE)

# Make the cursor look a little nicer:
installer.get_window().set_cursor(cursor_new(GDK.LEFT_PTR))

installer.show()

mainloop()

sys.exit(0)
