#!/usr/bin/perl -w
# Used to watch TV with mplayer
# Copyright (C) Peter Ivanov <ivanovp@gmail.com>, 2004, 2005, 2006
# Licensed under GPL version 2
# For more information run: mplayerTV -h
# 
# Last modify: 2006-12-25 19:18:24 ivanovp {Time-stamp}
require 5.8.0;

use warnings;
use Gtk2;               # load the Gtk2-Perl module
use Getopt::Std;        # for command line options
use Gtk2::SimpleMenu;

#use FindBin;
#use lib "$FindBin::RealBin/MPlayer-0.03/lib"; # for testing purposes only
use MPlayer;            # :)
use threads;

set_locale Gtk2;        # internationalize
init Gtk2;              # initialize Gtk2-Perl

my $cmd = $0;
my %opts;
my $prog_name = 'mplayerTV';
my $prog_version = 'v0.2.1rc1';
my $prog = $prog_name . " " . $prog_version;
my $release_date = '2006-12-25';
my $copy = 'Copyright (C) Peter Ivanov <ivanovp@gmail.com>, 2004, 2005, 2006';
my $homepage = 'Homepage: http://mplayertv.ivanov.eu/';
my $header = "$prog\n$copy\n$homepage";
my $verbose_level = 0;  # you could change with -d to 1 and with -D to 2

my $copyright_text = <<"EOT";
$header

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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

EOT

my $mplayer_path = "mplayer";
my $scantv_path = "scantv";
my $terminal_path = "xterm";
my $browser_path = "x-www-browser";

getopts("hcvwdD", \%opts);

$verbose_level = 1 if ($opts{d});
$verbose_level = 2 if ($opts{D});

if ($opts{h}) {
    usage();
}
if ($opts{v}) {
    print "$prog\n";
    print "Release date: $release_date\n";
    exit (1);
}
if ($opts{c}) {
    copyright();
}

if ($verbose_level >= 1) {
    print "$prog\n";
    print "Release date: $release_date\n";
    print "\n";
    print "Verbose level: $verbose_level\n";
    print "OS: ", $^O, "\n";
    open F, "</proc/version" || warn "Can't open /proc/version\n";
    print "Kernel: ", <F>;
    close F;
    print "lspci:\n", `lspci`, "\n";
}
if ($verbose_level >= 2) {
    print "lspci -v\n", `lspci -v`, "\n"
}

my $app = new MyApp ();
$app->show ();
if ($opts{w} || $cmd =~ /mplayerTVw$/) {
    $app->start_mplayer ($app);
}

# eats CPU and does not working :(
#Glib::Idle->add (sub {
#        if ($app->{new_channel} >= 0) {
#            $app->{name_list}->select_item ($app->{new_channel});
#            $app->{new_channel} = -1;
#            print "name_list update!\n";
#        }
#        return 1;
#   });

# Gtk2 event loop
main Gtk2;

# Should never get here
exit (0);

##############################################################################
sub usage
{
    die<<"EOT";
$header

This program comes with ABSOLUTELY NO WARRANTY; for details 
type '$cmd -c'.
This is free software, and you are welcome to redistribute it
under certain conditions; type '$cmd -c' for details.

Usage: $cmd [-w] [-h] [-v] [-c] [-d] [-D]
Options:
  -w: run in 'watch it' mode
  -h: prints help
  -v: prints version
  -c: prints copyright informations
  -d: verbose
  -D: very verbose
EOT
}

sub copyright
{
    die $copyright_text;
    exit;
}

### ConfigureDialog ojjektum ###
package ConfigureDialog;

