diff options
Diffstat (limited to 'perl-install/diskdrake/hd_gtk.pm')
| -rw-r--r-- | perl-install/diskdrake/hd_gtk.pm | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/perl-install/diskdrake/hd_gtk.pm b/perl-install/diskdrake/hd_gtk.pm new file mode 100644 index 000000000..f46f4f1b5 --- /dev/null +++ b/perl-install/diskdrake/hd_gtk.pm @@ -0,0 +1,415 @@ +package diskdrake::hd_gtk; # $Id: hd_gtk.pm 97952 2006-12-15 22:56:43Z blino $ + +use diagnostics; +use strict; + +use common; +use mygtk2 qw(gtknew); +use ugtk2 qw(:helpers :wrappers :create); +use partition_table; +use fs::type; +use detect_devices; +use diskdrake::interactive; +use run_program; +use devices; +use log; +use fsedit; + +my ($width, $height, $minwidth) = (400, 50, 5); +my ($all_hds, $in, $do_force_reload, $current_kind, $current_entry, $update_all); +my ($w, @notebook, $done_button); + +=begin + + +=head1 SYNOPSYS + +struct { + string name # which is displayed in tab of the notebook + bool no_auto # wether the kind can disappear after creation + string type # one of { 'hd', 'raid', 'lvm', 'loopback', 'removable', 'nfs', 'smb' } + hd | hd_lvm | part_raid[] | part_loopback[] | raw_hd[] val + + # + widget main_box + widget display_box + widget action_box + widget info_box +} current_kind + +part current_entry + +notebook current_kind[] + +=cut + +sub main { + ($in, $all_hds, $do_force_reload) = @_; + + @notebook = (); + + local $in->{grab} = 1; + + $w = ugtk2->new(N("Partitioning"), icon => 'banner-part'); + $::main_window = $w->{real_window} if !$::isEmbedded && !$::isInstall; + my $rc = "/usr/share/libDrakX/diskdrake.rc"; + -r $rc or $rc = dirname(__FILE__) . "/../diskdrake.rc"; + -r $rc or $rc = dirname(__FILE__) . "/../share/diskdrake.rc"; + Gtk2::Rc->parse($rc); + + # TODO +# is_empty_array_ref($all_hds->{raids}) or raid::stopAll; +# updateLoopback(); + + gtkadd($w->{window}, + gtkpack_(Gtk2::VBox->new(0,7), + 0, filesystems_button_box(), + 1, (my $notebook_widget = Gtk2::Notebook->new), + 0, (my $per_kind_action_box = Gtk2::HBox->new(0,0)), + 0, (my $general_action_box = Gtk2::HBox->new(0,0)), + ), + ); + my $lock; + $update_all = sub { + $lock and return; + $lock = 1; + partition_table::assign_device_numbers($_) foreach fs::get::hds($all_hds); + create_automatic_notebooks($notebook_widget); + general_action_box($general_action_box); + per_kind_action_box($per_kind_action_box, $current_kind); + current_kind_changed($in, $current_kind); + current_entry_changed($current_kind, $current_entry); + $lock = 0; + }; + create_automatic_notebooks($notebook_widget); + + $notebook_widget->signal_connect(switch_page => sub { + $current_kind = $notebook[$_[2]]; + $current_entry = ''; + $update_all->(); + }); + $w->sync; + $done_button->grab_focus; + $in->ask_from_list_(N("Read carefully!"), N("Please make a backup of your data first"), + [ N_("Exit"), N_("Continue") ], N_("Continue")) eq N_("Continue") or return + if $::isStandalone; + $in->ask_warn('', +N("If you plan to use aboot, be careful to leave a free space (2048 sectors is enough) +at the beginning of the disk")) if arch() eq 'alpha' && !$::isEmbedded; + + $w->main; +} + +sub try { + my ($name, @args) = @_; + my $f = $diskdrake::interactive::{$name} or die "unknown function $name"; + try_($name, \&$f, @args); +} +sub try_ { + my ($name, $f, @args) = @_; + + diskdrake::interactive::undo_prepare($all_hds) if $name ne 'Undo'; + + my $v = eval { $f->($in, @args, $all_hds) }; + if (my $err = $@) { + $in->ask_warn(N("Error"), formatError($err)); + } + if ($v eq 'force_reload') { + $all_hds = $do_force_reload->(); + } + + $current_entry = '' if !diskdrake::interactive::is_part_existing($current_entry, $all_hds); + $update_all->(); + + Gtk2->main_quit if $v && member($name, 'Done'); +} + +################################################################################ +# generic: helpers +################################################################################ +sub add_kind2notebook { + my ($notebook_widget, $kind) = @_; + die if $kind->{main_box}; + + $kind->{display_box} = gtkset_size_request(Gtk2::HBox->new(0,0), $width, $height); + $kind->{action_box} = gtkset_size_request(Gtk2::VBox->new(0,0), $::isStandalone ? 165 : 150, $::isEmbedded ? 150 : 180); + $kind->{info_box} = Gtk2::VBox->new(0,0); + $kind->{main_box} = + gtkpack_(Gtk2::VBox->new(0,7), + 0, $kind->{display_box}, + 1, gtkpack_(Gtk2::HBox->new(0,7), + 0, $kind->{action_box}, + 1, $kind->{info_box})); + ugtk2::add2notebook($notebook_widget, $kind->{name}, $kind->{main_box}); + push @notebook, $kind; + $kind; +} + +sub interactive_help { + my ($in) = @_; + if ($::isInstall) { + $in->interactive_help_sub_display_id('partition_with_diskdrake'); + } else { + require run_program; + run_program::raw({ detach => 1 }, 'drakhelp', '--id', 'diskdrake'); + } +} + +sub general_action_box { + my ($box) = @_; + $_->destroy foreach $box->get_children; + + gtkadd($box, gtksignal_connect(Gtk2::Button->new(N("Help")), clicked => \&interactive_help)); + + my @actions = ( + diskdrake::interactive::general_possible_actions($in, $all_hds), + N_("Done")); + foreach my $s (@actions) { + my $button = Gtk2::Button->new(translate($s)); + $done_button = $button if $s eq 'Done'; + gtkadd($box, gtksignal_connect($button, clicked => sub { try($s) })); + } +} +sub per_kind_action_box { + my ($box, $kind) = @_; + $_->destroy foreach $box->get_children; + + $kind->{type} =~ /hd|lvm/ or return; + + foreach my $s (diskdrake::interactive::hd_possible_actions($in, kind2hd($kind), $all_hds)) { + gtkadd($box, + gtksignal_connect(Gtk2::Button->new(translate($s)), + clicked => sub { try($s, kind2hd($kind)) })); + } +} +sub per_entry_action_box { + my ($box, $kind, $entry) = @_; + $_->destroy foreach $box->get_children; + + if ($entry) { + my @buttons = map { + my $s = $_; + my $w = Gtk2::Button->new(translate($s)); + $w->signal_connect(clicked => sub { try($s, kind2hd($kind), $entry) }); + $w; + } diskdrake::interactive::part_possible_actions($in, kind2hd($kind), $entry, $all_hds); + + gtkadd($box, gtkadd(Gtk2::Frame->new(N("Choose action")), + create_scrolled_window(gtkpack__(Gtk2::VBox->new(0,0), @buttons)))) if @buttons; + } else { + my $txt = !$::isStandalone && fsedit::is_one_big_fat_or_NT($all_hds->{hds}) ? +N("You have one big Microsoft Windows partition. +I suggest you first resize that partition +(click on it, then click on \"Resize\")") : N("Please click on a partition"); + gtkpack($box, gtktext_insert(Gtk2::TextView->new, $txt)); + } +} + +sub per_entry_info_box { + my ($box, $kind, $entry) = @_; + $_->destroy foreach $box->get_children; + my $info; + if ($entry) { + $info = diskdrake::interactive::format_part_info(kind2hd($kind), $entry); + } elsif ($kind->{type} =~ /hd|lvm/) { + $info = diskdrake::interactive::format_hd_info($kind->{val}); + } + gtkpack($box, gtkadd(gtkcreate_frame(N("Details")), + gtknew('HBox', border_width => 5, children_loose => [ + gtkset_alignment(gtkset_justify(Gtk2::Label->new($info), 'left'), 0, 0) ]))); +} + +sub current_kind_changed { + my ($_in, $kind) = @_; + + $_->destroy foreach $kind->{display_box}->get_children; + + my $v = $kind->{val}; + my @parts = + $kind->{type} eq 'raid' ? grep { $_ } @$v : + $kind->{type} eq 'loopback' ? @$v : fs::get::hds_fstab_and_holes($v); + my $totalsectors = + $kind->{type} =~ /raid|loopback/ ? sum(map { $_->{size} } @parts) : $v->{totalsectors}; + create_buttons4partitions($kind, $totalsectors, @parts); +} + +sub current_entry_changed { + my ($kind, $entry) = @_; + $current_entry = $entry; + if ($kind) { + per_entry_action_box($kind->{action_box}, $kind, $entry); + per_entry_info_box($kind->{info_box}, $kind, $entry); + } +} + +sub create_automatic_notebooks { + my ($notebook_widget) = @_; + + $_->{marked} = 0 foreach @notebook; + my $may_add = sub { + my ($kind) = @_; + my @l = grep { $kind->{val} == $_->{val} } @notebook; + @l > 1 and log::l("weird: create_automatic_notebooks"); + $kind = $l[0] || add_kind2notebook($notebook_widget, $kind); + $kind->{marked} = 1; + }; + $may_add->(hd2kind($_)) foreach @{$all_hds->{hds}}; + $may_add->(lvm2kind($_)) foreach @{$all_hds->{lvms}}; + $may_add->(raid2kind()) if @{$all_hds->{raids}}; + $may_add->(loopback2kind()) if @{$all_hds->{loopbacks}}; + + @notebook = grep_index { + my $b = $_->{marked} or $notebook_widget->remove_page($::i); + $b; + } @notebook; + @notebook or die N("No hard drives found"); +} + +################################################################################ +# parts: helpers +################################################################################ +sub create_buttons4partitions { + my ($kind, $totalsectors, @parts) = @_; + + $width = max($width, 0.9 * second($w->{window}->window->get_size)) if $w->{window}->window; + + my $ratio = $totalsectors ? ($width - @parts * $minwidth) / $totalsectors : 1; + while (1) { + my $totalwidth = sum(map { $_->{size} * $ratio + $minwidth } @parts); + $totalwidth <= $width and last; + $ratio /= $totalwidth / $width * 1.1; + } + + my $current_button; + my $set_current_button = sub { + my ($w) = @_; + $current_button->set_active(0) if $current_button; + ($current_button = $w)->set_active(1); + }; + + foreach my $entry (@parts) { + my $info = $entry->{mntpoint}; + $info .= "\n" . ($entry->{size} ? formatXiB($entry->{size}, 512) : N("Unknown")) if $info; + my $w = Gtk2::ToggleButton->new_with_label($info) or internal_error('new_with_label'); + $w->signal_connect(clicked => sub { + $current_button != $w or return; + current_entry_changed($kind, $entry); + $set_current_button->($w); + }); + $w->signal_connect(key_press_event => sub { + my (undef, $event) = @_; + member('control-mask', @{$event->state}) && $w == $current_button or return; + my $c = chr $event->keyval; + + foreach my $s (diskdrake::interactive::part_possible_actions($in, kind2hd($kind), $entry, $all_hds)) { + ${{ + Create => 'c', Delete => 'd', Format => 'f', + Loopback => 'l', Resize => 'r', Type => 't', + Mount => 'M', Unmount => 'u', 'Mount point' => 'm', + 'Add to LVM' => 'L', 'Remove from LVM' => 'L', + 'Add to RAID' => 'R', 'Remove from RAID' => 'R', + }}{$s} eq $c or next; + + try($s, kind2hd($kind), $entry); + last; + } + }); + $w->set_name("PART_" . fs::type::part2type_name($entry)); + $w->set_size_request($entry->{size} * $ratio + $minwidth, 0); + gtkpack__($kind->{display_box}, $w); + if ($current_entry && fsedit::are_same_partitions($current_entry, $entry)) { + $set_current_button->($w); + $w->grab_focus; + } + } +} + + +################################################################################ +# disks: helpers +################################################################################ +sub current_hd() { + $current_kind->{type} eq 'hd' or die 'current_hd called but $current_kind is not an hd'; + $current_kind->{val}; +} +sub current_part() { + current_hd(); + $current_entry; +} + +sub kind2hd { + my ($kind) = @_; + $kind->{type} =~ /hd|lvm/ ? $kind->{val} : {}; +} + +sub hd2kind { + my ($hd) = @_; + { type => 'hd', name => $hd->{device}, val => $hd }; +} + +sub filesystems_button_box() { + my @types = (N_("Ext2"), N_("Journalised FS"), N_("Swap"), arch() =~ /sparc/ ? N_("SunOS") : arch() eq "ppc" ? N_("HFS") : N_("Windows"), + N_("Other"), N_("Empty")); + my %name2fs_type = (Ext2 => 'ext2', 'Journalised FS' => 'ext3', Swap => 'swap', Other => 'other', "Windows" => 'vfat', HFS => 'hfs'); + + gtkpack(Gtk2::HBox->new(0,0), + N("Filesystem types:"), + map { my $w = Gtk2::Button->new(translate($_)); + my $t = $name2fs_type{$_}; + $w->signal_connect(clicked => sub { try_('', \&createOrChangeType, $t, current_hd(), current_part()) }); + $w->can_focus(0); + $w->set_name($_); + $w; + } @types); +} + +sub createOrChangeType { + my ($in, $fs_type, $hd, $part, $all_hds) = @_; + + $part ||= !fs::get::hds_fstab($hd) && + { pt_type => 0, start => 1, size => $hd->{totalsectors} - 1 }; + $part or return; + if ($fs_type eq 'other') { + if (isEmpty($part)) { + try('Create', $hd, $part); + } else { + try('Type', $hd, $part); + } + } elsif (!$fs_type) { + if (isEmpty($part)) { + $in->ask_warn(N("Warning"), N("This partition is already empty")); + } else { + try('Delete', $hd, $part); + } + } elsif (isEmpty($part)) { + fs::type::set_fs_type($part, $fs_type); + diskdrake::interactive::Create($in, $hd, $part, $all_hds); + } else { + return if $fs_type eq $part->{fs_type}; + $in->ask_warn('', isBusy($part) ? N("Use ``Unmount'' first") : N("Use ``%s'' instead", N("Type"))); + } +} + +################################################################################ +# lvms: helpers +################################################################################ +sub lvm2kind { + my ($lvm) = @_; + { type => 'lvm', name => $lvm->{VG_name}, val => $lvm }; +} + +################################################################################ +# raids: helpers +################################################################################ +sub raid2kind() { + { type => 'raid', name => 'raid', val => $all_hds->{raids} }; +} + +################################################################################ +# loopbacks: helpers +################################################################################ +sub loopback2kind() { + { type => 'loopback', name => 'loopback', val => $all_hds->{loopbacks} }; +} + +1; |
