#!/usr/bin/perl -w

# Control-center
# $Id$
# 
# Copyright (C) 2001-2002 MandrakeSoft
# Yves Duret <yduret at mandrakesoft.com>
#
# 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, 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
# MA 02111-1307, USA.


use POSIX;
use Gtk;
use lib qw(/usr/lib/libDrakX);

use standalone;     #- warning, standalone must be loaded very first, for 'explanations'

use interactive;
use Config;
use any;
init Gtk;
Gtk->set_locale;

#-------------------------------------------------------------
# i18n routines
# IMPORTANT: next two routines have to be redefined here to
#         get correct namespace (drakconf instead of libDrakX)
#         (This version is now UTF8 compliant - Sg 2001-08-18)
#-------------------------------------------------------------

sub _ {
    my $s = shift @_; my $t = translate($s);
    sprintf $t, @_;
}

sub translate {
    my ($s) = @_;
    $s ? c::dgettext('drakfloppy', $s) : '';
}

$::isEmbedded = ($::XID, $::CCPID) = "@ARGV" =~ /--embedded (\S*) (\S*)/;
if ($::isEmbedded) {
  print "EMBED\n";
  print "XID : $::XID\n";
  print "CCPID :  $::CCPID\n";
}

$in = 'interactive'->vnew('su', 'default');
local $_ = join '', @ARGV;

/-h/ and die _("usage: drakfloppy\n");

$expert_mode = 0;
# we have put here the list in order to do $list->clear() when we have to do
$fixed_font = Gtk::Gdk::Font->fontset_load(_("-misc-Fixed-Medium-r-*-*-*-140-*-*-*-*-*-*,*"));
my @titles = ( _("Module name"), _("Size") );
my $list = new_with_titles Gtk::CList( @titles );

my $window = $::isEmbedded ? new Gtk::Plug ($::XID) : new Gtk::Window -toplevel;
$window->signal_connect( 'delete_event', sub { $::isEmbedded ? kill(USR1, $::CCPID) : Gtk->exit(0) });
$window->set_title( _("drakfloppy") );
$window->set_policy(1, 1, 1);
$window->border_width (5);

### menus definition
# the menus are not shown
# but they provides shiny shortcut like C-q
my @menu_items = ( 
		  { path => _("/_File"), type => '<Branch>' },
		  { path => _("/File/_Quit"), accelerator => _("<control>Q"), callback => sub { $::isEmbedded ? kill(USR1, $::CCPID) : Gtk->exit(0) } },
		 );
my $menubar = get_main_menu( $window );

######### menus end

my $global_vbox = new Gtk::VBox();

$::isEmbedded or $global_vbox->pack_start (new Gtk::Label(_("boot disk creation")), 0, 0, 0);

######## up part
my $up_vbox  = new Gtk::VBox (0, 0);

# device part
my $dev_hbox = new Gtk::HBox (1, 0);
my $device_combo = new Gtk::Combo();
my $device_button = new Gtk::Button( _("default") );

$device_combo->set_popdown_strings( "/dev/fd0", "/dev/fd1", );
$device_button->signal_connect( 'clicked', sub { $device_combo->entry->set_text("/dev/fd0");});

$dev_hbox->pack_start (new Gtk::Label( _("device") ), 0, 0, 0);
$dev_hbox->pack_start ($device_combo, 0, 0, 0);
$dev_hbox->pack_start ($device_button, 0, 0, 0);
$up_vbox->pack_start  ($dev_hbox, 0, 0, 0);

# kernel part
my $ker_hbox = new Gtk::HBox (1, 0);
my $kernel_combo = new Gtk::Combo();
my $kernel_button = new Gtk::Button( _("default") );
$kernel_combo->disable_activate();
$kernel_combo->set_popdown_strings( do {
    opendir YREP, "/lib/modules" or die _("DrakFloppy Error: %s", $!);
    my @files_modules = grep !/^\.\.?$/, readdir YREP;
    closedir YREP;
    @files_modules;
});
#$kernel_combo->entry->set_text(`uname -r`);
$kernel_combo->entry->signal_connect( "changed", sub { change_tree($kernel_combo->entry->get_text()); $list->clear();});
$aaaa= `uname -r`;
chomp ($aaaa);
$kernel_button->signal_connect( 'clicked', sub { $kernel_combo->entry->set_text($aaaa); $list->clear(); });