sub new 
{
    my ($class, $config, $cfg_file, $app) = @_;
    my $self = {};
    bless $self, $class;
    $self->{selected_channel} = undef;
    
    my @pp_filters = (
            "Linear blend deinterlacer (lb)",
            "Linear interpolating deinterlace (li)",
            "Cubic interpolating deinterlacer (ci)",
            "Median deinterlacer (md)",
            "FFmpeg deinterlacer (fd)",
            "Temporal noise reducer (tn)",
            "Horizontal deblocking (hb)", 
            "Vertical deblocking (vb)", 
            "Experimental horizontal deblock (h1)",
            "Experimental vertical deblock (v1)",
            "Deringing (dr)",
            "Automatic brightness/contrast (al)"
            );
    $self->{pp_codes} = $app->{pp_codes};
    $self->{vf_table} = $app->{vf_table};
    $self->{config} = $config;
    $self->{cfg_file} = $cfg_file;
    $self->{app} = $app;
    $self->{tooltips} = new Gtk2::Tooltips ();
    $self->{dialog} = new Gtk2::Dialog ();

    ### NOTEBOOKS AND PAGES ###
    
    $self->{notebook} = new Gtk2::Notebook;
    $self->{dialog}->vbox->pack_start ($self->{notebook}, 0, 0, 10);
    $self->{page1} = new Gtk2::VBox (0, 0);
    $self->{page1}->set_border_width (5);
    $self->{notebook}->append_page ($self->{page1}, "Channels");
    $self->{page2} = new Gtk2::VBox (0, 0);
    $self->{page2}->set_border_width (5);
    $self->{notebook}->append_page ($self->{page2}, "General");
    $self->{page3} = new Gtk2::VBox (0, 0);
    $self->{page3}->set_border_width (5);
    $self->{notebook}->append_page ($self->{page3}, "Video");
    $self->{page5} = new Gtk2::VBox (0, 0);
    $self->{page5}->set_border_width (5);
    $self->{notebook}->append_page ($self->{page5}, "Audio");
    $self->{page4} = new Gtk2::VBox (0, 0);
    $self->{page4}->set_border_width (5);
    $self->{notebook}->append_page ($self->{page4}, "Filters");
    #$self->{page6} = new Gtk2::VBox (0, 10);
    #$self->{notebook}->append_page ($self->{page6}, "Audio filters");
    
    ### PAGE 1: CHANNELS
    $self->{table1} = new Gtk2::Table (3, 6, 0);
    $self->{page1}->pack_start ($self->{table1}, 1, 1, 0);
    $self->{name_list} = new Gtk2::List ();
    $self->{name_list}->select_item (0);
    $self->{name_list_sw} = new Gtk2::ScrolledWindow (undef, undef);
    $self->{name_list_sw}->set_policy (GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    $self->{name_list_sw}->add_with_viewport($self->{name_list});
    $self->{table1}->attach ($self->{name_list_sw}, 0, 1, 0, 4, ['fill', 'expand'], ['fill', 'expand'], 4, 4);
    $self->update_channel_list ();
    
    $self->{scantv_btn} = Gtk2::Button->new_from_stock ("scan");
    $self->{scantv_btn}->set_size_request (60, -1);
    $self->{tooltips}->set_tip ($self->{scantv_btn}, "Runs scantv and updates channel list");
    $self->{table1}->attach ($self->{scantv_btn},       1, 2, 0, 1, [], [], 2, 2);
    
    $self->{table1b} = new Gtk2::Table (11, 3, 0);
    $self->{table1}->attach ($self->{table1b},          1, 2, 2, 3, ['fill'], [], 2, 2);
    $self->{name_label} = new Gtk2::Label ("Name:");
    $self->{name_label}->set_alignment (0, 0);
    $self->{name_ent} = new Gtk2::Entry ();
    $self->{name_ent}->set_size_request (140, -1);
    $self->{channel_label} = new Gtk2::Label ("Channel:");
    $self->{channel_label}->set_alignment (0, 0);
    $self->{channel_ent} = new Gtk2::Entry ();
    $self->{channel_ent}->set_size_request (140, -1);
    $self->{table1b}->attach ($self->{name_label},      0, 3, 0, 1, ['fill'], [], 2, 2);
    $self->{table1b}->attach ($self->{name_ent},        0, 3, 1, 2, ['fill'], [], 2, 2);
    $self->{table1b}->attach ($self->{channel_label},   0, 3, 2, 3, ['fill'], [], 2, 2);
    $self->{table1b}->attach ($self->{channel_ent},     0, 3, 3, 4, ['fill'], [], 2, 2);
    
    $self->{brightness_label} = new Gtk2::Label ("Brightness:");
    $self->{brightness_label}->set_alignment (0, 0);
    $self->{table1b}->attach ($self->{brightness_label},0, 1, 4, 5, ['fill', 'expand'], [], 2, 2);

    $self->{brightness_spin} = Gtk2::SpinButton->new_with_range (-100, 100, 1);
    $self->{brightness_spin}->set_value (0);
    $self->{table1b}->attach ($self->{brightness_spin}, 1, 2, 4, 5, ['fill', 'expand'], [], 2, 2);
    
    $self->{contrast_label} = new Gtk2::Label ("Contrast:");
    $self->{contrast_label}->set_alignment (0, 0);
    $self->{table1b}->attach ($self->{contrast_label},  0, 1, 5, 6, ['fill', 'expand'], [], 2, 2);
    
    $self->{contrast_spin} = Gtk2::SpinButton->new_with_range (-100, 100, 1);
    $self->{contrast_spin}->set_value (0);
    $self->{table1b}->attach ($self->{contrast_spin},   1, 2, 5, 6, ['fill', 'expand'], [], 2, 2);
    
    $self->{hue_label} = new Gtk2::Label ("Hue:");
    $self->{hue_label}->set_alignment (0, 0);
    $self->{table1b}->attach ($self->{hue_label},       0, 1, 6, 7, ['fill', 'expand'], [], 2, 2);
    
    $self->{hue_spin} = Gtk2::SpinButton->new_with_range (-100, 100, 1);
    $self->{hue_spin}->set_value (0);
    $self->{table1b}->attach ($self->{hue_spin},        1, 2, 6, 7, ['fill', 'expand'], [], 2, 2);
    
    $self->{saturation_label} = new Gtk2::Label ("Saturation:");
    $self->{saturation_label}->set_alignment (0, 0);
    $self->{table1b}->attach ($self->{saturation_label},0, 1, 7, 8, ['fill', 'expand'], [], 2, 2);
    
    $self->{saturation_spin} = Gtk2::SpinButton->new_with_range (-100, 100, 1);
    $self->{saturation_spin}->set_value (0);
    $self->{table1b}->attach ($self->{saturation_spin}, 1, 2, 7, 8, ['fill', 'expand'], [], 2, 2);
    
    $self->{volume_label} = new Gtk2::Label ("Volume:");
    $self->{volume_label}->set_alignment (0, 0);
    $self->{table1b}->attach ($self->{volume_label},    0, 1, 8, 9, ['fill', 'expand'], [], 2, 2);
    
    $self->{volume_spin} = Gtk2::SpinButton->new_with_range (-100, 100, 1);
    $self->{volume_spin}->set_value (0);
    $self->{table1b}->attach ($self->{volume_spin},     1, 2, 8, 9, ['fill', 'expand'], [], 2, 2);
    
    $self->{table1d} = new Gtk2::Table (2, 2, 1);
    $self->{table1}->attach ($self->{table1d},          1, 2, 3, 4, ['fill'], [], 2, 2);
    $self->{modify_btn} = Gtk2::Button->new_from_stock ("modify");
    $self->{new_btn} = Gtk2::Button->new_from_stock ("new");
    $self->{delete_btn} = Gtk2::Button->new_from_stock ("delete");
    $self->{deleteall_btn} = Gtk2::Button->new_from_stock ("deleteall");
    $self->{table1d}->attach ($self->{modify_btn},      0, 1, 0, 1, ['fill', 'expand'], [], 2, 2);
    $self->{table1d}->attach ($self->{new_btn},         1, 2, 0, 1, ['fill', 'expand'], [], 2, 2);
    $self->{table1d}->attach ($self->{delete_btn},      0, 1, 1, 2, ['fill', 'expand'], [], 2, 2);
    $self->{table1d}->attach ($self->{deleteall_btn},   1, 2, 1, 2, ['fill', 'expand'], [], 2, 2);

    $self->{up_btn} = Gtk2::Button->new_from_stock ("up");
    $self->{down_btn} = Gtk2::Button->new_from_stock ("down");
    $self->{table1c} = new Gtk2::Table (1, 2, 1);
    $self->{table1}->attach ($self->{table1c},          0, 1, 4, 5, [], [], 2, 2);
    $self->{table1c}->attach ($self->{up_btn},          0, 1, 0, 1, ['fill'], [], 2, 2);
    $self->{table1c}->attach ($self->{down_btn},        1, 2, 0, 1, ['fill'], [], 2, 2);

    $self->{up_btn}->signal_connect ("clicked", \&UpBtn, $self);
    $self->{down_btn}->signal_connect ("clicked", \&DownBtn, $self);
    $self->{new_btn}->signal_connect ("clicked", \&NewBtn, $self);
    $self->{modify_btn}->signal_connect ("clicked", \&ModifyBtn, $self);
    $self->{delete_btn}->signal_connect ("clicked", \&DeleteBtn, $self);
    $self->{deleteall_btn}->signal_connect ("clicked", \&DeleteAllBtn, $self);
    $self->{scantv_btn}->signal_connect ("clicked", \&ScantvBtn, $self);
    
    ### PAGE 2: GENERAL
    $self->{table2} = new Gtk2::Table (4, 2, 0);
    $self->{page2}->pack_start ($self->{table2}, 1, 1, 0);
    
    #$self->{mplayer_path_label} = new Gtk2::Label ("mplayer:");
    #$self->{mplayer_path_label}->set_alignment (0, 0);
    #$self->{table2}->attach ($self->{mplayer_path_label},       0, 1, 1, 2, ['fill',], [], 2, 2);
    #$self->{mplayer_path_ent} = new Gtk2::Entry ();
    #$self->{mplayer_path_ent}->set_size_request (100, -1);
    #$self->{mplayer_path_ent}->set_text ($self->{config}->{mplayer});
    #$self->{table2}->attach ($self->{mplayer_path_ent},         1, 2, 1, 2, ['fill', 'expand'], [], 2, 2);
    #$self->{mplayer_browse_btn} = new Gtk2::Button ("...");
    #$self->{mplayer_browse_btn}->signal_connect ("clicked", \&MplayerBrowseBtn, $self);
    #$self->{tooltips}->set_tip ($self->{mplayer_browse_btn}, "Browse");
    #$self->{table2}->attach ($self->{mplayer_browse_btn},       2, 3, 1, 2, [], [], 2, 2);

    $self->{preexec_label} = new Gtk2::Label ("Pre-exec:");
    $self->{preexec_label}->set_alignment (0, 0);
    $self->{preexec_ent} = new Gtk2::Entry ();
    #$self->{preexec_ent}->set_size_request (100, -1);
    $self->{preexec_ent}->set_text ($self->{config}->{preexec});
    $self->{table2}->attach ($self->{preexec_label},            0, 1, 0, 1, ['fill'], [], 2, 2);
    $self->{table2}->attach ($self->{preexec_ent},              1, 2, 0, 1, ['fill', 'expand'], [], 2, 2);
    $self->{postexec_label} = new Gtk2::Label ("Post-exec:");
    $self->{postexec_label}->set_alignment (0, 0);
    $self->{postexec_ent} = new Gtk2::Entry ();
    #$self->{postexec_ent}->set_size_request (100, -1);
    $self->{postexec_ent}->set_text ($self->{config}->{postexec});
    $self->{table2}->attach ($self->{postexec_label},           0, 1, 1, 2, ['fill'], [], 2, 2);
    $self->{table2}->attach ($self->{postexec_ent},             1, 2, 1, 2, ['fill', 'expand'], [], 2, 2);
    
    $self->{terminal_emulator_label} = new Gtk2::Label ("Terminal:");
    $self->{terminal_emulator_label}->set_alignment (0, 0);
    $self->{table2}->attach ($self->{terminal_emulator_label},  0, 1, 4, 5, ['fill'], [], 2, 2);
    $self->{terminal_emulator_ent} = new Gtk2::Entry ();
    $self->{terminal_emulator_ent}->set_size_request (100, -1);
    $self->{terminal_emulator_ent}->set_text ($self->{config}->{terminal_emulator});
    $self->{table2}->attach ($self->{terminal_emulator_ent},    1, 2, 4, 5, ['fill'], [], 2, 2);
    $self->{terminal_emulator_browse_btn} = new Gtk2::Button ("...");
    $self->{terminal_emulator_browse_btn}->signal_connect ("clicked", \&TerminalEmulatorBrowseBtn, $self);
    $self->{tooltips}->set_tip ($self->{terminal_emulator_browse_btn}, "Browse");
    $self->{table2}->attach ($self->{terminal_emulator_browse_btn}, 2, 3, 4, 5, [], [], 2, 2);
    
    $self->{mplayer_opts_label} = new Gtk2::Label ("MPlayer options:");
    $self->{mplayer_opts_label}->set_alignment (0, 0);
    $self->{table2}->attach ($self->{mplayer_opts_label},       0, 1, 6, 7, ['fill'], [], 2, 2);
    $self->{mplayer_opts_ent} = new Gtk2::Entry ();
    $self->{mplayer_opts_ent}->set_size_request (100, -1);
    $self->{mplayer_opts_ent}->set_text ($self->{config}->{mplayer_opts});
    $self->{table2}->attach ($self->{mplayer_opts_ent},         1, 2, 6, 7, ['fill'], [], 2, 2);
    
    #$self->{scantv_label} = new Gtk2::Label ("scantv:");
    #$self->{scantv_label}->set_alignment (0, 0);
    #$self->{scantv_ent} = new Gtk2::Entry ();
    #$self->{scantv_ent}->set_size_request (100, -1);
    #$self->{scantv_ent}->set_text ($self->{config}->{scantv});
    #$self->{table2}->attach ($self->{scantv_label},             0, 1, 5, 6, ['fill'], [], 2, 2);
    #$self->{table2}->attach ($self->{scantv_ent},               1, 2, 5, 6, ['fill'], [], 2, 2);
    #$self->{scantv_browse_btn} = new Gtk2::Button ("...");
    #$self->{scantv_browse_btn}->signal_connect ("clicked", \&ScantvBrowseBtn, $self);
    #$self->{tooltips}->set_tip ($self->{scantv_browse_btn}, "Browse");
    #$self->{table2}->attach ($self->{scantv_browse_btn},        2, 3, 5, 6, [], [], 2, 2);
   
    ### PAGE 3: VIDEO
    $self->{table3} = new Gtk2::Table (13, 3, 0);
    $self->{page3}->pack_start ($self->{table3}, 1, 1, 0);

    $self->{tv_drivers_label} = new Gtk2::Label ("TV driver: ");
    $self->{tv_drivers_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{tv_drivers_label},         0, 1, 1, 2, ['fill'], [], 2, 2);
    $self->{tv_drivers_option} = new Gtk2::OptionMenu;
    $self->{tv_drivers_menu} = new Gtk2::Menu;
    $self->{tv_drivers_option}->set_menu ($self->{tv_drivers_menu});
    $self->{tv_drivers_option}->set_size_request (100, -1);
    $self->{tv_drivers_option}->set_history (0);
    my $i = 0;
    foreach ("v4l", "v4l2", "bsdbt848", "dummy") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{tv_drivers_menu}->append($item);
        $self->{tv_drivers_option}->set_history ($i) if ($_ eq $self->{config}->{tv});
        $i++;
    }
    $self->{table3}->attach ($self->{tv_drivers_option},        1, 2, 1, 2, ['fill'], [], 2, 2);
    
    $self->{tv_device_label} = new Gtk2::Label ("TV device:");
    $self->{tv_device_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{tv_device_label},          0, 1, 2, 3, ['fill'], [], 2, 2);
    $self->{tv_device_ent} = new Gtk2::Entry ();
    $self->{tv_device_ent}->set_size_request (100, -1);
    $self->{tv_device_ent}->set_text ($self->{config}->{tv_device});
    $self->{table3}->attach ($self->{tv_device_ent},            1, 2, 2, 3, ['fill'], [], 2, 2);

    $self->{vo_drivers_label} = new Gtk2::Label ("Video output driver:");
    $self->{vo_drivers_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{vo_drivers_label},         0, 1, 3, 4, ['fill'], [], 2, 2);
    $self->{vo_drivers_option} = new Gtk2::OptionMenu;
    $self->{vo_drivers_menu} = new Gtk2::Menu;
    $self->{vo_drivers_option}->set_menu($self->{vo_drivers_menu});
    $self->{vo_drivers_option}->set_size_request (100, -1);
    $self->{vo_drivers_option}->set_history(0);
    $self->update_vo_drivers_list ();
    $self->{table3}->attach ($self->{vo_drivers_option},        1, 2, 3, 4, ['fill'], [], 2, 2);
    #$self->{vo_update_btn} = new Gtk2::Button ("_Update");
    #$self->{vo_update_btn}->set_size_request (60, -1);
    #$self->{vo_update_btn}->signal_connect ("clicked", sub { $self->update_vo_drivers_list ()});
    #$self->{tooltips}->set_tip ($self->{vo_update_btn}, "Runs mplayer and fetch video output drivers list");
    #$self->{table3}->attach ($self->{vo_update_btn},            2, 3, 3, 4, ['fill'], [], 2, 2);
    
    $self->{vo_subdriver_label} = new Gtk2::Label ("Video output subdriver:");
    $self->{vo_subdriver_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{vo_subdriver_label},       0, 1, 4, 5, ['fill'], [], 2, 2);
    $self->{vo_subdriver_ent} = new Gtk2::Entry ();
    $self->{vo_subdriver_ent}->set_size_request (100, -1);
    $self->{vo_subdriver_ent}->set_text ($self->{config}->{subvo});
    $self->{table3}->attach ($self->{vo_subdriver_ent},         1, 2, 4, 5, ['fill'], [], 2, 2);
    
    $self->{outfmts_label} = new Gtk2::Label ("Output format of tuner:");
    $self->{outfmts_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{outfmts_label},            0, 1, 5, 6, ['fill'], [], 2, 2);
    $self->{outfmts_option} = new Gtk2::OptionMenu;
    $self->{outfmts_menu} = new Gtk2::Menu;
    $self->{outfmts_option}->set_menu ($self->{outfmts_menu});
    $self->{outfmts_option}->set_size_request (100, -1);
    $self->{outfmts_option}->set_history(0);
    $self->update_outfmts_list ();
    $self->{table3}->attach ($self->{outfmts_option},           1, 2, 5, 6, ['fill'], [], 2, 2);
    
    $self->{norm_label} = new Gtk2::Label ("Norm:");
    $self->{norm_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{norm_label},               0, 1, 6, 7, ['fill'], [], 2, 2);
    $self->{norm_option} = new Gtk2::OptionMenu;
    $self->{norm_menu} = new Gtk2::Menu;
    $self->{norm_option}->set_menu ($self->{norm_menu});
    $self->{norm_option}->set_size_request (100, -1);
    $self->{norm_option}->set_history (0);
    $i = 0;
    foreach ("PAL", "NTSC", "SECAM", "PAL-Nc", "PAL-M", "PAL-N", "NTSC-JP", "PAL-60") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{norm_menu}->append($item);
        $self->{norm_option}->set_history ($i) if ($_ eq $self->{config}->{_defaults}->{norm});
        $i++;
    }
    $self->{table3}->attach ($self->{norm_option},              1, 2, 6, 7, ['fill'], [], 2, 2);

    $self->{input_label} = new Gtk2::Label ("Input:");
    $self->{input_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{input_label},              0, 1, 7, 8, ['fill'], [], 2, 2);
    $self->{input_option} = new Gtk2::OptionMenu;
    $self->{input_menu} = new Gtk2::Menu;
    $self->{input_option}->set_menu ($self->{input_menu});
    $self->{input_option}->set_size_request (120, -1);
    $self->{input_option}->set_history (0);
    $i = 0;
    foreach ("#0 Television", "#1 Composite", "#2 S-Video", "#3", "#4") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{input_menu}->append($item);
        $self->{input_option}->set_history ($i) if ($i eq $self->{config}->{_defaults}->{input});
        $i++;
    }
    $self->{table3}->attach ($self->{input_option},             1, 2, 7, 8, ['fill'], [], 2, 2);

    $self->{ratio_label} = new Gtk2::Label ("Ratio:");
    $self->{ratio_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{ratio_label},              0, 1, 8, 9, ['fill'], [], 2, 2);
    $self->{ratio_option} = new Gtk2::OptionMenu;
    $self->{ratio_menu} = new Gtk2::Menu;
    $self->{ratio_option}->set_menu ($self->{ratio_menu});
    $self->{ratio_option}->set_size_request (100, -1);
    $self->{ratio_option}->set_history (0);
    $i = 0;
    foreach ("4:3", "16:9") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{ratio_menu}->append($item);
        $self->{ratio_option}->set_history ($i) if ($_ eq $self->{config}->{_global}->{ratio});
        $i++;
    }
    $self->{table3}->attach ($self->{ratio_option},             1, 2, 8, 9, ['fill'], [], 2, 2);

    $self->{channel_list_label} = new Gtk2::Label ("Channel list:");
    $self->{channel_list_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{channel_list_label},       0, 1, 9, 10, ['fill'], [], 2, 2);
    $self->{channel_list_option} = new Gtk2::OptionMenu;
    $self->{channel_list_menu} = new Gtk2::Menu;
    $self->{channel_list_option}->set_menu ($self->{channel_list_menu});
    $self->{channel_list_option}->set_history (0);
    $i = 0;
    foreach ("europe-east", "europe-west", "us-bcast", "us-cable", 
        "us-cable-hrc", "japan-bcast", "japan-cable", "italy", "newzealand",
        "australia", "ireland", "france", "china-bcast", "southafrica",
        "argentina", "russia") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{channel_list_menu}->append($item);
        $self->{channel_list_option}->set_history ($i) if ($_ eq $self->{config}->{_global}->{freqtab});
        $i++;
    }
    $self->{table3}->attach ($self->{channel_list_option},      1, 2, 9, 10, ['fill'], [], 2, 2);

    $self->{osd_label} = new Gtk2::Label ("OSD:");
    $self->{osd_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{osd_label},                0, 1, 10, 11, ['fill'], [], 2, 2);
    $self->{table3b} = new Gtk2::Table (2, 1, 0);
    $self->{table3}->attach ($self->{table3b},                  1, 2, 10, 11, ['fill'], [], 2, 2);
    $self->{osd_radio1} = new Gtk2::RadioButton (undef, "On");
    $self->{osd_radio1}->signal_connect ("pressed", sub { $self->{osd} = "yes"}); #TODO
    $self->{table3b}->attach ($self->{osd_radio1}, 0, 1, 0, 1, ['fill'], [], 2, 2);
    $self->{osd_radio2} = new Gtk2::RadioButton ($self->{osd_radio1}, "Off");
    $self->{osd_radio2}->signal_connect ("pressed", sub { $self->{osd} = "no"}); #TODO
    $self->{table3b}->attach ($self->{osd_radio2}, 1, 2, 0, 1, ['fill'], [], 2, 2);
    if ($self->{config}->{_global}->{osd} eq "no") {
        $self->{osd_radio2}->set_active (1);
        $self->{osd} = "no";
    }
    else {
        $self->{osd_radio1}->set_active (1);
        $self->{osd} = "yes";
    }
   
    $self->{fullscreen_label} = new Gtk2::Label ("Start in full screen:");
    $self->{fullscreen_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{fullscreen_label},         0, 1, 11, 12, ['fill'], [], 2, 2);
    $self->{table3c} = new Gtk2::Table (2, 1, 0);
    $self->{table3}->attach ($self->{table3c},                  1, 2, 11, 12, ['fill'], [], 2, 2);
    $self->{fullscreen_radio1} = new Gtk2::RadioButton (undef, "On");
    $self->{fullscreen_radio1}->signal_connect ("pressed", sub { $self->{fullscreen} = "yes"}); #TODO
    $self->{table3c}->attach ($self->{fullscreen_radio1},       0, 1, 0, 1, ['fill'], [], 2, 2);
    $self->{fullscreen_radio2} = new Gtk2::RadioButton ($self->{fullscreen_radio1}, "Off");
    $self->{fullscreen_radio2}->signal_connect ("pressed", sub { $self->{fullscreen} = "no"}); #TODO
    $self->{table3c}->attach ($self->{fullscreen_radio2},       1, 2, 0, 1, ['fill'], [], 2, 2);
    if ($self->{config}->{fullscreen} eq "no") {
        $self->{fullscreen_radio2}->set_active (1);
        $self->{fullscreen} = "no";
    }
    else {
        $self->{fullscreen_radio1}->set_active (1);
        $self->{fullscreen} = "yes";
    }

    $self->{mjpeg_label} = new Gtk2::Label ("Hardware MJPEG:");
    $self->{mjpeg_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{mjpeg_label},              0, 1, 12, 13, ['fill'], [], 2, 2);
    $self->{mjpeg_option} = new Gtk2::OptionMenu;
    $self->{mjpeg_menu} = new Gtk2::Menu;
    $self->{mjpeg_option}->set_menu ($self->{mjpeg_menu});
    $self->{mjpeg_option}->set_size_request (100, -1);
    $self->{mjpeg_option}->set_history (0);
    $i = 0;
    foreach $size ("no", "full size", "medium size", "small size") {
        my $item = new Gtk2::MenuItem($size);
        $item->{label} = $size;
        $self->{mjpeg_menu}->append($item);
        $item->signal_connect("activate", sub {
                if ($_[1] eq "no") {
                    $self->{capt_label}->show_all ();
                    $self->{size_option}->show_all ();
                }
                else {
                    $self->{capt_label}->hide_all ();
                    $self->{size_option}->hide_all ();
                }
            }, $size);
        $self->{mjpeg_option}->set_history ($i) if ($size eq $self->{config}->{mjpeg});
        $i++;
    }
    $self->{table3}->attach ($self->{mjpeg_option},             1, 2, 12, 13, ['fill'], [], 2, 2);
    
    $self->{capt_label} = new Gtk2::Label ("Capture size:");
    $self->{capt_label}->set_alignment (0, 0);
    $self->{table3}->attach ($self->{capt_label},               0, 1, 13, 14, ['fill'], [], 2, 2);
    $self->{size_option} = new Gtk2::OptionMenu;
    $self->{size_menu} = new Gtk2::Menu;
    $self->{size_option}->set_menu ($self->{size_menu});
    $self->{size_option}->set_size_request (100, -1);
    $self->{size_option}->set_history (0);
    $i = 0;
    foreach $size ("768x576", "720x576 (PAL)", "720x480 (NTSC)", 
                   "640x480", "512x384",
                   "360x288 (Half PAL)", "360x240 (Half NTSC)") {
        my $item = new Gtk2::MenuItem($size);
        $item->{label} = $size;
        $self->{size_menu}->append($item);
        $_ = $size; s/^(\d+)x\d+.*/$1/; $w = $_;
        $_ = $size; s/^\d+x(\d+).*/$1/; $h = $_;
        if ($self->{config}->{width} == $w && $self->{config}->{height} == $h) {
            $self->{size_option}->set_history ($i);
        }
        $i++;
    }
    $self->{table3}->attach ($self->{size_option},              1, 2, 13, 14, ['fill'], [], 2, 2);
    
    ### PAGE 4: VIDEO FILTERS
    $self->{table4} = new Gtk2::Table (1, 1, 0);
    $self->{page4}->pack_start ($self->{table4}, 1, 1, 0);

    $self->{vf_notebook} = new Gtk2::Notebook;
    $self->{vf_notebook}->set_tab_pos ('left');
    $self->{vf_notebook}->set_scrollable (1);
    $self->{table4}->attach ($self->{vf_notebook},              0, 1, 0, 1, ['fill'], ['fill', 'expand'], 2, 2);

    $i = 0;
    foreach $filter (@{$self->{vf_table}}) {
        my $name = $filter->{name};
        my $desc = $filter->{desc};
        my $longdesc = $filter->{longdesc};
        my @params = @{$filter->{params}};
        my $help = undef;
        $help = $filter->{help} if defined ($filter->{help});

        print "\n*** video filter name: $name\n" if ($verbose_level >= 2);
        $self->{"vf_page$i"} = new Gtk2::VBox (0, 5);
        $self->{"vf_page$i"}->set_border_width (5);
        $self->{vf_notebook}->append_page ($self->{"vf_page$i"}, $desc);
        $self->{"vf_table$i"} = new Gtk2::Table (2, $#params + 1, 0);

        $self->{"vf_page" . $i . "top"} = new Gtk2::Table (2, 1, 0);
        $self->{"vf_page$i"}->pack_start ($self->{"vf_page$i" . "top"}, 0, 1, 0);
        $self->{"vf_" . $name . "_check"} = new Gtk2::CheckButton ("Activate this filter");
        $self->{tooltips}->set_tip ($self->{"vf_" . $name . "_check"}, $filter->{longdesc} . " ($name)");
        $self->{"vf_" . $name . "_check"}->set_active ($self->{config}->{"vf_$name"} eq "yes");
        $self->{"vf_page" . $i . "top"}->attach ($self->{"vf_" . $name . "_check"},    0, 1, 0, 1, ['fill', 'expand'], [], 2, 2);
        if (defined $help) {
            $self->{"vf_" . $name . "_help_btn"} = Gtk2::Button->new_from_stock ("help");
            $self->{tooltips}->set_tip ($self->{"vf_" . $name . "_help_btn"}, "Brief help");
            $self->{"vf_page" . $i . "top"}->attach ($self->{"vf_" . $name . "_help_btn"}, 1, 2, 0, 1, ['fill'], [], 2, 2);
            $self->{"vf_" . $name . "_help_btn"}->signal_connect ("clicked", sub {
                    my ($button, $self) = @_;
                    my $dialog = new Gtk2::MessageDialog ($self->{window}, 
                        GTK_DIALOG_MODAL,
                        GTK_MESSAGE_INFO,
                        GTK_BUTTONS_OK,
                        "$longdesc\n\n$help"
                    );
                    $dialog->run;
                    $dialog->destroy;
                }, $self);
        }
        
        if ($#params + 1 > 0 || $name eq "pp") {
            $self->{"vf_" . $name . "_label"} = new Gtk2::Label ("Filter options");
            $self->{"vf_" . $name . "_label"}->set_alignment (0, 0);
            $self->{"vf_page$i"}->pack_start ($self->{"vf_" . $name . "_label"}, 0, 0, 0);
        }
        
        $self->{"vf_page$i"}->pack_start ($self->{"vf_table$i"}, 0, 0, 5);
        
        if ($name eq "pp") {
            my $j = 1;
            foreach $filter (@pp_filters) {
                my $code = $filter;
                $code =~ s/^.*\((\w+)\).*$/$1/;
                $filter =~ /^(.*)\(\w+\)/;
                $self->{"vf_pp_" . $code . "_check"} = new Gtk2::CheckButton ($1);
                $self->{"vf_pp_" . $code . "_check"}->set_active ($self->{config}->{"vf_pp_" . $code} eq "yes");
                $self->{"vf_pp_" . $code . "_check"}->signal_connect ("button_press_event", \&vf_pp_button_press_event);
                $self->{tooltips}->set_tip ($self->{"vf_pp_" . $code . "_check"}, 
                    "$code filter, right click: pop-up menu");
                $self->{"vf_table$i"}->attach ($self->{"vf_pp_" . $code . "_check"}, 0, 2, $j, $j + 1, ['fill'], [], 2, 0);
                $self->{"vf_pp_" . $code . "_check"}->{auto_off} = 
                    ($self->{config}->{"vf_pp_" . $code . "_a"} eq "yes");
                $self->{"vf_pp_" . $code . "_check"}->{chroma} = 
                    ($self->{config}->{"vf_pp_" . $code . "_c"} eq "yes");
                $self->{"vf_pp_" . $code . "_check"}->{code} = $code;
                $j++;
            }
     
            $self->{vf_pp_autoq_check} = new Gtk2::CheckButton ("Auto quality");
            $self->{vf_pp_autoq_check}->set_active ($self->{config}->{vf_pp_autoq} eq "yes");
            $self->{"vf_table$i"}->attach ($self->{vf_pp_autoq_check}, 0, 1, $j, $j + 1, ['fill'], [], 2, 0);
            $self->{vf_pp_autoq_level_ent} = new Gtk2::Entry;
            $self->{vf_pp_autoq_level_ent}->set_text ($self->{config}->{vf_pp_autoq_level});
            $self->{vf_pp_autoq_level_ent}->set_size_request (10, -1);
            $self->{"vf_table$i"}->attach ($self->{vf_pp_autoq_level_ent}, 1, 2, $j, $j + 1, ['fill'], [], 2, 0);
        }
        else {
            my $j = 1;
            foreach $param (@params) {
                print "$param\n" if ($verbose_level >= 2);
                if ($param =~ /^(\w+)\s+(Integer|Float|Flag)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/) {
                #if ($param =~ /^(\w+)\s+(Integer)\s+([-+\w\d]+\.\.[-+\w\d]+)/ ||
                #    $param =~ /^(\w+)\s+(Float)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/ ||
                #    $param =~ /^(\w+)\s+(Flag)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/
                #    ) {
                    my $param_name = $1;
                    my $param_type = $2;
                    my $param_range = $3;
                    my $param_default = undef;
                    if ($param =~ /^(\w+)\s+(\w+)\s+([-+\w\d.]+\.\.[-+\w\d.]+)\s+([-+\w\d.]+)/) {
                        $param_default = $4;
                    }
                    my $code = "vf_" . $name . "_" . $param_name;
                    my $label = $param_name;
                    $label =~ s/_/ /g;
                    $self->{$code . "_label"} = new Gtk2::Label ($label);
                    $self->{$code . "_label"}->set_size_request (120, -1);
                    $self->{$code . "_label"}->set_alignment (0.95, 0);
                    $self->{"vf_table$i"}->attach ($self->{$code . "_label"}, 
                        0, 1, $j, $j + 1, ['fill'], [], 2, 2);
                   
                    if ($param_type eq "Integer" || $param_type eq "Float") {
                        $self->{$code . "_ent"} = new Gtk2::Entry ();
                        $self->{$code . "_ent"}->set_size_request (80, -1);
                        $self->{$code . "_ent"}->set_text ($self->{config}->{$code});
                        my $tip = "Type: $param_type\nRange: $param_range";
                        $tip .= "\nDefault: $param_default" if (defined ($param_default));
                        $self->{tooltips}->set_tip ($self->{$code . "_ent"}, $tip);
                        $self->{"vf_table$i"}->attach ($self->{$code . "_ent"}, 
                            1, 2, $j, $j + 1, ['fill'], [], 2, 2);
                    }
                    if ($param_type eq "Flag") {
                        $self->{$code . "_option"} = new Gtk2::OptionMenu;
                        $self->{$code . "_menu"} = new Gtk2::Menu;
                        $self->{$code . "_option"}->set_menu ($self->{$code . "_menu"});
                        $self->{$code . "_option"}->set_size_request (80, -1);
                        $self->{$code . "_option"}->set_history (0);
                        my $k = 0;
                        foreach ("No", "Yes") {
                            my $item = new Gtk2::MenuItem($_);
                            $item->{label} = $_;
                            $self->{$code . "_menu"}->append($item);
                            $self->{$code . "_option"}->set_history ($k) if ($k eq $self->{config}->{$code});
                            $k++;
                        }
                        $self->{"vf_table$i"}->attach ($self->{$code . "_option"}, 
                            1, 2, $j, $j + 1, ['fill'], [], 2, 2);
                    }
                }
                else {
                    print "*** [mplayerTV] Internal error: Invalid parameter: $param\n";
                }
                $j++;
            }
        }
        $i++;
    }
    
    ### PAGE 5: AUDIO
    $self->{table5} = new Gtk2::Table (8, 3, 0);
    $self->{page5}->pack_start ($self->{table5}, 1, 1, 0);
    $self->{ao_drivers_label} = new Gtk2::Label ("Audio output driver:");
    $self->{ao_drivers_label}->set_alignment (0, 0);
    $self->{table5}->attach ($self->{ao_drivers_label},         0, 1, 0, 1, ['fill'], [], 2, 2);
    $self->{ao_drivers_option} = new Gtk2::OptionMenu;
    $self->{ao_drivers_menu} = new Gtk2::Menu;
    $self->{ao_drivers_option}->set_menu($self->{ao_drivers_menu});
    $self->{ao_drivers_option}->set_history(0);
    $self->update_ao_drivers_list ();
    $self->{table5}->attach ($self->{ao_drivers_option},        1, 2, 0, 1, ['fill', 'expand'], [], 2, 2);
    
    #$self->{ao_update_btn} = new Gtk2::Button ("_Update");
    #$self->{ao_update_btn}->signal_connect ("clicked", sub { update_ao_drivers_list ($self)});
    #$self->{tooltips}->set_tip ($self->{ao_update_btn}, "Runs mplayer and fetch audio output drivers list");
    #$self->{ao_update_btn}->set_size_request (60, -1);
    #$self->{table5}->attach ($self->{ao_update_btn},            2, 3, 0, 1, ['fill'], [], 2, 2);
    
    $self->{ao_subdriver_label} = new Gtk2::Label ("Audio output subdriver:");
    $self->{ao_subdriver_label}->set_alignment (0, 0);
    $self->{ao_subdriver_ent} = new Gtk2::Entry ();
    $self->{ao_subdriver_ent}->set_size_request (100, -1);
    $self->{ao_subdriver_ent}->set_text ($self->{config}->{subao});
    $self->{table5}->attach ($self->{ao_subdriver_label},       0, 1, 1, 2, ['fill'], [], 2, 2);
    $self->{table5}->attach ($self->{ao_subdriver_ent},         1, 2, 1, 2, ['fill'], [], 2, 2);
    
    #$self->{captalsa_check} = new Gtk2::CheckButton ("Capture from alsa");
    #$self->{captalsa_check}->set_active ($self->{config}->{captalsa} eq "yes");
    #$self->{table5}->attach ($self->{captalsa_check}, 0, 1, 2, 3, ['fill'], [], 2, 0);
    
    $self->{adevice_label} = new Gtk2::Label ("Audio device:");
    $self->{adevice_label}->set_alignment (0, 0);
    $self->{adevice_ent} = new Gtk2::Entry ();
    $self->{adevice_ent}->set_size_request (100, -1);
    $self->{adevice_ent}->set_text ($self->{config}->{adevice});
    $self->{table5}->attach ($self->{adevice_label},            0, 1, 2, 3, ['fill'], [], 2, 2);
    $self->{table5}->attach ($self->{adevice_ent},              1, 2, 2, 3, ['fill'], [], 2, 2);
    
    $self->{amode_label} = new Gtk2::Label ("Audio mode:");
    $self->{amode_label}->set_alignment (0, 0);
    $self->{table5}->attach ($self->{amode_label},              0, 1, 3, 4, ['fill'], [], 2, 2);
    $self->{amode_option} = new Gtk2::OptionMenu;
    $self->{amode_menu} = new Gtk2::Menu;
    $self->{amode_option}->set_menu ($self->{amode_menu});
    $self->{amode_option}->set_size_request (100, -1);
    $self->{amode_option}->set_history (0);
    $i = 0;
    foreach ("Mono", "Stereo", "Language 1", "Language 2", "Disabled") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{amode_menu}->append ($item);
        $self->{amode_option}->set_history ($i) if ($i == $self->{config}->{amode});
        $i++;
    }
    $self->{table5}->attach ($self->{amode_option},             1, 2, 3, 4, ['fill'], [], 2, 2);
    
    $self->{amixer_device_label} = new Gtk2::Label ("Audio mixer device:");
    $self->{amixer_device_label}->set_alignment (0, 0);
    $self->{table5}->attach ($self->{amixer_device_label},      0, 1, 4, 5, ['fill'], [], 2, 2);
    $self->{amixer_device_ent} = new Gtk2::Entry ();
    $self->{amixer_device_ent}->set_size_request (100, -1);
    $self->{amixer_device_ent}->set_text ($self->{config}->{amixer_device});
    $self->{table5}->attach ($self->{amixer_device_ent},        1, 2, 4, 5, ['fill'], [], 2, 2);

    $self->{amixer_channel_label} = new Gtk2::Label ("Audio mixer channel:");
    $self->{amixer_channel_label}->set_alignment (0, 0);
    $self->{table5}->attach ($self->{amixer_channel_label},     0, 1, 5, 6, ['fill'], [], 2, 2);
    $self->{amixer_channel_option} = new Gtk2::OptionMenu;
    $self->{amixer_channel_menu} = new Gtk2::Menu;
    $self->{amixer_channel_option}->set_menu ($self->{amixer_channel_menu});
    $self->{amixer_channel_option}->set_size_request (100, -1);
    $self->{amixer_channel_option}->set_history (0);
    $i = 0;
    foreach ("vol", "pcm", "pcm2", "line", "line1","line2", "line3", "mic", 
             "igain", "ogain", "video", "radio") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $self->{amixer_channel_menu}->append ($item);
        $self->{amixer_channel_option}->set_history ($i) if ($_ eq $self->{config}->{amixer_channel});
        $i++;
    }
    $self->{table5}->attach ($self->{amixer_channel_option},    1, 2, 5, 6, ['fill'], [], 2, 2);
    
    $self->{extvolume_check} = new Gtk2::CheckButton ("Enable external volume control program");
    $self->{extvolume_check}->set_size_request (100, -1);
    if ($self->{config}->{extvolume} eq "yes") {
        $self->{extvolume_check}->set_active (1);
    }
    $self->{table5}->attach ($self->{extvolume_check},          0, 3, 6, 7, ['fill'], [], 2, 2);
    
    $self->{extvolume_label} = new Gtk2::Label ("Volume control prog:");
    $self->{extvolume_label}->set_alignment (0, 0);
    $self->{extvolume_ent} = new Gtk2::Entry ();
    $self->{extvolume_ent}->set_size_request (100, -1);
    $self->{extvolume_ent}->set_text ($self->{config}->{extvolume_program});
    $self->{table5}->attach ($self->{extvolume_label},          0, 1, 7, 8, ['fill'], [], 2, 2);
    $self->{table5}->attach ($self->{extvolume_ent},            1, 2, 7, 8, ['fill'], [], 2, 2);
    
    $self->{extvolume_option} = new Gtk2::OptionMenu;
    $self->{extvolume_menu} = new Gtk2::Menu;
    $self->{extvolume_option}->set_menu ($self->{extvolume_menu});
    $self->{extvolume_option}->set_size_request (60, -1);
    $self->{extvolume_option}->set_history (0);
    #$i = 0;
    foreach ("aumix", "ALSA") {
        my $item = new Gtk2::MenuItem($_);
        $item->{label} = $_;
        $item->signal_connect("activate", sub {
                $self->{extvolume_ent}->set_text ('aumix -l%s%d') if ($_[1] eq "aumix");
                $self->{extvolume_ent}->set_text ('amixer set Line %d%%s') if ($_[1] eq "ALSA");
            }, $_);
        $self->{extvolume_menu}->append ($item);
        #$self->{extvolume_option}->set_history ($i) if ($i == $self->{config}->{amode});
        #$i++;
    }
    $self->{table5}->attach ($self->{extvolume_option},         2, 3, 7, 8, ['fill', 'expand'], [], 2, 2);

    $self->{usbaudio_check} = new Gtk2::CheckButton ("Enable USB audio capturing");
    $self->{usbaudio_check}->set_size_request (100, -1);
    $self->{tooltips}->set_tip ($self->{usbaudio_check}, "Captures sound of the USB tuner using sox program");
    if ($self->{config}->{usbaudio} eq "yes") {
        $self->{usbaudio_check}->set_active (1);
    }
    $self->{table5}->attach ($self->{usbaudio_check},           0, 3, 8, 9, ['fill'], [], 2, 2);
    
    $self->{usbaudio_label} = new Gtk2::Label ("USB audio device:");
    $self->{usbaudio_label}->set_alignment (0, 0);
    $self->{usbaudio_ent} = new Gtk2::Entry ();
    $self->{usbaudio_ent}->set_size_request (100, -1);
    $self->{usbaudio_ent}->set_text ($self->{config}->{usbaudio_device});
    $self->{table5}->attach ($self->{usbaudio_label},           0, 1, 9, 10, ['fill'], [], 2, 2);
    $self->{table5}->attach ($self->{usbaudio_ent},             1, 2, 9, 10, ['fill'], [], 2, 2);
    
    ### PAGE 6: AUDIO FILTERS
    #$self->{table6} = new Gtk2::Table (3, 3, 0);
    #$self->{page6}->pack_start ($self->{table6}, 1, 1, 0);
    # TODO audio filters
    
    ### DIALOG BUTTONS ###
    $self->{save_btn} = Gtk2::Button->new_from_stock ("save");
    $self->{cancel_btn} = Gtk2::Button->new_from_stock ("cancel");
    $self->{save_btn}->signal_connect ("clicked", \&SaveBtn, $self);
    $self->{cancel_btn}->signal_connect ("clicked",  \&CancelBtn, $self);
    $self->{dialog}->signal_connect ("delete_event", \&CancelBtn, $self);
    $self->{dialog}->action_area->pack_start ($self->{save_btn}, TRUE, TRUE, 5);
    $self->{dialog}->action_area->pack_start ($self->{cancel_btn}, TRUE, TRUE, 5);
    
    ### ETC
    $self->{dialog}->set_border_width (5);
    $self->{dialog}->set_title ("Configure");
    
    return $self;
}

