package diskdrake;





use common qw(:common :functional :file);
use resize_fat::main;
use my_gtk qw(:helpers :wrappers :ask);
use partition_table qw(:types);
use devices;
use raid;
use fsedit;
use fs;

my @actions4free = __("Create");
my @actions4mounted = __("Unmount");
my @actions4nonmounted = (__("Type"), __("Resize"), __("Delete"), __("Format"));#, __("Move"));
my @actions4nonmountedfs = __("Mount point");
my @actions4expert = qw(Move Active Type);
my @actions4expertOrStandalone = qw(Format Mount);
my @actionsnot4raid = qw(Resize Move);

my (%hds_widget, %part_widget);
my ($width, $height, $minwidth) = (400, 50, 5);
my ($w, $current_part, $action_widget, $action_text, $info_widget, $notebook_widget);
my ($hds, $Raid, $raid, $in, $part_suggestions);
my ($buttons1, $buttons2);



$buttons1->{buttons} = [
   [ 0, 0, __("Write /etc/fstab") => \&WriteFstab ],
   [ 0,-1, __("Toggle to expert mode") => sub { $::expert = 1 } ],
   [ 0, 0, __("Toggle to normal mode") => sub { $::expert = 0 } ],
   [ 0, 0, __("Restore from file") => \&ReadFromFile ],
   [ 0, 0, __("Save in file") => \&SaveInFile ],
   [-1, 0, __("Restore from floppy") => \&ReadFromFile ],
   [-1, 0, __("Save on floppy") => \&SaveInFile ],
   [ 1, 1, __("Done") => \&Exit ],
];
$buttons2->{buttons} = [
   [ 1, 1, __("Clear all") => \&partition_table::read, 'clear_all' ],
   [ 0, 0, __("Format all") => \&FormatAll ],
   [ 1, 1, __("Auto allocate") => sub {
	     eval { fsedit::auto_allocate([ sort { $a == current_hd() ? -1 : 0 } @$hds ], $part_suggestions) };
	     $@ =~ /partition table already full/ ?
	       $in->ask_warn("", [ _("All primary partitions are used"), _("I can't add any more partition"), _("To have more partitions, please delete one to be able to create an extended partition") ]) :
		 $@ && die '';
	 } ],

   [ 1, 1, __("Undo") => \&Undo ],
   [ 0, 0, __("Write partition table") => \&WritePartitions ],
   [-1, 0, __("Reload") => \&Reload ],
];

1;

sub update_button_box {
    my ($o) = @_;
    foreach (@{$o->{buttons}}) {
	my $w = $o->{$_->[2]};
	$_->[0] + $::isStandalone && $_->[1] + $::expert ? $w->show : $w->hide;
    }
}
sub generate_button_box {
    my ($o) = @_;
    my $w = new Gtk::HBox(0,0);
    foreach (@{$o->{buttons}}) {
	$w->add(gtksignal_connect($o->{$_->[2]} = new Gtk::Button(translate($_->[2])),
				  clicked => [ \&try, $_->[3], $_->[4] ]));
    }
    update_button_box($o);
    $w;
}