$ker_hbox->pack_start (new Gtk::Label( _("kernel version") ), 0, 0, 0);
$ker_hbox->pack_start ($kernel_combo, 0, 0, 0);
$ker_hbox->pack_start ($kernel_button, 0, 0, 0);
$up_vbox->pack_start  ($ker_hbox, 0, 0, 5);

# vbox part
my $up_frame = new Gtk::Frame( _("General") );
$up_frame->add($up_vbox);
$global_vbox->pack_start ($up_frame, 1, 1, 0);

### expert mode
my $expert_main_frame = new Gtk::Frame( _("Expert Area") );
my $expert_dedans = new Gtk::VBox( 0, 5 );
$expert_dedans->border_width (5);
my $expert_button_frame = new Gtk::Frame( _("mkinitrd optional arguments") );
my $expert_mod_frame = new Gtk::Frame( _("Add a module") );
my $expert_pane = new Gtk::HPaned();
$expert_pane->set_handle_size( 10 );
$expert_pane->set_gutter_size( 8 );

my $expert_button = new Gtk::Button( _("Expert Mode") );
$expert_button->signal_connect( "clicked", sub {
				  if ($expert_mode) {
				    $expert_mod_frame->hide();
				    $expert_button_frame->hide()
				  } else {
				    $expert_mod_frame->show();
				    $expert_button_frame->show();
				  }
				    $expert_mode = !$expert_mode;
				});

my $expert_button_vbox = new Gtk::VBox(0, 5);
my $expert_button_hbox = new Gtk::HBox(0, 5);
my $expert_button_hbox2 = new Gtk::HBox(0, 5);
my $force_button = new Gtk::ToggleButton( _("force") );
my $needed_button = new Gtk::ToggleButton( _("if needed") );
my $scsi_button = new Gtk::ToggleButton( _("omit scsi modules") );
my $raid_button = new Gtk::ToggleButton( _("omit raid modules") );
$expert_button_hbox->pack_start( $force_button, 0, 0, 0 );
$expert_button_hbox->pack_start( $raid_button, 0, 0, 0 );

$expert_button_hbox2->pack_start( $needed_button, 0, 0, 0 );
$expert_button_hbox2->pack_start( $scsi_button, 0, 0, 0 );

$expert_button_vbox->pack_start($expert_button_hbox, 0, 0, 0);
$expert_button_vbox->pack_start($expert_button_hbox2, 0, 0, 0);
$expert_button_frame->add($expert_button_vbox);
$expert_dedans->pack_start ($expert_button_frame, 0, 0, 0);
$expert_mod_frame->add($expert_pane);
$expert_dedans->pack_start ($expert_mod_frame, 1, 1, 0);
$expert_main_frame->add($expert_dedans);
$global_vbox->pack_start ($expert_main_frame, 1, 1, 0);

### the tree

# Create a ScrolledWindow for the tree
my $tree_scrolled_win = new Gtk::ScrolledWindow();
$tree_scrolled_win->set_usize( 200, $::isEmbedded ? 0 : 175);
$expert_pane->add1( $tree_scrolled_win );
$tree_scrolled_win->set_policy( 'automatic', 'automatic' );

# Create root tree
my $tree = new Gtk::Tree();
my $leaf;
my $root_dir;
$tree_scrolled_win->add_with_viewport( $tree );
$tree->set_selection_mode( 'single' );
$tree->set_view_mode( 'item' );

fill_tree ($kernel_combo->entry->get_text());

# Create a ScrolledWindow for the list
my $list_scrolled_win = new Gtk::ScrolledWindow( undef, undef );
my $rmmod_button = new Gtk::Button( _("Remove a module") );
my $expert_inside_pane2 = new Gtk::VBox (0, 0);
my $list_selected_row;

$expert_inside_pane2->pack_start ($list_scrolled_win, 1, 1, 0);
$expert_inside_pane2->pack_start ($rmmod_button, 0, 0, 0);
$expert_pane->add2( $expert_inside_pane2 );
$list_scrolled_win->set_policy( 'automatic', 'automatic' );
$rmmod_button->signal_connect('clicked', sub {$list->remove($list_selected_row);});