sub get_vo_drivers
{
    my ($self) = @_;
    my @vo_drivers;
    my $prg = $mplayer_path;
    $prg .=  " -vo help";
    print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
    @output = `$prg`;
    print "\n", @output, "\n" if ($verbose_level >= 2);
    foreach $line (@output) {
        if ($line =~ /^\s+(\w+)\s+/) {
            push @vo_drivers, $1;
        }
    }
    if ($#vo_drivers <= 1) {
        push @vo_drivers, "null";
        my $error = join ("\n", @output);
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 
            "Can't execute mplayer!\n$error");
        $dialog->run;
        $dialog->destroy;
    }
    return @vo_drivers;
}

sub update_vo_drivers_list
{
    my ($self) = @_;
    my @vo_drivers = $self->get_vo_drivers ();
    # removing menuitems
    my @items = $self->{vo_drivers_menu}->get_children;
    foreach (@items) {
        $self->{vo_drivers_menu}->remove ($_);
    }
    # adding new menuitems
    my $i = 0;
    foreach (sort @vo_drivers) {
        my $item = new Gtk2::MenuItem ($_);
        $item->{label} = $_;
        $self->{vo_drivers_menu}->append ($item);
        $self->{vo_drivers_option}->set_history ($i) if ($_ eq $self->{config}->{vo});
        $item->show;
        $i++;
    }
}

