#
# partition_gui.py: allows the user to choose how to partition their disks
#
# Copyright (C) 2001, 2002  Red Hat, Inc.  All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Author(s): Matt Wilson <msw@redhat.com>
#            Michael Fulbright <msf@redhat.com>
#

import os
import gobject
import gtk
import gtk.glade
try:
    import gnomecanvas
except ImportError:
    import gnome.canvas as gnomecanvas
import pango
import gui
import parted
import string
import types
import copy
from decimal import Decimal

import storage
from iw_gui import *
from flags import flags

import datacombo
import lvm_dialog_gui as l_d_g
import raid_dialog_gui as r_d_g
import partition_dialog_gui as p_d_g

from partIntfHelpers import *
from constants import *
from partition_ui_helpers_gui import *
from storage.partitioning import doPartitioning
from storage.devicelibs import lvm
from storage.devices import devicePathToName, PartitionDevice
from storage.devices import deviceNameToDiskByPath
from storage.errors import DeviceNotFoundError

import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z)

import logging
log = logging.getLogger("anaconda")

STRIPE_HEIGHT = 35.0
LOGICAL_INSET = 3.0
TREE_SPACING = 2

# XXX hack but will work for now
if gtk.gdk.screen_width() > 640:
    CANVAS_WIDTH = 490
else:
    CANVAS_WIDTH = 390
CANVAS_HEIGHT = 200

MODE_ADD = 1
MODE_EDIT = 2

class Slice:
    """Class representing a slice of a stripe.

    parent -- the stripe that the slice belongs too.
    text -- what will appear in the slice
    type -- either SLICE or SUBSLICE
    xoffset -- start percentage
    xlength -- a length percentage
    dcCB -- function that is called on a double click.
    cCB -- function that is called when one click (selected)
    sel_col -- color when selected
    unsel_col -- color when unselected
    obj -- some python object that is related to this slice.
    selected -- initial state of slice.
    """
    SLICE = 0
    SUBSLICE = 1
    CONTAINERSLICE = 2

    def __init__(self, parent, text, type, xoffset, xlength, dcCB=lambda: None,
            cCB=lambda x: None, sel_col="cornsilk1", unsel_col="white",
            obj = None, selected = False):
        self.text = text
        self.type = type
        self.xoffset = xoffset
        self.xlength = xlength
        self.parent = parent
        self.dcCB = dcCB
        self.cCB = cCB
        self.sel_col = sel_col
        self.unsel_col = unsel_col
        self.obj = obj
        self.selected = selected

    def eventHandler(self, widget, event):
        if event.type == gtk.gdk.BUTTON_PRESS:
            if event.button == 1:
                self.select()
                self.cCB(self.obj)
        elif event.type == gtk.gdk._2BUTTON_PRESS:
            #self.select()
            self.dcCB()

        return True

    def putOnCanvas(self):
        pgroup = self.parent.getGroup()
        self.group = pgroup.add(gnomecanvas.CanvasGroup)
        self.box = self.group.add(gnomecanvas.CanvasRect)
        self.group.connect("event", self.eventHandler)
        canvas_text = self.group.add(gnomecanvas.CanvasText,
                                    font="sans", size_points=8)

        xoffset = self.xoffset * CANVAS_WIDTH
        xlength = self.xlength * CANVAS_WIDTH

        if self.type == Slice.SUBSLICE:
            yoffset = 0.0 + LOGICAL_INSET
            yheight = STRIPE_HEIGHT - (LOGICAL_INSET * 2)
            texty = 0.0
        else:
            yoffset = 0.0
            yheight = STRIPE_HEIGHT
            texty = LOGICAL_INSET

        if self.selected:
            fill_color = self.sel_col
        else:
            fill_color = self.unsel_col

        self.group.set(x=xoffset, y=yoffset)
        self.box.set(x1=0.0, y1=0.0, x2=xlength,
                     y2=yheight, fill_color=fill_color,
                     outline_color='black', width_units=1.0)
        canvas_text.set(x=2.0, y=texty + 2.0, text=self.text,
                            fill_color='black',
                            anchor=gtk.ANCHOR_NW, clip=True,
                            clip_width=xlength-1, clip_height=yheight-1)

    def shutDown(self):
        self.parent = None
        if self.group:
            self.group.destroy()
            self.group = None

    def select(self):
        for slice in self.parent.slices:
            slice.deselect()
        self.selected = True

        if self.group and self.box:
            if self.type != Slice.CONTAINERSLICE:
                self.group.raise_to_top()
            self.box.set(outline_color="red")
            self.box.set(fill_color=self.sel_col)

    def deselect(self):
        self.selected = False
        if self.box:
            self.box.set(outline_color="black", fill_color=self.unsel_col)

class Stripe:
    """
    canvas -- the canvas where everything goes
    text -- the text that will appear on top of the stripe
    yoff -- its the position in the y axis where this stripe should be drawn
    dcCB -- function that should be called on a double click
    obj -- some python object that is related to this stripe

    """
    def __init__(self, canvas, text, dcCB, obj = None):
        self.canvas_text = None
        self.canvas = canvas
        self.text = text
        self.group = None
        self._slices = []
        self.dcCB = dcCB
        self.selected = None
        self.obj = obj

    def putOnCanvas(self, yoff):
        """
        returns the yposition after drawhing this stripe.

        """
        # We set the text for the stripe.
        self.canvas_text = self.canvas.root().add(gnomecanvas.CanvasText,
                x=0.0, y=yoff, font="sans", size_points=9)
        self.canvas_text.set(text=self.text, fill_color='black',
                anchor=gtk.ANCHOR_NW, weight=pango.WEIGHT_BOLD)

        (xxx1, yyy1, xxx2, yyy2) =  self.canvas_text.get_bounds()
        textheight = yyy2 - yyy1 + 2
        self.group = self.canvas.root().add(gnomecanvas.CanvasGroup,
                                       x=0, y=yoff+textheight)

        self.group.add(gnomecanvas.CanvasRect, x1=0.0, y1=0.0, x2=CANVAS_WIDTH,
                  y2=STRIPE_HEIGHT, fill_color='green',
                  outline_color='grey71', width_units=1.0)
        self.group.lower_to_bottom()

        # We paint all the container slices first.  So the contained slices
        # actually show up.
        for slice in [s for s in self.slices if s.type == Slice.CONTAINERSLICE]:
            slice.putOnCanvas()
        # After painting the containers we paint the rest.
        for slice in [s for s in self.slices if s.type != Slice.CONTAINERSLICE]:
            slice.putOnCanvas()

        # 10 is a separator space.
        return yoff + STRIPE_HEIGHT+textheight+10

    def shutDown(self):
        for slice in self.slices:
            slice.shutDown()
        self._slices = []

        if self.canvas_text:
            self.canvas_text.destroy()

        if self.group:
            self.group.destroy()
            self.group = None

    def getGroup(self):
        return self.group

    @property
    def slices(self):
        return self._slices

    def addSlice(self, new_slice):
        # check to see if they overlap.
        for slice in self.slices:
            # Container slices and subslices can overlap.
            if new_slice.type+slice.type == Slice.CONTAINERSLICE+Slice.SUBSLICE:
                continue

            if new_slice.xoffset > slice.xoffset \
                    and new_slice.xoffset < slice.xoffset + slice.xlength:
                # there is a colission, we cannot add.
                return

        self._slices.append(new_slice)

    def getSelectedSlice(self):
        for slice in self.slices:
            if slice.selected:
                return slice
        return None

