#!/usr/bin/perl -w
# Date: 14-02-2001	Alex DU <dxiaoming@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 IO::File;
use Locale::GetText;
use POSIX;
use Gtk;
use Gtk::Atoms;
use strict 'vars';
use vars qw($input $output);

# initializations
setlocale (LC_ALL, "");
Locale::GetText::textdomain ("nlpr");
import Locale::GetText I_;
init Gtk;

# Flag variable to indicate if we've got something from standard input.
my($FlagStdin) = 0;
my($APPNAME) = I_("Printer Filter Frontend");
my($SysConfig) = "/etc/nlpr/nlpr.conf";
my($UsrConfig) = $ENV{'HOME'}."/.nlpr.conf";
my( $win_main,
    $box_base,
    $box_left,
    $box_input,
    $lbl_input,
    $cmb_input,
    $btn_browse,
    $scrolled_win,
    $item,
    $label,
    $list_enc,
    $tgl_debug,
    $box_main,
    $box_debug,
    $text_debug,
    $vscrollbar,
    $separator,
    $box_right,
    $btn_preview,
    $btn_gen,
    $btn_print,
    $btn_quit,
    $curr_enc,
    $tmpstdin,
    @files
    );
my(%programs, %filters);

#####
#  The subroutine will parse the command line.
#  	If there is nothing at command line, it will try to read from STDIN, create a tmpfile to hold the standard 
#	input.
# 	Else it will think all parameters as filename to print, and return the list.
#
#####
sub parse_cmd {
    my($df, $inputLine, $testLast);
    
    if((scalar @ARGV) == 0) {
	do {
	    $tmpstdin = tmpnam();
    	} until $df = IO::File->new($tmpstdin, O_RDWR|O_CREAT|O_EXCL);
	unless (-t STDIN) {
	    while (<STDIN>) { print $df $_; }
	    $FlagStdin = 1;
	}
	return;
    }
    else {
	return @ARGV;
    }
}

#####
#  The configuration file has a format like following:
#       [PROGRAM]
#       preview=gv
#       [FILTERS]
#       zh_TW.Big5=bg5ps -i %input -o %output
#           The %input and %output will be replaced by program to generate the 
#           filtered PS file. That means that I assume all the filter has more
#           or less a command line like this.
#####
sub parse_conf {
    my($config) = @_;
    my($readp, $readf) = (0, 0);
    my(@lines);
    
    open(CONFIG, $config) || return 0;
    @lines= <CONFIG>;
    close(CONFIG);
    
    foreach(@lines) {
	chomp;
        s/#.*//;
        s/^\s+//;
        s/\s+$//;
        next unless length;
        if($_ eq '[PROGRAM]') { $readp = 1; $readf = 0; next;}
        if($_ eq '[FILTERS]') { $readf = 1; $readp = 0; next;}
        if($readp) {
            my($p, $n) = split(/\s*=\s*/, $_, 2);
            $programs{lc($p)} = $n;
        }
        if($readf) {
            my($f, $n) = split(/\s*=\s*/, $_, 2);
            $filters{$f} = $n;
        }
    }
}

#####
#  This subroutine will save the user configurations.
#####
sub save_conf {
    my($config) = @_;
    
    open RC, ">$config";
    print RC "[PROGRAM]\n";
    print RC "Preview=$programs{'preview'}\n";
    print RC "Print=$programs{'print'}\n";
    print RC "[FILTERS]\n";
    foreach (keys %filters) {
	print RC "$_=$filters{$_}\n";
    }
    close RC;
}

#####
#  This subroutine will generate filtered PS file.
#   Parameters: $encoding, $input, $output
#   Return:     the output filename.
#####
sub generate {
    (my($enc), $input, $output) = @_;
    my($df);
    
    unless($output) {
	do {
	    $output = tmpnam();
    	} until $df = IO::File->new($output, O_RDWR|O_CREAT|O_EXCL);
    }
    
    my($cmd) = $filters{$enc};
    $cmd =~ s/%(\w+)/${$1}/g;
    runcmd("$cmd");
    return $output;
}

#####
# Subroutine
#####
sub runcmd {
    my($cmd) = @_;

    $cmd .= " 2>&1 |"; # redirect the STDERR to STDOUT
    open STATUS, $cmd or die("Can't fork: $!");
    while (<STATUS>) {
	$text_debug->insert( undef, undef, undef, $_ );
#        Gtk->main_iteration while Gtk->events_pending;
    }
    close STATUS or die("unable to close properly nlpr: $! $?");
    return;
}

#####
#  This subroutine memorises the selectin in encoding box.
#####
sub select_item {
    $curr_enc = pop(@_);
}