# Create list box
########################################################## from here my $list
$list->signal_connect('select_row', sub { (undef, $list_selected_row) = @_; });
$list_scrolled_win->add( $list );
$list->set_column_justification(1, 'right');
$list->set_column_width( 0, 200 );
$list->set_column_width( 1, 50 );
$list->set_selection_mode( 'single' );
$list->set_shadow_type( 'none' );
$list->show();

### output
my $output_frame = new Gtk::Frame( _("Output") );
my $output = new Gtk::Text( undef, undef );
my $vscrollbar = new Gtk::VScrollbar( $output->vadj );
my $output_hbox = new Gtk::HBox (0, 0);
$output_hbox->border_width (5);
$output_hbox->set_usize( 30, 75 );
$output_hbox->pack_start( $output, 1, 1, 0 );
$output_hbox->pack_start( $vscrollbar, 0, 0, 0 );
$output_frame->add ($output_hbox);
$global_vbox->pack_start ($output_frame, 0, 0, 0);

### final buttons
my $build_button = new Gtk::Button( _("Build the disk") );
my $cancel_button = new Gtk::Button( _("Cancel") );
my $fin_hbox = new Gtk::HBox( 0, 0 );
$cancel_button->signal_connect( clicked=> sub {$::isEmbedded ? kill(USR1, $::CCPID) : Gtk->exit(0)});
$build_button->signal_connect('clicked', \&build_it);
$fin_hbox->pack_end($cancel_button, 0, 0, 0);
$fin_hbox->pack_end($build_button,  0, 0, 10);
$fin_hbox->pack_end($expert_button, 0, 0, 10);
$global_vbox->pack_start ($fin_hbox, 0, 0, 0);

### back to window
$window->add( $global_vbox );

$window->show_all();
$expert_mod_frame->hide();
$expert_button_frame->hide();

Gtk->main_iteration while Gtk->events_pending;
$::isEmbedded and kill USR2, $::CCPID;
Gtk->main;



#-------------------------------------------------------------
# tree functions
#-------------------------------------------------------------
### Subroutines

sub fill_tree {
  ($root_dir) = @_;
  $root_dir = "/lib/modules/" . $root_dir;
  # Create root tree item widget
  $leaf = new_with_label Gtk::TreeItem( $root_dir );
  $tree->append( $leaf );
  $leaf->signal_connect( 'select', \&select_item, $root_dir );
  $leaf->set_user_data( $root_dir );

  # Create the subtree
  if ( has_sub_trees( $root_dir ) ) {
    my $subtree = new Gtk::Tree();
    $leaf->set_subtree( $subtree );
    $leaf->signal_connect( 'expand', \&expand_tree, $subtree );
    $leaf->signal_connect( 'collapse', \&collapse_tree );
    $leaf->expand();
  }
}

sub change_tree {
  $leaf->destroy();
  fill_tree (@_);
  $leaf->show();
}

# Callback for expanding a tree - find subdirectories, files and add them to tree
sub expand_tree
  {
    my ( $item, $subtree ) = @_;

    my $dir_entry;
    my $path;
    my $item_new;
    my $new_subtree;

    my $dir = $item->get_user_data();

    chdir( $dir );

    foreach $dir_entry ( <*> ) {
      if (( -d $dir_entry ) or ( $dir_entry =~ /\.o(\.gz)?$/)) {
	$path = $dir . "/" . $dir_entry;
	$path =~ s|//|/|g;
	$item_new = new_with_label Gtk::TreeItem( $dir_entry );
	$item_new->set_user_data( $path );
	$item_new->signal_connect( 'select', \&select_item, $path );
	$subtree->append( $item_new );
	$item_new->show();

	if ( has_sub_trees( $path ) ) {
	  $new_subtree = new Gtk::Tree();
	  $item_new->set_subtree( $new_subtree );
	  $item_new->signal_connect( 'expand', \&expand_tree, $new_subtree );
	  $item_new->signal_connect( 'collapse', \&collapse_tree );
	}
      }
    }
    chdir( ".." );
  }


# Callback for collapsing a tree -- removes the subtree
sub collapse_tree
  {
    my ( $item ) = @_;
    my $subtree = new Gtk::Tree();

    $item->remove_subtree();
    $item->set_subtree( $subtree );
    $item->signal_connect( 'expand', \&expand_tree, $subtree );
  }