class StripeGraph:
    """ This class will only handle one stripe."""

    __canvas = None
    def __init__(self):
        self.stripe = None
        self.next_ypos = 0.0

    def __del__(self):
        self.shutDown()

    def shutDown(self):
        if self.stripe:
            self.stripe.shutDown()
            self.stripe = None

        self.next_ypos = 0.0

    @classmethod
    def getCanvas(cls):
        if not StripeGraph.__canvas:
            StripeGraph.__canvas = gnomecanvas.Canvas()
        return StripeGraph.__canvas

    def setDisplayed(self, obj):
        # Check to see if we already have the correct obj displayed.
        if self.getDisplayed() and self.getDisplayed().obj == obj:
            return

        if self.stripe:
            self.stripe.shutDown()

        self.stripe = self._createStripe(obj)
        self.stripe.putOnCanvas(0)

        # Trying to center the picture.
        apply(self.getCanvas().set_scroll_region, self.getCanvas().root().get_bounds())

    def getDisplayed(self):
        return self.stripe

    def selectSliceFromObj(self, obj):
        """Search for obj in the slices """
        stripe = self.getDisplayed()
        if not stripe:
            return

        for slice in stripe.slices:
            # There is a part object in each slice.
            if not slice.obj:
                continue

            if obj == slice.obj and not slice.selected:
                slice.select()
                break

    def _createStripe(self, obj):
        #This method needs to be overridden
        pass

    def getSelectedSlice(self):
        return self.stripe.getSelectedSlice()


class DiskStripeGraph(StripeGraph):
    """Handles the creation of a bar view for the 'normal' devies.

    storage -- the storage object

    cCB -- call back function used when the user clicks on a slice. This function
           is passed a device object when its executed.
    dcCB -- call back function used when the user double clicks on a slice.
    drive -- drive to display
    """
    def __init__(self, storage, drive=None, cCB=lambda x:None, dcCB=lambda:None):
        StripeGraph.__init__(self)
        self.storage = storage
        self.cCB = cCB
        self.dcCB = dcCB
       # Define the default colors per partition type.
        self.part_type_colors = \
                {"sel_logical": "cornsilk1", "unsel_logical": "white",
                 "sel_extended": "cornsilk1", "unsel_extended": "white",
                 "sel_normal": "cornsilk1", "unsel_normal": "white",
                 "sel_freespace": "grey88", "unsel_freespace": "grey88"}
        if drive:
            self.setDisplayed(drive)

    def _createStripe(self, drive):
        # Create the stripe
        drivetext = _("Drive %(drive)s (%(size)-0.f MB) (Model: %(model)s)") \
                    % {'drive': drive.path,
                       'size': drive.size,
                       'model': drive.model}
        stripe = Stripe(self.getCanvas(), drivetext, self.dcCB, obj = drive)

        # Free Extended Calculation
        # Free slice/partition in the extended partition "free space".  If there
        # is space between the last logical partition and the ending of the
        # extended partition we create a "free space" in the extended part.
        # Create the slices.

        # These offsets are where the partition/slices end. 0<offset<1
        last_logical_offset = None
        last_extended_offset = None

        for part in drive.format.partedDisk.getFreeSpacePartitions() \
                + [d for d in drive.format.partitions]:
            if part.getSize(unit="MB") <= 1.0:
                continue

            # Create the start and length for the slice.
            xoffset = (Decimal(str(part.geometry.start))
                        / Decimal(str(drive.partedDevice.length)))
            xlength = (Decimal(str(part.geometry.length))
                        / Decimal(str(drive.partedDevice.length)))

            if part.type == parted.PARTITION_LOGICAL:
                partstr = "%s\n%.0f MB" % (part.path, float(part.getSize()))
                stype = Slice.SUBSLICE
                unsel_col = self.part_type_colors["unsel_logical"]
                sel_col = self.part_type_colors["sel_logical"]

                # Free Extended Calculation
                if last_logical_offset == None:
                    last_logical_offset = xoffset + xlength
                elif last_logical_offset < xoffset + xlength:
                    last_logical_offset = xoffset + xlength

            elif part.type == parted.PARTITION_FREESPACE:
                partstr = "%s\n%.0f MB" % (_("Free"), float(part.getSize()))
                stype = Slice.SLICE
                unsel_col = self.part_type_colors["unsel_freespace"]
                sel_col = self.part_type_colors["sel_freespace"]

            elif part.type == parted.PARTITION_EXTENDED:
                partstr = ""
                stype = Slice.CONTAINERSLICE
                unsel_col = self.part_type_colors["unsel_extended"]
                sel_col = self.part_type_colors["sel_extended"]

                # Free Extended Calculation
                last_extended_offset = xoffset + xlength

            elif part.type == parted.PARTITION_NORMAL:
                partstr = "%s\n%.0f MB" % (part.path, float(part.getSize()))
                stype = Slice.SLICE
                unsel_col = self.part_type_colors["unsel_normal"]
                sel_col = self.part_type_colors["sel_normal"]

            else:
                # We don't really want to draw anything in this case.
                continue

            # We need to use the self.storage objects not the partedDisk ones.
            # The free space has not storage object.
            if part.type != parted.PARTITION_FREESPACE:
                partName = devicePathToName(part.getDeviceNodeName())
                o_part = self.storage.devicetree.getDeviceByName(partName)
            else:
                o_part = None

            slice = Slice(stripe, partstr, stype, xoffset, xlength,
                    dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col,
                    unsel_col = unsel_col, obj = o_part)
            stripe.addSlice(slice)

        # Free Extended Calculation
        if (last_logical_offset != None and last_extended_offset != None) \
                and last_logical_offset < last_extended_offset:
            # We must create a "free extended" slice
            stype = Slice.SUBSLICE
            unsel_col = self.part_type_colors["unsel_freespace"]
            sel_col = self.part_type_colors["sel_freespace"]
            xoffset = last_logical_offset
            xlength = last_extended_offset - last_logical_offset
            # don't add a slice for free regions of <= 1MB
            size = Decimal(str(drive.size)) * xlength
            if size > 1:
                slcstr = "%s\n%.0f MB" % (_("Free"), size)

                slice = Slice(stripe, slcstr, stype, xoffset, xlength,
                        dcCB = self.dcCB, cCB = self.cCB, sel_col=sel_col,
                        unsel_col=unsel_col)
                stripe.addSlice(slice)

        return stripe

class LVMStripeGraph(StripeGraph):
    """
    storage -- the storage object

    cCB -- call back function used when the user clicks on a slice. This function
           is passed a device object when its executed.
    dcCB -- call back function used when the user double clicks on a slice.
    vg -- volume group to display
    """
    def __init__(self, storage, vg=None, cCB=lambda x:None, dcCB=lambda:None):
        StripeGraph.__init__(self)
        self.storage = storage
        self.cCB = cCB
        self.dcCB = dcCB
       # Define the default colors per partition type.
        self.part_type_colors = \
                {"sel_lv": "cornsilk1", "unsel_lv": "white",
                 "sel_freespace": "grey88", "unsel_freespace": "grey88"}
        if vg:
            self.setDisplayed(vg)

    def _createStripe(self, vg):
        # Create the stripe
        vgtext = _("LVM Volume Group %s (%-0.f MB)") % (vg.name, vg.size)
        stripe = Stripe(self.getCanvas(), vgtext, self.dcCB, obj = vg)

        # Create the slices.
        # Since se don't have a start and length like in the partitions, we
        # put all the LVs next to each other and put the free space at the end.
        curr_offset = Decimal(0)
        for lv in vg.lvs:
            lvstr = "%s\n%.0f MB" % (lv.name, float(lv.size))
            stype = Slice.SLICE
            sel_col = self.part_type_colors["sel_lv"]
            unsel_col = self.part_type_colors["unsel_lv"]

            #xoffset = float(curr_offset) / float(vg.size)
            xoffset = curr_offset
            xlength = Decimal(str(lv.size)) / Decimal(str(vg.size))

            slice = Slice(stripe, lvstr, stype, xoffset, xlength,
                    dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col,
                    unsel_col = unsel_col, obj = lv)
            stripe.addSlice(slice)

            curr_offset += xlength

        # We add the free space if there is any space left.
        if curr_offset < 1:
            #freestr = _("Free")
            stype = Slice.SLICE
            sel_col = self.part_type_colors["sel_freespace"]
            unsel_col = self.part_type_colors["unsel_freespace"]

            xoffset = curr_offset
            xlength = Decimal(1 - curr_offset)

            # with the xlength we give an approximate size
            freestr = "%s\n%.0f MB" % (_("Free"), Decimal(str(vg.size)) * xlength)

            # We append no object.
            slice = Slice(stripe, freestr, stype, xoffset, xlength,
                    dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col,
                    unsel_col = unsel_col)

            stripe.addSlice(slice)

        return stripe