#####
#  This subroutine check_type.
#	return 	True:	PS, txt files.
#		False:	others
#####
sub check_type {
    my($fname) = @_;
    my($type, $tmp);
    
    $type = `file $fname`;
    chop($type);
    ($tmp, $type) = split(':', $type, 2);
    $type =~ s/^\s+//;
    
    if($type =~ m/^PDL/g || $type =~ m/^PS/g || $type =~ m/text/g) {
	return 1;
    }
    else {
	return 0;
    }
}

#####
#  This subroutine will be called on browse button click.
#####
sub button_brw_event {
    my($file_dialog, $pos, $path, $dir);
    
    $file_dialog = new Gtk::FileSelection($APPNAME);
    $file_dialog->hide_fileop_buttons();
    $file_dialog->set_position("center");
    #$file_dialog->file_list->set_selection_mode('multiple');
    
    # Redirect to the last directory. Nice!
    $path = $cmb_input->entry->get_text();
    $pos = rindex($path, "/")+1;
    $dir = substr($path, 0, $pos);
    $file_dialog->set_filename($dir);
    
    $file_dialog->signal_connect("destroy", sub {destroy $file_dialog});
    $file_dialog->ok_button->signal_connect("clicked", 
					    sub { 
						$cmb_input->entry->set_text($file_dialog->get_filename());
						destroy $file_dialog; 
					    });
    $file_dialog->cancel_button->signal_connect("clicked", sub {destroy $file_dialog});
    $file_dialog->show();
}

#####
#	This subroutine will display a dialog and let user to choose OK or Cancel.
#	Return true:	OK 
#		   false:	Cancel 
#####
sub confirm_dialog {
    my($msgstr) = @_;
    my($confirmed) = 0;
    
    my($dialog) = new Gtk::Dialog();
    $dialog->set_modal(1);
    $dialog->set_policy(0, 0, 0);
    $dialog->set_position("center");
    
    $dialog->action_area->set_homogeneous(1);
    my($yes) = new Gtk::Button(I_("OK"));
    $yes->signal_connect("clicked", sub {destroy $dialog; Gtk::main_quit($dialog); $confirmed = 1;});
    $dialog->action_area->pack_start($yes, 1, 1, 10 );
    $yes->show();
    my($no) = new Gtk::Button(I_("Cancel"));
    $no->signal_connect("clicked", sub {destroy $dialog; Gtk::main_quit($dialog); $confirmed = 0;});
    $dialog->action_area->pack_start($no, 1, 1, 10);
    $no->show();
    
    $dialog->vbox->border_width(20);
    my($label) = new Gtk::Label("\n\n$msgstr\n\n");
    $label->set_justify('center');
    $label->set_line_wrap(1);
    $dialog->vbox->pack_start( $label, 1, 1, 0 );
    $label->show();
    $dialog->show();
    main Gtk;
    return $confirmed;
}

#####
#  This subroutine will be called on preview button click.
#####
sub button_view_event {
    my($src, $fview);
    
    # First we want to know if there is already something read from STDIN.
    if($FlagStdin) {
	$fview = generate($curr_enc, $tmpstdin);
	runcmd("$programs{'preview'} $fview");
	unlink($fview);
	return;
    }
    
    # First we grab the filename in input box.
    $src = $cmb_input->entry->get_text();
    # we verify if the file exists.
    unless (-e $src) {
	confirm_dialog(I_("The file: $src does not exist!"));
	return;
    }
    if(check_type($src)) {
	$fview = generate($curr_enc, $src);
	runcmd("$programs{'preview'} $fview");
	unlink($fview);
    }
    else {
	my($msgstr) = I_("File Type of ").`file $src`.I_("Do you really want to view it?");
	if(confirm_dialog($msgstr)){
	    runcmd("$programs{'preview'} $src");
	}
    }
}