sub get_ao_drivers
{
    my ($self) = @_;
    my @ao_drivers;
    my $prg = $mplayer_path; #$self->{mplayer_path_ent}->get_text ();
    $prg .=  " -ao help";
    print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
    @output = `$prg`;
    print "\n", @output, "\n" if ($verbose_level >= 2);
    foreach $line (@output) {
        if ($line =~ /^\s+(\w+)\s+/) {
            push @ao_drivers, $1;
        }
    }
    if ($#ao_drivers <= 1) { 
        push @ao_drivers, "null";
        my $error = join ("\n", @output);
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 
            "Can't execute mplayer!\n$error");
        $dialog->run;
        $dialog->destroy;
    }
    return @ao_drivers;
}

sub update_ao_drivers_list
{
    my ($self) = @_;
    my @ao_drivers = $self->get_ao_drivers ();
    # removing menuitems
    my @items = $self->{ao_drivers_menu}->get_children;
    foreach (@items) {
        $self->{ao_drivers_menu}->remove ($_);
    }
    # adding new menuitems
    my $i = 0;
    foreach (sort @ao_drivers) {
        my $item = new Gtk2::MenuItem ($_);
        $item->{label} = $_;
        $self->{ao_drivers_menu}->append ($item);
        $self->{ao_drivers_option}->set_history ($i) if ($_ eq $self->{config}->{ao});
        $item->signal_connect ('activate', sub {
                my ($item, $self) = @_;
                if ($item->{label} eq "oss") {
                    $self->{amixer_device_ent}->show_all;
                    $self->{amixer_device_label}->show_all;
                    $self->{amixer_channel_option}->show_all;
                    $self->{amixer_channel_label}->show_all;
                }
                else {
                    $self->{amixer_device_ent}->hide_all;
                    $self->{amixer_device_label}->hide_all;
                    $self->{amixer_channel_option}->hide_all;
                    $self->{amixer_channel_label}->hide_all;
                }
            }, $self);
        $item->show;
        $i++;
    }
}

sub get_outfmts
{
    my ($self) = @_;
    my @outfmts = ("default");
    my $prg = $mplayer_path;
    $prg .=  " -tv outfmt=help";
    print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
    @output = `$prg`;
    print "\n", @output, "\n" if ($verbose_level >= 2);
    foreach $line (@output) {
        if ($line =~ /^Available formats: (.*)$/) {
            push @outfmts, split /\s+/, $1;
        }
    }
    if ($#outfmts <= 1) {
        push @outfmts, "null";
        my $error = join ("\n", @output);
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 
            "Can't execute mplayer!\n$error");
        $dialog->run;
        $dialog->destroy;
    }
    return @outfmts;
}

sub update_outfmts_list
{
    my ($self) = @_;
    my @outfmts = $self->get_outfmts ();
    # removing menuitems
    my @items = $self->{outfmts_menu}->get_children;
    foreach (@items) {
        $self->{outfmts_menu}->remove ($_);
    }
    # adding new menuitems
    my $i = 0;
    foreach (@outfmts) {
        my $item = new Gtk2::MenuItem ($_);
        $item->{label} = $_;
        $self->{outfmts_menu}->append ($item);
        $self->{outfmts_option}->set_history ($i) if ($_ eq $self->{config}->{outfmt});
        $item->show;
        $i++;
    }
}

### updates channel list on Configuration/Channels page
sub update_channel_list
{
    my ($self) = @_;
    my @list = $self->{name_list}->get_children;
    my $items = $#list;
    $self->{name_list}->clear_items (0, $items + 1);
    my $folders = $self->{config}->{_folders};
    my @channels = ();
    my $i = 1;
    foreach (@$folders) {
        next if ($_ eq "defaults" || $_ eq "global");
        my $item = new Gtk2::ListItem ($_);
        push @channels, $item;
        $item->{name} = $_;
        $item->{number} = $i++;
        $item->{channel} = $self->{config}->{"_$_"}->{channel};
        $item->{brightness} = $self->{config}->{"_$_"}->{brightness};
        $item->{contrast} = $self->{config}->{"_$_"}->{contrast};
        $item->{hue} = $self->{config}->{"_$_"}->{hue};
        $item->{saturation} = $self->{config}->{"_$_"}->{saturation};
        $item->{volume} = $self->{config}->{"_$_"}->{volume};
        $item->signal_connect ('select', \&select_channel_item, $self);
        $self->{name_list}->add ($item);
    }
    $self->{channels} = \@channels;
}

sub get_option_menu_text
{
    my ($option_menu) = @_;
    my $menu = $option_menu->get_menu ();
    my $menuitem = $menu->get_active ();
    return $menuitem->{label};
}

### Configuration/Filters, callback function for mouse button press on
# pp filter names (eg. lb, hb)
sub vf_pp_button_press_event
{
    my ($widget, $event) = @_;
    if ($event->type () eq 'button-press' && $event->button () == 3) {
    my ($x,$y) = $event->get_coords;

    my $menu = Gtk2::Menu->new ();
    my $tooltips = new Gtk2::Tooltips ();

    my $menuitem1 = new Gtk2::CheckMenuItem ("Auto off");
    $menuitem1->set_active ($widget->{auto_off});
    $tooltips->set_tip ($menuitem1, 
        "Automatically switches the " . $widget->{code} . " filter off if the CPU is too slow");
    my $menuitem2 = new Gtk2::CheckMenuItem ("Chroma filtering");
    $menuitem2->set_active ($widget->{chroma});
    $tooltips->set_tip ($menuitem2, 
        "Do chrominance filtering on " . $widget->{code});
    $menuitem1->show ();
    $menuitem2->show ();
    $menuitem1->signal_connect (activate => sub { $widget->{auto_off} = !$widget->{auto_off}});
    $menuitem2->signal_connect (activate => sub { $widget->{chroma} = !$widget->{chroma}});

    $menu->append ($menuitem1);
    $menu->append ($menuitem2);

    $menu->popup (undef, # parent menu shell
                  undef, # parent menu item
                  undef, # menu pos func
                  undef, # data
                  $event->button,
                  $event->time);
        return 1;
    }
    return undef;
}

sub show
{
    my ($self) = @_;
    $self->{dialog}->show_all;
    if ($self->{config}->{ao} ne "oss") {
        $self->{amixer_device_ent}->hide_all;
        $self->{amixer_device_label}->hide_all;
        $self->{amixer_channel_option}->hide_all;
        $self->{amixer_channel_label}->hide_all;
    }
    if ($self->{config}->{mjpeg} ne "no") {
        $self->{capt_label}->hide_all ();
        $self->{size_option}->hide_all ();
    }
}

### Configuration/Channels, callback function for selecting item on channel list
sub select_channel_item
{
    my ($item, $self) = @_;
    if ($self->{app}->mplayer_running ()) {
        $self->{app}->{mplayer}->tv_set_channel ($item->{number});
        $self->{app}->restore_mplayer_parameters ();
        $self->{app}->{brightness_delta} = $item->{brightness};
        $self->{app}->{contrast_delta} = $item->{contrast};
        $self->{app}->{hue_delta} = $item->{hue};
        $self->{app}->{saturation_delta} = $item->{saturation};
        $self->{app}->{volume_delta} = $item->{volume};
        $self->{app}->set_mplayer_parameters ();
    }
    $self->{name_ent}->set_text ($item->{name});
    $self->{channel_ent}->set_text ($item->{channel});
    $self->{brightness_spin}->set_text ($item->{brightness});
    $self->{contrast_spin}->set_text ($item->{contrast});
    $self->{hue_spin}->set_text ($item->{hue});
    $self->{saturation_spin}->set_text ($item->{saturation});
    $self->{volume_spin}->set_text ($item->{volume});
    $self->{selected_channel} = $item;
    #print "number: ", $item->{number}, "\n";
}

### Configuration/Channels page, callback function for up button
# moves channel up
sub UpBtn
{
    my ($btn, $self) = @_;
    my $item = $self->{selected_channel};
    my $pos = $self->{name_list}->child_position ($item);
    my @channels = @{$self->{channels}};
    if (defined ($item) && $pos > 0) {
        my @list = $self->{name_list}->get_children;
        my $items = $#list;
        $self->{name_list}->clear_items (0, $items + 1);
        my $item2 = $self->{channels}[$pos - 1];
        $channels[$pos - 1] = $channels[$pos];
        $channels[$pos] = $item2;
        #my $i = 1;
        foreach (@channels) {
            #$_->{number} = $i++;
            $self->{name_list}->add ($_);
        }
        $item->select;
        $self->{channels} = \@channels;
    }
}

### Configuration/Channels page, callback function down button
# moves channel down
sub DownBtn
{
    my ($btn, $self) = @_;
    my $item = $self->{selected_channel};
    my $pos = $self->{name_list}->child_position ($item);
    my @channels = @{$self->{channels}};
    my @list = $self->{name_list}->get_children;
    my $items = $#list;
    if (defined ($item) && $pos < $items) {
        $self->{name_list}->clear_items (0, $items + 1);
        my $item2 = $self->{channels}[$pos + 1];
        $channels[$pos + 1] = $channels[$pos];
        $channels[$pos] = $item2;
        #my $i = 1;
        foreach (@channels) {
            #$_->{number} = $i++;
            $self->{name_list}->add ($_);
        }
        $item->select;
        $self->{channels} = \@channels;
    }
}

### Configuration/Channels page, callback function for new channel button
sub NewBtn
{
    my ($btn, $self) = @_;
    my $name = $self->{name_ent}->get_text ne "" ? $self->{name_ent}->get_text : "Unnamed";
    my $brightness = $self->{brightness_spin}->get_text ne "" ? $self->{brightness_spin}->get_text : "0";
    my $contrast = $self->{contrast_spin}->get_text ne "" ? $self->{contrast_spin}->get_text : "0";
    my $hue = $self->{hue_spin}->get_text ne "" ? $self->{hue_spin}->get_text : "0";
    my $saturation = $self->{saturation_spin}->get_text ne "" ? $self->{saturation_spin}->get_text : "0";
    my $volume = $self->{volume_spin}->get_text ne "" ? $self->{volume_spin}->get_text : "0";
    if ($name eq "defaults" || $name eq "global") {
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, 
            "'defaults' and 'global' are invalid names!");
        $dialog->run;
        $dialog->destroy;
        return;
    }
    $name =~ s/ /_/g;
    if ($name !~ /^[_a-zA-Z0-9]+$/) {
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 
            "Invalid channel name! Only alphanumeric characters are allowed.");
        $dialog->run;
        $dialog->destroy;
        return;
    }
    $self->{name_ent}->set_text ($name);
    my $channel = $self->{channel_ent}->get_text ne "" ? $self->{channel_ent}->get_text : "1";
    my $item = new Gtk2::ListItem ($name);
    $item->{name} = $name;
    my @channels = @{$self->{channels}};
    $item->{number} = $#channels + 2;
    $item->{channel} = $channel;
    $item->{brightness} = $brightness;
    $item->{contrast} = $contrast;
    $item->{hue} = $hue;
    $item->{saturation} = $saturation;
    $item->{volume} = $volume;
    $item->signal_connect ('select', \&select_channel_item, $self);
    $self->{name_list}->add ($item);
    push @{$self->{channels}}, $item;
    $item->show;
}

### Configuration/Channels page, callback function for modify channel button
sub ModifyBtn
{
    my ($btn, $self) = @_;
    my $name = $self->{name_ent}->get_text;
    my $brightness = $self->{brightness_spin}->get_text ne "" ? $self->{brightness_spin}->get_text : "0";
    my $contrast = $self->{contrast_spin}->get_text ne "" ? $self->{contrast_spin}->get_text : "0";
    my $hue = $self->{hue_spin}->get_text ne "" ? $self->{hue_spin}->get_text : "0";
    my $saturation = $self->{saturation_spin}->get_text ne "" ? $self->{saturation_spin}->get_text : "0";
    my $volume = $self->{volume_spin}->get_text ne "" ? $self->{volume_spin}->get_text : "0";
    if ($name eq "defaults" || $name eq "global") {
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, 
            "'defaults' and 'global' are invalid names!");
        $dialog->run;
        $dialog->destroy;
        return;
    }
    $name =~ s/ /_/g;
    if ($name !~ /^[_a-zA-Z0-9]+$/) {
        my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 
            "Invalid channel name! Only alphanumeric are characters allowed.");
        $dialog->run;
        $dialog->destroy;
        return;
    }
    $self->{name_ent}->set_text ($name);
    my $channel = $self->{channel_ent}->get_text;
    my $item = $self->{selected_channel};
    return unless defined ($item);
   
    $item->{name} = $name;
    $item->{channel} = $channel;
    $item->{brightness} = $brightness;
    $item->{contrast} = $contrast;
    $item->{hue} = $hue;
    $item->{saturation} = $saturation;
    $item->{volume} = $volume;
    my $label = $item->get_children;
    $label->set_label ($name);
    $item->signal_connect ('select', \&select_channel_item, $self);
    #$self->{name_list}->add ($item);
    $item->show;
}

### Configuration/Channels page, callback function for delete channel button
sub DeleteBtn
{
    my ($btn, $self) = @_;
    my $item = $self->{selected_channel};
    if (defined ($item))
    {
        $pos = $self->{name_list}->child_position ($item);
        $self->{name_list}->remove ($item);
        splice (@{$self->{channels}}, $pos, 1);
    }
}

### Configuration/Channels page, callback function for delete all channel button
sub DeleteAllBtn
{
    my ($btn, $self) = @_;
    my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_WARNING,
            GTK_BUTTONS_YES_NO,
            "Do you want to delete all channels?"
            );
    if ($dialog->run () eq "yes")
    {
        $self->{selected_channel} = undef;
        $self->{name_list}->foreach (sub {
                    my ($item, $self) = @_;
                    $self->{name_list}->remove ($item);
                }, $self);
        $self->{name_list}->remove ($item) if defined ($item);
        @{$self->{channels}} = ();
    }
    $dialog->destroy;
}

sub scantv_callback
{
    my ($scantv_dialog, $channel, $freq, $channel_name) = @_;
    $scantv_dialog->set_label ("Channel: $channel ($freq)");
    if ($channel_name ne "no station") {
        $channel_name = "unknown_$channel" if ($channel_name eq "???");
        $scantv_dialog->set_label2 ("New station: $channel_name");
    }
}