class MDRaidArrayStripeGraph(StripeGraph):
    """
    storage -- the storage object

    cCB -- call back function used when the user clicks on a slice. This function
           is passed a device object when its executed.
    dcCB -- call back function used when the user double clicks on a slice.
    md -- RAID device to display.
    """
    def __init__(self, storage, md=None, cCB=lambda x:None, dcCB=lambda:None):
        StripeGraph.__init__(self)
        self.storage = storage
        self.cCB = cCB
        self.dcCB = dcCB
        self.part_type_colors = \
                {"sel_md": "cornsilk1", "unsel_md": "white"}
        if md:
            self.setDisplayed(md)

    def _createStripe(self, md):
        mdtext = _("MD RAID ARRAY %s (%-0.f MB)") % (md.path, md.size)
        stripe = Stripe(self.getCanvas(), mdtext, self.dcCB, obj = md)

        # Since we can't really create subslices with md devices we will only
        # show the md device size in the bar.
        mdstr = "%s\n%.0f MB" % (md.path, float(md.size))
        stype = Slice.SLICE
        sel_col = self.part_type_colors["sel_md"]
        unsel_col = self.part_type_colors["unsel_md"]
        xoffset = 0
        xlength = 1

        slice = Slice(stripe, mdstr, stype, xoffset, xlength,
                dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col,
                unsel_col = unsel_col, obj = md)
        stripe.addSlice(slice)

        return stripe

class MessageGraph:
    def __init__(self, canvas, message):
        self.canvas = canvas
        self.message = message
        self.canvas_text = None

    def display(self):
        if self.canvas_text != None:
            # This means that its already displayed.
            return

        self.canvas_text = self.canvas.root().add(gnomecanvas.CanvasText,
                x=0.0, y=20, font="sans", size_points=16)
        self.canvas_text.set(text=self.message, fill_color='black',
                anchor=gtk.ANCHOR_CENTER, weight=pango.WEIGHT_BOLD)

        # Trying to center the picture.
        apply(self.canvas.set_scroll_region, self.canvas.root().get_bounds())

    def destroy(self):
        if self.canvas_text:
            self.canvas_text.destroy()
            self.canvas_text = None

class DiskTreeModelHelper:
    def __init__(self, model, columns, iter):
        self.model = model
        self.iter = iter
        self.columns = columns

    def __getitem__(self, key):
        if type(key) == types.StringType:
            key = self.columns[key]
        try:
            return self.model.get_value(self.iter, key)
        except:
            return None

    def __setitem__(self, key, value):
        if type(key) == types.StringType:
            key = self.columns[key]
        self.model.set_value(self.iter, key, value)

class DiskTreeModel(gtk.TreeStore):
    isLeaf = -3
    isFormattable = -2
    
    # format: column header, type, x alignment, hide?, visibleKey
    titles = ((N_("Device"), gobject.TYPE_STRING, 0.0, 0, 0),
              (N_("Label"), gobject.TYPE_STRING, 0.0, 1, 0),
              (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, 0),
              (N_("Mount Point"), gobject.TYPE_STRING, 0.0, 0, isLeaf),
              (N_("Type"), gobject.TYPE_STRING, 0.0, 0, 0),
              (N_("Format"), gobject.TYPE_OBJECT, 0.5, 0, isFormattable),
              ("", gobject.TYPE_STRING, 0.0, 0, 0),
              # the following must be the last two
              ("IsLeaf", gobject.TYPE_BOOLEAN, 0.0, 1, 0),
              ("IsFormattable", gobject.TYPE_BOOLEAN, 0.0, 1, 0),
              ("PyObject", gobject.TYPE_PYOBJECT, 0.0, 1, 0))
    
    def __init__(self):
	self.hiddenPartitions = []
        self.titleSlot = {}
        i = 0
        types = [self]
        self.columns = []
        for title, kind, alignment, hide, key in self.titles:
            self.titleSlot[title] = i
            types.append(kind)
            if hide:
                i += 1
                continue
            elif kind == gobject.TYPE_OBJECT:
                renderer = gtk.CellRendererPixbuf()
                propertyMapping = {'pixbuf': i}
            elif kind == gobject.TYPE_BOOLEAN:
                renderer = gtk.CellRendererToggle()
                propertyMapping = {'active': i}
            elif (kind == gobject.TYPE_STRING or
                  kind == gobject.TYPE_INT):
                renderer = gtk.CellRendererText()
                propertyMapping = {'markup': i}

            # wire in the cells that we want only visible on leaf nodes to
            # the special leaf node column.
            if key < 0:
                propertyMapping['visible'] = len(self.titles) + key
                
            renderer.set_property('xalign', alignment)
	    if title == "Mount Point":
		title = _("Mount Point/\nRAID/Volume")
	    elif title == "Size (MB)":
		title = _("Size\n(MB)")
            elif title != "":
                title = _(title)
            col = apply(gtk.TreeViewColumn, (title, renderer),
                        propertyMapping)
	    col.set_alignment(0.5)
	    if kind == gobject.TYPE_STRING or kind == gobject.TYPE_INT:
		col.set_property('sizing', gtk.TREE_VIEW_COLUMN_AUTOSIZE)
            self.columns.append(col)
            i += 1

        apply(gtk.TreeStore.__init__, types)

        self.view = gtk.TreeView(self)
        # append all of the columns
        map(self.view.append_column, self.columns)

    def getTreeView(self):
        return self.view

    def selectRowFromObj(self, obj, iter=None):
        """Find the row in the tree containing obj and select it.

        obj -- the object that we are searching
        iter -- an iter from the tree. If None, get the first one.

        Returns the iter where obj was found.  None otherwise.
        """
        retval = None
        r_obj = None
        #FIXME: watch out for hidden rows.

        if not iter:
            iter = self.get_iter_first()

        while iter:
            # r_obj -> (row object)
            r_obj = self[iter]["PyObject"]

            if obj and r_obj == obj:
                # We have fond our object, select this row and break.
                selection = self.view.get_selection()
                if selection is not None:
                    selection.unselect_all()
                    selection.select_iter(iter)

                # Make sure the tree view shows what we have selected.
                path = self.get_path(iter)
                col = self.view.get_column(0)
                self.view.set_cursor(path, col, False)
                self.view.scroll_to_cell(path, col, True, 0.5, 0.5)
                retval = iter
                break

            if self.iter_has_child(iter):
                # Call recursively if row has children.
                rv = self.selectRowFromObj(obj, iter=self.iter_children(iter))
                if rv != None:
                    retval = rv
                    break

            iter = self.iter_next(iter)

        return iter

    def getCurrentDevice(self):
        """ Return the device representing the current selection,
            None otherwise.
        """
        selection = self.view.get_selection()
        model, iter = selection.get_selected()
        if not iter:
            return None

        return model[iter]['PyObject']

    def getCurrentDeviceParent(self):
        """ Return the parent of the selected row.  Returns an iter.
            None if there is no parent.
        """
        selection = self.view.get_selection()
        model, iter = selection.get_selected()
        if not iter:
            return None

        return model.iter_parent(iter)

    def resetSelection(self):
        pass

    def clear(self):
        selection = self.view.get_selection()
        if selection is not None:
            selection.unselect_all()
        gtk.TreeStore.clear(self)

    def __getitem__(self, iter):
        if type(iter) == gtk.TreeIter:
            return DiskTreeModelHelper(self, self.titleSlot, iter)
        raise KeyError, iter