#####
#  This subroutine will be called on preference button click.
#####
sub button_pref_event {
    my($confirmed) = 0;
    
    my($dialog) = new Gtk::Dialog();
    $dialog->set_modal(1);
    $dialog->set_policy(0, 0, 0);
    $dialog->set_position('center');
    $dialog->border_width(10);
    
    $dialog->action_area->set_homogeneous(1);
    my($save) = new Gtk::Button(I_('Save'));
    $save->signal_connect('clicked', sub {destroy $dialog; Gtk::main_quit($dialog); $confirmed = 1;});
    $dialog->action_area->pack_start($save, 1, 1, 10);
    $save->show();
    my($cancel) = new Gtk::Button(I_('Cancel'));
    $cancel->signal_connect('clicked', sub {destroy $dialog; Gtk::main_quit($dialog); $confirmed = 0;});
    $dialog->action_area->pack_start($cancel, 1, 1, 10);
    $cancel->show();
    
    $dialog->vbox->set_spacing(15);
    my($view_box) = new Gtk::HBox(0, 0);
    $dialog->vbox->pack_start($view_box, 0, 0, 0);
    $view_box->show();
    my($view_label) = new Gtk::Label(I_('Preview command: '));
    $view_box->pack_start($view_label, 0, 0, 0);
    $view_label->show;
    my($view_entry) = new Gtk::Entry();
    $view_entry->set_text($programs{'preview'});
    $view_box->pack_start($view_entry, 1, 1, 0);
    $view_entry->show;
    
    my($print_box) = new Gtk::HBox(0, 0);
    $dialog->vbox->pack_start($print_box, 0, 0, 0);
    $print_box->show();
    my($print_label) = new Gtk::Label(I_('  Print command: '));
    $print_box->pack_start($print_label, 0, 0, 0);
    $print_label->show;
    my($print_entry) = new Gtk::Entry();
    $print_entry->set_text($programs{'print'});
    $print_box->pack_start($print_entry, 1, 1, 0);
    $print_entry->show;
    
    $dialog->show;
    main Gtk;
    
    ##### We should check the existance of the new command. #####
    if($confirmed) {
	$programs{'preview'} = $view_entry->get_text();
	$programs{'print'}   = $print_entry->get_text();
    }
}

#####
#  This subroutine will be called on print button click.
#####
sub button_print_event {
    my($src, $fprint);
	
    if($FlagStdin) {
	$fprint = generate($curr_enc, $tmpstdin);
	runcmd("$programs{'print'} $fprint");
	unlink($fprint);
	return;
    }
    
    # First we grab the filename in input box.
    $src = $cmb_input->entry->get_text();
    # we verify if the file exists.
    unless (-e $src) {
	confirm_dialog(I_("The file: $src does not exist!"));
	return;
    }
    
    if(check_type($src)) {
	$fprint = generate($curr_enc, $src);
	runcmd("$programs{'print'} $fprint");
	unlink($fprint);
    }
    else {
	my($msgstr) = I_("File Type of ").`file $src`.I_("Do you really want to print it?");
	if(confirm_dialog($msgstr)){
	    runcmd("$programs{'print'} $src");
	}
    }
}

#####
#	This subroutine will print all files in the combo box.
#####
sub button_all_event {
    my($src, $fprint);
    
    if($FlagStdin || !(@files)) {
	button_print_event();
	return;
    }
    
    $" = "\n";
    my($msgstr) = I_("Following files will be printed:\n@files\n\nAre you really sure?");
    if(!confirm_dialog($msgstr)) { return; }
    foreach $src (@files) {
	unless (-e $src) {
	    confirm_dialog(I_("The file: $src does not exist!"));
	    return;
	}
	
	if(check_type($src)) {
	    $fprint = generate($curr_enc, $src);
	    runcmd("$programs{'print'} $fprint");
	    unlink($fprint);
	}
	else {
	    my($msgstr) = I_("File Type of ").`file $src`.I_("Do you really want to print it?");
	    if(confirm_dialog($msgstr)){
		runcmd("$programs{'print'} $src");
	    }
	}
    }
}

#####
# Subroutine:
#####
sub debug_toggle_event {
    my($togglebutton) = $_[0];
    if ($togglebutton->active) {
    	# If control reaches here, the toggle button is down
	if(!visible $box_debug) { $box_debug->show(); }
    }
    else {
    	# If control reaches here, the toggle button is up
	if(visible $box_debug) { $box_debug->hide; }
    }
}