sub scantv_exit
{
    my ($self) = @_;
    print "Reading scantv output file...\n";
    my $config = MyConfigFile::read_config_file ($cfg); # if -r $cfg; TODO
    my @folders = @{$config->{_folders}};
    foreach (@folders) {
        next if ($_ eq "defaults" || $_ eq "global");
        my $name = $_;
        my $channel = $config->{"_$_"}->{channel};
        my $exists = 0;
        foreach $ch (@{$self->{channels}}) {
            if ($ch->{channel} eq $channel) {
                #print "$channel exists!\n";
                $exists = 1;
            }
        }
        next if ($exists);
        # adding new channel item to channel list...
        my $item = new Gtk2::ListItem ($name);
        $item->{name} = $name;
        $item->{channel} = $channel;
        $item->{brightness} = 0;
        $item->{contrast} = 0;
        $item->{hue} = 0;
        $item->{saturation} = 0;
        $item->{volume} = 0;
        $item->signal_connect ('select', \&select_channel_item, $self);
        $self->{name_list}->add ($item);
        push @{$self->{channels}}, $item;
        $item->show;
    }
    # deleting temporary file
    unlink $cfg;
    # running mplayer if needed
    $self->{app}->start_mplayer ($self->{app}) if ($self->{mplayer_running});
    $self->{scantv_dialog}->destroy ();
}

### Configuration/Channels page, callback function for scan button
sub ScantvBtn
{
    my ($btn, $self) = @_;
    my $cfg = "/tmp/mplayertv_chan_list";
    my $norm = get_option_menu_text ($self->{norm_option});
    my $chan_list = get_option_menu_text ($self->{channel_list_option});
    my $prg;
    my $vbi = "/dev/vbi";
    $vbi = "/dev/vbi0" unless (-f $vbi); # Quick workaround if /dev/vbi isn't exists
    my $tv_device = $self->{config}->{tv_device};
    # TODO: getting and interpreting output of scantv
    # Quick workaround for '/dev/vbi0: Device or resource busy' bug
    my $dialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_QUESTION,
            GTK_BUTTONS_YES_NO,
            "May I use teletext interface ($vbi) to get channel names?\n" .
            "Notice: If you see 'Device or resource busy' error message, choose next time no!"
            );
    if ($dialog->run () ne "yes")
    {
        $vbi = "/dev/null";
    }
    $dialog->destroy;
    if ($terminal_path =~ /gnome-terminal/) {
        # gnome-terminal incompatible with other terminal emulators (-e switch, Bug 152717)
        $prg = "$terminal_path \"-e sh -c '$scantv_path -f $chan_list -n $norm -o $cfg; sleep 2'\"";
    }
    else {
        $prg = "$terminal_path -e sh -c '$scantv_path -f $chan_list -n $norm -o $cfg -c $tv_device -C $vbi; sleep 2'";
    }
    my $mplayer_running = $self->{app}->mplayer_running ($self->{app});
    print "chan_list: $chan_list\n" if ($verbose_level >= 2);
    print "norm: $norm\n" if ($verbose_level >= 2);
    print "cfg: $cfg\n" if ($verbose_level >= 2);

    $self->{app}->stop_mplayer ($self->{app});
    print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
    @output = `$prg`;
    print "\n", @output, "\n" if ($verbose_level >= 2);
    if (!-r $cfg) {
        print "*** [mplayerTV] Can't read output of scantv: $!\n" if ($verbose_level >= 1);
        my $errdialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK, 
            "Can't read output of scantv!\nscantv not installed or not executable by user."
        );
        $errdialog->run;
        $errdialog->destroy;
        return undef;
    }
    my $config = MyConfigFile::read_config_file ($cfg) if -r $cfg;
    my @folders = @{$config->{_folders}};
    foreach (@folders) {
        next if ($_ eq "defaults" || $_ eq "global");
        my $name = $_;
        my $channel = $config->{"_$_"}->{channel};
        my $exists = 0;
        foreach $ch (@{$self->{channels}}) {
            if ($ch->{channel} eq $channel) {
                #print "$channel exists!\n";
                $exists = 1;
            }
        }
        next if ($exists);
        # adding new channel item to channel list...
        my $item = new Gtk2::ListItem ($name);
        $item->{name} = $name;
        $item->{channel} = $channel;
        $item->{brightness} = 0;
        $item->{contrast} = 0;
        $item->{hue} = 0;
        $item->{saturation} = 0;
        $item->{volume} = 0;
        $item->signal_connect ('select', \&select_channel_item, $self);
        $self->{name_list}->add ($item);
        push @{$self->{channels}}, $item;
        $item->show;
    }
    unlink $cfg;
    $self->{app}->start_mplayer ($self->{app}) if ($mplayer_running);

    return 1;
}

### Configuration/General page, callback function for browse button
#sub MplayerBrowseBtn
#{
#    my ($btn, $self) = @_;
#    my $mplayer_browser = Gtk2::FileSelection->new ("Select mplayer");
#    $mplayer_browser->ok_button->signal_connect (clicked => sub {
#            $self->{mplayer_path_ent}->set_text ($mplayer_browser->get_filename); 
#            $mplayer_browser->destroy; 
#            update_vo_drivers_list ();
#        });
#    $mplayer_browser->cancel_button->signal_connect (clicked => sub {$mplayer_browser->destroy});
#    $mplayer_browser->show_all;
#}

### Configuration/General page, callback function for browse button
sub TerminalEmulatorBrowseBtn
{
    my ($btn, $self) = @_;
    my $terminal_browser = Gtk2::FileSelection->new ("Select terminal emulator");
    $terminal_browser->ok_button->signal_connect (clicked => sub {
            $self->{terminal_emulator_ent}->set_text ($terminal_browser->get_filename); 
            $terminal_browser->destroy; 
        });
    $terminal_browser->cancel_button->signal_connect (clicked => sub {$terminal_browser->destroy});
    $terminal_browser->show_all;
}

### Configuration/General page, callback function for browse button
#sub ScantvBrowseBtn
#{
#    my ($btn, $self) = @_;
#    my $scantv_browser = Gtk2::FileSelection->new ("Select scantv");
#    $scantv_browser->ok_button->signal_connect (clicked => sub {
#            $self->{scantv_ent}->set_text ($scantv_browser->get_filename); 
#            $scantv_browser->destroy; 
#        });
#    $scantv_browser->cancel_button->signal_connect (clicked => sub {$scantv_browser->destroy});
#    $scantv_browser->show_all;
#}

sub YesNo
{
    $_[0] ? return "yes" : return "no";
}

### DIALOG BUTTONS ###
sub CancelBtn
{
    my ($btn, $self) = @_;
    $self->{dialog}->destroy();
}

sub SaveBtn
{
    my ($btn, $self) = @_;
    my @pp_codes = @{$self->{pp_codes}};
    my @vf_table = @{$self->{vf_table}};
    my $config;

    #$config->{mplayer} = $self->{mplayer_path_ent}->get_text ();
    $config->{tv} = get_option_menu_text ($self->{tv_drivers_option});
    $config->{tv_device} = $self->{tv_device_ent}->get_text ();
    $config->{vo} = get_option_menu_text ($self->{vo_drivers_option});
    $config->{subvo} = $self->{vo_subdriver_ent}->get_text ();
    $config->{outfmt} = get_option_menu_text ($self->{outfmts_option});

    $_ = get_option_menu_text ($self->{size_option});
    s/^(\d+)x\d+.*/$1/;
    $config->{width} = $_;
    $_ = get_option_menu_text ($self->{size_option});
    s/^\d+x(\d+).*/$1/;
    $config->{height} = $_;
    $config->{mjpeg} = get_option_menu_text ($self->{mjpeg_option});
    
    $config->{preexec} = $self->{preexec_ent}->get_text ();
    $config->{postexec} = $self->{postexec_ent}->get_text ();
    foreach $f (@vf_table) {
        $config->{"vf_" . $f->{name}} = YesNo ($self->{"vf_" . $f->{name} . "_check"}->get_active ());
        foreach $p (@{$f->{params}}) {
            my $pname = "";
            my $ptype = "";
            if ($p =~ /^(\w+)\s+(\w+)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/) {
                $pname = $1;
                $ptype = $2;
            }
            if ($ptype eq "Integer" || $ptype eq "Float") {
                $config->{"vf_" . $f->{name} . "_" . $pname} = 
                    $self->{"vf_" . $f->{name} . "_" . $pname . "_ent"}->get_text ();
            }
            elsif ($ptype eq "Flag") {
                $config->{"vf_" . $f->{name} . "_" . $pname} = 
                    $self->{"vf_" . $f->{name} . "_" . $pname . "_option"}->get_history ();
            }
        }
    }
    $config->{vf_pp} = YesNo ($self->{vf_pp_check}->get_active ());
    foreach $code (@pp_codes) {
        $config->{"vf_pp_" . $code} = YesNo ($self->{"vf_pp_" . $code . "_check"}->get_active ());
        $config->{"vf_pp_" . $code . "_a"} = YesNo ($self->{"vf_pp_" . $code . "_check"}->{auto_off});
        $config->{"vf_pp_" . $code . "_c"} = YesNo ($self->{"vf_pp_" . $code . "_check"}->{chroma});
    }
    $config->{vf_pp_autoq} = YesNo ($self->{vf_pp_autoq_check}->get_active ());
    $config->{vf_pp_autoq_level} = $self->{vf_pp_autoq_level_ent}->get_text ();
    
    $config->{ao} = get_option_menu_text ($self->{ao_drivers_option});
    $config->{subao} = $self->{ao_subdriver_ent}->get_text ();
    #$config->{captalsa} = YesNo ($self->{captalsa_check}->get_active ());
    $config->{adevice} = $self->{adevice_ent}->get_text ();
    $config->{amode} = $self->{amode_option}->get_history ();
    $config->{amixer_device} = $self->{amixer_device_ent}->get_text ();
    $config->{amixer_channel} = get_option_menu_text ($self->{amixer_channel_option});
    
    $config->{fullscreen} = $self->{fullscreen};
    $config->{_global}->{freqtab} = get_option_menu_text ($self->{channel_list_option});
    $config->{_global}->{ratio} = get_option_menu_text ($self->{ratio_option});
    $config->{_global}->{osd} = $self->{osd};
    $config->{_defaults}->{norm} = get_option_menu_text ($self->{norm_option});
    $config->{_defaults}->{input} = $self->{input_option}->get_history ();
    $config->{terminal_emulator} = $self->{terminal_emulator_ent}->get_text ();
    $config->{mplayer_opts} = $self->{mplayer_opts_ent}->get_text ();
    #$config->{scantv} = $self->{scantv_ent}->get_text ();
    $config->{extvolume} = YesNo ($self->{extvolume_check}->get_active ());
    $config->{extvolume_program} = $self->{extvolume_ent}->get_text ();
    $config->{usbaudio} = YesNo ($self->{usbaudio_check}->get_active ());
    $config->{usbaudio_device} = $self->{usbaudio_ent}->get_text ();

    # creating folders
    my @folders = ("global", "defaults");
    foreach $item ($self->{name_list}->get_children) {
        push @folders, $item->{name};
        $config->{"_" . $item->{name}}->{channel} = $item->{channel};
        $config->{"_" . $item->{name}}->{brightness} = $item->{brightness};
        $config->{"_" . $item->{name}}->{contrast} = $item->{contrast};
        $config->{"_" . $item->{name}}->{hue} = $item->{hue};
        $config->{"_" . $item->{name}}->{saturation} = $item->{saturation};
        $config->{"_" . $item->{name}}->{volume} = $item->{volume};
    }
    $config->{_folders} = \@folders;
    
    # writing config file
    if (!MyConfigFile::write_config_file ($self->{cfg_file}, $config)) {
        print "\n*** [mplayerTV] can't save configuration to " . $self->{cfg_file} . ", reason: $!\n" if ($verbose_level >= 1);
        my $errdialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK, 
            "Can't save configuration to " . $self->{cfg_file} . "!\n$!"
        );
        $errdialog->run;
        $errdialog->destroy;
    }
    #$self->{config} = $config; #? nem kell, gyis megdglik...
    $self->{app}->{config} = $config;
    $self->{dialog}->destroy();

    $self->{app}->update_channel_list ();

    if ($self->{app}->mplayer_running ()) {
        $self->{app}->start_mplayer ();
    }
}

package MyConfigFile;
# this object based on ConfigFile

sub read_config_file($) {
    my $line;
    my $Conf;
    my $file = shift;
    my $folder = "";
    my @folders = ();
    unless (open(CONF, "< " . $file)) {
        warn "Can't read configuration from $file: $!";
        return undef;
    }
    while(<CONF>) {
        chomp;
        next if m/^\s*#/;
        $line = $_;
        $line =~ s/[^\\]#.*$//;
        $line =~ s/\\#/#/g;
        $line =~ s/-(.*)=/_$1/g; # - -> _
        next if $line =~ m/^\s*$/;
        if ($line =~ /^\[(.*)\]/) {
            $folder = $1;
            $folder =~ tr/ -/__/;
            $folder =~ s/[\(\)]//g;
            push @folders, $folder;
            next;
        }
        $line =~ s{\$(\w+)}{
        exists($Conf->{$1}) ? $Conf->{$1} : "\$$1"
        }gsex;
        $line =~ m/\s*([^\s=]+)\s*=\s*(.*?)\s*$/;
        if ($folder eq "") {
            eval '$Conf->{' . join("}->{", split /[][]+/, $1) .
            "} = '" . $2 . "'";
        }
        else {
            eval '$Conf->{_' . $folder . '}->{' . join("}->{", split /[][]+/, $1) .
            "} = '" . $2 . "'";
        }
    }
    $Conf->{_folders} = \@folders;
    close CONF;
    return $Conf;
}

sub write_config_file($$)
{
    my ($file, $config) = @_;
    unless (open (CONF, ">" . $file)) {
        warn "Can't write configuration to $file: $!";
        return undef;
    }
    foreach $s (sort keys %$config) {
        print CONF "$s = ", $config->{$s}, "\n" unless $s =~ /^_/;
    }
    print CONF "\n";
    my @folders = @{$config->{_folders}};
    foreach $f (@folders) {
        print CONF "[$f]\n";
        my $keys = $config->{"_$f"};
        foreach $s (sort keys %$keys) {
            print CONF "$s = ", $config->{"_$f"}->{$s}, "\n";
        }
        print CONF "\n";
    }
    print CONF "\n";
    close CONF;
}

### MyApp ojjektum ###
package MyApp;