class PartitionWindow(InstallWindow):
    def __init__(self, ics):
	InstallWindow.__init__(self, ics)
        ics.setTitle(_("Partitioning"))
        ics.setNextEnabled(True)
        self.parent = ics.getICW().window

    def quit(self):
        pass

    def presentPartitioningComments(self,title, labelstr1, labelstr2, comments,
				    type="ok", custom_buttons=None):

        if flags.autostep:
            return 1

        win = gtk.Dialog(title)
        gui.addFrame(win)
        
        if type == "ok":
            win.add_button('gtk-ok', 1)
	    defaultchoice = 0
        elif type == "yesno":
            win.add_button('gtk-no', 2)
            win.add_button('gtk-yes', 1)
	    defaultchoice = 1
	elif type == "continue":
            win.add_button('gtk-cancel', 0)
            win.add_button(_("Continue"), 1)
	    defaultchoice = 1
	elif type == "custom":
	    rid=0

	    for button in custom_buttons:
		widget = win.add_button(button, rid)
		rid = rid + 1

            defaultchoice = rid - 1
	    
        image = gtk.Image()
        image.set_from_stock('gtk-dialog-warning', gtk.ICON_SIZE_DIALOG)
        hbox = gtk.HBox(False, 9)
	al=gtk.Alignment(0.0, 0.0)
	al.add(image)
        hbox.pack_start(al, False)

        buffer = gtk.TextBuffer(None)
        buffer.set_text(comments)
        text = gtk.TextView()
        text.set_buffer(buffer)
        text.set_property("editable", False)
        text.set_property("cursor_visible", False)
        text.set_wrap_mode(gtk.WRAP_WORD)
        
        sw = gtk.ScrolledWindow()
        sw.add(text)
	sw.set_size_request(400, 200)
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        sw.set_shadow_type(gtk.SHADOW_IN)
        
        info1 = gtk.Label(labelstr1)
        info1.set_line_wrap(True)
        info1.set_size_request(400, -1)

        info2 = gtk.Label(labelstr2)
        info2.set_line_wrap(True)
        info2.set_size_request(400, -1)
        
        vbox = gtk.VBox(False, 9)

	al=gtk.Alignment(0.0, 0.0)
	al.add(info1)
        vbox.pack_start(al, False)
	
        vbox.pack_start(sw, True, True)

	al=gtk.Alignment(0.0, 0.0)
	al.add(info2)
        vbox.pack_start(al, True)
	
        hbox.pack_start(vbox, True, True)

        win.vbox.pack_start(hbox)
        win.set_position(gtk.WIN_POS_CENTER)
        win.set_default_response(defaultchoice)
        win.show_all()
        rc = win.run()
        win.destroy()
        return rc
        
    def getNext(self):
        (errors, warnings) = self.storage.sanityCheck()
        if errors:
            labelstr1 =  _("The partitioning scheme you requested "
                           "caused the following critical errors.")
            labelstr2 = _("You must correct these errors before "
                          "you continue your installation of "
                          "%s.") % (productName,)

            commentstr = string.join(errors, "\n\n")
            
            self.presentPartitioningComments(_("Partitioning Errors"),
                                             labelstr1, labelstr2,
                                             commentstr, type="ok")
            raise gui.StayOnScreen
        
        if warnings:
            # "storage configuration"
            labelstr1 = _("The partitioning scheme you requested "
                          "generated the following warnings.")
            labelstr2 = _("Would you like to continue with "
                         "your requested partitioning "
                         "scheme?")
            
            commentstr = string.join(warnings, "\n\n")
            rc = self.presentPartitioningComments(_("Partitioning Warnings"),
                                                  labelstr1, labelstr2,
                                                  commentstr,
						  type="yesno")
            if rc != 1:
                raise gui.StayOnScreen

        formatWarnings = getPreExistFormatWarnings(self.storage)
        if formatWarnings:
            labelstr1 = _("The following pre-existing devices have been "
                          "selected to be formatted, destroying all data.")