sub main($$$;$) {
    ($hds, $Raid, $in, $part_suggestions) = @_;
    $in->{grab} = 1;

    add2hash_($Raid ||= {}, { device => "md", raid => [] });
    $raid = $Raid->{raid};

    $w = my_gtk->new('DiskDrake');
    my $rc = "/etc/gtk/diskdrake.rc";
    -r $rc or $rc = dirname(__FILE__) . "/diskdrake.rc";
    Gtk::Rc->parse($rc);

    foreach (@$hds, $Raid) {
	$hds_widget{$_} = gtkset_usize(new Gtk::HBox(0,0), 0, 50);
	create_buttons4partitions($_);
    }
    unless (is_empty_array_ref($Raid->{raid})) {
	raid::stopAll;
	$hds = [ @$hds, $Raid ];
    }

    my @types = (__("Ext2"), __("Swap"), __("FAT"), __("Other"), __("Empty"));
    my %name2type = (Ext2 => 0x83, Swap => 0x82, Other => 1, FAT => 0xb);

    gtkadd($w->{window},
	   gtkpack_(new Gtk::VBox(0,7),
		    0, gtkpack(new Gtk::HBox(0,0), 
			       _("Filesystem types:"),
			       map { my $w = new Gtk::Button(translate($_));
				     my $t = $name2type{$_};
				     $w->signal_connect(clicked => sub { my $t = $t; try('', sub { createOrChangeType($_[0], $_[1], $t) }, $current_part) });
				     $w->can_focus(0);
				     $w->set_name($_); $w } @types),
		    0, $notebook_widget = create_notebook(map { $_->{device}, $hds_widget{$_} } @$hds),
		    1, gtkpack_(new Gtk::HBox(0,7),
				0, $action_widget = gtkset_usize(new Gtk::VBox(0,0), 130, 150),
				1, gtkadd(new Gtk::Frame(_("Details")),
					  createScrolledWindow(gtkset_justify($info_widget = new Gtk::Label, 'left')),
					 )
			       ),
		    0, generate_button_box($buttons2),
		    0, generate_button_box($buttons1),
		   ),
	  );
    $hds_widget{$_}{widget_title}->signal_connect('event' => [ \&display_drive_info, $_ ]) foreach @$hds;
    $w->sync;
    create_buttons4partitions($_) foreach @$hds;
    $buttons1->{Done}->grab_focus;    

    $action_text =
_("You have one big fat partition
(generally use by MicroSoft Dos/Windows).
I suggest you first resize that partition
(click on it, then click on \"Resize\")") if !$::isStandalone && fsedit::is_one_big_fat($hds);

    $::isStandalone and $in->ask_okcancel(_("Read carefully!"), _("Please make a backup of your data first"), 1) || return;

    $w->main;

    $hds;
}

sub ask_alldatawillbelost($$) {
    my ($part, $msg) = @_;

    $part->{notFormatted} && !$part->{isFormatted} and return 1;

    
    
    $in->ask_okcancel(_("Read carefully!"), [ _("After %s partition %s,", $msg, $part->{device}) , _("all data on this partition will be lost") ], 1);
}

sub hbox2 { gtkpack_(new Gtk::HBox(0,0), 0, new Gtk::Label($_[0]), 1, $_[1]) }
sub current_hd { $hds->[$notebook_widget->get_current_page] }
sub from_Mb($$$) {
    my ($mb, $min, $max) = @_;
    $mb == $min >> 11 and return $min;
    $mb == $max >> 11 and return $max;
    $mb << 11;
}

sub try($$;$) {
    my (undef, $f, $part) = @_;

    fsedit::undo_prepare($hds) unless ref $f && $f == \&Undo;

    eval { &$f(current_hd(), $part); };
    $@ && $@ =~ /setstep/ and die;
    $@ and $in->ask_warn(_("Error"), $@);
    create_buttons4partitions($_) foreach @$hds;
    clear();

    update_button_box($_) foreach ($buttons1, $buttons2);

    $part or return;
    foreach (fsedit::get_fstab(current_hd())) {
	$part->{device} eq $_->{device} || $part->{start} == $_->{start} or next;
	display($_);
	$part_widget{$_} and $part_widget{$_}->grab_focus;
    }
}

sub clear { 
    $info_widget->set('');
    $_->widget->destroy foreach $action_widget->children;
}
sub get_info {
    local $_ = shift;
    my $info = '';
    my $hd = shift || current_hd();
    my $cylinder = partition_table::cylinder_size($hd);

    $info .= _("Mount point: ") . "$_->{mntpoint}\n" if $_->{mntpoint};
    $info .= _("Device: ") . "$_->{device}\n" if $_->{device};
    $info .= _("DOS drive letter: %s (just a guess)\n", $_->{device_windobe}) if $_->{device_windobe};
    $info .= _("Type: ") . type2name($_->{type}) . ($::expert ? sprintf " (0x%x)", $_->{type} : '') . "\n";
    $info .= _("Start: sector %s\n", $_->{start}) if $::expert && !raid::is($_);
    $info .= _("Size: %s MB", $_->{size} >> 11);
    $info .= sprintf " (%s%%)", int 100 * $_->{size} / $hd->{totalsectors} if $hd->{totalsectors};
    $info .= _(", %s sectors", $_->{size}) if $::expert;
    $info .= "\n";
    $info .= _("Cylinder %d to cylinder %d\n", int ($_->{start} / $cylinder), int (($_->{start} + $_->{size} - 1) / $cylinder)) if $::expert && !raid::is($_);
    $info .= _("Formatted\n") if $_->{isFormatted};
    $info .= _("Not formatted\n") if !$_->{isFormatted} && $_->{notFormatted};
    $info .= _("Mounted\n") if $_->{isMounted};
    $info .= _("RAID md%s\n", $_->{raid}) if defined $_->{raid};
    $info .= _("Partition booted by default\n    (for MS-DOS boot, not for lilo)\n") if $_->{active};
    if (raid::is($_)) {
	$info .= _("Level %s\n", $_->{level});
	$info .= _("Chunk size %s\n", $_->{'chunk-size'});
	$info .= _("RAID-disks %s\n", join ", ", map {$_->{device}} @{$_->{disks}});
    }
    $info;
}
sub display {
    clear();
    $current_part = $_[0];
    $info_widget->set(get_info($_[0]));
    display_actions($_[0]);
}

sub display_drive_info {
    my $hd = $_[1] || current_hd();
    my $info = '';

    clear(); 
    undef $current_part;
    gtkpack($action_widget, gtktext_insert(new Gtk::Text, $action_text || _("Please click on a partition")));
    $action_text = '';

    return if $hd->{device} eq "md";
    $info .= _("Device: ") . "$hd->{device}\n";
    $info .= _("Size: %d MB\n", $hd->{totalsectors} >> 11);
    $info .= _("Geometry: %s cylinders, %s heads, %s sectors\n", @{$hd->{geom}}{qw(cylinders heads sectors)}) if $::expert;
    $info .= _("Type: ") . $hd->{info} . "\n" if $::expert;
    $info .= _("on bus %d id %d\n", $hd->{bus}, $hd->{id}) if $::expert;
    $info_widget->set($info);
}

sub display_actions {
    my ($part) = @_;
    my @entries;

    $part->{type} == 0 and push @entries, @actions4free;
    if ($part->{type}) {
	!isSwap($part) && !$part->{isMounted} && !isRAID($part) and push @entries, @actions4nonmountedfs;
	$part->{isMounted} and push @entries, @actions4mounted;
	!$part->{isMounted} && !exists $part->{raid} and push @entries, @actions4nonmounted;
	!$part->{isMounted} && ($part->{mntpoint} || isSwap($part)) && ($part->{isFormatted} || !$part->{notFormatted}) and push @entries, __("Mount");
	!isSwap($part) && isPrimary($part, current_hd()) && !$part->{active} and push @entries, __("Active");
	isRAID($part) && !exists $part->{raid} && !raid::is($part) and push @entries, __("Add to RAID");
	isRAID($part) && exists $part->{raid} and push @entries, __("Remove from RAID");
	raid::is($part) && !$part->{isMounted} and push @entries, __("Modify RAID");
    }
    @entries = difference2(\@entries, \@actions4expert) unless $::expert;
    @entries = difference2(\@entries, \@actions4expertOrStandalone) unless $::expert || $::isStandalone;
    @entries = difference2(\@entries, \@actionsnot4raid) if raid::is($part);

    gtkpack($action_widget,
	    gtkadd(new Gtk::Frame(_("Choose action")),
		   createScrolledWindow(gtkpack_(new Gtk::VBox(0,0),
	              map {
		          0, gtksignal_connect(new Gtk::Button(translate($_)), 
					       'clicked' => [ \&try, $diskdrake::{$_}, $part ])
		      } @entries))));
}

sub create_buttons4partitions($) {
    my $hd = shift;

    $width = max($width, 0.9 * second($w->{window}->window->get_size)) if $w->{window}->window;
    $_->widget->destroy foreach $hds_widget{$hd}->children;
    gtkpack_($hds_widget{$hd},
	    map { 0, $_ }
	    create_buttons4partitions_($hd->{totalsectors},
				       sort { $a->{start} <=> $b->{start} } fsedit::get_fstab($hd)));
    partition_table::assign_device_numbers($hd) if ref $hd eq "HASH";
}

sub create_buttons4partitions_($@) {
    my $totalsectors = shift;
    my $ratio = $totalsectors ? ($width - @_ * $minwidth) / $totalsectors : 1;
    my $totalwidth;
    my @r;

    my $button = sub {
	my ($part) = @_;
	my $w = $part_widget{$part} = new Gtk::Button($part->{mntpoint} || '') or die '';
	$w->signal_connect(focus_in_event     => sub { display($part) });
	$w->signal_connect(button_press_event => sub { display($part) });
	$w->signal_connect(key_press_event => sub {
	    my ($w, $e) = @_;
	    $e->{state} == 0 && $e->{keyval} == 0xffff 
	           and return try('', \&Delete, $part);
	    $e->{state} & 4 or return; 
	    my $f = ${{
		"a" => \&Active,
		"b" => \&Move,
		"c" => \&Create,
		"d" => \&Delete,
		"f" => \&Format,
		"m" => \&Mount_point,
		"M" => \&Mount,
		"n" => \&Create,
		"o" => \&ReadFromFile,
		"s" => \&SaveInFile,
		"q" => \&Exit,
		"r" => \&Resize,
		"R" => exists $part->{raid} ? \&RemoveFromRAID : \&Add2RAID,
		"t" => \&Type,
		"u" => \&Unmount,
		"w" => \&WritePartitions,
		"W" => \&WriteFstab,
	    }}{chr $e->{keyval}};
	    $f and try('', $f, $part);
	});
	$w->set_name("PART_" . type2name($part->{type}));
	$w->set_usize(my $s = $part->{size} * $ratio + $minwidth, 0);
	$totalwidth += $s;
	$w;
    };
    my $add_empty = sub {
	my $o = shift;
	$o->{type} = 0;
	$o->{size} * $ratio > 1 || $o->{size} > 5 << 11 and push @r, &$button($o);
    };

  again:
    my $last = 1;
    foreach (@_) {
	&$add_empty({ start => $last, size => $_->{start} - $last });
	$last = $_->{start} + $_->{size};

	push @r, &$button($_);
    }
    &$add_empty({ start => $last, size => $totalsectors - $last });

    if ($totalwidth > $width) {
	$ratio /= $totalwidth / $width * 1.1;
	$totalwidth = 0;
	@r = ();
	goto again;
    }

    @r;
}

sub check_mntpoint {
    eval { fsedit::check_mntpoint(@_) };
    local $_ = $@;
    if (m|/boot ending on cylinder > 1024|) {
	$in->ask_warn('',
_("Sorry I won't accept to create /boot so far onto the drive (on a cylinder > 1024).
Either you use LILO and it won't work, or you don't use LILO and you don't need /boot"));
    } elsif (m|/ ending on cylinder > 1024|) {
	$in->ask_warn('',
_("The partition you've selected to add as root (/) is physically located beyond
the 1024th cylinder of the hard drive, and you have no /boot partition.
If you plan to use the LILO boot manager, be careful to add a /boot partition"));
	undef $_;
    } elsif ($_) {
	$in->ask_warn('', $_);
    }
    !$_;
}

sub createOrChangeType($) {
    my ($hd, $part, $type) = @_;

    $part ||= !fsedit::get_fstab($hd) && 
              { type => 0, start => 1, size => $hd->{totalsectors} - 1 };
    $part or return;
    if ($type == 1) {
	$in->ask_warn('', _("Use ``%s'' instead", $part->{type} ? _("Type") : _("Create")));
    } elsif (!$type) {
	$in->ask_warn('', _("Use ``%s'' instead", _("Delete"))) if $part->{type};
    } elsif ($part->{type}) {
	return if $type == $part->{type};
	$part->{isMounted} || exists $part->{raid} and $in->ask_warn('', _("Use ``Unmount'' first")), return;
	return if !ask_alldatawillbelost($part, _("changing type of"));
	fsedit::change_type($hd, $part, $type);
    } else {
	$part->{type} = $type;
	Create($hd, $part);
    }
}

sub Exit(;$$) {
    eval { raid::verify($Raid) };
    if ($@) {
	$::expert || die;
	$in->ask_okcancel('', [ $@, _("Continue anyway?")]) or return;
    }
    foreach (@$hds) {
	unless (WritePartitions($_)) {
	    return unless $::isStandalone;
	    $in->ask_yesorno(_("Quit without saving"), _("Quit without writing the partition table?"), 1) || return;
	}
    }
    Gtk->main_quit;
}

sub Active($$) {
    &partition_table::active;
}
sub Delete($$) {
    my ($hd, $part) = @_;
    raid::is($part) ? raid::delete($raid, $part) : &partition_table::remove;
}
sub Type($$) {
    my ($hd, $part) = @_;

    ask_alldatawillbelost($part, _("changing type of")) or return;

    my $type = type2name($part->{type});
    $in->ask_from_entries_ref(_("Change partition type"),
			      _("Which partition type do you want?"),
			      [ _("Type") ],
			      [ { val => \$type, list => [ partition_table::important_types($::expert) ], not_edit => !$::expert } ]) or return;
    fsedit::change_type($hd, $part, name2type($type)) if defined $type;
}
sub Mount_point($$) {
    my ($hd, $part) = @_;

    my $mntpoint = $part->{mntpoint} || do {
	my $part_ = { %$part };
	if (fsedit::suggest_part($hd, $part_, $hds, $part_suggestions)) {
	    fsedit::has_mntpoint('/', $hds) || $part_->{mntpoint} eq '/boot' ? $part_->{mntpoint} : '/';
	} else { '' }
    };
    $in->ask_from_entries_ref(
        '',
_("Where do you want to mount device %s?", $part->{device}),
        [ _("Mount point") ],
	[ { val => \$mntpoint, list => [ fsedit::suggestions_mntpoint($hds), '' ] } ],
	complete => sub {
	    !($part->{mntpoint} eq $mntpoint ||
	      check_mntpoint($mntpoint, $hd, $part, $hds))
	}
    ) or return;
    $part->{mntpoint} = $mntpoint;
}
sub Mount($$) {
    my ($hd, $part) = @_;
    WritePartitions($hd) || return;
    fs::mount_part($part);
}
sub Unmount($$) {
    my ($hd, $part) = @_;
    fs::umount_part($part);
}
sub Format($$) {
    my ($hd, $part) = @_;
    WritePartitions($_) or return foreach raid::is($part) ? @$hds : $hd;
    ask_alldatawillbelost($part, _("formatting")) or return;
    $part->{isFormatted} = 0; 
    my $w = $in->wait_message(_("Formatting"), _("Formatting partition %s", $part->{device}));
    raid::format_part($Raid, $part);
}
sub FormatAll() {
    foreach (@$hds) { WritePartitions($_) || return; }
    $in->ask_okcancel(_("Read carefully!"), [ _("After formatting all partitions,") , _("all data on these partitions will be lost") ], 1) or return;

    raid::format_part($Raid, $_) foreach fsedit::get_fstab(@$hds);
}
sub Move($$) {
    my ($hd, $part) = @_;
    my $hd2;
    if (@$hds == 1) {
	$hd2 = $hds->[0];
    } else {
	my $hd2_name = $in->ask_from_list(_("Move"),
					  _("Which disk do you want to move to?"),
					  [ map { $_->{device} } @$hds ]) or return;
	($hd2) = grep { $_->{device} eq $hd2_name } @$hds;
    }
    my $start2 = $in->ask_from_entry(_("Sector"),
				     _("Which sector do you want to move to?"));
    defined $start2 or return;

    my $w = $in->wait_message(_("Moving"), _("Moving partition..."));
    fsedit::move($hd, $part, $hd2, $start2);
}


sub WritePartitions {
    my ($hd) = @_;
    $hd->{isDirty} or return 1;
    exists $hd->{raid} and return 1;

    $in->ask_okcancel(_("Read carefully!"), _("Partition table of drive %s is going to be written to disk!", $hd->{device}), 1) or return;
    partition_table::write($hd) unless $::testing;
    $hd->{rebootNeeded} and die _("You'll need to reboot before the modification can take place");
    1;
}
sub WriteFstab {
    fs::write_fstab([ fsedit::get_fstab(@$hds) ]);
}

sub Resize($$) {
    my ($hd, $part) = @_;
    my ($resize_fat, $resize_ext2);
    my ($min, $max) = (1, partition_table::next_start($hd, $part) - $part->{start});

    if ($part->{isFormatted} || !$part->{notFormatted}) {
	# here we may have a non-formatted or a formatted partition
	# -> doing as if it was formatted

	isFat($part) || isExt2($part) or WritePartitions($hd) or return;
	if (isFat($part)) {
	    WritePartitions($hd) or return;

	    
	    my $w = $in->wait_message(_("Resizing"), _("Computing fat filesystem bounds"));

	    $resize_fat = resize_fat::main->new($part->{device}, devices::make($part->{device}));
	    $min = max($min, $resize_fat->min_size);
	    $max = min($max, $resize_fat->max_size);	    
	} elsif (0 && isExt2($part)) {
	    WritePartitions($hd) or return;
	    $resize_ext2 = devices::make($part->{device});
	    my ($m) = `ext2resize $resize_ext2 0 2>/dev/null`;
	    $? ?
	      undef $resize_ext2 :
	      ($min = max($min, to_int($m) >> 9));
	}
	unless ($resize_fat || $resize_ext2) {
	    ask_alldatawillbelost($part, _("resizing")) or return;
	}
    }
    my $w = my_gtk->new(_("Resize"), %$in);
    my $adj = create_adjustment($part->{size} >> 11, $min >> 11, $max >> 11);
    my $spin = gtkset_usize(new Gtk::SpinButton($adj, 0, 0), 100, 0);

    gtkadd($w->{window},
	  gtkpack(new Gtk::VBox(0,20),
		 create_packtable({ col_spacings => 10 },
				  [ _("Choose the new size"), $spin, _("MB"), ],
				  [ undef, new Gtk::HScrollbar($adj) ],
			       ),
		 create_okcancel($w)
		)
	 );
    $spin->signal_connect(activate => sub { $w->{retval} = 1; Gtk->main_quit });
    $spin->grab_focus();
    $w->main or return;

    my $size = from_Mb($spin->get_value_as_int, $min, $max);

    $part->{size} == $size and return;

    my $oldsize = $part->{size};
    $hd->{isDirty} = $hd->{needKernelReread} = 1;
    $part->{size} = $size;
    partition_table::adjustEnd($hd, $part);

    undef $@;
    my $b = before_leaving { $@ and $part->{size} = $oldsize };
    $w = $in->wait_message(_("Resizing"), '');

    if ($resize_fat) {
	local *log::l = sub { $w->set(join(' ', @_)) };
	$resize_fat->resize($size);
    } elsif ($resize_ext2) {
	my $s = $size << 9;
	local *F;
	open F, "ext2resize $resize_ext2 $s 2>&1 |";
	$w->set($_) foreach <F>;

	close F or die "ext2resize failed";
    } else {
	$part->{notFormatted} = 1;
	$part->{isFormatted} = 0;
	return;
    }
    $part->{isFormatted} = 1;
    partition_table::adjust_local_extended($hd, $part);
    partition_table::adjust_main_extended($hd);
}

sub Create($$) {
    my ($hd, $part) = @_;
    my ($start, $size, $max) = ($part->{start}, $part->{size}, $part->{start} + $part->{size});

    $part->{maxsize} = $part->{size}; $part->{size} = 0;
    unless (fsedit::suggest_part($hd, $part, $hds, $part_suggestions)) {
	$part->{size} = $part->{maxsize};
	$part->{type} ||= 0x83;
    }    

    my $w = my_gtk->new(_("Create a new partition"), %$in);

    my ($type_combo, $mntpoint_combo, $primaryextended_combo);
    my $adj_start = create_adjustment($part->{start}, $start, $max - 1);
    my $adj_size  = create_adjustment($part->{size} >> 11, 1, $size >> 11);
    my $spin_start = new Gtk::SpinButton($adj_start, 0, 0);
    my $spin_size  = new Gtk::SpinButton($adj_size,  0, 0);

    ($start, $size) = ($part->{start}, $size = $part->{size});
    my $update_start = sub {  $adj_size->set_value($size  = $spin_size->get_value_as_int); $size <<= 11; $start + $size < $max or $spin_start->set_value($start = $max - $size); };
    my $update_size  = sub { $adj_start->set_value($start = $spin_start->get_value_as_int);              $start + $size < $max or  $spin_size->set_value(($size = $max - $start) >> 11); };

    my $h;
    gtkadd($w->{window},
	  gtkpack(new Gtk::VBox(0,20),
		 create_packtable({},
		  [ _("Start sector: "), gtksignal_connect($spin_start, changed => $update_size) ],
		  [ undef, new Gtk::HScrollbar($adj_start) ],
		  [ '' ],
		  [ _("Size in MB: "),   gtksignal_connect($spin_size,  changed => $update_start) ],
		  [ undef, new Gtk::HScrollbar($adj_size) ],
		  [ '' ],
		  [ _("Filesystem type: "), $type_combo = new Gtk::Combo ],
		  [ _("Mount point: "), $mntpoint_combo = new Gtk::Combo ],
                  ($::expert ? [ _("Preference: "), $primaryextended_combo = new Gtk::Combo ] : ()),
				 ),
		 create_okcancel($w)
		 ),
    );
    $type_combo->set_popdown_strings(partition_table::important_types($::expert));
    $type_combo->entry->set_text(type2name($part->{type})) if $part->{type};
    $mntpoint_combo->set_popdown_strings(fsedit::suggestions_mntpoint($hds), '');
    $mntpoint_combo->entry->set_text($part->{mntpoint});
    if ($::expert) {
	$primaryextended_combo->set_popdown_strings("Extended", "Primary");
    }

    foreach ($type_combo, $mntpoint_combo, $primaryextended_combo) {
	$_ or next;
	$_->disable_activate;
	$_->set_use_arrows_always(1);
    }
    $type_combo->entry->set_editable(0) unless $::expert;
    $type_combo->entry->signal_connect(changed => sub { $mntpoint_combo->set_sensitive(bool($type_combo->entry->get_text !~ /swap|RAID/)) });
    $mntpoint_combo->entry->signal_connect(activate => sub { $w->{retval} = 1; Gtk->main_quit });
    $mntpoint_combo->entry->grab_focus;

    $type_combo->entry->signal_emit("changed");

    $w->main(sub {
		 $part->{start} = $start;
		 $part->{size}  = from_Mb($size >> 11, 1, $max - $start); 
		 ($part->{type} = name2type($type_combo->entry->get_text)) =~ s/0x(.*)/hex($1)/e;
		 $part->{mntpoint} = $mntpoint_combo->entry->get_text;
		 $part->{mntpoint} = '' if isRAID($part);
		 $part->{mntpoint} = 'swap' if isSwap($part);

		 check_mntpoint($part->{mntpoint}, $hd, $part, $hds) or return;
		 fsedit::add($hd, $part, $hds,
			     { force => 1,
			       primaryOrExtended => $::expert ? $primaryextended_combo->entry->get_text : '' }
			    );
		 1;
	     });
}

sub ReadFromFile() {
    my $file = $::isStandalone ?
		 ask_file(_("Select file")) :
                 devices::make("fd0") or return;

    $_->widget->destroy foreach $hds_widget{current_hd()}->children;

    catch_cdie { partition_table::load(current_hd(), $file) }
      sub {
	  $@ =~ /Bad totalsectors/ or return;
	  $in->ask_yesorno('',
_("The backup partition table has not the same size
Still continue?"), 0);
      };
}

sub SaveInFile() {
    my $file = $::isStandalone ?
		 ask_file(_("Select file")) :
                 $in->ask_okcancel(_("Warning"),
_("Insert a floppy in drive
All data on this floppy will be lost"), 1) && devices::make("fd0") or return;

    partition_table::save(current_hd(), $file);
}

sub Undo() {
#    $_->widget->destroy foreach current_hd()->{widget}->children;
#    my @widgets = map { delete $_->{widget} } @$hds;
    fsedit::undo($hds);
#    mapn { $_[0]{widget} = $_[1] } $hds, \@widgets;
}

sub Rescuept {
    my $w = $in->wait_message('', _("Trying to rescue partition table"));
    fsedit::rescuept(current_hd());
}

sub Reload { %$hds = (); %$Raid = (); $::setstep and die "setstep partitionDisks\n" }

sub modifyRAID {
    my ($raid, $nb) = @_;
    my $md = "md$nb";
    $in->ask_from_entries_refH('', '',
			       [
_("device") => { val => \$md, list => [ map { "md$_" } grep { $nb == $_ || !$raid->[$_] } 0..8 ] },
_("level") => { val => \$raid->[$nb]{level}, list => [ qw(0 1 4 5 linear) ] },
_("chunk size") => \$raid->[$nb]{'chunk-size'},
			       ],
			      ) or return;
    raid::changeNb($raid, $nb, first($md =~ /(\d+)/));
}

sub ModifyRAID { modifyRAID($raid, raid::nb($_[1])) }

sub Add2RAID {
    my ($hd, $part) = @_;

    local $_ = is_empty_array_ref($raid) ? "new" :
      $in->ask_from_list_('', _("Choose an existing RAID to add to"),
			  [ (grep {$_} map_index { $_ && "md$::i" } @$raid), __("new") ]);

    if (/new/) {
	my $nb1 = raid::new($raid, $part);
	defined modifyRAID($raid, $nb1) or return raid::delete($raid, $nb1);
    } else {
	raid::add($raid, $part, $_);
    }
    raid::update(@$raid);

    unless (member($Raid, @$hds)) {
	$hds = [ @$hds, $Raid ];
	raid::stopAll;
	my_gtk::add2notebook($notebook_widget, "raid", $hds_widget{$Raid});
	$hds_widget{$Raid}{widget_title}->signal_connect('event' => [ \&display_drive_info, $Raid ]);
    }
}
sub RemoveFromRAID { raid::removeDisk($raid, $_[1]) }


{ 
    no strict; 
    *{"Mount point"} = *Mount_point;
    *{"Modify RAID"} = *ModifyRAID;
    *{"Add to RAID"} = *Add2RAID;
    *{"Remove from RAID"} = *RemoveFromRAID; 
}