# Called whenever an item is clicked on the tree widget.
sub select_item  {
  my ( $widget, $file ) = @_;
  return  if (-d $file);
  my  $size = ( lstat( $file ) )[ 7 ];
  my $lr = $list->rows();
  my $i;
  $file =~ s|/lib/modules/.*?/||g;
  for ($i=0; $i < $lr; $i++) {
    last if ($file eq $list->get_text($i, 0));
  }
  print $file,"\n";
  
  $list->append($file, $size) if ($i == $lr) or ($lr == 0);
}

#-------------------------------------------------------------
# menu callback functions
#-------------------------------------------------------------

sub print_hello {
  print "mcdtg !\n";
}

sub get_main_menu {
  my ( $window ) = @_;
  my $accel_group = new Gtk::AccelGroup();
  my $item_factory = new Gtk::ItemFactory( 'Gtk::MenuBar', '<main>', $accel_group );
  $item_factory->create_items( @menu_items );
  $window->add_accel_group( $accel_group );
  return ( $item_factory->get_widget( '<main>' ) );
}


sub create_dialog {
    my ( $label, $c ) = @_;
    my $ret = 0;
    my $dialog = new Gtk::Dialog;
    $dialog->signal_connect ( delete_event => sub {Gtk->main_quit();});
    $dialog->set_title(_("drakfloppy"));
    $dialog->border_width(10);
    $dialog->vbox->pack_start(new Gtk::Label($label),1,1,0);

    my $button = new Gtk::Button _("OK");
    $button->can_default(1);
    $button->signal_connect(clicked => sub { $ret = 1; $dialog->destroy(); Gtk->main_quit(); });
    $dialog->action_area->pack_start($button, 1, 1, 0);
    $button->grab_default;

    if ($c) {
	my $button2 = new Gtk::Button _("Cancel");
	$button2->signal_connect(clicked => sub { $ret = 0; $dialog->destroy(); Gtk->main_quit(); });
	$button2->can_default(1);
	$dialog->action_area->pack_start($button2, 1, 1, 0);
    }

    $dialog->show_all;
    Gtk->main();
    $ret;
}

sub destroy_window {
	my($widget, $windowref, $w2) = @_;
	$$windowref = undef;
	$w2 = undef if defined $w2;
	0;
}


#-------------------------------------------------------------
# the function
#-------------------------------------------------------------
sub build_it {
  my $y;
  my $co = "/sbin/mkbootdisk --noprompt --verbose --device ". $device_combo->entry->get_text();
  if ($expert_mode) {
    $co .= " --mkinitrdargs -f" if $force_button->get_active;
    $co .= " --mkinitrdargs --ifneeded" if $needed_button->get_active;
    $co .= " --mkinitrdargs --omit-scsi-modules" if $scsi_button->get_active;
    $co .= " --mkinitrdargs --omit-raid-modules" if $raid_button->get_active;
    for (my $i=0; $i<$list->rows(); $i++) {
      $y = $list->get_text($i, 0);
      $y =~ s|.*?/||g;
      $co .= " --mkinitrdargs --with=" . $y; #. "/usr/lib/" . $kernel_combo->entry->get_text() . "/" . $y;
    }
  }
  $co .= " " . $kernel_combo->entry->get_text();
  $co .= " 2>&1 |";
  create_dialog(_("Be sure a media is present for the device %s",  $device_combo->entry->get_text()), 1) or return;
# we test if the media is present
 test:
  my $a = "dd count=1 if=/dev/null of=". $device_combo->entry->get_text() ." 2>&1";
  my $b= `$a`;
  if ($b =~ "dd") {create_dialog(_("There is no medium or it is write-protected for device %s.\nPlease insert one.", $device_combo->entry->get_text()), 1) ? goto test : return 0; }
  
  open STATUS, $co or do { create_dialog(_("Unable to fork: %s", $!), 0); return; };
  while (<STATUS>) {
      $output->insert( $fixed_font, undef, undef, $_ );
  }
  close STATUS or create_dialog(_("Unable to close properly mkbootdisk: \n %s \n %s", $!, $?), 0);
  
  return (0);
}

####
# This is put at the end of the file because any translatable string
# appearing after this will not be found by xgettext, and so wont end in
# the pot file...
####

# Test whether a directory has subdirectories
sub has_sub_trees
  {
    my ( $dir ) = @_;
    my $file;

    foreach $file ( <$dir/*> ) {
      return 1 if ( -d $file ) or ($file =~ /\.o(\.gz)?$/);
    }

    return (0);
  }