sub new 
{
    my ($class) = @_;
    my $self = {
        window => undef,
        config => undef,
        mplayer_running => 0,
        mplayer => undef
    };
    my @pp_codes = ("hb", "vb", "dr", "al", "lb", "li", "ci", "md", "fd", "tn", "h1", "v1");
    $self->{pp_codes} = \@pp_codes;
    my @vf_table = (
        {name => "pp", desc => "Postprocess", default => "yes",
         longdesc => "Postprocessing filter",
         params => [] # special parameters
        },
        {name => "spp", desc => "Simple PP", default => "no",
         longdesc => "Simple postprocessing filter",
         params => ["Quality    Integer 0..6       3",
                    "Force_quantization Integer 0..No       0",
                    "Mode       Integer 0..1       0"
                    ],
         help => <<EOT
Quality: 0-6 (default: 3)
Force_quantization: force quantization parameter (default: 0, use qp from video)
Mode:
0: hard thresholding (default)
1: soft thresholding (better deringing, but blurrier)
EOT
        },
        {name => "lavcdeint", desc => "lavc Deint", default => "no",
         longdesc => "libavcodec's deinterlace filter",
         params => []
        },
        {name => "kerndeint", desc => "KernDeint", default => "no",
         longdesc => "Kernel deinterlacer filter",
         params => ["Threshold  Integer 0..255  10",
                    "Map        Flag    0..1    0",
                    "Order      Flag    0..1    0",
                    "Sharp      Flag    0..1    0",
                    "Twoway     Flag    0..1    0"
                    ],
         help => <<EOT
Donald Graft's adaptive kernel deinterlacer. Deinterlaces parts of a video if a configurable threshold is exceeded.

Threshold (0 - 255): Threshold (default 10).
Map (0 or 1): Paint pixels which exceed the threshold white (default: 0).
Order (0 or 1): Swap fields if 1 (default: 0).
Sharp (0 or 1): Enable additional sharpening (default: 0).
Twoway (0 or 1): Enable twoway sharpening (default: 0).
EOT
        },
        {name => "rectangle", desc => "Rectangle", default => "no",
         longdesc => "Draw rectangle filter",
         params => ["Width      Integer -1..No    -1", 
                    "Height     Integer -1..No    -1", 
                    "X          Integer -1..No    -1",
                    "Y          Integer -1..No    -1"],
         help => <<EOT
The plugin responds to the input.conf directive 'change_rectangle' that takes two parameters.

Width, Height: width and height (default: -1, maximum possible width where boundaries are still visible.)
X, Y: top left corner position (default: -1, uppermost leftmost)
EOT
        },
        {name => "crop", desc => "Crop", default => "no",
         longdesc => "Crop filter",
         params => ["Width      Integer -1..No    -1", 
                    "Height     Integer -1..No    -1", 
                    "X          Integer -1..No     0",
                    "Y          Integer -1..No     0"],
         help => <<EOT
Crops the given part of the image and discards the rest. Useful to remove black bands from widescreen movies.

Width, Height: Cropped width and height, defaults to original width and height.
X,Y: Position of the cropped picture, defaults to center.
EOT
        },
        {name => "cropdetect", desc => "Crop detect", default => "no",
         longdesc => "Crop detect",
         params => ["Treshold   Integer 0..255    24"],
         help => <<EOT
Calculates necessary cropping parameters and prints the recommended parameters to stdout. The threshold can be optionally specified from nothing (0) to everything (255). Default: 24.
EOT
        },
        {name => "delogo", desc => "Delogo", default => "no",
         longdesc => "Delogo filter",
         params => [
                    "X          Integer -1..No     0",
                    "Y          Integer -1..No     0",
                    "Width      Integer -1..No     0",
                    "Height     Integer -1..No     0",
                    "Thickness  Integer -1..No     10",
                    ],
         help => <<EOT
Suppresses a TV  station logo by a simple interpolation of the surrounding pixels. Just set a rectangle covering the logo and watch it disappear (and sometimes something even uglier appear - your mileage may vary).

X,Y: Position of the top left corner of the logo.
Width, Height: Width and height of the cleared rectangle.
Thickness: Thickness of the fuzzy edge of the rectangle (added to width and height). When set to -1, a green rectangle is drawn on the screen to simplify finding the right x,y,w,h parameters.
EOT
        },
        {name => "boxblur", desc => "Box blur", default => "no",
         longdesc => "Box blur",
         params => ["Radius     Integer No..No     2",
                    "Power      Integer No..No     5"
                    ],
         help => <<EOT
Radius: size of the filter
Power: How often the filter should be applied.
EOT
        },
        #{name => "sab", desc => "SAB", default => "no",
        # longdesc => "Shape adaptive blur",
        # params => ["Radius             Float   0.1..4.0       2.0",
        #            "Prefilter_strength Float   0.1..2.0       1.5",
        #            "Color_diff         Float   0.1..100.0     20.0"
        #            ],
        # help => "Radius: blur filter strength (~0.1-4.0) (slower if larger)\n" .
        #         "Prefilter strength: prefilter strength (~0.1-2.0)\n" .
        #         "Color diff: How different the pixels are allowed to  be  considered."
        #},
        {name => "smartblur", desc => "Smart blur", default => "no",
         longdesc => "Smart blur",
         params => ["Radius     Float   0.1..5.0   2.0",
                    "Strength   Float   -1.0..1.0  0.5",
                    "Tresh      Float   0.1..100.0 10"
                    ],
         help => <<EOT
Radius: blur filter strength (~0.1-5.0) (slower if larger)
Strength: blur (0.0-1.0) or sharpen (-1.0-0.0)
Thresh: filter all (0), filter flat areas (0-30) or filter edges (-30-0)
EOT
        },
        {name => "scale", desc => "Scale", default => "no",
         longdesc => "Software scaling",
         params => ["Width      Integer -3..No    -1", 
                    "Height     Integer -3..No    -1", 
                    "Interlaced Integer 0..1       0",
                    "Chr_drop   Integer 0..3       0", 
                    "Param      Integer 0..100     0"],
         help => <<EOT
Scales the image with the software scaler (slow) and performs a YUV<->RGB colorspace conversion (also see -sws option).

Width, Height:
    scaled width/height (default: original width/height)
    NOTE:  If -zoom is used, and underlying filters (including libvo) are incapable of scaling, it defaults  to
    d_width/d_height!
     0:   scaled d_width/d_height
    -1:   original width/height
    -2:   Calculate w/h using the other dimension and the prescaled aspect ratio.
    -3:   Calculate w/h using the other dimension and the original aspect ratio.

Interlaced:
    Toggle interlaced scaling.

Chr_drop:
    chroma skipping
    0: use all available input lines for chroma
    1: use only every 2. input line for chroma
    2: use only every 4. input line for chroma
    3: use only every 8. input line for chroma

Param:
    scaling parameter (depends upon the scaling method used)
    -sws 2 (bicubic):  sharpness (0 (soft) - 100 (sharp))
    -sws 7 (gaussian): sharpness (0 (soft) - 100 (sharp))
    -sws 9 (lanczos):  filter length (1-10)
EOT
        },
        {name => "expand", desc => "Expand", default => "no",
         longdesc => "Expanding & OSD",
         params => ["Width      Integer No..No     0", 
                    "Height     Integer No..No     0", 
                    "X          Integer -1..No    -1", 
                    "Y          Integer -1..No    -1",
                    "OSD        Flag    0..1       0"],
         help => <<EOT
Expands (not scales) movie resolution to the given value and places the unscaled original at coordinates x, y.  Can be used for placing subtitles/OSD in the resulting black bands.

Width, Height: expanded width, height (default: original width, height). Negative values for width and hheight are treated as offsets to the original size.

X,Y: position  of  original  image on the expanded image (default: center)

OSD: OSD/subtitle rendering
    0: disable (default)
    1: enable
EOT
        },
        {name => "denoise3d", desc => "Denoise", default => "no",
         longdesc => "Image noise reducer",
         params => ["Luma       Integer 0..No      4", 
                    "Chroma     Integer 0..No      3",
                    "Time       Integer 0..No      6"
                    ],
         help => "This filter aims to reduce image noise producing smooth images " .
                 "and making still images really still (This should enhance " .
                 "compressibility.). It can be given from 0 to 3 parameters. If you " .
                 "omit a parameter, a reasonable value will be inferred.\n\n" .
                 "Luma: spatial luma strength (default = 4)\n" .
                 "Chroma: spatial chroma strength (default = 3)\n" .
                 "Time: temporal strength (default = 6)\n"
        },
        {name => "hqdn3d", desc => "HQDenoise", default => "no",
         longdesc => "High quality image noise reducer",
         params => ["Luma       Integer 0..No      4", 
                    "Chroma     Integer 0..No      3",
                    "Time       Integer 0..No      6"
                    ],
         help => "High precision/quality version of the denoise3d filter. " .
                 "Parameters and usage are the same. " .
                 "This filter aims to reduce image noise producing smooth images " .
                 "and making still images really still (This should enhance " .
                 "compressibility.). It can be given from 0 to 3 parameters. If you " .
                 "omit a parameter, a reasonable value will be inferred.\n\n" .
                 "Luma: spatial luma strength (default = 4)\n" .
                 "Chroma: spatial chroma strength (default = 3)\n" .
                 "Time: temporal strength (default = 6)\n"
        },
        {name => "hue", desc => "Hue",
         longdesc => "Hue changer filter", default => "no",
         params => ["Hue        Float   -180..180  0", 
                    "Saturation Float   -10..10    1"]
        },
        {name => "eq", desc => "Equalizer", default => "yes",
         longdesc => "Software video equalizer",
         params => ["Brightness Integer -100..100  0", 
                    "Contrast   Integer -100..100  0"],
         help => <<EOT
Software equalizer with interactive controls just like the hardware equalizer, for cards/drivers that do not support brightness and contrast controls in hardware.  
EOT
        },
        {name => "eq2", desc => "Equalizer 2", default => "no",
         longdesc => "Software video equalizer 2 (slow)",
         params => ["Gamma       Float 0.1..10   1.0", 
                    "Contrast    Float -2..2     1.0",
                    "Brightness  Float -1..1     0.0", 
                    "Saturation  Float 0..3      1.0", 
                    "Red_Gamma   Float 0.1..10   1.0", 
                    "Green_Gamma Float 0.1..10   1.0", 
                    "Blue_Gamma  Float 0.1..10   1.0", 
                    "Weight      Float 0..1      1.0"
                    ],
         help => <<EOT
Alternative software equalizer that uses lookup tables (very slow), allowing gamma correction in addition to simple brightness and contrast adjustment. Note that it uses the same MMX optimized code as -vf eq if all gamma values are 1.0. The parameters are given as floating point values.  Parameters rg, gg, bg are the independent gamma values for the Red, Green and  Blue components. The weight parameter can be used to reduce the effect of a high gamma value on bright image areas, e.g. keep them from getting overamplified and just plain white.  A value of 0.0 turns the gamma correction all the way down while 1.0 leaves it at its full strength. Defaults are gamma=1.0, contrast=1.0, brightness=0.0, saturation=1.0, weight=1.0. Value ranges are 0.1-10 for gamma, -2-2 for contrast (negative values result in a negative image), -1-1 for brightness, 0-3 for saturation and 0-1 for weight.
EOT
        },
        {name => "flip", desc => "Flip", default => "no",
         longdesc => "Flip image upside-down",
         params => []
        },
        {name => "mirror", desc => "Mirror", default => "no",
         longdesc => "Horizontal mirror filter",
         params => []
        },
        {name => "rotate", desc => "Rotate", default => "no",
         longdesc => "Rotate filter",
         params => ["Rotate     Integer 0..7  0"]
        },
        {name => "yuy2", desc => "YUY2", default => "no",
         longdesc => "Software YUY2 conversion forcing",
         params => []
        },
        {name => "palette", desc => "Palette", default => "no",
         longdesc => "RGB/BGR 8 -> 15/16/24/32bpp colorspace\nconversion using palette.",
         params => []
        },
        {name => "test", desc => "Test", default => "no",
         longdesc => "Various test patterns",
         params => []
        },
        {name => "rgbtest", desc => "RGB Test", default => "no",
         longdesc => "RGB test pattern",
         params => []
        }
    );
    $self->{vf_table} = \@vf_table;

    $self->{window} = new Gtk2::Window ("toplevel");
    
    create_stock_buttons ();

    my $menu_tree = [
        _File => {
            item_type => '<Branch>',
            children => [
                _Configuration => {
                    callback => \&ConfigureMenu,
                    callback_data => $self,
                    item_type => '<StockItem>',
                    extra_data => 'configure'
                },
                _Export => {
                    callback => \&ExportMenu,
                    callback_data => $self,
                    item_type => '<StockItem>',
                    extra_data => 'export'
                },
                _Quit => {
                    callback => \&CloseAppWindow,
                    callback_data => $self,
                    item_type => '<StockItem>',
                    extra_data => 'quit'
                },
            ], 
        },
        _mplayer => {
            item_type => '<Branch>',
            children => [
                _Start => {
                    callback => \&start_mplayer,
                    callback_data => $self,
                    groupid => 1,
                    item_type => '<StockItem>',
                    extra_data => 'start'
                },
                S_top => {
                    callback => \&stop_mplayer,
                    callback_data => $self,
                    groupid => 1,
                    item_type => '<StockItem>',
                    extra_data => 'stop'
                },
                "_Next channel" => {
                    callback => \&NextChMenu,
                    callback_data => $self,
                    groupid => 1,
                    item_type => '<StockItem>',
                    extra_data => 'nextch'
                },
                "_Previous channel" => {
                    callback => \&PrevChMenu,
                    callback_data => $self,
                    groupid => 1,
                    item_type => '<StockItem>',
                    extra_data => 'prevch'
                },
                "Toggle _fullscreen" => {
                    callback => \&FullscreenMenu,
                    callback_data => $self,
                    groupid => 1,
                    item_type => '<StockItem>',
                    extra_data => 'togglefs'
                }
            ]
        },
        _Help => {
            item_type => '<Branch>',
            children => [
                _Contents => {
                    callback => \&ContentsMenu,
                    callback_data => $self,
                },
                _About => {
                    callback => \&AboutMenu,
                    callback_data => $self,
                }
            ]
        }
    ];

    my $vbox = new Gtk2::VBox (0, 0);
    $self->{window}->add ($vbox);
    
    my $menu = Gtk2::SimpleMenu->new (
        menu_tree => $menu_tree
        );
    $vbox->pack_start ($menu->{widget}, 0, 0, 0);

    $self->{table} = new Gtk2::Table (2, 2, 1);
    $vbox->pack_start ($self->{table}, 0, 0, 5);
    $self->{table2} = new Gtk2::Table (1, 1, 1);
    $vbox->pack_start ($self->{table2}, 1, 1, 0);

    $self->{start_btn} = Gtk2::Button->new_from_stock ("start");
    $self->{table}->attach ($self->{start_btn},         0, 1, 0, 1, ['fill', 'expand'], ['fill'], 5, 5);
    $self->{quit_btn} = Gtk2::Button->new_from_stock ("quit");
    $self->{table}->attach ($self->{quit_btn},          1, 2, 0, 1, ['fill', 'expand'], ['fill'], 5, 5);
    $self->{nextch_btn} = Gtk2::Button->new_from_stock ("nextch");
    $self->{table}->attach ($self->{nextch_btn},        0, 1, 1, 2, ['fill', 'expand'], ['fill'], 5, 5);
    $self->{prevch_btn} = Gtk2::Button->new_from_stock ("prevch");
    $self->{table}->attach ($self->{prevch_btn},        1, 2, 1, 2, ['fill', 'expand'], ['fill'], 5, 5);

    # callback registration
    $self->{window}->signal_connect ("delete_event", \&CloseAppWindowX, $self);
    $self->{start_btn}->signal_connect ("clicked", \&StartBtn, $self);
    $self->{quit_btn}->signal_connect ("clicked", \&CloseAppWindow, $self);
    $self->{nextch_btn}->signal_connect ("clicked", \&NextChBtn, $self);
    $self->{prevch_btn}->signal_connect ("clicked", \&PrevChBtn, $self);
    
    $self->{name_list} = new Gtk2::List ();
    $self->{name_list}->select_item (0);
    $self->{name_list_sw} = new Gtk2::ScrolledWindow (undef, undef);
    $self->{name_list_sw}->set_policy (GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    $self->{name_list_sw}->add_with_viewport($self->{name_list});
    $self->{name_list_sw}->set_size_request (200, 300);
    $self->{table2}->attach ($self->{name_list_sw}, 0, 1, 0, 1, ['fill', 'expand'], ['fill', 'expand'], 10, 10);
    $self->{new_channel} = -1;

    # set window attributes and show it
    $self->{window}->set_border_width (0);
    #$self->{window}->set_resizable (0);

    # config file searching...
    $self->{cfg_file} = $ENV{HOME} . "/.mplayertv";
    my $cfg = $self->{cfg_file};
    my $xawtv_cfg = $ENV{HOME} . "/.xawtv";
    my $xawtv_cfg2 = "/etc/X11/xawtvrc";
    # if ~/.mplayertv not exists then copying ~/.xawtvrc or /etc/X11/xawtvrc
    print `cp -v $xawtv_cfg $cfg` if (-r $xawtv_cfg && not -r $cfg);
    print `cp -v $xawtv_cfg2 $cfg` if (-r $xawtv_cfg && not -r $cfg);

    # reading config file
    my %config = %{MyConfigFile::read_config_file ($cfg) if -r $cfg};
    $self->{config} = \%config;
    my @folders = @{$config{_folders}};
    # processing config file
    if ($verbose_level >= 2) {
        print "### Configuration\n";
        foreach $s (keys %config) {
            print "$s = ", $config{$s}, "\n" unless $s =~ /^_/;
        }
        print "\n";
        print "### Folders: \n";
        foreach $f (@folders) {
            print "[$f]:\n";
            my $keys = $config{"_$f"};
            foreach $s (keys %$keys) {
                print "$s = ", $config{"_$f"}->{$s}, "\n";
            }
            print "\n";
        }
        print "\n";
    }
    ######
    # substituting default values
    $self->{config}->{_global}->{freqtab} = "europe-east" if (!defined ($self->{config}->{_global}->{freqtab}));
    $self->{config}->{_global}->{ratio} = "4:3" if (!defined ($self->{config}->{_global}->{ratio}));
    $self->{config}->{_global}->{osd} = "yes" if (!defined ($self->{config}->{_global}->{osd}));
    $self->{config}->{fullscreen} = "yes" if (!defined ($self->{config}->{fullscreen}));
    $self->{config}->{_defaults}->{norm} = "PAL" if (!defined ($self->{config}->{_defaults}->{norm}));
    $self->{config}->{_defaults}->{input} = "0" if (!defined ($self->{config}->{_defaults}->{input}));
    $self->{config}->{tv} = "v4l2" if (!defined ($self->{config}->{tv}));
    $self->{config}->{tv_device} = "/dev/video0" if (!defined ($self->{config}->{tv_device}));
    $self->{config}->{defaults}->{input} = "0" if (!defined ($self->{config}->{input}));
    $self->{config}->{vo} = "xv" if (!defined ($self->{config}->{vo}));
    $self->{config}->{subvo} = "" if (!defined ($self->{config}->{subvo}));
    $self->{config}->{outfmt} = "default" if (!defined ($self->{config}->{outfmt}));
    $self->{config}->{width} = "768" if (!defined ($self->{config}->{width}));
    $self->{config}->{height} = "576" if (!defined ($self->{config}->{height}));
    $self->{config}->{mjpeg} = "no" if (!defined ($self->{config}->{mjpeg}));
    $self->{config}->{preexec} = "" if (!defined ($self->{config}->{preexec}));
    $self->{config}->{postexec} = "" if (!defined ($self->{config}->{postexec}));
    foreach $f (@vf_table) {
        if (!defined ($self->{config}->{"vf_" . $f->{name}})) {
            $self->{config}->{"vf_" . $f->{name}} = $f->{default};
        }
        foreach $p (@{$f->{params}}) {
            my $pdefault = 0;
            my $pname = "";
            if ($p =~ /^(\w+)\s+(\w+)\s+([-+\w\d.]+\.\.[-+\w\d.]+)\s+([-+\w\d.]+)/) {
                $pname = $1;
                $pdefault = $4;
            }
            else {
                if ($p =~ /^(\w+)\s+(\w+)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/) {
                    $pname = $1;
                }
            }
            if (!defined ($self->{config}->{"vf_" . $f->{name} . "_" . $pname})) {
                $self->{config}->{"vf_" . $f->{name} . "_" . $pname} = $pdefault;
            }
        }
    }
    $self->{config}->{vf_pp} = "yes" if (!defined ($self->{config}->{vf_pp}));
    $self->{config}->{vf_pp_hb} = "yes" if (!defined ($self->{config}->{vf_pp_hb}));
    $self->{config}->{vf_pp_vb} = "yes" if (!defined ($self->{config}->{vf_pp_vb}));
    $self->{config}->{vf_pp_al} = "no" if (!defined ($self->{config}->{vf_pp_al}));
    $self->{config}->{vf_pp_dr} = "yes" if (!defined ($self->{config}->{vf_pp_dr}));
    $self->{config}->{vf_pp_lb} = "yes" if (!defined ($self->{config}->{vf_pp_lb}));
    $self->{config}->{vf_pp_li} = "no" if (!defined ($self->{config}->{vf_pp_li}));
    $self->{config}->{vf_pp_ci} = "no" if (!defined ($self->{config}->{vf_pp_ci}));
    $self->{config}->{vf_pp_md} = "no" if (!defined ($self->{config}->{vf_pp_md}));
    $self->{config}->{vf_pp_fd} = "no" if (!defined ($self->{config}->{vf_pp_fd}));
    $self->{config}->{vf_pp_tn} = "yes" if (!defined ($self->{config}->{vf_pp_tn}));
    $self->{config}->{vf_pp_h1} = "no" if (!defined ($self->{config}->{vf_pp_h1}));
    $self->{config}->{vf_pp_v1} = "no" if (!defined ($self->{config}->{vf_pp_v1}));
    foreach (@{$self->{pp_codes}}) {
        $self->{config}->{"vf_pp_" . $_ . "_a"} = "no" if (!defined ($self->{config}->{"vf_pp_" . $_ . "_a"}));
        $self->{config}->{"vf_pp_" . $_ . "_c"} = "yes" if (!defined ($self->{config}->{"vf_pp_" . $_ . "_c"}));
    }
    $self->{config}->{vf_pp_autoq} = "yes" if (!defined ($self->{config}->{vf_pp_autoq}));
    $self->{config}->{vf_pp_autoq_level} = "6" if (!defined ($self->{config}->{vf_pp_autoq_level}));
    $self->{config}->{ao} = "oss" if (!defined ($self->{config}->{ao}));
    $self->{config}->{subao} = "" if (!defined ($self->{config}->{subao}));
    #$self->{config}->{captalsa} = "no" if (!defined ($self->{config}->{captalsa}));
    $self->{config}->{adevice} = "" if (!defined ($self->{config}->{adevice}));
    $self->{config}->{amode} = "4" if (!defined ($self->{config}->{amode}));
    $self->{config}->{amixer_device} = "/dev/mixer" if (!defined ($self->{config}->{amixer_device}));
    $self->{config}->{amixer_channel} = "pcm" if (!defined ($self->{config}->{amixer_channel}));
    #$self->{config}->{mplayer} = "mplayer" if (!defined ($self->{config}->{mplayer}));
    $self->{config}->{terminal_emulator} = $terminal_path if (!defined ($self->{config}->{terminal_emulator}));
    $terminal_path = $self->{config}->{terminal_emulator};
    $self->{config}->{mplayer_opts} = "" if (!defined ($self->{config}->{mplayer_opts}));
    #$self->{config}->{scantv} = "scantv" if (!defined ($self->{config}->{scantv}));
    foreach $f (@folders) {
        next if ($f eq "defaults" || $f eq "global");
        my $keys = $config{"_$f"};
        $config{"_$f"}->{brightness} = 0 if (!defined $keys->{brightness});
        $config{"_$f"}->{contrast} = 0 if (!defined $keys->{contrast});
        $config{"_$f"}->{hue} = 0 if (!defined $keys->{hue});
        $config{"_$f"}->{saturation} = 0 if (!defined $keys->{saturation});
        $config{"_$f"}->{volume} = 0 if (!defined $keys->{volume});
    }
    $self->{config}->{extvolume} = "no" if (!defined ($self->{config}->{extvolume}));
    $self->{config}->{extvolume_program} = 'aumix -l%s%d' if (!defined ($self->{config}->{extvolume_program}));
    $self->{config}->{usbaudio} = "no" if (!defined ($self->{config}->{usbaudio}));
    $self->{config}->{usbaudio_device} = "/dev/dsp1" if (!defined ($self->{config}->{usbaudio_program}));
    
    bless $self, $class;
    
    $self->update_channel_list ();

    return $self;
}

# update channel list for main window
sub update_channel_list
{
    my ($self) = @_;

    my @list = $self->{name_list}->get_children;
    my $items = $#list;
    $self->{name_list}->clear_items (0, $items + 1);
    my $folders = $self->{config}->{_folders};
    my @channels = ();
    my $i = 1;
    foreach (@$folders) {
        next if ($_ eq "defaults" || $_ eq "global");
        my $item = new Gtk2::ListItem ($_);
        push @channels, $item;
        $item->{name} = $_;
        $item->{number} = $i++;
        $item->{channel} = $self->{config}->{"_$_"}->{channel};
        $item->{brightness} = $self->{config}->{"_$_"}->{brightness};
        $item->{contrast} = $self->{config}->{"_$_"}->{contrast};
        $item->{hue} = $self->{config}->{"_$_"}->{hue};
        $item->{saturation} = $self->{config}->{"_$_"}->{saturation};
        $item->{volume} = $self->{config}->{"_$_"}->{volume};
        $item->signal_connect ('select', \&select_channel_item, $self);
        $self->{name_list}->add ($item);
    }
    $self->{channels} = \@channels;
    $self->{name_list}->show_all;
}

# channel item select for main window
sub select_channel_item
{
    my ($item, $self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->tv_set_channel ($item->{number});
        $self->restore_mplayer_parameters ();
        $self->{brightness_delta} = $item->{brightness};
        $self->{contrast_delta} = $item->{contrast};
        $self->{hue_delta} = $item->{hue};
        $self->{saturation_delta} = $item->{saturation};
        $self->{volume_delta} = $item->{volume};
        $self->set_mplayer_parameters ();
    }
    $self->{selected_channel} = $item;
}

sub show
{
    my ($self) = @_;
    $self->{window}->show_all;
}

# stolen from AcidRip...
sub create_stock_buttons
{    
    my $factory = Gtk2::IconFactory->new;
    $factory->add_default;    
    my $style = new Gtk2::Style;
    my @items = (
        { stock_id => "start",          label => "_Start",      icon => 'gtk-execute' },
        { stock_id => "stop",           label => "S_top",       icon => 'gtk-stop' },
        { stock_id => "nextch",         label => "_Next",       icon => 'gtk-go-forward' },
        { stock_id => "prevch",         label => "_Previous",   icon => 'gtk-go-back' },
        { stock_id => "quit",           label => "_Quit",       icon => 'gtk-quit' },
        { stock_id => "configure",      label => "_Configure",  icon => 'gtk-preferences' },
        { stock_id => "export",         label => "_Export",     icon => 'gtk-convert' },
        { stock_id => "help",                                   icon => 'gtk-help' },
        { stock_id => "togglefs",       label => "Toggle _fullscreen", icon => 'gtk-zoom-fit'},
        { stock_id => "cancel",         label => "_Cancel",     icon => 'gtk-cancel' },
        { stock_id => "save",           label => "_Save",       icon => 'gtk-save' },
        { stock_id => "down",           label => "_Down",       icon => 'gtk-go-down' },
        { stock_id => "up",             label => "_Up",         icon => 'gtk-go-up' },
        { stock_id => "new",            label => "_New",        icon => 'gtk-new' },
        { stock_id => "modify",         label => "_Modify",     icon => 'gtk-properties' },
        { stock_id => "delete",         label => "D_elete",     icon => 'gtk-delete' },
        { stock_id => "deleteall",      label => "Delete a_ll", icon => 'gtk-delete' },
        { stock_id => "scan",           label => "Sc_an",       icon => 'gtk-find' },
        { stock_id => "details",        label => "_Details",    icon => 'gtk-dialog-info' },
        { stock_id => "ok",             label => "_Ok",         icon => 'gtk-ok' },
    );
    Gtk2::Stock->add(@items);
    foreach (@items) {
        $factory->add($_->{'stock_id'}, $style->lookup_icon_set($_->{'icon'}));
    }
}

sub restore_mplayer_parameters
{
    my ($self) = @_;
    $self->{mplayer}->brightness (-($self->{brightness_delta}));
    $self->{mplayer}->contrast (-($self->{contrast_delta}));
    $self->{mplayer}->hue (-($self->{hue_delta}));
    $self->{mplayer}->saturation (-($self->{saturation_delta}));
    if ($self->{config}->{extvolume} eq "yes") {
        if ($self->{volume_delta} != 0) {
            my $prg = $self->{config}->{extvolume_program};
            my $s = -($self->{volume_delta}) < 0 ? "-": "+";
            my $d = abs ($self->{volume_delta});
            $prg =~ s/%s/$s/g;
            $prg =~ s/%d/$d/g;
            print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
            my @output = `$prg`;
            print @output if ($verbose_level >= 2);
        }
    }
    else {
        $self->{mplayer}->volume (-($self->{volume_delta}));
    }
    return 1;
}

sub set_mplayer_parameters
{
    my ($self) = @_;
    if ($verbose_level >= 2) {
        print "setting params, brightness: ", $self->{brightness_delta}, " ";
        print "contrast: ", $self->{contrast_delta}, " ";
        print "hue: ", $self->{hue_delta}, " ";
        print "saturation: ", $self->{saturation_delta}, " ";
        print "volume: ", $self->{volume_delta}, "\n";
    }
    $self->{mplayer}->brightness ($self->{brightness_delta});
    $self->{mplayer}->contrast ($self->{contrast_delta});
    $self->{mplayer}->hue ($self->{hue_delta});
    $self->{mplayer}->saturation ($self->{saturation_delta});
    if ($self->{config}->{extvolume} eq "yes") {
        if ($self->{volume_delta} != 0) {
            my $prg = $self->{config}->{extvolume_program};
            my $s = $self->{volume_delta} < 0 ? "-": "+";
            my $d = abs ($self->{volume_delta});
            $prg =~ s/%s/$s/g;
            $prg =~ s/%d/$d/g;
            print "*** [mplayerTV] Running: $prg\n" if ($verbose_level >= 1);
            my @output = `$prg`;
            print @output if ($verbose_level >= 2);
        }
    }
    else {
        $self->{mplayer}->volume ($self->{volume_delta});
    }
    return 1;
}

### callback function for channel stepping
sub ChannelStep
{
    my ($self, $param) = @_;
    print "*** [mplayerTV] ChannelStep channel: $param\n" if ($verbose_level >= 1);
    # searching channel
    my $i = 0;
    foreach $item ($self->{name_list}->get_children) {
        if ($item->{name} eq $param) {
            # channel found
            #print "Channel $param found! i=$i\n"; 
            # TODO fix it! Does not work...
            #$self->{name_list}->select_item ($i);
            $self->{new_channel} = $i;
        }
        $i++;
    }
    # restoring parameters
    $self->restore_mplayer_parameters ();
    # setting new parameters
    $self->{brightness_delta} = $self->{config}->{"_$param"}->{brightness};
    $self->{contrast_delta} = $self->{config}->{"_$param"}->{contrast};
    $self->{hue_delta} = $self->{config}->{"_$param"}->{hue};
    $self->{saturation_delta} = $self->{config}->{"_$param"}->{saturation};
    $self->{volume_delta} = $self->{config}->{"_$param"}->{volume};
    $self->set_mplayer_parameters ();
    return 1;
}

sub mplayer_running
{
    my ($self) = @_;
    if ($self->{mplayer_started} && $self->{mplayer}->tv_thread_running ()) {
        print "*** [mplayerTV] mplayer is running\n" if ($verbose_level >= 1);
        return 1;
    }
    print "*** [mplayerTV] mplayer is not running\n" if ($verbose_level >= 1);
    return 0;
}

# Stop USB audio capturing
sub stop_usbaudio
{
    my ($self) = @_;
    my $pid = $self->{usbaudio_pid};
    my $killed_processes = 0;
    $killed_processes += kill 9, $pid;

    open (PS, "ps -o pid -o comm=Command|");
    while (<PS>) {
        #($uid, $pid, $ppid, $restOfLine) = split; # ps -f
        my ($pid, $command) = split;
        $killed_processes += kill 9, $pid if ($command eq "sox" or $command eq "sh");
    }
    close (PS);
    print "*** [mplayerTV] $killed_processes processes killed.\n" if ($verbose_level >= 1);
    $self->{usbaudio_pid} = 0;
}

# Start USB audio capturing
sub start_usbaudio
{
    my ($self) = @_;
    my $pid = fork ();
    if (not defined $pid) {
        warn "Cannot fork for USB audio capturing! Resources not avilable.\n";
    } 
    elsif ($pid == 0) {
        # Child
        print "USB audio capturing child started\n" if ($verbose_level >= 1);
        my $audio_device = $self->{config}->{adevice};
        my $usbaudio = $self->{config}->{usbaudio_device};
        # A "while" loop is required because sox sometimes dies
        my $prg = "while [ 1 ]; do sox -t ossdsp -r 44100 -w -c 2 $usbaudio -t ossdsp $audio_device 2>/dev/null; done";
        exec ($prg) or die "Cannot execute sox!\n";
        exit (0);
    } 
    else {
        # Parent
        $self->{usbaudio_pid} = $pid;
        #waitpid ($pid, 0);
    }
}

sub stop_mplayer
{
    my ($self) = @_;
    if ($self->{usbaudio_pid}) 
    {
        $self->stop_usbaudio ();
    }
    my $postexec = $self->{config}->{postexec};
    if ($self->mplayer_running ()) {
        print "*** [mplayerTV] stopping mplayer\n" if ($verbose_level >= 1);
        $self->{mplayer}->quit ();
        $self->{mplayer} = undef;
        $self->{mplayer_started} = 0;
    }
    print "*** [mplayerTV] Running: $postexec\n" if ($verbose_level >= 1 && $postexec);
    @output = `$postexec` if ($postexec);
    print "\n", @output, "\n" if ($verbose_level >= 2 && $postexec);
}

sub start_mplayer
{
    # $self->{export}: filename of script
    my ($self) = @_;
    
    print "*** [mplayerTV] start_mplayer\n" if ($verbose_level >= 2);
    $self->{brightness_delta} = 0;
    $self->{contrast_delta} = 0;
    $self->{hue_delta} = 0;
    $self->{saturation_delta} = 0;
    $self->{volume_delta} = 0;
    # creating local config variables
    my $chanlist = $self->{config}->{_global}->{freqtab};
    my $ratio = $self->{config}->{_global}->{ratio};
    my $osd = $self->{config}->{_global}->{osd} eq "yes";
    my $fullscreen = ($self->{config}->{fullscreen} eq "yes") ? "-fs" : "-nofs";
    my $norm = $self->{config}->{_defaults}->{norm};
    my $input = $self->{config}->{_defaults}->{input};
    my $tv = $self->{config}->{tv};
    my $tv_device = $self->{config}->{tv_device};
    my $vo = $self->{config}->{vo};
    my $outfmt = $self->{config}->{outfmt};
    my $subvo = $self->{config}->{subvo};
    $vo .= ":$subvo" if ($subvo ne "");
    my $width = $self->{config}->{width};
    my $height = $self->{config}->{height};
    my $mjpeg = $self->{config}->{mjpeg};
    my $preexec = $self->{config}->{preexec};
    my $postexec = $self->{config}->{postexec};
    my $mplayer_opts = $self->{config}->{mplayer_opts};
    my $channels = "";
    my @folders = @{$self->{config}->{_folders}};
    my $first_channel = "";
    my $export = $self->{export};
    my $sel_channel = $self->{selected_channel};
    $first_channel = $sel_channel->{name} if (defined $sel_channel);
    my $i = 0;
    foreach (@folders) {
        next if ($_ eq "defaults" || $_ eq "global");
        $first_channel = $_ if ($first_channel eq "");
        $channels .= "," if ($channels ne "");
        $channels .= $self->{config}->{"_$_"}->{channel} . "-" . $_;
        $i++;
    }
    if ($tv_device && !-r $tv_device) {
        print "*** [mplayerTV] $tv_device device not readable by user!\n" if ($verbose_level >= 1);
        my $errdialog1 = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK, 
            "$tv_device device not readable by user or does not exists!\n$!"
            #"\nls say:" . `ls -l $tv_device 2>&1`
        );
        $errdialog1->run;
        $errdialog1->destroy;
        #$self->ConfigureMenu ();
        return 0;
    }
    if (!$i) {
        print "*** [mplayerTV] No channels!\n" if ($verbose_level >= 1);
#        my $errdialog1 = new Gtk2::MessageDialog ($self->{window}, 
#            GTK_DIALOG_MODAL,
#            GTK_MESSAGE_ERROR,
#            GTK_BUTTONS_OK, 
#            "No channels!"
#        );
#        $errdialog1->run;
#        $errdialog1->destroy;
#        $self->ConfigureMenu ();
#        return 0;
    }
    my $pp = $self->{config}->{vf_pp};
    my $pp_autoq = $self->{config}->{vf_pp_autoq};
    my $pp_autoq_level = $self->{config}->{vf_pp_autoq_level};
    my $ao = $self->{config}->{ao};
    my $subao = $self->{config}->{subao};
    $ao .= ":$subao" if ($subao ne "");
    #my $captalsa = $self->{config}->{captalsa};
    my $adevice = $self->{config}->{adevice};
    my $amode = $self->{config}->{amode};
    my $mixer = $self->{config}->{amixer_device};
    my $mixer_channel = $self->{config}->{amixer_channel};
    my %opt = ();
    
    $opt{"-vo"} = $vo;
    $opt{"-ao"} = $ao;

    my @vf_table = @{$self->{vf_table}};

    # creating video filters (-vf)
    my $vf_ok = 0; # no video filters enabled
    my $vf_opt = "";
    foreach $f (@vf_table) {
        if ($self->{config}->{"vf_" . $f->{name}} eq "yes") {
            $vf_ok = 1;
        }
    }
    if ($vf_ok) {
        my $firstfilt = 1;
        foreach $f (@vf_table) {
            next if ($f->{name} eq "pp" || 
                $self->{config}->{"vf_" . $f->{name}} eq "no");
            if (!$firstfilt) {
                $vf_opt .= ",";
            }
            else {
                $firstfilt = 0;
            }
            $vf_opt .= $f->{name};
            my @params = @{$f->{params}};
            #print "params: ", @params, " -> ", $#params + 1, "\n";
            $vf_opt .= "=" if ($#params + 1 > 0);
            my $first = 1;
            foreach $p (@{$f->{params}}) {
                $vf_opt .= ":" unless ($first);
                my $pname = "";
                if ($p =~ /^(\w+)\s+(\w+)\s+([-+\w\d.]+\.\.[-+\w\d.]+)/) {
                    $pname = $1;
                }
                #print "id: ", "vf_" . $f->{name} . "_" . $pname, "\n";
                $vf_opt .= $self->{config}->{"vf_" . $f->{name} . "_" . $pname};
                $first = 0;
            }
        }

        # creating postprocessing video filter (-vf pp)
        if ($pp eq "yes") {
            my @pp_codes = @{$self->{pp_codes}};
            my $pp_ok = 0;
            foreach $code (@pp_codes) {
                $pp_ok = 1 if ($self->{config}->{"vf_pp_" . $code} eq "yes");
            }
            if ($pp_ok) {
                $vf_opt .= "," unless ($firstfilt);
                $vf_opt .="pp=";
                my $first = 1;
                foreach $code (@pp_codes) {
                    if ($self->{config}->{"vf_pp_" . $code} eq "yes") {
                        $vf_opt .= "/" unless ($first);
                        $vf_opt .= "$code";
                        if ($code ne "tn") {
                            if ($self->{config}->{"vf_pp_" . $code . "_c"} eq "yes") {
                                $vf_opt .= ":c" 
                            }
                            else {
                                $vf_opt .= ":y";
                            }
                            $vf_opt .= ":a" if ($self->{config}->{"vf_pp_" . $code . "_a"} eq "yes");
                        }
                        $first = 0;
                    }
                }
                $opt{"-autoq"} = "$pp_autoq_level" if ($pp_autoq eq "yes");
            }
        }
    }
    $opt{"-vf"} = $vf_opt if ($vf_ok);
    my $tv_opt = "driver=$tv:chanlist=$chanlist:norm=$norm:";
    $tv_opt .= "outfmt=$outfmt:" if ($outfmt ne "default");
    $tv_opt .= "device=$tv_device:" if ($tv_device ne "");
    $tv_opt .= "input=$input:";
    if ($mjpeg eq "no") {
        $tv_opt .= "width=$width:height=$height:";
    }
    else {
        my $decimation;
        my %sizes = ("full size" => 1, "medium size" => 2, "small size" => 4);
        $decimation = $sizes{$mjpeg};
        $tv_opt .= "mjpeg:decimation=$decimation:";
    }
    $tv_opt .= "adevice=$adevice:" if ($adevice ne "");
    $tv_opt .= "amode=$amode:" if ($amode < 4);
    #$tv_opt .= "alsa:" if ($captalsa eq "yes");
    $tv_opt .= "channels=$channels";
    $opt{"-tv"} = $tv_opt;
    if ($osd) {
        $opt{"-osdlevel"} = "1";
    }
    else {
        $opt{"-osdlevel"} = "0";
    }
    my $aspect_opt = $ratio;
    $aspect_opt =~ s#:#/#;
    $opt{"-aspect"} = "$aspect_opt" if ($aspect_opt ne "");
    if ($ao eq "oss") {
        $opt{"-mixer"} = $mixer;
        $opt{"-mixer-channel"} = $mixer_channel;
    }
    #$opt{"-v"} = 3 if ($verbose_level >= 2);
   
    ### Script exporting ###
    if (defined $export) {
        $opt{$fullscreen} = undef;
        unless (open FH, ">$export") {
            print "*** [mplayerTV] Can't create $export: $!!\n";
            my $errdialog2 = new Gtk2::MessageDialog ($self->{window},
                GTK_DIALOG_MODAL,
                GTK_MESSAGE_ERROR,
                GTK_BUTTONS_OK,
                "Can't create $export!\n$!"
                );
            $errdialog2->run;
            $errdialog2->destroy;
            return 0;
        };
        print FH "#!/bin/sh\n";
        print FH "# Generated by $prog\n# $copy\n# $homepage\n";
        print FH "$preexec\n" if ($preexec);
        print FH "$mplayer_path ";
        foreach (keys %opt) {
            my $s = defined ($opt{$_}) ? " " . $opt{$_} : ""; # for $fullscreen opt
            print FH " " . $_ . $s;
        }
        print FH " $mplayer_opts tv://\n";
        print FH "$postexec\n" if ($postexec);
        close FH;
        chmod 0755, $export;
        return 1;
    }
    
    # running programs
    #select STDOUT; $| = 1;      # make unbuffered
    $self->stop_mplayer ();
    print "*** [mplayerTV] Running: $preexec\n" if ($verbose_level >= 1 && $preexec);
    my $ch = "";
    $ch = $sel_channel->{name} if (defined $sel_channel);
    @output = `$preexec` if ($preexec);
    print "\n", @output, "\n" if ($verbose_level >= 2 && $preexec);
    if ($verbose_level >= 1) {
        print "*** [mplayerTV] starting mplayer, options: ";
        foreach (keys %opt) {
            print $_, " ", $opt{$_}, " ";
        }
        print " $fullscreen $mplayer_opts tv://$ch\n";
    }
    $self->{mplayer} = new MPlayer ("tv://$ch", %opt, $fullscreen, split (/\s+/, $mplayer_opts));
    $self->{mplayer_started} = 1;
    my $error = join ("\n", $self->{mplayer}->error ());
    if ($error) {
        $self->{mplayer} = undef;
        $self->{mplayer_started} = 0;
        print "*** [mplaterTV] Can't start mplayer: $error" if ($verbose_level >= 1);
        my $errdialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK, 
            "$error"
            );
        $errdialog->run;
        $errdialog->destroy;
        return 0;
    }
    my $out_mode = 0;
    $out_mode = 2 if ($verbose_level >= 2);
    $self->{mplayer}->tv_set_channel_step_callback ($out_mode, \&ChannelStep, $self);
    sleep (1);
    unless ($self->{mplayer}->tv_thread_running ()) {
        my @output = $self->{mplayer}->output ();
        my $error = join "\n", grep (/error/i || /fail/i || /unknown/i, @output);
        print "*** [mplaterTV] mplayer died within one second. Error string: $error\n" if ($verbose_level >= 1);
        my $errdialog = new Gtk2::MessageDialog ($self->{window}, 
            GTK_DIALOG_MODAL,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK, 
            "MPlayer died within one second.\n$error"
            );
        $errdialog->add_button ("details", 1);
        my $response = $errdialog->run;
        $errdialog->destroy;
        if ($response eq "1") {
            my $dialog = new Gtk2::Dialog ("MPlayer output", $errdialog, 'modal');
            $dialog->resize (640, 400);
            $dialog->add_button ("ok", 1);
            $dialog->{buffer} = new Gtk2::TextBuffer ();
            $dialog->{buffer}->insert_at_cursor (join "\n", @output);
            $dialog->{textview} = Gtk2::TextView->new_with_buffer ($dialog->{buffer});
            $dialog->{textview}->set_editable (0);
            $dialog->{textview_sw} = new Gtk2::ScrolledWindow (undef, undef);
            $dialog->{textview_sw}->set_policy (GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
            $dialog->{textview_sw}->add_with_viewport($dialog->{textview});
            $dialog->vbox->pack_start ($dialog->{textview_sw}, 1, 1, 10);
            $dialog->show_all;
            $dialog->run;
            $dialog->destroy;
        }
        $self->{mplayer} = undef;
        $self->{mplayer_started} = 0;
        return 0;
    }
    # resetting to defaults
    $self->{mplayer}->brightness ("1", "abs");
    $self->{mplayer}->contrast ("1", "abs");
    $self->{mplayer}->hue ("1", "abs");
    $self->{mplayer}->saturation ("1", "abs");
    $self->{mplayer}->volume ("1", "abs");
    # setting parameters for first channel
    $self->{brightness_delta} = $self->{config}->{"_$first_channel"}->{brightness};
    $self->{contrast_delta} = $self->{config}->{"_$first_channel"}->{contrast};
    $self->{hue_delta} = $self->{config}->{"_$first_channel"}->{hue};
    $self->{saturation_delta} = $self->{config}->{"_$first_channel"}->{saturation};
    $self->{volume_delta} = $self->{config}->{"_$first_channel"}->{volume};
    $self->set_mplayer_parameters ();
    if ($self->{config}->{usbaudio} eq "yes") 
    {
        $self->start_usbaudio ();
    }
    return 1;
}

### Callback function to close the window (with Quit button)
sub CloseAppWindow
{
    my ($window, $self) = @_;
    $self->stop_mplayer ();
    Gtk2->main_quit;
    1;
}

### Callback function to close the window (with X icon)
sub CloseAppWindowX
{
    my ($window, $event, $self) = @_;
    CloseAppWindow ($window, $self);
}

### Callback function for 'Start' button
sub StartBtn
{
    my ($btn, $self) = @_;
    $self->start_mplayer ();
}

### Callback function for 'Export' menu
sub ExportMenu
{
    my ($self) = @_;
    my $dialog = new Gtk2::FileChooserDialog ("Export script", 
        $self->{window}, GTK_FILE_CHOOSER_ACTION_SAVE,
        "gtk-save" => 'ok', "gtk-cancel" => 'cancel');
    $dialog->{mplayer} = $self;
    $dialog->signal_connect ('response', sub {
            my ($self, $response) = @_;
            #print "response is $response\n";
            if ($response eq "ok") {
                my $self2 = $self->{mplayer};

                $self2->{export} = $dialog->get_filename ();
                print "*** [mplayerTV] exporting script: " . $self2->{export} . "\n" if ($verbose_level >= 1);
                start_mplayer ($self2);
                $self2->{export} = undef;
            }
            $self->hide;
            1;
        });
    $dialog->show ();
}

### Callback function for 'Configure' menu
sub ConfigureMenu
{
    my ($self) = @_;
    my $conf = new ConfigureDialog ($self->{config}, $self->{cfg_file}, $self);
    $conf->show ();
}

### Callback function for 'Next channel' menu
sub NextChMenu
{
    my ($self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->tv_step_channel (1);
    }
}

### Callback function for 'Prev channel' menu
sub PrevChMenu
{
    my ($self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->tv_step_channel (-1);
    }
}

### Callback function for 'Next channel' button
sub NextChBtn
{
    my ($btn, $self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->tv_step_channel (1);
    }
}

### Callback function for 'Prev channel' button
sub PrevChBtn
{
    my ($btn, $self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->tv_step_channel (-1);
    }
}

### Callback function for 'Toggle fullscreen' menu
sub FullscreenMenu
{
    my ($self) = @_;
    if ($self->mplayer_running ()) {
        $self->{mplayer}->vo_fullscreen ();
    }
}

### Callback function for 'About' menu item
sub AboutMenu
{
    my ($self) = @_;
    my $dialog = new Gtk2::MessageDialog ($self->{window}, 
        GTK_DIALOG_MODAL,
        GTK_MESSAGE_INFO,
        GTK_BUTTONS_OK, 
        "$copyright_text"
        );
    #$dialog->set_default_size (480, 280);
    $dialog->run;
    $dialog->destroy;
}

### Callback function for 'Contens' menu item
sub ContentsMenu
{
    my ($self) = @_;
    # getting path of this script
    @_ = split /\//, $ENV{_};
    pop @_; 
    my $mplayertv_path = join "/", @_;
    my $doc = "$mplayertv_path/DOCS/manual-eng.html";
    threads->create (sub { `$browser_path $doc`; });
}

# Thank you for reading the source code :)