#            labelstr2 = _("Select 'Yes' to continue and format these "
#                          "partitions, or 'No' to go back and change these "
#                          "settings.")
            labelstr2 = ""
            commentstr = ""
            for (dev, type, mntpt) in formatWarnings:
                commentstr = commentstr + \
                        "%s         %s         %s\n" % (dev,type,mntpt)

            rc = self.presentPartitioningComments(_("Format Warnings"),
                                                  labelstr1, labelstr2,
                                                  commentstr,
						  type="custom",
						  custom_buttons=["gtk-cancel",
								  _("_Format")])
            if rc != 1:
                raise gui.StayOnScreen

        self.stripeGraph.shutDown()
        self.tree.clear()
        del self.parent
        return None

    def getPrev(self):
        self.stripeGraph.shutDown()
        self.tree.clear()
        del self.parent
        return None

    def addDevice(self, device, treeiter):
        if device.format.hidden:
            return

        if device.format.type == "luks":
            # we'll want to grab format info from the mapped
            # device, not the encrypted one
            try:
                dm_dev = self.storage.devicetree.getChildren(device)[0]
            except IndexError:
                format = device.format
            else:
                format = dm_dev.format
        else:
            format = device.format

        # icon for the format column
        if device.format.type == "luks" and not device.format.exists:
            # we're creating the LUKS header
            format_icon = self.lock_pixbuf
        elif not format.exists:
            # we're creating a format on the device
            format_icon = self.checkmark_pixbuf
        else:
            format_icon = None

        # mount point string
        if format.type == "lvmpv":
            vg = None
            for _vg in self.storage.vgs:
                if _vg.dependsOn(device):
                    vg = _vg
                    break

            mnt_str = getattr(vg, "name", "")
        elif format.type == "mdmember":
            array = None
            for _array in self.storage.mdarrays:
                if _array.dependsOn(device):
                    array = _array
                    break

            mnt_str = getattr(array, "name", "")
        else:
            mnt_str = getattr(format, "mountpoint", "")
            if mnt_str is None:
                mnt_str = ""

        # device name
        name_str = getattr(device, "lvname", device.name)

        # label
        label_str = getattr(format, "label", "")
        if label_str is None:
            label_str = ""

        self.tree[treeiter]['Device'] = name_str
        self.tree[treeiter]['Size (MB)'] = "%Ld" % device.size
        self.tree[treeiter]['PyObject'] = device
        self.tree[treeiter]['IsFormattable'] = format.formattable
        self.tree[treeiter]['Format'] = format_icon
        self.tree[treeiter]['Mount Point'] = mnt_str
        self.tree[treeiter]['IsLeaf'] = True
        self.tree[treeiter]['Type'] = format.name
        self.tree[treeiter]['Label'] = label_str

    def populate(self, initial = 0):
        self.tree.resetSelection()

        # first do LVM
        vgs = self.storage.vgs
        if vgs:
	    lvmparent = self.tree.append(None)
	    self.tree[lvmparent]['Device'] = _("LVM Volume Groups")
            for vg in vgs:
                vgparent = self.tree.append(lvmparent)
                self.addDevice(vg, vgparent)
                self.tree[vgparent]['Type'] = ""
                for lv in vg.lvs:
                    iter = self.tree.append(vgparent)
                    self.addDevice(lv, iter)

                # We add a row for the VG free space.
                if vg.freeSpace > 0:
                    iter = self.tree.append(vgparent)
                    self.tree[iter]['Device'] = _("Free")
                    self.tree[iter]['Size (MB)'] = "%.0f" % vg.freeSpace
                    self.tree[iter]['PyObject'] = None
                    self.tree[iter]['Mount Point'] = ""
                    self.tree[iter]['IsLeaf'] = True

        # handle RAID next
        mdarrays = self.storage.mdarrays
        if mdarrays:
	    raidparent = self.tree.append(None)
	    self.tree[raidparent]['Device'] = _("RAID Devices")
            for array in mdarrays:
                iter = self.tree.append(raidparent)
                self.addDevice(array, iter)
                name = "%s <span size=\"small\" color=\"gray\">(%s)</span>" % \
                            (array.name, array.path)
                self.tree[iter]['Device'] = name

        # now normal partitions
        disks = self.storage.partitioned
        # also include unpartitioned disks that aren't mpath or biosraid
        whole = filter(lambda d: not d.partitioned and not d.format.hidden,
                       self.storage.disks)
        disks.extend(whole)
        disks.sort(key=lambda d: d.name)
        drvparent = self.tree.append(None)
        self.tree[drvparent]['Device'] = _("Hard Drives")
        for disk in disks:
            # add a parent node to the tree
            parent = self.tree.append(drvparent)

            self.tree[parent]['PyObject'] = disk
            if disk.partitioned:
                part = disk.format.firstPartition
                extendedParent = None
                while part:
                    if part.type & parted.PARTITION_METADATA:
                        part = part.nextPartition()
                        continue

                    partName = devicePathToName(part.getDeviceNodeName())
                    device = self.storage.devicetree.getDeviceByName(partName)
                    if not device and not part.type & parted.PARTITION_FREESPACE:
                        log.debug("can't find partition %s in device"
                                           " tree" % partName)

                    # ignore any free space region that is less than the
                    # grain size of the disklabel alignment we are using
                    if part.type & parted.PARTITION_FREESPACE:
                        min_length = disk.format.alignment.grainSize
                        if part.type & parted.PARTITION_LOGICAL:
                            # ignored free regions in the extended can be up
                            # to twice the alignment grain size, to account
                            # for logical partition metadata
                            min_length *= 2

                        if part.geometry.length < min_length:
                            part = part.nextPartition()
                            continue

                    if device and device.isExtended:
                        if extendedParent:
                            raise RuntimeError, ("can't handle more than "
                                                 "one extended partition per disk")
                        extendedParent = self.tree.append(parent)
                        iter = extendedParent
                    elif part.type & parted.PARTITION_LOGICAL:
                        if not extendedParent:
                            raise RuntimeError, ("crossed logical partition "
                                                 "before extended")
                        iter = self.tree.append(extendedParent)
                    else:
                        iter = self.tree.append(parent)

                    if device and not device.isExtended:
                        self.addDevice(device, iter)
                    else:
                        # either extended or freespace
                        if part.type & parted.PARTITION_FREESPACE:
                            devstring = _("Free")
                            ptype = ""
                        else:
                            devstring = partName
                            ptype = _("Extended")

                        self.tree[iter]['Device'] = devstring
                        self.tree[iter]['Type'] = ptype
                        size = part.getSize(unit="MB")
                        if size < 1.0:
                            sizestr = "< 1"
                        else:
                            sizestr = "%Ld" % (size)
                        self.tree[iter]['Size (MB)'] = sizestr
                        self.tree[iter]['PyObject'] = device

                    part = part.nextPartition()
            else:
                # whole-disk formatting
                self.addDevice(disk, parent)

            ident = None
            try:
                if disk.type == "dasd" or disk.type == "zfcp":
                    ident = deviceNameToDiskByPath(disk.name)
                    if ident.startswith("/dev/disk/by-path/"):
                        ident = os.path.basename(ident)
                elif disk.type == "dm-multipath":
                    ident = disk.wwid
            except DeviceNotFoundError:
                ident = None

            if not ident:
                ident = disk.path

            # Insert a '\n' when device string is too long.  Usually when it
            # contains '/dev/mapper'.  First column should be around 20 chars.
            if len(disk.name) + len(ident) > 20:
                separator = "\n"
            else:
                separator= " "
            self.tree[parent]['Device'] = \
                    "%s%s<span size=\"small\" color=\"gray\">(%s)</span>" \
                    % (disk.name, separator, ident)

        self.treeView.expand_all()
        self.messageGraph.display()

    def barviewActivateCB(self):
        """ Should be called when we double click on a slice"""
        # This is a bit of a hack to make the double click on free space work.
        # This function is useful when the selected slice is a free space,
        # in any other case it calls self.treeActiveCB.

        # We first see if the double click was from a free space or from another
        # slice.
        sel_slice = self.stripeGraph.getSelectedSlice()

        if sel_slice == None:
            # This really should not happen. Do nothing.
            return

        # The selected slice is a free slice if the object contained in it is
        # None.
        if sel_slice.obj != None:
            # This is not a free slice, we should call treeActivateCB
            return self.treeActivateCB()
        else:
            # Display a create window according to the stripe object.
            # Get the device from the stripe.obj
            disp_stripe = self.stripeGraph.getDisplayed()
            if disp_stripe == None:
                # this should not happen
                return

            # Display a create dialog.
            stripe_dev = disp_stripe.obj
            if stripe_dev.partitioned:
                tempformat = self.storage.defaultFSType
                device = self.storage.newPartition(fmt_type=tempformat, size=200)
                self.editPartition(device, isNew = True)

            elif isinstance(stripe_dev, storage.LVMVolumeGroupDevice):
                self.editLVMLogicalVolume(vg = stripe_dev)
                return

    def treeActivateCB(self, *args):
        curr_dev = self.tree.getCurrentDevice()
        if isinstance(curr_dev, storage.PartitionDevice) and \
                not curr_dev.isExtended:
            self.editCB()

        elif isinstance(curr_dev, storage.LVMLogicalVolumeDevice) \
                or isinstance(curr_dev, storage.LVMVolumeGroupDevice) \
                or isinstance(curr_dev, storage.MDRaidArrayDevice):
            self.editCB()

        elif curr_dev == None:
            # Its probably a free space
            iparent = self.tree.getCurrentDeviceParent()
            if iparent == None:
                # it was not free space, it is a root row.
                return

            # We execute a create function given the type of parent that was
            # found.
            # FIXME: This code might repeat itself.  might be a good idea to
            # put it in a function.
            curr_parent = self.tree[iparent]["PyObject"]
            if curr_parent.partitioned:
                tempformat = self.storage.defaultFSType
                device = self.storage.newPartition(fmt_type=tempformat, size=200)
                self.editPartition(device, isNew = True)

            elif isinstance(curr_parent, storage.LVMVolumeGroupDevice):
                self.editLVMLogicalVolume(vg = curr_parent)
                return

    def treeSelectCB(self, selection, *args):
        # The edit and create buttons will be enabled if the user has chosen
        # something editable and/or deletable.
        self.deleteButton.set_sensitive(False)
        self.editButton.set_sensitive(False)

        # I have no idea why this iter might be None.  Its best to return
        # without any action.
        model, iter = selection.get_selected()
        if not iter:
            return

        # If we return because there is no parent, make sure we show the user
        # the infoGraph and no stripeGraph.  The 'create' and 'delete' buttons
        # will be deactivated.
        iparent = model.iter_parent(iter)
        if not iparent:
            self.stripeGraph.shutDown()
            self.messageGraph.display()
            return # This is a root row.

        # We destroy the message first.  We will make sure to repaint it later
        # if no stipe is displayed.  Can't destroy it at the end of this func
        # because it uncenters the created stripe, if any.
        self.messageGraph.destroy()

        device = model[iter]['PyObject']

        # See if we need to change what is in the canvas. In all possibilities
        # we must make sure we have the correct StripeGraph class.
        if not device:
            # This is free space.
            parent = self.tree[iparent]["PyObject"]
            if parent.partitioned:
                if not isinstance(self.stripeGraph, DiskStripeGraph):
                    self.stripeGraph.shutDown()
                    self.stripeGraph = DiskStripeGraph(self.storage,
                            drive = parent, cCB = self.tree.selectRowFromObj,
                            dcCB = self.barviewActivateCB)
                self.stripeGraph.setDisplayed(parent)

            elif isinstance(parent, storage.LVMVolumeGroupDevice):
                if not isinstance(self.stripeGraph, LVMStripeGraph):
                    self.stripeGraph.shutDown()
                    self.stripeGraph = LVMStripeGraph(self.storage,
                            vg = parent, cCB = self.tree.selectRowFromObj,
                            dcCB = self.barviewActivateCB)
                self.stripeGraph.setDisplayed(parent)

        elif device.partitioned:
            if not isinstance(self.stripeGraph, DiskStripeGraph):
                self.stripeGraph.shutDown()
                self.stripeGraph = DiskStripeGraph(self.storage,
                        drive = device,
                        cCB = self.tree.selectRowFromObj,
                        dcCB = self.barviewActivateCB)
            self.stripeGraph.setDisplayed(device)
            # this is deletable but not editable.
            self.deleteButton.set_sensitive(True)

        elif isinstance(device, storage.PartitionDevice):
            if not isinstance(self.stripeGraph, DiskStripeGraph):
                self.stripeGraph.shutDown()
                self.stripeGraph = DiskStripeGraph(self.storage,
                        drive = device.parents[0],
                        cCB = self.tree.selectRowFromObj,
                        dcCB = self.barviewActivateCB)
            self.stripeGraph.setDisplayed(device.parents[0])
            self.stripeGraph.selectSliceFromObj(device)
            self.deleteButton.set_sensitive(True)
            if not device.isExtended:
                self.editButton.set_sensitive(True)

        elif isinstance(device, storage.LVMVolumeGroupDevice):
            if not isinstance(self.stripeGraph, LVMStripeGraph):
                self.stripeGraph.shutDown()
                self.stripeGraph = LVMStripeGraph(self.storage, vg = device,
                        cCB = self.tree.selectRowFromObj,
                        dcCB = self.barviewActivateCB)
            self.stripeGraph.setDisplayed(device)
            self.deleteButton.set_sensitive(True)
            self.editButton.set_sensitive(True)

        elif isinstance(device, storage.LVMLogicalVolumeDevice):
            if not isinstance(self.stripeGraph, LVMStripeGraph):
                self.stripeGraph.shutDown()
                self.stripeGraph = LVMStripeGraph(self.storage, vg = device.vg,
                        cCB = self.tree.selectRowFromObj,
                        dcCB = self.barviewActivateCB)
            self.stripeGraph.setDisplayed(device.vg)
            self.stripeGraph.selectSliceFromObj(device)
            self.deleteButton.set_sensitive(True)
            self.editButton.set_sensitive(True)

        elif isinstance(device, storage.MDRaidArrayDevice):
            if not isinstance(self.stripeGraph, MDRaidArrayStripeGraph):
                self.stripeGraph.shutDown()
                self.stripeGraph = MDRaidArrayStripeGraph(self.storage,
                        md = device,
                        cCB = self.tree.selectRowFromObj,
                        dcCB = self.barviewActivateCB)
            self.stripeGraph.setDisplayed(device)
            self.deleteButton.set_sensitive(True)
            self.editButton.set_sensitive(True)

        else:
            # This means that the user selected something that is not showable
            # in the bar view.  Just show the information message.
            self.stripeGraph.shutDown()
            self.messageGraph.display()
            self.deleteButton.set_sensitive(False)
            self.editButton.set_sensitive(False)

    def deleteCB(self, widget):
        """ Right now we can say that if the device is partitioned we
            want to delete all of the devices it contains. At some point
            we will want to support creation and removal of partitionable
            devices. This will need some work when that time comes.
        """

        device = self.tree.getCurrentDevice()
        if device.partitioned:
            if doClearPartitionedDevice(self.intf,
                                        self.storage,
                                        device):
                self.refresh()
        elif doDeleteDevice(self.intf,
                            self.storage,
                            device):
            if isinstance(device, storage.devices.PartitionDevice):
                justRedraw = False
            else:
                justRedraw = True
                if device.type == "lvmlv" and device in device.vg.lvs:
                    device.vg._removeLogVol(device)

            self.refresh(justRedraw=justRedraw)

    def _deleteCB(self, widget):
        """ deleteCB wrapper

        Blocks and unblocks handlers for the other buttons """

        self._blockButtonHandlers("delete")
        gui.processEvents()
        self.deleteCB(widget)
        self._unblockButtonHandlers("delete")

    def createCB(self, *args):
        # First we must decide what parts of the create_storage_dialog
        # we will activate.

        # get the currently selected device
        device = self.tree.getCurrentDevice()

        # check if the current device is partitioned,
        # if it isn't, we don't support it
        activate_create_partition = True
        if device:
            # a device is selected in the GUI,
            # disable the create dialog if it is unpartitioned
            activate_create_partition = device.partitioned
        else:
            # device was not yet selected in the GUI,
            # check all devices and disable the create dialog
            # if there are no partitioned devices
            activate_create_partition = bool(self.storage.partitioned)

        # We activate the create Volume Group radio button if there is a free
        # partition with a Physical Volume format.
        activate_create_vg = False
        availpvs = len(self.storage.unusedPVs())
        if (lvm.has_lvm()
                and getFormat("lvmpv").supported
                and availpvs > 0):
            activate_create_vg = True

        # We activate the create RAID dev if there are partitions that have
        # raid format and are not related to any raid dev.
        activate_create_raid_dev = False
        availraidparts = len(self.storage.unusedMDMembers())
        availminors = self.storage.unusedMDMinors
        if (len(availminors) > 0
                and getFormat("software RAID").supported
                and availraidparts > 1):
            activate_create_raid_dev = True

        # Must check if all the possibilities are False.  In this case tell the
        # user that he can't create anything and the reasons.
        if (not activate_create_partition
                and not activate_create_vg
                and not activate_create_raid_dev):
            self.intf.messageWindow(_("Cannot perform any creation action"),
                        _("Note that the creation action requires one of the "
                        "following:\n\n"
                        "* Free space in one of the Hard Drives.\n"
                        "* At least two free Software RAID partitions.\n"
                        "* At least one free physical volume (LVM) partition.\n"
                        "* At least one Volume Group with free space."),
                        custom_icon="warning")
            return

        # We will activate the create lv button when we have a VG to put the
        # LVs on.
        activate_create_lv = False
        vgs_with_free_space = []
        for vg in self.storage.vgs:
            if vg.freeSpace > 0:
                vgs_with_free_space.append(vg)
        if len(vgs_with_free_space) > 0:
            activate_create_lv = True

        # GTK crap starts here.
        create_storage_xml = gtk.glade.XML(
                gui.findGladeFile("create-storage.glade"), domain="anaconda")
        self.dialog = create_storage_xml.get_widget("create_storage_dialog")

        # Activate the partition radio buttons if needed.
        # sp_rb -> standard partition
        sp_rb = create_storage_xml.get_widget("create_storage_rb_standard_part")
        # lp_rb -> lvm partition (physical volume)
        lp_rb = create_storage_xml.get_widget("create_storage_rb_lvm_part")
        # rp_rb -> RAID partition
        rp_rb = create_storage_xml.get_widget("create_storage_rb_raid_part")
        if activate_create_partition:
            sp_rb.set_sensitive(True)
            lp_rb.set_sensitive(True)
            rp_rb.set_sensitive(True)

        # Activate the Volume Group radio buttons if needed.
        # vg_rb -> Volume Group
        vg_rb = create_storage_xml.get_widget("create_storage_rb_lvm_vg")
        if activate_create_vg:
            vg_rb.set_sensitive(True)

        # Activate the Logical Volume radio button if needed.
        # We must also take care to control the combo box.
        lv_rb = create_storage_xml.get_widget("create_storage_rb_lvm_lv")
        if activate_create_lv:
            # The combobox will be visible if the radio button is active.
            # The combobox will be sensitive when the radio button is active.
            def toggle_vg_cb_CB(button, vg_cb, selected_dev):
                if button.get_active():
                    vg_cb.set_sensitive(True)

                    # We set the VG to whatever the user has chosen in the tree
                    # view. We will fall back on the first item on the list if
                    # there is no chosen VG.
                    if selected_dev and selected_dev.name \
                            and vg_cb.set_active_text(selected_dev.name):
                        # if set_active is True, we don't need to do anything else
                        pass
                    else:
                        vg_cb.set_active_text(vgs_with_free_space[0].name)

                else:
                    vg_cb.set_sensitive(False)

            vg_cb_st = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
            vg_cb = datacombo.DataComboBox(store = vg_cb_st)
            vg_cb.set_sensitive(False)

            for vg in vgs_with_free_space:
                # FIXME: the name length might be a problem.
                vg_cb.append(vg.name, vg)
            lv_hb = create_storage_xml.get_widget("create_storage_hb_lvm_lv")
            lv_hb.pack_start(vg_cb)

            lv_rb.set_sensitive(True)
            selected_dev = self.tree.getCurrentDevice()
            lv_rb.connect("toggled", toggle_vg_cb_CB, vg_cb, selected_dev)

        # Activate the RAID dev if needed.
        # rd_rb -> RAID device
        rd_rb = create_storage_xml.get_widget("create_storage_rb_raid_dev")
        if activate_create_raid_dev:
            rd_rb.set_sensitive(True)

        # Before drawing lets select the first radio button that is sensitive:
        # How can I get sensitivity from gtk.radiobutton?
        if activate_create_partition:
            sp_rb.set_active(True)
            sp_rb.grab_focus()
        elif activate_create_vg:
            vg_rb.set_active(True)
            vg_rb.grab_focus()
        elif activate_create_raid_dev:
            rd_rb.set_active(True)
            rd_rb.grab_focus()

        gui.addFrame(self.dialog)
        self.dialog.show_all()

        # Lets work the information messages with CB
        # The RAID info message
        rinfo_button = create_storage_xml.get_widget("create_storage_info_raid")
        whatis_r = _("Software RAID allows you to combine several disks into "
                    "a larger RAID device.  A RAID device can be configured "
                    "to provide additional speed and reliability compared "
                    "to using an individual drive.  For more information on "
                    "using RAID devices please consult the %s "
                    "documentation.\n") % (productName,)
        whatneed_r = _("To use RAID you must first create at least two "
                "partitions of type 'software RAID'.  Then you can create a "
                "RAID device that can be formatted and mounted.\n\n")
        whathave_r = P_(
                "You currently have %d software RAID partition free to use.",
                "You currently have %d software RAID partitions free to use.",
                availraidparts) % (availraidparts,)
        rinfo_message = "%s\n%s%s" % (whatis_r, whatneed_r, whathave_r)
        rinfo_cb = lambda x : self.intf.messageWindow(_("About RAID"),
                                rinfo_message, custom_icon="information")
        rinfo_button.connect("clicked", rinfo_cb)

        # The LVM info message
        lvminfo_button = create_storage_xml.get_widget("create_storage_info_lvm")
        whatis_lvm = _("Logical Volume Manager (LVM) is a 3 level construct. "
                "The first level is made up of disks or partitions formated with "
                "LVM metadata called Physical Volumes (PV).  A Volume Group "
                "(VG) sits on top of one or more PVs. The VG, in turn, is the "
                "base to create one or more Logical Volumes (LV).  Note that a "
                "VG can be an aggregate of PVs from multiple physical disk.  For "
                "more information on using LVM please consult the %s "
                "documentation\n") % (productName, )
        whatneed_lvm = _("To create a PV you need a partition with "
                "free space.  To create a VG you need a PV that is not "
                "part of any existing VG.  To create a LV you need a VG with "
                "free space.\n\n")
        whathave_lvm = P_("You currently have %d available PV free to use.\n",
                            "You currently have %d available PVs free to use.\n",
                            availpvs) % (availpvs, )
        lvminfo_message = "%s\n%s%s" % (whatis_lvm, whatneed_lvm, whathave_lvm)
        lvminfo_cb = lambda x : self.intf.messageWindow(_("About LVM"),
                                    lvminfo_message, custom_icon="information")
        lvminfo_button.connect("clicked", lvminfo_cb)

        dialog_rc = self.dialog.run()

        # If Cancel was pressed
        if dialog_rc == 0:
            self.dialog.destroy()
            return

        # If Create was pressed  Make sure we do a dialog.destroy before
        # calling any other screen.  We don't want the create dialog to show
        # in the back when we pop up other screens.
        if dialog_rc != 1:
            log.error("I received a dialog_rc != 1 (%d) witch should not "
                    "happen" % dialog_rc)
            self.dialog.destroy()
            return

        self.dialog.destroy()
        if rp_rb.get_active():
            member = self.storage.newPartition(fmt_type="software RAID",
                                               size=200)
            self.editPartition(member, isNew = True, restrictfs=["mdmember"])
            return

        elif rd_rb.get_active():
            array = self.storage.newMDArray(fmt_type=self.storage.defaultFSType)
            self.editRaidArray(array, isNew = True)
            return

        elif lp_rb.get_active():
            member = self.storage.newPartition(fmt_type="physical volume (LVM)",
                                               size=200)
            self.editPartition(member, isNew = True, restrictfs=["lvmpv"])
            return

        elif vg_rb.get_active():
            tempvg = self.storage.newVG()
            self.editLVMVolumeGroup(tempvg, isNew = True)
            return

        elif lv_rb.get_active():
            selected_vg = vg_cb.get_active_value()
            self.editLVMLogicalVolume(vg = selected_vg)
            return

        elif sp_rb.get_active():
            tempformat = self.storage.defaultFSType
            device = self.storage.newPartition(fmt_type=tempformat, size=200)
            self.editPartition(device, isNew = True)
            return

    def _createCB(self, *args):
        """ createCB wrapper

        Blocks and unblocks handlers for the other buttons """

        self._blockButtonHandlers("create")
        gui.processEvents()
        self.createCB(*args)
        self._unblockButtonHandlers("create")

    def resetCB(self, *args):
        if not confirmResetPartitionState(self.intf):
            return

        self.stripeGraph.shutDown()
        # temporarily unset storage.clearPartType so that all devices will be
        # found during storage reset
        clearPartType = self.storage.clearPartType
        self.storage.clearPartType = None
        self.storage.reset()
        self.storage.clearPartType = clearPartType
        self.tree.clear()
        self.populate()

    def _resetCB(self, *args):
        """ resetCB wrapper

        Blocks and unblocks handlers for the other buttons """

        self._blockButtonHandlers("reset")
        gui.processEvents()
        self.resetCB(*args)
        self._unblockButtonHandlers("reset")

    def refresh(self, justRedraw=None):
        log.debug("refresh: justRedraw=%s" % justRedraw)
        self.stripeGraph.shutDown()
        self.tree.clear()

        if justRedraw:
            rc = 0
        else:
            try:
                doPartitioning(self.storage)
                rc = 0
            except PartitioningError, msg:
                self.intf.messageWindow(_("Error Partitioning"),
                       _("Could not allocate requested partitions: %s.") % (msg),
                                        custom_icon="error")
                rc = -1
            except PartitioningWarning, msg:
                # XXX somebody other than me should make this look better
                # XXX this doesn't handle the 'delete /boot partition spec' case
                #     (it says 'add anyway')
                dialog = gtk.MessageDialog(self.parent, 0, gtk.MESSAGE_WARNING,
                                           gtk.BUTTONS_NONE,
                                           _("Warning: %s.") % (msg))
                gui.addFrame(dialog)
                button = gtk.Button(_("_Modify Partition"))
                dialog.add_action_widget(button, 1)
                button = gtk.Button(_("_Continue"))
                dialog.add_action_widget(button, 2)
                dialog.set_position(gtk.WIN_POS_CENTER)

                dialog.show_all()
                rc = dialog.run()
                dialog.destroy()
                
                if rc == 1:
                    rc = -1
                else:
                    rc = 0
                    all_devices = self.storage.devicetree.devices
                    bootDevs = [d for d in all_devices if d.bootable]
                    #if reqs:
                    #    for req in reqs:
                    #        req.ignoreBootConstraints = 1

	if not rc == -1:
	    self.populate()

        return rc

    def editCB(self, *args):
        device = self.tree.getCurrentDevice()
        reason = self.storage.deviceImmutable(device, ignoreProtected=True)
        if reason:
            self.intf.messageWindow(_("Unable To Edit"),
                                    _("You cannot edit this device:\n\n%s")
                                    % reason,
                                    custom_icon="error")
            return

        if device.type == "mdarray":
            self.editRaidArray(device)
        elif device.type == "lvmvg":
            self.editLVMVolumeGroup(device)
        elif device.type == "lvmlv":
            self.editLVMLogicalVolume(lv = device)
        elif isinstance(device, storage.devices.PartitionDevice):
            self.editPartition(device)

    def _editCB(self, *args):
        """ editCB wrapper

        Blocks and unblocks handlers for the other buttons """

        self._blockButtonHandlers("edit")
        gui.processEvents()
        self.editCB(*args)
        self._unblockButtonHandlers("edit")

    # isNew implies that this request has never been successfully used before
    def editRaidArray(self, raiddev, isNew = False):
        # r_d_g -> raid_dialog_gui
        raideditor = r_d_g.RaidEditor(self.storage, self.intf, self.parent,
                raiddev, isNew)

        while True:
            actions = raideditor.run()

            for action in actions:
                # FIXME: this needs to handle exceptions
                self.storage.devicetree.registerAction(action)

            if self.refresh(justRedraw=True):
                actions.reverse()
                for action in actions:
                    self.storage.devicetree.cancelAction(action)
                    if self.refresh():
                        raise RuntimeError, ("Returning partitions to state "
                                             "prior to RAID edit failed")

                gui.processEvents()
                raideditor.dialog.present()
            else:
                break

        raideditor.destroy()

    def editPartition(self, device, isNew = False, restrictfs = None):
        # p_d_g -> partition_dialog_gui
        parteditor = p_d_g.PartitionEditor(self.anaconda, self.parent, device,
                isNew = isNew, restrictfs = restrictfs)

        while True:
            orig_device = copy.copy(device)
            actions = parteditor.run()

            for action in actions:
                # XXX we should handle exceptions here
                self.anaconda.id.storage.devicetree.registerAction(action)

            if self.refresh(justRedraw=not actions):
                # autopart failed -- cancel the actions and try to get
                # back to previous state
                actions.reverse()
                for action in actions:
                    self.anaconda.id.storage.devicetree.cancelAction(action)

                # FIXME: proper action/device management would be better
                if not isNew:
                    device.req_size = orig_device.req_size
                    device.req_base_size = orig_device.req_base_size
                    device.req_grow = orig_device.req_grow
                    device.req_max_size = orig_device.req_max_size
                    device.req_primary = orig_device.req_primary
                    device.req_disks = orig_device.req_disks

                if self.refresh():
                    # this worked before and doesn't now...
                    raise RuntimeError, ("Returning partitions to state "
                                         "prior to edit failed")

                gui.processEvents()
                parteditor.dialog.present()
            else:
                break

        parteditor.destroy()
        return 1

    def editLVMVolumeGroup(self, device, isNew = False):
        # l_d_g -> lvm_dialog_gui
        vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent,
                device, isNew)

        while True:
            actions = vgeditor.run()

            for action in actions:
                # FIXME: handle exceptions
                self.storage.devicetree.registerAction(action)

            if self.refresh(justRedraw=True):
                actions.reverse()
                for action in actions:
                    self.storage.devicetree.cancelAction(action)

                if self.refresh():
                    raise RuntimeError, ("Returning partitions to state "
                                         "prior to edit failed")

                gui.processEvents()
                vgeditor.dialog.present()
            else:
                break

        vgeditor.destroy()

    def editLVMLogicalVolume (self, lv = None, vg = None):
        """Will be consistent with the state of things and use this funciton
        for creating and editing LVs.

        lv -- the logical volume to edit.  If this is set there is no need
              for the other two arguments.
        vg -- the volume group where the new lv is going to be created. This
              will only be relevant when we are createing an LV.
        """

        if lv != None:
            # l_d_g -> lvm_dialog_gui
            vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent,
                    lv.vg, isNew = False)
            lv = vgeditor.lvs[lv.lvname]
            isNew = False

        elif vg != None:
            # l_d_g -> lvm_dialog_gui
            vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent,
                    vg, isNew = False)
            tempvg = vgeditor.getTempVG()
            name = self.storage.createSuggestedLVName(tempvg)
            format = getFormat(self.storage.defaultFSType)
            vgeditor.lvs[name] = {'name': name,
                              'size': vg.freeSpace,
                              'format': format,
                              'originalFormat': format,
                              'copies': 1,
                              'logSize': 0,
                              'metaDataSize': 0,
                              'snapshotSpace': 0,
                              'exists': False}
            lv = vgeditor.lvs[name]
            isNew = True

        else:
            # This is non-sense.
            return


        while True:
            vgeditor.editLogicalVolume(lv, isNew = isNew)
            actions = vgeditor.convertToActions()

            for action in actions:
                # FIXME: handle exceptions
                self.storage.devicetree.registerAction(action)

            if self.refresh(justRedraw=True):
                actions.reverse()
                for action in actions:
                    self.storage.devicetree.cancelAction(action)

                if self.refresh():
                    raise RuntimeError, ("Returning partitions to state "
                                         "prior to edit failed")

                gui.processEvents()
                vgeditor.dialog.present()
            else:
                break

        vgeditor.destroy()

    def getScreen(self, anaconda):
        self.anaconda = anaconda
        self.storage = anaconda.id.storage
        self.intf = anaconda.intf
        self.checkmark_pixbuf = gui.getPixbuf("checkMark.png")
        self.lock_pixbuf = gui.getPixbuf("gnome-lock.png")

        checkForSwapNoMatch(anaconda)

        # Beginning of the GTK stuff.
        # create the operational buttons
        buttonBox = gtk.HButtonBox()
        buttonBox.set_spacing(6)
        buttonBox.set_layout(gtk.BUTTONBOX_END)

        ops = ((_("_Create"), self._createCB, "create"),
               (_("_Edit"), self._editCB, "edit"),
               (_("_Delete"), self._deleteCB, "delete"),
               (_("Re_set"), self._resetCB, "reset"))

        self._buttons = {}
        self._handlers = {}

        for label, cb, ident in ops:
            button = gtk.Button(label)
            buttonBox.add (button)

            self._buttons[ident] = button
            self._handlers[ident] = button.connect ("clicked", cb)

            # We need these to control their sensitivity.
            if label == _("_Edit"):
                self.editButton = button
                self.editButton.set_sensitive(False)
            elif label == _("_Delete"):
                self.deleteButton = button
                self.deleteButton.set_sensitive(False)

        # Create the disk tree (Fills the tree and the Bar View)
        self.tree = DiskTreeModel()
        self.treeView = self.tree.getTreeView()
        self.treeView.connect('row-activated', self.treeActivateCB)
        self.treeViewSelection = self.treeView.get_selection()
        self.treeViewSelection.connect("changed", self.treeSelectCB)
        self.stripeGraph = StripeGraph()
        self.messageGraph = MessageGraph(self.stripeGraph.getCanvas(),
                _("Please Select A Device"))
        self.populate(initial = 1)

        # Create the top scroll window
        # We don't actually need a *scroll* window but nuthing else worked.
        hadj = gtk.Adjustment(step_incr = 5.0)
        vadj = gtk.Adjustment(step_incr = 5.0)
        swt = gtk.ScrolledWindow(hadjustment = hadj, vadjustment = vadj)
        swt.add(self.stripeGraph.getCanvas())
        swt.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        swt.set_shadow_type(gtk.SHADOW_IN)

        # Create the bottom scroll window
        swb = gtk.ScrolledWindow()
        swb.add(self.treeView)
        swb.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        swb.set_shadow_type(gtk.SHADOW_IN)

        # Create main vertical box and add everything.
        MVbox = gtk.VBox(False, 5)
        MVbox.pack_start(swt, False, False)
        MVbox.pack_start(swb, True)
        MVbox.pack_start(buttonBox, False, False)
        MVbox.pack_start(gtk.HSeparator(), False)

        return MVbox

    def _blockButtonHandlers(self, pressed):
        for ident in self._buttons:
            if ident != pressed:
                self._buttons[ident].handler_block(self._handlers[ident])

    def _unblockButtonHandlers(self, pressed):
        for ident in self._buttons:
            if ident != pressed:
                self._buttons[ident].handler_unblock(self._handlers[ident])