#####
#  This subroutine will create the main application window.
#####
sub create_win {
    $win_main = new Gtk::Window('toplevel');
    $win_main->signal_connect("destroy" => \&Gtk::main_quit);
    $win_main->signal_connect("delete_event" => \&Gtk::false);
    $win_main->set_title($APPNAME);
    $win_main->border_width(0);
#    $win_main->set_policy( 0, 0, 1);
    $win_main->set_uposition(20, 20);
    $win_main->set_usize(420, 350);
    
    $box_main = new Gtk::VBox(0, 20);
    $box_main->set_border_width(20);
    $win_main->add($box_main);
    $box_main->show;
	
    $box_base = new Gtk::HBox(0, 5);
    $box_base->set_border_width(0);
    $box_main->pack_start($box_base, 1, 1, 0);
    $box_base->show;
    
    #####
    #  Here we have an input area to let user enter file name.
    #####
    $box_left = new Gtk::VBox(0, 0);
    $box_base->pack_start($box_left, 1, 1, 0);
    $box_left->show;
    
    $box_input = new Gtk::HBox(0, 5);
    $box_left->pack_start($box_input, 0, 0, 0);
    $box_input->show;
    
#    $lbl_input = new Gtk::Label("File: ");
#    $lbl_input->set_alignment(0, 0);
#    $box_input->pack_start($lbl_input, 0, 0, 0);
#    $lbl_input->show;
    
    $cmb_input = new Gtk::Combo();
    $box_input->pack_start($cmb_input, 1, 1, 0);
    $cmb_input->show;
    
    $btn_browse = new Gtk::Button(I_("Browse.."));
    $box_input->pack_start($btn_browse, 1, 1, 0);
    $btn_browse->signal_connect("clicked" => \&button_brw_event);
    $btn_browse->show;
    
    #####
    #  Here we have a List box to let user choose encoding.
    #####
    $separator = new Gtk::HSeparator;
    $separator->show;		
    $box_left->pack_start($separator, 0, 1, 10);
    
    $scrolled_win = new Gtk::ScrolledWindow(undef, undef);
    $scrolled_win->set_policy(-automatic, -automatic);
    $box_left->pack_start($scrolled_win, 1, 1, 0);
    $scrolled_win->show;
    
    $list_enc = new Gtk::List;
    $list_enc->set_selection_mode(-single);
    $list_enc->set_selection_mode(-browse);
    $scrolled_win->add_with_viewport($list_enc);
    $list_enc->show;
    
    # Add all the filters' informations parsed from config file to list.
    foreach (sort(keys %filters)) {
	$item = new Gtk::ListItem($_);
	$item->signal_connect("select", \&select_item, $_);
	$list_enc->add($item);
	$item->show;
    }
    
    $tgl_debug = new Gtk::CheckButton(I_("Open output window"));
    $tgl_debug->set_active(0);
    $tgl_debug->signal_connect("toggled" => \&debug_toggle_event);
    $box_left->pack_start($tgl_debug, 0, 0, 0);
    $tgl_debug->show;
    
    #####
    #  Here we create several buttons.
    #####
    $box_right = new Gtk::VBox(1, 20);
    $box_base->pack_start($box_right, 1, 1, 10);
    $box_right->show;
    
    # Preview Button
    $btn_preview = new Gtk::Button(I_("Preview"));
    $box_right->pack_start($btn_preview, 0, 0, 0);
    $btn_preview->signal_connect("clicked" => \&button_view_event);
    $btn_preview->show;
    # Generate Button
    $btn_gen = new Gtk::Button(I_("Preferences"));
    $box_right->pack_start($btn_gen, 0, 0, 0);
    $btn_gen->signal_connect("clicked" => \&button_pref_event);
    $btn_gen->show;
    # Print Button
    $btn_print = new Gtk::Button(I_("Print"));
    $box_right->pack_start($btn_print, 0, 0, 0);
    $btn_print->signal_connect("clicked" => \&button_print_event);	
    $btn_print->show;
    # Print all Button
    $btn_print = new Gtk::Button(I_("Print All"));
    $box_right->pack_start($btn_print, 0, 0, 0);
    $btn_print->signal_connect("clicked" => \&button_all_event);	
    $btn_print->show;

    # Quit Button;
    $btn_quit = new Gtk::Button(I_("Quit"));
    $box_right->pack_start($btn_quit, 0, 0, 0);
    $btn_quit->signal_connect("clicked", sub { destroy $win_main; });
    $btn_quit->show;
    
    $box_debug = new Gtk::HBox(0, 0);
    $box_main->pack_start($box_debug, 1, 1, 0);
#   $box_debug->show;
    
    $text_debug = new Gtk::Text(undef, undef);
    $text_debug->set_editable(1);
    $text_debug->set_word_wrap(1);
    $text_debug->set_usize(0, 50);
    $box_debug->pack_start($text_debug, 1, 1, 0);
    $text_debug->show;

    $vscrollbar = new Gtk::VScrollbar($text_debug->vadj);
    $box_debug->pack_start($vscrollbar, 0, 0, 0); 
    $vscrollbar->show;
    
    #####
    #  Display the main window.
    #####
    if (!visible $win_main) {
        show $win_main;
    } 
    else {
        destroy $win_main;
    }
}

sub DEBUG {
    foreach(keys %programs) {
        print("$_ => $programs{$_}\n");
    }
    foreach(keys %filters) {
        print("$_ => $filters{$_}\n");
    }
}

sub main {
    #In case that both sysconfig and usrconfig don't exist.
    # We use the default programs.
    $programs{'preview'} = 'gv';
    $programs{'print'} = 'lpr';
    
    parse_conf($SysConfig);
    parse_conf($UsrConfig);
    
    @files = parse_cmd();
    create_win();
    
    if ($FlagStdin) { 
	$cmb_input->entry->set_text(I_("<STDIN Accepted>"));
	$cmb_input->entry->set_editable(1);
	$btn_browse->hide();
    }
    elsif (@files) { 
	$cmb_input->set_popdown_strings(@files);
    }

#    DEBUG;

    main Gtk;
    save_conf($UsrConfig);
    unlink($tmpstdin);
}

main();
    
