#!/usr/bin/perl -w

use strict;
use utf8;


package kRip;
# for querying FreeDB
use CDDB_get qw( get_cddb get_discids );
# for getting covers from Amazon
use Net::Amazon;
use Net::Amazon::Request::Keyword;
use Net::Amazon::Request::ASIN;
# for creating the image
use Image::Magick;
# for tagging mp3 files
use MP3::Tag;

my $modulename        = "kRip";
my $date              = "2005-08-23";             # Date of last modification
my $version           = "0.0.2";                  # Version number for this script

my $HOME              = $ENV{HOME};               # Valid $HOME for current user
my $RCFILE            = "$HOME/.kRiprc";          # Default run-command file for kRip
my $COVERJPGFILE      = "cover.jpg";
my $COVERBMPFILE      = "cover.bmp";
my $ALBUMTXTFILE      = "Album.txt";
my $COVERTMP          = "covertmp.jpg";

my $CDDB_HOST         = "freedb.freedb.org";      # CDDB host to find the server on
my $CDDB_PORT         = 888;                      # CDDB port the CDDB server is using


my $JOB_NAME          = 0;
my $JOB_ENABLED       = 1;
my $JOB_ENCODER       = 2;
my $JOB_ENCOPTS       = 3;
my $JOB_ENCFORMAT     = 4;
my $JOB_TAG           = 5;
my $JOB_TAGCOVER      = 6;
my $JOB_COVERJPG      = 7;
my $JOB_COVERBMP      = 8;
my $JOB_ALBUMTXT      = 9;

my $localCDDBpath     = "$HOME/.cddb/";           # Default path to the local CDDB
my $coverPath         = "$HOME/.covers/";         # Default path for the cover images
my $coverfilename     = "";


my $dev               = "";
my $cddb_id           = "";                       # Album's CDDB ID
my $artist            = "";                       # Artist's name
my $title             = "";                       # Album's title
my $year              = "";                       # Album's year of recording
my $genre             = "";                       # Album's category (Genre)
# my $comment           = "";                       # Comment
my @trackNameList     = ();                       # List of proper names for the tracks
my @trackNameFile     = ();                       # List of WAV filenames
my @trackNameLength   = ();                       # List of length of the tracks
my @trackNameLevel    = ();                       # List of volume level of the tracks
my @trackNamePeak     = ();                       # List of volume peak of the tracks

my $state             = 0;
my $numTracks         = 0;
my $maxTracks         = 50;
my $numHiddenTracks   = 0;

my $actDevice         = 0;

my $defaultGenre      = 0;
my $defaultYear       = 2005;
my $defaultAmazonLocale = 0;
my $amazonToken       = "";

my $ripperTRACK       = "";                       # cdda2wav option to select the tracks
my $ripperDEVICE      = "";                       # cdda2wav option to select the device
my $ripperOTHER       = "";                       # other options for cdda2wav

my $removeSilence     = 0;                        # remove silence at the beginning and at the end
my $checkHiddenTracks = 0;                        # check for hidden tracks

my $normalizeToLevel  = "";                       # level for 'normalizer'
my $normalizeByLevel  = "";                       # level for 'normalizer'
my $normalizeIgnL     = 0;                        # ignore louder songs
my $normalizeOpts     = "";                       # parameter/options for 'normalizer'

my $numEncoders       = 0;
my @encoderName       = ();                       # List of supported encoders

my $numJobs           = 0;
my $numJobAttr        = 10;

my $countItems        = 0;
my $currentItem       = -1;
my $currItemLast      = -1;
my $initialFill       = 1;

my @AoA               = ();

my $pwd               = "";                       # Where all output will be placed
my $command           = "";
#my $result            = 0;

my $debug             = 0;                        # debug verbosity: 0: no debug  1: sosme messages
                                                  # 2: verbose  3: more verbose  4: ...

my $queryTypeCDDB     = 0;                        # 0: use CDDB_get    1: use cdda2wav


use Qt;
use Qt::attributes qw(
    C_arrow
    C_wait
);
#use threads;
#use POSIX ":sys_wait_h";use Qt;
use Qt::isa qw(Qt::Widget);
use Qt::slots
    CB_device_activated => [],
    PB_scanCD_clicked => [],
    CB_sampler_stateChanged => [],
    T_tracks_valueChanged => ['int', 'int'],
    PB_openCover_clicked => [],
    PB_searchCover_clicked => [],
    CB_amazonLocale_activated => [],
    PB_queryCDDB_clicked => [],
    PB_queryLocalCDDB_clicked => [],
    PB_saveLocalCDDB_clicked => [],
    PB_autoCorrection_clicked => [],
    T_tracks_2_selectionChanged => [],
    PB_rip_clicked => [],
    PB_normalizeTo_clicked => [],
    PB_normalizeBy_clicked => [],
    PB_jobNew_clicked => [],
    PB_jobDelete_clicked => [],
    PB_jobMoveUp_clicked => [],
    PB_jobMoveDown_clicked => [],
    LB_jobs_currentChanged => [],
    LB_jobTag_stateChanged => [],
    PB_jobsRun_clicked => [],
    PB_saveSettings_clicked => [],
    PB_nextTab_clicked => [],
    PB_prevTab_clicked => [],
    readFieldData => [];
use Qt::attributes qw(
    PB_saveSettings
    PB_exit
    TW_tabs
    tab
    TL_device
    CB_device
    PB_scanCD
    TL_cddbid
    CB_live
    CB_sampler
    TL_artist
    LE_artist
    TL_album
    LE_album
    TL_year
    SB_year
    TL_genre
    CB_genre
    TL_cover
    PL_cover
    TL_coverinfo
    PB_openCover
    PB_searchCover
    CB_amazonLocale
    TL_tracks
    T_tracks
    PB_queryCDDB
    PB_queryLocalCDDB
    PB_saveLocalCDDB
    PB_autoCorrection
    TL_amazonLink
    PB_nextTo2
    tab_2
    TL_tracks_2
    T_tracks_2
    TL_avg
    TL_max
    TL_min
    TL_levelValAvg
    TL_levelValMax
    TL_levelValMin
    TL_peakValAvg
    TL_peakValMax
    TL_peakValMin
    PB_rip
    CB_removeSilence
    CB_checkHiddenTracks
    PB_normalizeTo
    SB_normalizeToLevel
    CB_normalizeIgnoreLouder
    PB_normalizeBy
    SB_normalizeByLevel
    TL_normalizeOpts
    LE_normalizeOpts
    PB_prevTo1
    PB_nextTo3
    TabPage
    TL_jobs
    LB_jobs
    GB_jobProp
    TL_jobName
    LE_jobName
    TL_jobEncoder
    CB_jobEncoder
    TL_jobEncOpts
    LE_jobEncOpts
    TL_jobEncFormat
    LE_jobEncFormat
    CB_jobTag
    CB_jobTagCover
    CB_jobCreateCoverJPG
    CB_jobCreateCoverBMP
    CB_jobCreateAlbumTxt
    PB_jobNew
    PB_jobDelete
    PB_jobMoveUp
    PB_jobMoveDown
    PB_jobsRun
    PB_prevTo2
);



sub NEW
{
    shift->SUPER::NEW(@_[0..2]);

    if ( name() eq "unnamed" )
    {
        setName("kRip" );
    }
    setSizePolicy(Qt::SizePolicy(0, 0, 0, 0, this->sizePolicy()->hasHeightForWidth()) );
    setMinimumSize(Qt::Size(800, 480) );
    setMaximumSize(Qt::Size(800, 480) );


    PB_saveSettings = Qt::PushButton(this, "PB_saveSettings");
    PB_saveSettings->setGeometry( Qt::Rect(120, 440, 124, 29) );

    PB_exit = Qt::PushButton(this, "PB_exit");
    PB_exit->setGeometry( Qt::Rect(20, 440, 59, 29) );

    TW_tabs = Qt::TabWidget(this, "TW_tabs");
    TW_tabs->setEnabled( 1 );
    TW_tabs->setGeometry( Qt::Rect(10, 10, 780, 420) );

    tab = Qt::Widget(TW_tabs, "tab");

    TL_device = Qt::Label(tab, "TL_device");
    TL_device->setGeometry( Qt::Rect(20, 20, 50, 29) );

    CB_device = Qt::ComboBox(0, tab, "CB_device");
    CB_device->setGeometry( Qt::Rect(70, 20, 140, 29) );
    CB_device->setEditable( 0 );

    PB_scanCD = Qt::PushButton(tab, "PB_scanCD");
    PB_scanCD->setGeometry( Qt::Rect(250, 20, 120, 29) );
    PB_scanCD->setDefault( 1 );

    TL_cddbid = Qt::Label(tab, "TL_cddbid");
    TL_cddbid->setGeometry( Qt::Rect(620, 20, 120, 29) );

    CB_live = Qt::CheckBox(tab, "CB_live");
    CB_live->setEnabled( 0 );
    CB_live->setGeometry( Qt::Rect(620, 54, 140, 20) );

    CB_sampler = Qt::CheckBox(tab, "CB_sampler");
    CB_sampler->setEnabled( 0 );
    CB_sampler->setGeometry( Qt::Rect(620, 70, 140, 20) );

    TL_artist = Qt::Label(tab, "TL_artist");
    TL_artist->setGeometry( Qt::Rect(20, 70, 40, 20) );

    LE_artist = Qt::LineEdit(tab, "LE_artist");
    LE_artist->setEnabled( 0 );
    LE_artist->setGeometry( Qt::Rect(70, 70, 300, 20) );

    TL_album = Qt::Label(tab, "TL_album");
    TL_album->setGeometry( Qt::Rect(20, 100, 40, 20) );

    LE_album = Qt::LineEdit(tab, "LE_album");
    LE_album->setEnabled( 0 );
    LE_album->setGeometry( Qt::Rect(70, 100, 300, 20) );

    TL_year = Qt::Label(tab, "TL_year");
    TL_year->setGeometry( Qt::Rect(20, 130, 40, 20) );

    SB_year = Qt::SpinBox(tab, "SB_year");
    SB_year->setEnabled( 0 );
    SB_year->setGeometry( Qt::Rect(70, 130, 54, 21) );
    SB_year->setMaxValue( int(2100) );
    SB_year->setMinValue( int(1900) );
    SB_year->setValue( int(2004) );

    TL_genre = Qt::Label(tab, "TL_genre");
    TL_genre->setGeometry( Qt::Rect(200, 126, 40, 29) );

    CB_genre = Qt::ComboBox(0, tab, "CB_genre");
    CB_genre->setEnabled( 0 );
    CB_genre->setGeometry( Qt::Rect(250, 126, 120, 29) );
    CB_genre->setEditable( 1 );

    TL_cover = Qt::Label(tab, "TL_cover");
    TL_cover->setGeometry( Qt::Rect(20, 160, 40, 20) );

    PL_cover = Qt::Label(tab, "PL_cover");
    PL_cover->setEnabled( 0 );
    PL_cover->setGeometry( Qt::Rect(70, 170, 120, 120) );
    PL_cover->setFrameShape( &Qt::Label::Box() );
    PL_cover->setScaledContents( 1 );

    TL_coverinfo = Qt::Label(tab, "TL_coverinfo");
    TL_coverinfo->setGeometry( Qt::Rect(210, 250, 160, 50) );

    PB_openCover = Qt::PushButton(tab, "PB_openCover");
    PB_openCover->setEnabled( 0 );
    PB_openCover->setGeometry( Qt::Rect(210, 170, 60, 29) );

    PB_searchCover = Qt::PushButton(tab, "PB_searchCover");
    PB_searchCover->setEnabled( 0 );
    PB_searchCover->setGeometry( Qt::Rect(210, 210, 60, 29) );

    CB_amazonLocale = Qt::ComboBox(0, tab, "CB_amazonLocale");
    CB_amazonLocale->setEnabled( 0 );
    CB_amazonLocale->setGeometry( Qt::Rect(280, 210, 60, 29) );

    TL_tracks = Qt::Label(tab, "TL_tracks");
    TL_tracks->setGeometry( Qt::Rect(410, 70, 40, 20) );

    T_tracks = Qt::Table(tab, "T_tracks");
    T_tracks->setEnabled( 0 );
    T_tracks->setGeometry( Qt::Rect(409, 92, 350, 200) );
    T_tracks->setVScrollBarMode( &Qt::Table::AlwaysOn() );
    T_tracks->setNumRows( int(0) );
    T_tracks->setNumCols( int(1) );
    T_tracks->setShowGrid( 0 );
    T_tracks->setSelectionMode( &Qt::Table::SingleRow() );
    T_tracks->setFocusStyle( &Qt::Table::SpreadSheet() );

    PB_queryCDDB = Qt::PushButton(tab, "PB_queryCDDB");
    PB_queryCDDB->setEnabled( 0 );
    PB_queryCDDB->setGeometry( Qt::Rect(20, 310, 120, 29) );
    PB_queryCDDB->setDefault( 0 );

    PB_queryLocalCDDB = Qt::PushButton(tab, "PB_queryLocalCDDB");
    PB_queryLocalCDDB->setEnabled( 0 );
    PB_queryLocalCDDB->setGeometry( Qt::Rect(160, 310, 120, 29) );
    PB_queryLocalCDDB->setDefault( 0 );

    PB_saveLocalCDDB = Qt::PushButton(tab, "PB_saveLocalCDDB");
    PB_saveLocalCDDB->setEnabled( 0 );
    PB_saveLocalCDDB->setGeometry( Qt::Rect(300, 310, 140, 29) );
    PB_saveLocalCDDB->setDefault( 0 );

    PB_autoCorrection = Qt::PushButton(tab, "PB_autoCorrection");
    PB_autoCorrection->setEnabled( 0 );
    PB_autoCorrection->setGeometry( Qt::Rect(20, 350, 120, 29) );

    TL_amazonLink = Qt::Label(tab, "TL_amazonLink");
    TL_amazonLink->setGeometry( Qt::Rect(210, 350, 170, 29) );

    PB_nextTo2 = Qt::PushButton(tab, "PB_nextTo2");
    PB_nextTo2->setEnabled( 0 );
    PB_nextTo2->setGeometry( Qt::Rect(680, 350, 80, 29) );
    TW_tabs->insertTab( tab, "" );

    tab_2 = Qt::Widget(TW_tabs, "tab_2");

    TL_tracks_2 = Qt::Label(tab_2, "TL_tracks_2");
    TL_tracks_2->setGeometry( Qt::Rect(20, 20, 40, 20) );

    T_tracks_2 = Qt::Table(tab_2, "T_tracks_2");
    T_tracks_2->setEnabled( 0 );
    T_tracks_2->setGeometry( Qt::Rect(70, 20, 690, 180) );
    T_tracks_2->setResizePolicy( &Qt::Table::Manual() );
    T_tracks_2->setVScrollBarMode( &Qt::Table::AlwaysOn() );
    T_tracks_2->setNumRows( int(0) );
    T_tracks_2->setNumCols( int(4) );
    T_tracks_2->setShowGrid( 0 );
    T_tracks_2->setReadOnly( 1 );
    T_tracks_2->setSelectionMode( &Qt::Table::MultiRow() );
    T_tracks_2->setFocusStyle( &Qt::Table::FollowStyle() );

    TL_avg = Qt::Label(tab_2, "TL_avg");
    TL_avg->setGeometry( Qt::Rect(490, 220, 20, 20) );
    TL_avg->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_max = Qt::Label(tab_2, "TL_max");
    TL_max->setGeometry( Qt::Rect(520, 210, 20, 20) );
    TL_max->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_min = Qt::Label(tab_2, "TL_min");
    TL_min->setGeometry( Qt::Rect(520, 230, 20, 20) );
    TL_min->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_levelValAvg = Qt::Label(tab_2, "TL_levelValAvg");
    TL_levelValAvg->setGeometry( Qt::Rect(540, 220, 50, 20) );
    TL_levelValAvg->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_levelValMax = Qt::Label(tab_2, "TL_levelValMax");
    TL_levelValMax->setGeometry( Qt::Rect(590, 210, 50, 20) );
    TL_levelValMax->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_levelValMin = Qt::Label(tab_2, "TL_levelValMin");
    TL_levelValMin->setGeometry( Qt::Rect(590, 230, 50, 20) );
    TL_levelValMin->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_peakValAvg = Qt::Label(tab_2, "TL_peakValAvg");
    TL_peakValAvg->setGeometry( Qt::Rect(640, 220, 50, 20) );
    TL_peakValAvg->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_peakValMax = Qt::Label(tab_2, "TL_peakValMax");
    TL_peakValMax->setGeometry( Qt::Rect(690, 210, 50, 20) );
    TL_peakValMax->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    TL_peakValMin = Qt::Label(tab_2, "TL_peakValMin");
    TL_peakValMin->setGeometry( Qt::Rect(690, 230, 50, 20) );
    TL_peakValMin->setAlignment( int(&Qt::Label::AlignVCenter | &Qt::Label::AlignRight) );

    PB_rip = Qt::PushButton(tab_2, "PB_rip");
    PB_rip->setEnabled( 0 );
    PB_rip->setGeometry( Qt::Rect(70, 220, 120, 29) );
    PB_rip->setDefault( 1 );

    CB_removeSilence = Qt::CheckBox(tab_2, "CB_removeSilence");
    CB_removeSilence->setEnabled( 0 );
    CB_removeSilence->setGeometry( Qt::Rect(230, 226, 230, 20) );
    CB_removeSilence->setChecked( 1 );

    CB_checkHiddenTracks = Qt::CheckBox(tab_2, "CB_checkHiddenTracks");
    CB_checkHiddenTracks->setEnabled( 0 );
    CB_checkHiddenTracks->setGeometry( Qt::Rect(230, 250, 230, 20) );
    CB_checkHiddenTracks->setChecked( 1 );

    PB_normalizeTo = Qt::PushButton(tab_2, "PB_normalizeTo");
    PB_normalizeTo->setEnabled( 0 );
    PB_normalizeTo->setGeometry( Qt::Rect(70, 285, 120, 29) );

    SB_normalizeToLevel = Qt::SpinBox(tab_2, "SB_normalizeToLevel");
    SB_normalizeToLevel->setEnabled( 0 );
    SB_normalizeToLevel->setGeometry( Qt::Rect(230, 290, 80, 20) );
    SB_normalizeToLevel->setMaxValue( int(0) );
    SB_normalizeToLevel->setMinValue( int(-24) );
    SB_normalizeToLevel->setValue( int(-9) );

    CB_normalizeIgnoreLouder = Qt::CheckBox(tab_2, "CB_normalizeIgnoreLouder");
    CB_normalizeIgnoreLouder->setEnabled( 0 );
    CB_normalizeIgnoreLouder->setGeometry( Qt::Rect(370, 290, 230, 20) );
    CB_normalizeIgnoreLouder->setChecked( 1 );

    PB_normalizeBy = Qt::PushButton(tab_2, "PB_normalizeBy");
    PB_normalizeBy->setEnabled( 0 );
    PB_normalizeBy->setGeometry( Qt::Rect(70, 320, 120, 29) );

    SB_normalizeByLevel = Qt::SpinBox(tab_2, "SB_normalizeByLevel");
    SB_normalizeByLevel->setEnabled( 0 );
    SB_normalizeByLevel->setGeometry( Qt::Rect(229, 325, 80, 20) );
    SB_normalizeByLevel->setMaxValue( int(24) );
    SB_normalizeByLevel->setMinValue( int(-24) );
    SB_normalizeByLevel->setValue( int(3) );

    TL_normalizeOpts = Qt::Label(tab_2, "TL_normalizeOpts");
    TL_normalizeOpts->setGeometry( Qt::Rect(230, 360, 130, 20) );

    LE_normalizeOpts = Qt::LineEdit(tab_2, "LE_normalizeOpts");
    LE_normalizeOpts->setEnabled( 0 );
    LE_normalizeOpts->setGeometry( Qt::Rect(370, 360, 100, 20) );

    PB_prevTo1 = Qt::PushButton(tab_2, "PB_prevTo1");
    PB_prevTo1->setEnabled( 0 );
    PB_prevTo1->setGeometry( Qt::Rect(580, 350, 80, 29) );

    PB_nextTo3 = Qt::PushButton(tab_2, "PB_nextTo3");
    PB_nextTo3->setEnabled( 0 );
    PB_nextTo3->setGeometry( Qt::Rect(680, 350, 80, 29) );
    TW_tabs->insertTab( tab_2, "" );

    TabPage = Qt::Widget(TW_tabs, "TabPage");

    TL_jobs = Qt::Label(TabPage, "TL_jobs");
    TL_jobs->setGeometry( Qt::Rect(20, 20, 40, 20) );

    LB_jobs = Qt::ListBox(TabPage, "LB_jobs");
    LB_jobs->setEnabled( 0 );
    LB_jobs->setGeometry( Qt::Rect(70, 20, 120, 200) );

    GB_jobProp = Qt::GroupBox(TabPage, "GB_jobProp");
    GB_jobProp->setGeometry( Qt::Rect(330, 20, 430, 310) );
    GB_jobProp->setCheckable( 1 );

    TL_jobName = Qt::Label(GB_jobProp, "TL_jobName");
    TL_jobName->setGeometry( Qt::Rect(20, 30, 60, 20) );

    LE_jobName = Qt::LineEdit(GB_jobProp, "LE_jobName");
    LE_jobName->setGeometry( Qt::Rect(80, 30, 100, 20) );

    TL_jobEncoder = Qt::Label(GB_jobProp, "TL_jobEncoder");
    TL_jobEncoder->setGeometry( Qt::Rect(20, 60, 60, 27) );

    CB_jobEncoder = Qt::ComboBox(0, GB_jobProp, "CB_jobEncoder");
    CB_jobEncoder->setGeometry( Qt::Rect(80, 60, 100, 27) );

    TL_jobEncOpts = Qt::Label(GB_jobProp, "TL_jobEncOpts");
    TL_jobEncOpts->setGeometry( Qt::Rect(200, 60, 50, 27) );

    LE_jobEncOpts = Qt::LineEdit(GB_jobProp, "LE_jobEncOpts");
    LE_jobEncOpts->setGeometry( Qt::Rect(260, 64, 140, 20) );

    TL_jobEncFormat = Qt::Label(GB_jobProp, "TL_jobEncFormat");
    TL_jobEncFormat->setGeometry( Qt::Rect(20, 100, 90, 20) );

    LE_jobEncFormat = Qt::LineEdit(GB_jobProp, "LE_jobEncFormat");
    LE_jobEncFormat->setGeometry( Qt::Rect(110, 100, 290, 20) );

    CB_jobTag = Qt::CheckBox(GB_jobProp, "CB_jobTag");
    CB_jobTag->setGeometry( Qt::Rect(20, 140, 170, 20) );

    CB_jobTagCover = Qt::CheckBox(GB_jobProp, "CB_jobTagCover");
    CB_jobTagCover->setGeometry( Qt::Rect(240, 140, 170, 20) );

    CB_jobCreateCoverJPG = Qt::CheckBox(GB_jobProp, "CB_jobCreateCoverJPG");
    CB_jobCreateCoverJPG->setGeometry( Qt::Rect(20, 180, 390, 20) );

    CB_jobCreateCoverBMP = Qt::CheckBox(GB_jobProp, "CB_jobCreateCoverBMP");
    CB_jobCreateCoverBMP->setGeometry( Qt::Rect(20, 210, 390, 20) );

    CB_jobCreateAlbumTxt = Qt::CheckBox(GB_jobProp, "CB_jobCreateAlbumTxt");
    CB_jobCreateAlbumTxt->setGeometry( Qt::Rect(20, 250, 230, 20) );

    PB_jobNew = Qt::PushButton(TabPage, "PB_jobNew");
    PB_jobNew->setEnabled( 0 );
    PB_jobNew->setGeometry( Qt::Rect(210, 20, 100, 29) );

    PB_jobDelete = Qt::PushButton(TabPage, "PB_jobDelete");
    PB_jobDelete->setEnabled( 0 );
    PB_jobDelete->setGeometry( Qt::Rect(210, 60, 100, 29) );

    PB_jobMoveUp = Qt::PushButton(TabPage, "PB_jobMoveUp");
    PB_jobMoveUp->setEnabled( 0 );
    PB_jobMoveUp->setGeometry( Qt::Rect(210, 100, 100, 29) );

    PB_jobMoveDown = Qt::PushButton(TabPage, "PB_jobMoveDown");
    PB_jobMoveDown->setEnabled( 0 );
    PB_jobMoveDown->setGeometry( Qt::Rect(210, 140, 100, 29) );

    PB_jobsRun = Qt::PushButton(TabPage, "PB_jobsRun");
    PB_jobsRun->setEnabled( 0 );
    PB_jobsRun->setGeometry( Qt::Rect(70, 240, 120, 29) );
    PB_jobsRun->setDefault( 1 );

    PB_prevTo2 = Qt::PushButton(TabPage, "PB_prevTo2");
    PB_prevTo2->setGeometry( Qt::Rect(580, 350, 80, 29) );
    TW_tabs->insertTab( TabPage, "" );
    languageChange();
    my $resize = Qt::Size(800, 480);
    $resize = $resize->expandedTo(minimumSizeHint());
    resize( $resize );
    clearWState( &Qt::WState_Polished );

    Qt::Object::connect(PB_exit, SIGNAL "clicked()", this, SLOT "close()");
    Qt::Object::connect(CB_device, SIGNAL "activated(int)", this, SLOT "CB_device_activated()");
    Qt::Object::connect(PB_scanCD, SIGNAL "clicked()", this, SLOT "PB_scanCD_clicked()");
    Qt::Object::connect(CB_sampler, SIGNAL "stateChanged(int)", this, SLOT "CB_sampler_stateChanged()");
    Qt::Object::connect(LE_artist, SIGNAL "textChanged(const QString&)", this, SLOT "readFieldData()");
    Qt::Object::connect(LE_album, SIGNAL "textChanged(const QString&)", this, SLOT "readFieldData()");
    Qt::Object::connect(T_tracks, SIGNAL "valueChanged(int,int)", this, SLOT "T_tracks_valueChanged(int,int)");
    Qt::Object::connect(SB_year, SIGNAL "valueChanged(int)", this, SLOT "readFieldData()");
    Qt::Object::connect(CB_genre, SIGNAL "textChanged(const QString&)", this, SLOT "readFieldData()");
    Qt::Object::connect(PB_openCover, SIGNAL "clicked()", this, SLOT "PB_openCover_clicked()");
    Qt::Object::connect(PB_searchCover, SIGNAL "clicked()", this, SLOT "PB_searchCover_clicked()");
    Qt::Object::connect(CB_amazonLocale, SIGNAL "activated(int)", this, SLOT "CB_amazonLocale_activated()");
    Qt::Object::connect(PB_queryCDDB, SIGNAL "clicked()", this, SLOT "PB_queryCDDB_clicked()");
    Qt::Object::connect(PB_queryLocalCDDB, SIGNAL "clicked()", this, SLOT "PB_queryLocalCDDB_clicked()");
    Qt::Object::connect(PB_saveLocalCDDB, SIGNAL "clicked()", this, SLOT "PB_saveLocalCDDB_clicked()");
    Qt::Object::connect(PB_autoCorrection, SIGNAL "clicked()", this, SLOT "PB_autoCorrection_clicked()");
    Qt::Object::connect(T_tracks_2, SIGNAL "selectionChanged()", this, SLOT "T_tracks_2_selectionChanged()");
    Qt::Object::connect(PB_rip, SIGNAL "clicked()", this, SLOT "PB_rip_clicked()");
    Qt::Object::connect(PB_normalizeTo, SIGNAL "clicked()", this, SLOT "PB_normalizeTo_clicked()");
    Qt::Object::connect(PB_normalizeBy, SIGNAL "clicked()", this, SLOT "PB_normalizeBy_clicked()");
    Qt::Object::connect(PB_jobNew, SIGNAL "clicked()", this, SLOT "PB_jobNew_clicked()");
    Qt::Object::connect(PB_jobDelete, SIGNAL "clicked()", this, SLOT "PB_jobDelete_clicked()");
    Qt::Object::connect(PB_jobMoveUp, SIGNAL "clicked()", this, SLOT "PB_jobMoveUp_clicked()");
    Qt::Object::connect(PB_jobMoveDown, SIGNAL "clicked()", this, SLOT "PB_jobMoveDown_clicked()");
    Qt::Object::connect(LB_jobs, SIGNAL "currentChanged(QListBoxItem*)", this, SLOT "LB_jobs_currentChanged()");
    Qt::Object::connect(CB_jobTag, SIGNAL "stateChanged(int)", this, SLOT "LB_jobTag_stateChanged()");
    Qt::Object::connect(PB_jobsRun, SIGNAL "clicked()", this, SLOT "PB_jobsRun_clicked()");
    Qt::Object::connect(PB_saveSettings, SIGNAL "clicked()", this, SLOT "PB_saveSettings_clicked()");
    Qt::Object::connect(PB_nextTo2, SIGNAL "clicked()", this, SLOT "PB_nextTab_clicked()");
    Qt::Object::connect(PB_prevTo1, SIGNAL "clicked()", this, SLOT "PB_prevTab_clicked()");
    Qt::Object::connect(PB_nextTo3, SIGNAL "clicked()", this, SLOT "PB_nextTab_clicked()");
    Qt::Object::connect(PB_prevTo2, SIGNAL "clicked()", this, SLOT "PB_prevTab_clicked()");

    setTabOrder(TW_tabs, CB_device);
    setTabOrder(CB_device, PB_scanCD);
    setTabOrder(PB_scanCD, CB_live);
    setTabOrder(CB_live, CB_sampler);
    setTabOrder(CB_sampler, LE_artist);
    setTabOrder(LE_artist, LE_album);
    setTabOrder(LE_album, SB_year);
    setTabOrder(SB_year, CB_genre);
    setTabOrder(CB_genre, PB_openCover);
    setTabOrder(PB_openCover, PB_searchCover);
    setTabOrder(PB_searchCover, CB_amazonLocale);
    setTabOrder(CB_amazonLocale, T_tracks);
    setTabOrder(T_tracks, PB_queryCDDB);
    setTabOrder(PB_queryCDDB, PB_queryLocalCDDB);
    setTabOrder(PB_queryLocalCDDB, PB_saveLocalCDDB);
    setTabOrder(PB_saveLocalCDDB, PB_autoCorrection);
    setTabOrder(PB_autoCorrection, PB_nextTo2);
    setTabOrder(PB_nextTo2, T_tracks_2);
    setTabOrder(T_tracks_2, PB_rip);
    setTabOrder(PB_rip, PB_normalizeTo);
    setTabOrder(PB_normalizeTo, SB_normalizeToLevel);
    setTabOrder(SB_normalizeToLevel, CB_normalizeIgnoreLouder);
    setTabOrder(CB_normalizeIgnoreLouder, PB_normalizeBy);
    setTabOrder(PB_normalizeBy, SB_normalizeByLevel);
    setTabOrder(SB_normalizeByLevel, LE_normalizeOpts);
    setTabOrder(LE_normalizeOpts, PB_prevTo1);
    setTabOrder(PB_prevTo1, PB_nextTo3);
    setTabOrder(PB_nextTo3, LB_jobs);
    setTabOrder(LB_jobs, PB_jobNew);
    setTabOrder(PB_jobNew, PB_jobDelete);
    setTabOrder(PB_jobDelete, PB_jobMoveUp);
    setTabOrder(PB_jobMoveUp, PB_jobMoveDown);
    setTabOrder(PB_jobMoveDown, LE_jobName);
    setTabOrder(LE_jobName, CB_jobEncoder);
    setTabOrder(CB_jobEncoder, LE_jobEncOpts);
    setTabOrder(LE_jobEncOpts, LE_jobEncFormat);
    setTabOrder(LE_jobEncFormat, CB_jobTag);
    setTabOrder(CB_jobTag, CB_jobTagCover);
    setTabOrder(CB_jobTagCover, CB_jobCreateCoverJPG);
    setTabOrder(CB_jobCreateCoverJPG, CB_jobCreateCoverBMP);
    setTabOrder(CB_jobCreateCoverBMP, CB_jobCreateAlbumTxt);
    setTabOrder(CB_jobCreateAlbumTxt, PB_jobsRun);
    setTabOrder(PB_jobsRun, PB_prevTo2);
    setTabOrder(PB_prevTo2, PB_saveSettings);
    setTabOrder(PB_saveSettings, PB_exit);

    init();
}


#  Sets the strings of the subwidgets using the current
#  language.

sub languageChange
{
    setCaption(trUtf8("kRip") );
    PB_saveSettings->setText( trUtf8("&save configuration") );
    PB_saveSettings->setAccel( Qt::KeySequence( trUtf8("Alt+S") ) );
    Qt::ToolTip::add(PB_saveSettings, trUtf8("Save the current configuration to kRiprc."));
    PB_exit->setText( trUtf8("E&xit") );
    PB_exit->setAccel( Qt::KeySequence( trUtf8("Alt+X") ) );
    Qt::ToolTip::add(PB_exit, trUtf8("Exit this masterpiece of programming."));
    TL_device->setText( trUtf8("Device:") );
    Qt::ToolTip::add(CB_device, trUtf8("Select the device containing the audio CD\n" .
    "that should be ripped."));
    PB_scanCD->setText( trUtf8("scan &device") );
    PB_scanCD->setAccel( Qt::KeySequence( trUtf8("Alt+D") ) );
    Qt::ToolTip::add(PB_scanCD, trUtf8("Scan the device for audio tracks.\n" .
    "This function does NOT fill the fields with meaningful data,\n" .
    "You have to enter them manually or use the CDDB functions."));
    TL_cddbid->setText( trUtf8("CDDB id: ---") );
    Qt::ToolTip::add(TL_cddbid, trUtf8("This is the CDDB id.\n" .
    "It is used as the filename in Your local CDDB."));
    CB_live->setText( trUtf8("Live Album") );
    Qt::ToolTip::add(CB_live, trUtf8("If album is a live album,\n" .
    "normalization will adjust all tracks equally and\n" .
    "and silence at the beginning or at the end of a track\n" .
    "will be removed automatiiically."));
    CB_sampler->setText( trUtf8("Sampler/Soundtrack") );
    Qt::ToolTip::add(CB_sampler, trUtf8("If album is a sampler or soundtrack,\n" .
    "You should enter the name of the artist in the left\n" .
    "and the name of the song in the right column."));
    TL_artist->setText( trUtf8("Artist:") );
    Qt::ToolTip::add(LE_artist, trUtf8("The name of the artist."));
    TL_album->setText( trUtf8("Album:") );
    Qt::ToolTip::add(LE_album, trUtf8("The name of the album."));
    TL_year->setText( trUtf8("Year:") );
    Qt::ToolTip::add(SB_year, trUtf8("The date when the album has been published."));
    TL_genre->setText( trUtf8("Genre:") );
    Qt::ToolTip::add(CB_genre, trUtf8("The genre of the album."));
    TL_cover->setText( trUtf8("Cover:") );
    Qt::ToolTip::add(PL_cover, trUtf8("Cover image"));
    TL_coverinfo->setText( "" );
    Qt::ToolTip::add(TL_coverinfo, trUtf8("Additional info of the cover."));
    PB_openCover->setText( trUtf8("&open") );
    PB_openCover->setAccel( Qt::KeySequence( trUtf8("Alt+O") ) );
    Qt::ToolTip::add(PB_openCover, trUtf8("Choose an existing picture for the cover."));
    PB_searchCover->setText( trUtf8("s&earch") );
    PB_searchCover->setAccel( Qt::KeySequence( trUtf8("Alt+E") ) );
    Qt::ToolTip::add(PB_searchCover, trUtf8("Ask Amazon for one or more covers.\n" .
    "Therefore, the 'Artist' and 'Album' fields\n" .
    "must be filled with meaningful data."));
    Qt::ToolTip::add(CB_amazonLocale, trUtf8("Specify the Amazon locale.\n" .
    "You can choose between\n" .
    "  Amazon.com,\n" .
    "  Amazon.de,\n" .
    "  Amazon.co.jp and\n" .
    "  Amazon.co.uk."));
    TL_tracks->setText( trUtf8("Tracks:") );
    Qt::ToolTip::add(T_tracks, trUtf8("The names of the song.\n" .
    "If album is a sampler, this table contains\n" .
    "the name of the artist and the name of the song."));
    PB_queryCDDB->setText( trUtf8("&query CDDB") );
    PB_queryCDDB->setAccel( Qt::KeySequence( trUtf8("Alt+Q") ) );
    Qt::ToolTip::add(PB_queryCDDB, trUtf8("Query CDDB (freedb.org) for titles."));
    PB_queryLocalCDDB->setText( trUtf8("query &local CDDB") );
    PB_queryLocalCDDB->setAccel( Qt::KeySequence( trUtf8("Alt+L") ) );
    Qt::ToolTip::add(PB_queryLocalCDDB, trUtf8("Query local CDDB database for titles."));
    PB_saveLocalCDDB->setText( trUtf8("sa&ve to local CDDB") );
    PB_saveLocalCDDB->setAccel( Qt::KeySequence( trUtf8("Alt+V") ) );
    Qt::ToolTip::add(PB_saveLocalCDDB, trUtf8("Save actual titles to local CDDB database."));
    PB_autoCorrection->setText( trUtf8("&Autocorrection") );
    PB_autoCorrection->setAccel( Qt::KeySequence( trUtf8("Alt+A") ) );
    Qt::ToolTip::add(PB_autoCorrection, trUtf8("Perform a correction of the spelling:\n" .
    "  Words should begin with uppercase characters.\n" .
    "  Accencts and needless whitespaces will be removed.\n" .
    "  Umlauts will be replaced (�-> ae).\n" .
    "  (disc 1)->(CD1)."));
    TL_amazonLink->setText( trUtf8("The search cover function uses the") );
    PB_nextTo2->setText( trUtf8("&next >>") );
    PB_nextTo2->setAccel( Qt::KeySequence( trUtf8("Alt+N") ) );
    Qt::ToolTip::add(PB_nextTo2, trUtf8("Continue with ripping.\n" .
    "The device has to be scanned before."));
    TW_tabs->changeTab( tab, trUtf8("Step &1: get data") );
    TL_tracks_2->setText( trUtf8("Tracks:") );
    Qt::ToolTip::add(T_tracks_2, trUtf8("Contains all available tracks on the CD.\n" .
    "Only the selected tracks will be used for ripping,\n" .
    "normalizing and for encoding."));
    TL_avg->setText( trUtf8("Avg") );
    TL_max->setText( trUtf8("Max") );
    TL_min->setText( trUtf8("Min") );
    TL_levelValAvg->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_levelValAvg, trUtf8("This is the average level of the selected tracks."));
    TL_levelValMax->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_levelValMax, trUtf8("This is the maximum level of the selected tracks."));
    TL_levelValMin->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_levelValMin, trUtf8("This is the minimum level of the selected tracks."));
    TL_peakValAvg->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_peakValAvg, trUtf8("This is the average peak level of the selected tracks."));
    TL_peakValMax->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_peakValMax, trUtf8("This is the maximum peak level of the selected tracks."));
    TL_peakValMin->setText( trUtf8("...") );
    Qt::ToolTip::add(TL_peakValMin, trUtf8("This is the minimum peak level of the selected tracks."));
    PB_rip->setText( trUtf8("&rip tracks") );
    PB_rip->setAccel( Qt::KeySequence( trUtf8("Alt+R") ) );
    Qt::ToolTip::add(PB_rip, trUtf8("Rip the selected tracks."));
    CB_removeSilence->setText( trUtf8("remo&ve silence") );
    CB_removeSilence->setAccel( Qt::KeySequence( trUtf8("Alt+V") ) );
    Qt::ToolTip::add(CB_removeSilence, trUtf8("Remove silence at the beginning\n" .
    "and at the end of each track."));
    CB_checkHiddenTracks->setText( trUtf8("check for &hidden tracks") );
    CB_checkHiddenTracks->setAccel( Qt::KeySequence( trUtf8("Alt+H") ) );
    Qt::ToolTip::add(CB_checkHiddenTracks, trUtf8("Scans for silence in the middle of each track.\n" .
    "If there is silence for at least 3 seconds,\n" .
    "the track will be splitted\n" .
    "in two or more seperate tracks."));
    PB_normalizeTo->setText( trUtf8("normalize &to") );
    PB_normalizeTo->setAccel( Qt::KeySequence( trUtf8("Alt+T") ) );
    Qt::ToolTip::add(PB_normalizeTo, trUtf8("Normalize the selected tracks."));
    SB_normalizeToLevel->setPrefix( "" );
    SB_normalizeToLevel->setSuffix( trUtf8("dBFS") );
    Qt::ToolTip::add(SB_normalizeToLevel, trUtf8("Specify the normalization level."));
    CB_normalizeIgnoreLouder->setText( trUtf8("&ignore songs above this level") );
    CB_normalizeIgnoreLouder->setAccel( Qt::KeySequence( trUtf8("Alt+I") ) );
    Qt::ToolTip::add(CB_normalizeIgnoreLouder, trUtf8("Do not normalize tracks with levels\n" .
    "that are already higher than the specified level."));
    PB_normalizeBy->setText( trUtf8("normalize &by") );
    PB_normalizeBy->setAccel( Qt::KeySequence( trUtf8("Alt+B") ) );
    Qt::ToolTip::add(PB_normalizeBy, trUtf8("Normalize the selected tracks."));
    SB_normalizeByLevel->setPrefix( "" );
    SB_normalizeByLevel->setSuffix( trUtf8("dBFS") );
    Qt::ToolTip::add(SB_normalizeByLevel, trUtf8("Specify the normalization level."));
    TL_normalizeOpts->setText( trUtf8("additional parameters:") );
    Qt::ToolTip::add(LE_normalizeOpts, trUtf8("Specify additional parameters for 'normalize'.\n" .
    "Do not enter the '-a' parameter here,\n" .
    "it will be added automatically."));
    PB_prevTo1->setText( trUtf8("<< &prev") );
    PB_prevTo1->setAccel( Qt::KeySequence( trUtf8("Alt+P") ) );
    Qt::ToolTip::add(PB_prevTo1, trUtf8("Go back to Tab 1.\n" .
    "You can still make some changes there,\n" .
    "even if You have already ripped or\n" .
    "normalized some tracks."));
    PB_nextTo3->setText( trUtf8("&next >>") );
    PB_nextTo3->setAccel( Qt::KeySequence( trUtf8("Alt+N") ) );
    Qt::ToolTip::add(PB_nextTo3, trUtf8("Continue with encoding.\n" .
    "At least one track should have been ripped."));
    TW_tabs->changeTab( tab_2, trUtf8("Step &2: rip") );
    TL_jobs->setText( trUtf8("Jobs:") );
    LB_jobs->clear();
    LB_jobs->insertItem( trUtf8("New Item") );
    Qt::ToolTip::add(LB_jobs, trUtf8("This is a list of different encoding jobs.\n" .
    "Each job could be enabled or disabled.\n" .
    "All enabled jobs will be processed when\n" .
    "You start the encoding."));
    GB_jobProp->setTitle( trUtf8("Job is enabled") );
    TL_jobName->setText( trUtf8("Name:") );
    LE_jobName->setText( trUtf8("new Job") );
    Qt::ToolTip::add(LE_jobName, trUtf8("The name of the job."));
    TL_jobEncoder->setText( trUtf8("encoder:") );
    Qt::ToolTip::add(CB_jobEncoder, trUtf8("The encoder."));
    TL_jobEncOpts->setText( trUtf8("options:") );
    LE_jobEncOpts->setText( "" );
    Qt::ToolTip::add(LE_jobEncOpts, trUtf8("The parameters for the encoder."));
    TL_jobEncFormat->setText( trUtf8("Output format:") );
    Qt::ToolTip::add(LE_jobEncFormat, trUtf8("Specify the filenames of the results here.\n" .
    "You might use some placeholders:\n" .
    "  %A: name of the artist\n" .
    "  %B: name of the album\n" .
    "  %T: name of the track\n" .
    "  %Y: year of publication\n" .
    "  %G: genre\n" .
    "  %N: tracknumber\n" .
    "  %C: CDDB id"));
    CB_jobTag->setText( trUtf8("&tag the encoded files") );
    CB_jobTag->setAccel( Qt::KeySequence( trUtf8("Alt+T") ) );
    Qt::ToolTip::add(CB_jobTag, trUtf8("If enabled, the encoded files will be tagged.\n" .
    "Mp3 files will be tagged with an id3v1.1 and\n" .
    "an id3v2.4 header."));
    CB_jobTagCover->setText( trUtf8("&insert cover in ID3v2 tag") );
    CB_jobTagCover->setAccel( Qt::KeySequence( trUtf8("Alt+I") ) );
    Qt::ToolTip::add(CB_jobTagCover, trUtf8("If enabled, a cover (120x120 pixels)\n" .
    "will be included in the id3v2.4 tag."));
    CB_jobCreateCoverJPG->setText( trUtf8("create 'cover.jpg' (120x120, 16M colors, &JPG) in target directory") );
    CB_jobCreateCoverJPG->setAccel( Qt::KeySequence( trUtf8("Alt+J") ) );
    Qt::ToolTip::add(CB_jobCreateCoverJPG, trUtf8("If enabled, a file named 'cover.jpg' will be copied\n" .
    "to the target directory."));
    CB_jobCreateCoverBMP->setText( trUtf8("create 'cover.bmp' (48x48, 16 gray, Win &BMP) in target directory") );
    CB_jobCreateCoverBMP->setAccel( Qt::KeySequence( trUtf8("Alt+B") ) );
    Qt::ToolTip::add(CB_jobCreateCoverBMP, trUtf8("This small cover will be used for\n" .
    "one of my other projects (SiMPeL Palm)."));
    CB_jobCreateAlbumTxt->setText( trUtf8("create '&Album.txt' in target directory") );
    CB_jobCreateAlbumTxt->setAccel( Qt::KeySequence( trUtf8("Alt+A") ) );
    Qt::ToolTip::add(CB_jobCreateAlbumTxt, trUtf8("The 'Album.txt' is nothing else\n" .
    "like a CDDB entry without any comments.\n" .
    "I often use this file for other purposes\n" .
    "(re-tagging of the files,...)."));
    PB_jobNew->setText( trUtf8("&new job") );
    PB_jobNew->setAccel( Qt::KeySequence( trUtf8("Alt+N") ) );
    Qt::ToolTip::add(PB_jobNew, trUtf8("Create a new job."));
    PB_jobDelete->setText( trUtf8("&delete job") );
    PB_jobDelete->setAccel( Qt::KeySequence( trUtf8("Alt+D") ) );
    Qt::ToolTip::add(PB_jobDelete, trUtf8("Delete the selected job."));
    PB_jobMoveUp->setText( trUtf8("move &up") );
    PB_jobMoveUp->setAccel( Qt::KeySequence( trUtf8("Alt+U") ) );
    Qt::ToolTip::add(PB_jobMoveUp, trUtf8("Move the selected job up in the list.\n" .
    "If You start the encoding, the list will execute\n" .
    "all enabled jobs from top to bottom."));
    PB_jobMoveDown->setText( trUtf8("move do&wn") );
    PB_jobMoveDown->setAccel( Qt::KeySequence( trUtf8("Alt+W") ) );
    Qt::ToolTip::add(PB_jobMoveDown, trUtf8("Move the selected job down in the list.\n" .
    "If You start the encoding, the list will execute\n" .
    "all enabled jobs from top to bottom."));
    PB_jobsRun->setText( trUtf8("&encode tracks") );
    PB_jobsRun->setAccel( Qt::KeySequence( trUtf8("Alt+E") ) );
    Qt::ToolTip::add(PB_jobsRun, trUtf8("Start the encoding process.\n" .
    "All enabled jobs in the list will be\n" .
    "executed one after another."));
    PB_prevTo2->setText( trUtf8("<< &prev") );
    PB_prevTo2->setAccel( Qt::KeySequence( trUtf8("Alt+P") ) );
    Qt::ToolTip::add(PB_prevTo2, trUtf8("Go back to Tab 2.\n" .
    "You can still change the selected tracks\n" .
    "if You want to."));
    TW_tabs->changeTab( TabPage, trUtf8("Step &3: encode") );
}


sub init
{

  print "--> init()\n" if $debug>1;

  $numEncoders   = 0;
  $pwd = `pwd`;
  $pwd =~ s/^\s+//; $pwd =~ s/\s+$//;

  C_arrow = Qt::Cursor(&Qt::Cursor::ArrowCursor);
  C_wait = Qt::Cursor(&Qt::Cursor::WaitCursor);

  T_tracks->setNumRows(0);
  T_tracks->setNumCols(3);
  T_tracks->horizontalHeader()->hide();
  T_tracks->setTopMargin(0);
  T_tracks->setColumnWidth(0, T_tracks->visibleWidth()-1);
  T_tracks->setColumnWidth(1, 0);
  T_tracks->setColumnWidth(2, 0);
  T_tracks->insertRows(0, $maxTracks);

  T_tracks_2->setNumRows(0);
  T_tracks_2->setNumCols(4);
  T_tracks_2->horizontalHeader()->setLabel(0, "Track");
  T_tracks_2->horizontalHeader()->setLabel(1, "Length");
  T_tracks_2->horizontalHeader()->setLabel(2, "Level (in dBFS)");
  T_tracks_2->horizontalHeader()->setLabel(3, "Peak (in dBFS)");
  T_tracks_2->setColumnWidth(0, T_tracks_2->visibleWidth()-80-100-100-1);
  T_tracks_2->setColumnWidth(1, 80);
  T_tracks_2->setColumnWidth(2, 100);
  T_tracks_2->setColumnWidth(3, 100);
  T_tracks_2->insertRows(0, $maxTracks);

  rcFileRead();
  initValues();

  print "<-- init()\n" if $debug>1;

}

sub initValues
{

  print "--> initValues()\n" if $debug>1;

  $dev = CB_device->currentText();

  $cddb_id = "---";
  TL_cddbid->setText("CDDB id: ".$cddb_id);
  CB_live->setChecked(0);
  CB_sampler->setChecked(0);

  $artist = "";
  LE_artist->setText($artist);
  $title = "";
  LE_album->setText($title);

  SB_year->setValue($defaultYear);
  CB_genre->setCurrentItem($defaultGenre);
  $coverfilename = "";
  PL_cover->setPixmap( Qt::Pixmap() );
  PL_cover->setScaledContents( 0 );
  TL_coverinfo->setText("");

  for( my $i=0; $i<$maxTracks; $i++ ) {
    T_tracks->setText( $i, 0, "" );
    T_tracks->setText( $i, 1, "" );
    T_tracks->setText( $i, 2, "" );
    T_tracks->hideRow($i);
    T_tracks_2->setText( $i, 0, "" );
    T_tracks_2->setText( $i, 1, "" );
    T_tracks_2->setText( $i, 2, "" );
    T_tracks_2->setText( $i, 3, "" );
    T_tracks_2->hideRow($i);
  }


  $initialFill = 1;
  LB_jobs->clear();
  for( my $i=0; $i<$numJobs; $i++) {
    LB_jobs->insertItem($AoA[$i][$JOB_NAME     ], $i);
  }

  if($numJobs > 0) {
    GB_jobProp->show();
    $currItemLast = -1;
    $currentItem = 0;
    loadJobProperties();
    LB_jobs->setSelected(0, 1);
  } else {
    GB_jobProp->hide();
    $currItemLast = -1;
  }
  $initialFill = 0;

  $state = 0;
  refreshPanel();
  PB_scanCD->setFocus();

  system( "rm \"$pwd/$COVERJPGFILE\"" ) if( -e "$pwd/$COVERJPGFILE");
  system( "rm \"$pwd/$COVERBMPFILE\"" ) if( -e "$pwd/$COVERBMPFILE");
  system( "rm \"$pwd/found.jpg\"" )     if( -e "$pwd/found.jpg");
  system( "cd \"$pwd\"; rm found_*.jpg 2>/dev/null" );
# XXX only for test
  system( "cd \"$pwd\"; rm *.wav 2>/dev/null" );

  print "<-- initValues()\n" if $debug>1;

}

sub rcFileRead
{

  print "--> rcFileRead()\n" if $debug>1;

  my $variable;
  my $value;
  my @list;

  # Does an rc file even exist? If not, create one from scratch
  if( not(-e "$RCFILE") ) {
    # Create a default rc file
    $actDevice     = 0;
    $defaultYear   = 2005;
    $defaultGenre  = 0;
    $defaultAmazonLocale = 0;
    $amazonToken   = "request_one_from_amazon";
    $removeSilence = 1;
    $checkHiddenTracks = 1;
    $normalizeToLevel = 9;
    $normalizeIgnL = 1;
    $normalizeByLevel = 3;
    $normalizeOpts = "\"-l -6dBFS\"";
    $numJobs = 3;
    @list = ( "mp3 VBR", 1, 0, "-h --preset standard", "\%A/\%Y_\%B/\%N_\%T", 1, 1, 1, 1, 1 );
    for( my $i=0; $i<@list; $i++ ) {
      $AoA[0][$i] = $list[$i];
    }
    @list = ( "ogg q5", 0, 1, "-q 5", "\%A/\%Y_\%B/\%N_\%T", 1, 0, 1, 1, 1 );
    for( my $i=0; $i<@list; $i++ ) {
      $AoA[1][$i] = $list[$i];
    }
    @list = ( "mp3 ABR128", 0, 0, "-h --preset 128", "\%A/\%Y_\%B/\%N_\%T", 0, 0, 1, 1, 1 );
    for( my $i=0; $i<@list; $i++ ) {
      $AoA[2][$i] = $list[$i];
    }

    rcFileWrite();
  }

  $localCDDBpath = "$HOME/.cddb/";
  $coverPath     = "$HOME/.covers/";
  $actDevice     = 0;
  $defaultYear   = 2005;
  $defaultGenre  = 0;
  $defaultAmazonLocale = 0;
  $amazonToken   = "request_one_from_amazon";
  $removeSilence = 1;
  $checkHiddenTracks = 1;
  $normalizeToLevel = 9;
  $normalizeIgnL = 1;
  $normalizeByLevel = 3;
  $normalizeOpts = "\"-l -6dBFS\"";
  $numJobs       = 0;

  open( rcFile, "<$RCFILE" ) or print STDERR "cannot open $RCFILE for reading: $!\n" and exit(2);
  while( <rcFile> ) {
    chomp;
    s/#.*//;
    s/^\s+//;
    s/\s+$//;
    s/\"//g;
    next unless length;
    ($variable, $value) = split( /\s*=\s*/, $_, 2 );
    print "DEBUG: $variable = $value\n" if $debug;


    if( $variable eq "localCDDBpath" ) {
      $localCDDBpath = $value;
      $localCDDBpath =~ s/~/$HOME/g;      # substitute $HOME environment variable for '~' char
      $localCDDBpath =~ s/\.\//$pwd\//g;  # substitute $pwd  environment variable for '.' char
    }

    if( $variable eq "coverPath" ) {
      $coverPath = $value;
      $coverPath =~ s/~/$HOME/g;      # substitute $HOME environment variable for '~' char
      $coverPath =~ s/\.\//$pwd\//g;  # substitute $pwd  environment variable for '.' char
    }


    if( $variable eq "devices" ) {
      CB_device->clear();
      @list = split /,/, $value;
      foreach (@list) {
        CB_device->insertItem( $_ );
      }
    }
    if( $variable eq "defaultDev" ) {
      $actDevice = int($value);
    }

    if( $variable eq "genres" ) {
      CB_genre->clear();
      @list = split /,/, $value;
      foreach (@list) {
        CB_genre->insertItem( $_ );
      }
    }
    if( $variable eq "defaultGenre" ) {
      $defaultGenre = int($value);
    }

    if( $variable eq "defaultYear" ) {
      $defaultYear = int($value);
    }

    if( $variable eq "amazonLocales" ) {
      CB_amazonLocale->clear();
      @list = split /,/, $value;
      foreach (@list) {
        CB_amazonLocale->insertItem( $_ );
      }
    }
    if( $variable eq "defaultAmazonLocale" ) {
      $defaultAmazonLocale = int($value);
    }

    if( $variable eq "amazonToken" ) {
      $amazonToken  = $value;
    }

    if( $variable eq "ripperTRACK" ) {
      $ripperTRACK  = $value;
    }

    if( $variable eq "ripperDEVICE" ) {
      $ripperDEVICE = $value;
    }

    if( $variable eq "ripperOTHER" ) {
      $ripperOTHER  = $value;
    }

    if( $variable eq "removeSilence" ) {
      $removeSilence = $value;
      CB_removeSilence->setChecked($removeSilence != 0);
    }

    if( $variable eq "checkHiddenTracks" ) {
      $checkHiddenTracks = $value;
      CB_checkHiddenTracks->setChecked($checkHiddenTracks != 0);
    }


    if( $variable eq "normalizeToLevel" ) {
      $normalizeToLevel = $value;
      SB_normalizeToLevel->setValue($normalizeToLevel);
    }

    if( $variable eq "normalizeIgnL" ) {
      $normalizeIgnL = $value;
      CB_normalizeIgnoreLouder->setChecked($normalizeIgnL != 0);
    }

    if( $variable eq "normalizeByLevel" ) {
      $normalizeByLevel = $value;
      SB_normalizeByLevel->setValue($normalizeByLevel);
    }

    if( $variable eq "normalizeOpts" ) {
      $normalizeOpts = $value;
      LE_normalizeOpts->setText($normalizeOpts);
    }

    if( $variable eq "encoders" ) {
      CB_jobEncoder->clear();
      @list = split /,/, $value;
      foreach (@list) {
        CB_jobEncoder->insertItem( $_ );
        $encoderName[$numEncoders]  = "$_";
        $numEncoders++;
      }
    }

    if( $variable eq "job" ) {
      @list = split /,/, $value;
      for( my $i=0; $i<@list; $i++ ) {
        $AoA[$numJobs][$i] = $list[$i];
        $AoA[$numJobs][$i] =~ s/^\s+//;
        $AoA[$numJobs][$i] =~ s/\s+$//;
      }
      $numJobs++;
    }
  }

  if( ! -e "$localCDDBpath") {
    system( "mkdir -p \"$localCDDBpath\"" );
  }
  if( ! -e "$coverPath") {
    system( "mkdir -p \"$coverPath\"" );
  }

  if($actDevice < CB_device->count()) {
    CB_device->setCurrentItem($actDevice);
    CB_device->setCurrentText(CB_device->text($actDevice));
  }

  if($defaultGenre < CB_genre->count()) {
    CB_genre->setCurrentItem($defaultGenre);
    CB_genre->setCurrentText(CB_genre->text($defaultGenre));
  }

  if($defaultAmazonLocale < CB_amazonLocale->count()) {
    CB_amazonLocale->setCurrentItem($defaultAmazonLocale);
    CB_amazonLocale->setCurrentText(CB_amazonLocale->text($defaultAmazonLocale));
  }

  if(0 < CB_jobEncoder->count()) {
    CB_jobEncoder->setCurrentItem(0);
    CB_jobEncoder->setCurrentText(CB_jobEncoder->text(0));
  }

  print "<-- rcFileRead()\n" if $debug>1;

}

sub rcFileWrite
{

  print "--> rcFileWrite()\n" if $debug>1;

  # Open the rc file for writing
  open( rcFile, ">$RCFILE" ) or print STDERR "cannot open $RCFILE for writing: $!\n" and exit(2);

  print rcFile "# rc file for kRip\n\n";

  print rcFile "# The path to the local CDDB.\n";
  print rcFile "localCDDBpath = \"~/.cddb/\"\n\n";

  print rcFile "# The path to store the 120x120 cover images.\n";
  print rcFile "coverPath = \"~/.covers/\"\n\n";

  print rcFile "# The available devices to find the CD in.\n";
  print rcFile "devices = \"/dev/cdrom\",\"/dev/dvd\",\"/dev/dvd1\"\n";
  print rcFile "# The default device to find the CD in.\n";
  print rcFile "defaultDev = $actDevice\n\n";

  print rcFile "# some options for cdda2wav (do NOT change!)\n";
  print rcFile "ripperTRACK = \"-t\"\n";
  print rcFile "ripperDEVICE = \"-D\"\n";
  print rcFile "ripperOTHER = \"-Hg -paranoia\"\n\n";
#  -paranoia -S--speed -q--quiet
#  print rcFile "ripperQUERY = \"-JHg\"\n\n";

  print rcFile "# The default year\n";
  print rcFile "defaultYear = $defaultYear\n\n";

  print rcFile "# All genres in Genre Listbox\n";
  print rcFile "genres = ";
  print rcFile "\"Acid\",\"Acid Jazz\",\"Acid Punk\",\"AlternRock\",\"Alternative\",";
  print rcFile "\"Ambient\",\"Bass\",\"Blues\",\"Cabaret\",\"Christian Rap\",";
  print rcFile "\"Classic Rock\",\"Classical\",\"Comedy\",\"Country\",\"Cult\",";
  print rcFile "\"Dance\",\"Darkwave\",\"Death Metal\",\"Disco\",\"Dream\",";
  print rcFile "\"Electronic\",\"Ethnic\",\"Euro-Techno\",\"Eurodance\",\"Funk\",";
  print rcFile "\"Fusion\",\"Game\",\"Gangsta\",\"Gospel\",\"Gothic\",";
  print rcFile "\"Grunge\",\"Hard Rock\",\"Hip-Hop\",\"House\",\"Industrial\",";
  print rcFile "\"Instrumental\",\"Instrumental Pop\",\"Instrumental Rock\",\"Jazz\",\"Jazz+Funk\",";
  print rcFile "\"Jungle\",\"Lo-Fi\",\"Meditative\",\"Metal\",\"Musical\",";
  print rcFile "\"Native American\",\"New Age\",\"New Wave\",\"Noise\",\"Oldies\",";
  print rcFile "\"Other\",\"Polka\",\"Pop\",\"Pop-Folk\",\"Pop/Funk\",";
  print rcFile "\"Pranks\",\"Psychadelic\",\"Punk\",\"R&B\",\"Rap\",";
  print rcFile "\"Rave\",\"Reggae\",\"Retro\",\"Rock\",\"Rock & Roll\",";
  print rcFile "\"Showtunes\",\"Ska\",\"Soul\",\"Sound Clip\",\"Soundtrack\",";
  print rcFile "\"Southern Rock\",\"Space\",\"Techno\",\"Techno-Industrial\",\"Top 40\",";
  print rcFile "\"Trailer\",\"Trance\",\"Tribal\",\"Trip-Hop\",\"Vocal\"";
  print rcFile "\n";
  print rcFile "# The default genre\n";
  print rcFile "defaultGenre = $defaultGenre\n\n";

  print rcFile "# list of amazon catalogs\n";
  print rcFile "amazonLocales = \"\",\"de\",\"jp\",\"uk\"\n";
  print rcFile "# The default amazon locale\n";
  print rcFile "defaultAmazonLocale = $defaultAmazonLocale\n";
  print rcFile "# The Amazon token\n";
  print rcFile "amazonToken = \"$amazonToken\"\n\n";

  print rcFile "# Flag to remove silence at the beginning and at the end of the songs\n";
  print rcFile "removeSilence = $removeSilence\n";
  print rcFile "# Flag to check songs for silence in the middle and to split them\n";
  print rcFile "checkHiddenTracks = $checkHiddenTracks\n\n";

  print rcFile "# The level for 'normalize'. This value is used for the '-a' parameter.\n";
  print rcFile "# A <normalizeToLevel> of -9 means, that all tracks will be normalized\n";
  print rcFile "# to -9dbFS ('normalize -a -9dBFS').\n";
  print rcFile "normalizeToLevel = $normalizeToLevel\n";
  print rcFile "# Flag to ignore songs, that are already above the level\n";
  print rcFile "normalizeIgnL = $normalizeIgnL\n";
  print rcFile "# A <normalizeByLevel> of 3, means, that all tracks will be increased by +3dBFS'\n";
  print rcFile "normalizeByLevel = $normalizeByLevel\n";
  print rcFile "# additional parameters for 'normalize'\n";
  print rcFile "normalizeOpts = $normalizeOpts\n\n";

  print rcFile "# list of encoders (do NOT change!)\n";
  print rcFile "encoders = \"lame\",\"oggenc\"\n\n";

  print rcFile "# list of jobs\n";
  for(my $i=0; $i<$numJobs; $i++) {
    print rcFile "job = ";
    for(my $j=0; $j<$numJobAttr; $j++) {
      print rcFile $AoA[$i][$j];
      if( $j < $numJobAttr-1) {
        print rcFile ", ";
      }
    }
    print rcFile "\n";
  }

  close( rcFile );

  print "<-- rcFileWrite()\n" if $debug>1;

}

sub refreshPanel
{

  print "--> refreshPanel()\n" if $debug>2;

  TW_tabs->setTabEnabled(TW_tabs->page(1), $state > 0 );
  TW_tabs->setTabEnabled(TW_tabs->page(2), $state > 1 );

  # Tab 1
  PB_scanCD->setEnabled($dev ne "");

  LE_artist->setEnabled($state > 0);
  LE_album->setEnabled($state > 0);
  SB_year->setEnabled($state > 0);
  CB_genre->setEnabled($state > 0);
  PL_cover->setEnabled($state > 0);
  PB_openCover->setEnabled($state > 0);
  PB_searchCover->setEnabled(($state > 0) and ($artist ne "") and ($title ne ""));
  CB_amazonLocale->setEnabled(($state > 0) and ($artist ne "") and ($title ne ""));

  CB_live->setEnabled($state > 0);
  CB_sampler->setEnabled($state > 0);
  T_tracks->setEnabled($state > 0);

  PB_queryCDDB->setEnabled($state > 0);
  PB_queryLocalCDDB->setEnabled($state > 0);
  PB_saveLocalCDDB->setEnabled($state > 0);
  PB_autoCorrection->setEnabled($state > 0);

  PB_nextTo2->setEnabled($state > 0);

  # Tab 2
  T_tracks_2->setEnabled($state > 0);

  PB_rip->setEnabled($state > 0);
  CB_removeSilence->setEnabled($state > 0);
  CB_checkHiddenTracks->setEnabled($state > 0);

  PB_normalizeTo->setEnabled($state > 1);
  SB_normalizeToLevel->setEnabled($state > 1);
  CB_normalizeIgnoreLouder->setEnabled($state > 1);
  PB_normalizeBy->setEnabled($state > 1);
  SB_normalizeByLevel->setEnabled($state > 1);
  LE_normalizeOpts->setEnabled($state > 1);

  PB_prevTo1->setEnabled($state >= 0);
  PB_nextTo3->setEnabled($state > 1);

  # Tab 3
  LB_jobs->setEnabled($state > 1);

  PB_jobNew->setEnabled($state > 1);
  PB_jobDelete->setEnabled(($state > 1) and (LB_jobs->currentItem() > -1));
  PB_jobMoveUp->setEnabled(($state > 1) and (LB_jobs->currentItem() > 0));
  PB_jobMoveDown->setEnabled(($state > 1) and (LB_jobs->currentItem() < $numJobs-1));

  PB_jobsRun->setEnabled($state > 1);

  PB_prevTo2->setEnabled($state >= 0);

  print "<-- refreshPanel()\n" if $debug>2;

}

sub autoCorrect
{

  print "--> autoCorrect()\n" if $debug>2;

  my @list;
  my $str = $_[0];

  print ">>> original:             >$str<\n" if $debug>4;
  # replace non-ascii characters
  # replace 'A' likes
  $str =~ s/[\xC0-\xC3]/A/g;
  $str =~ s/\xC4/Ae/g;
  $str =~ s/\xC5/A/g;
  $str =~ s/\xC6/AE/g;
  $str =~ s/\xC7/C/g;
  # replace 'E' likes
  $str =~ s/[\xC8-\xCB]/E/g;
  # replace 'I' likes
  $str =~ s/[\xCC-\xCF]/I/g;
  $str =~ s/\xD0/D/g;
  $str =~ s/\xD1/N/g;
  # replace 'O' likes
  $str =~ s/[\xD2-\xD5]/O/g;
  $str =~ s/\xD6/Oe/g;
  $str =~ s/\xD7/x/g;
  $str =~ s/\xD8/O/g;
  # replace 'U' likes
  $str =~ s/[\xD9-\xDB]/U/g;
  $str =~ s/\xDC/Ue/g;
  $str =~ s/\xDD/Y/g;
  $str =~ s/\xDE/B/g;
  $str =~ s/\xDF/ss/g;
  # replace 'a' likes
  $str =~ s/[\xE0-\xE3]/a/g;
  $str =~ s/\xE4/ae/g;
  $str =~ s/\xE5/ae/g;
  $str =~ s/\xE6/ae/g;
  $str =~ s/\xE7/c/g;
  # replace 'e' likes
  $str =~ s/[\xE8-\xEB]/e/g;
  # replace 'i' likes
  $str =~ s/[\xEC-\xEF]/i/g;
  $str =~ s/\xF0/d/g;
  $str =~ s/\xF1/n/g;
  # replace 'o' likes
  $str =~ s/[\xF2-\xF5]/o/g;
  $str =~ s/\xF6/oe/g;
  $str =~ s/\xF7/\//g;
  $str =~ s/\xF8/o/g;
  # replace 'u' likes
  $str =~ s/[\xF9-\xFB]/u/g;
  $str =~ s/\xFC/ue/g;
  $str =~ s/\xFD/y/g;
  $str =~ s/\xFE/b/g;
  $str =~ s/\xFF/y/g;
  print ">>> chars replaced        >$str<\n" if $debug>4;

  # replace '`', '´', '"' with '''
  $str =~ s/(`|´|")/'/g;

  # remove unneccessary blanks around ,._:;-()[]{}
  $str =~ s/\s*([\s|,|.|_|:|;|\-|\(|\)|\[|\]|\{|\}])\s*/$1/g;
  print ">>> blanks removed        >$str<\n" if $debug>4;

  @list = split /([\s|,|.|_|:|;|\-|\(|\)|\[|\]|\{|\}])/, $str;
  $str = "";
  foreach (@list) {
  print ">>>                       >$_<\n" if $debug>4;
    $str .= ucfirst lc $_;
  }

  # insert spaces before some characters
  $str =~ s/(-|\(|\[|\{)/ $1/g;
  # insert spaces after  some characters
  $str =~ s/(,|-|:|;|\)|\]|\})/$1 /g;
  print ">>> blanks inserted       >$str<\n" if $debug>4;

  # replace { [ with (
  $str =~ s/[\{\[]/(/g;
  # and     ] } with )
  $str =~ s/[\]\}]/)/g;
  print ">>> braces replaced       >$str<\n" if $debug>4;

  $str =~ s/^\s+//; $str =~ s/\s+$//;
  print ">>> final                 >$str<\n" if $debug>4;

  print "<-- autoCorrect()\n" if $debug>2;
  return $str;

}

sub col0name
{

  my $str1 = $_[0];
  my $str2 = $_[1];
  my $str = "";

  print "--> col0name(".$str1.", ".$str2.")\n" if $debug>3;

  $str = $str1." - ".$str2;

  print "<-- col0name($str)\n" if $debug>3;
  return $str;

}

sub col1name
{

  my $str = $_[0];
  my $trArtist;
  my $trTitle;

  print "--> col1name($str)\n" if $debug>3;

  ($trArtist, $trTitle) = split( /\s+-\s+/, $str, 2 );
  if( !defined $trTitle ) {
    $trTitle = $trArtist;
    $trArtist = "";
  }
  $str = $trArtist;

  print "<-- col1name($str)\n" if $debug>3;
  return $str;

}

sub col2name
{

  my $str = $_[0];
  my $trArtist;
  my $trTitle;

  print "--> col2name($str)\n" if $debug>3;

  ($trArtist, $trTitle) = split( /\s+-\s+/, $str, 2 );
  if( !defined $trTitle ) {
    $trTitle = $trArtist;
    $trArtist = "";
  }
  $str = $trTitle;

  print "<-- col2name($str)\n" if $debug>3;
  return $str;

}

sub refreshJobs
{

  print "--> refreshJobs()\n" if $debug>2;

  print "    currentItem : $currentItem\n" if $debug>4;
  if($currentItem > -1) {
    # get job properties
    #  ===>
    GB_jobProp->show();
    loadJobProperties();
  } else {
    GB_jobProp->hide();
  }

  PB_jobDelete->setEnabled(($state > 1) and (LB_jobs->currentItem() > -1));
  PB_jobMoveUp->setEnabled(($state > 1) and (LB_jobs->currentItem() > 0));
  PB_jobMoveDown->setEnabled(($state > 1) and (LB_jobs->currentItem() < $numJobs-1));

  print "<-- refreshJobs()\n" if $debug>2;

}

sub loadJobProperties
{

  print "--> loadJobProperties()\n" if $debug>2;

  print "    currentItem: $currentItem\n" if $debug>4;
  if($currentItem > -1) {
    GB_jobProp          ->setChecked    ($AoA[$currentItem][$JOB_ENABLED  ] != 0);
    LE_jobName          ->setText       ($AoA[$currentItem][$JOB_NAME     ]);
    CB_jobEncoder       ->setCurrentItem($AoA[$currentItem][$JOB_ENCODER  ]);
    LE_jobEncOpts       ->setText       ($AoA[$currentItem][$JOB_ENCOPTS  ]);
    LE_jobEncFormat     ->setText       ($AoA[$currentItem][$JOB_ENCFORMAT]);
    CB_jobTag           ->setChecked    ($AoA[$currentItem][$JOB_TAG      ] != 0);

    if($AoA[$currentItem][$JOB_ENCODER] == 0) {
      # lame
      CB_jobTagCover    ->show();
      CB_jobTagCover    ->setEnabled    ($AoA[$currentItem][$JOB_TAG      ] != 0);
      CB_jobTagCover    ->setChecked    ($AoA[$currentItem][$JOB_TAGCOVER ] != 0);
    } else {
      # oggenc
      CB_jobTagCover->hide();
    }

    CB_jobCreateCoverJPG->setChecked    ($AoA[$currentItem][$JOB_COVERJPG ] != 0);
    CB_jobCreateCoverBMP->setChecked    ($AoA[$currentItem][$JOB_COVERBMP ] != 0);
    CB_jobCreateAlbumTxt->setChecked    ($AoA[$currentItem][$JOB_ALBUMTXT ] != 0);
  }

  print "<-- loadJobProperties()\n" if $debug>2;

}

sub saveJobProperties
{

  print "--> saveJobProperties()\n" if $debug>2;

  print "    currItemLast: $currItemLast\n" if $debug>4;
  if($currItemLast > -1) {
    # save old job properties
    #  <===
    $AoA[$currItemLast][$JOB_NAME     ] = LE_jobName->text();

    my $jobEnabled = 0;
    if( GB_jobProp->isChecked() ) {
      $jobEnabled = 1;
    }
    $AoA[$currItemLast][$JOB_ENABLED  ] = $jobEnabled;

    $AoA[$currItemLast][$JOB_ENCODER  ] = CB_jobEncoder->currentItem();
    $AoA[$currItemLast][$JOB_ENCOPTS  ] = LE_jobEncOpts->text();
    $AoA[$currItemLast][$JOB_ENCFORMAT] = LE_jobEncFormat->text();

    my $jobTagEnabled = 0;
    if( CB_jobTag->isChecked() ) {
      $jobTagEnabled = 1;
    }
    $AoA[$currItemLast][$JOB_TAG      ] = $jobTagEnabled;

    my $jobTagCoverEnabled = 0;
    if( CB_jobTagCover->isChecked() ) {
      $jobTagCoverEnabled = 1;
    }
    $AoA[$currItemLast][$JOB_TAGCOVER ] = $jobTagCoverEnabled;

    my $jobCreateCoverJPG = 0;
    if( CB_jobCreateCoverJPG->isChecked() ) {
      $jobCreateCoverJPG = 1;
    }
    $AoA[$currItemLast][$JOB_COVERJPG ] = $jobCreateCoverJPG;

    my $jobCreateCoverBMP = 0;
    if( CB_jobCreateCoverBMP->isChecked() ) {
      $jobCreateCoverBMP = 1;
    }
    $AoA[$currItemLast][$JOB_COVERBMP ] = $jobCreateCoverBMP;

    my $jobCreateAlbumTxt = 0;
    if( CB_jobCreateAlbumTxt->isChecked() ) {
      $jobCreateAlbumTxt = 1;
    }
    $AoA[$currItemLast][$JOB_ALBUMTXT ] = $jobCreateAlbumTxt;
  }

  print "<-- saveJobProperties()\n" if $debug>2;

}

sub execThread
{

#  my $cmd = $_[0];
#  my $result = 0;
#  print "--> exec($cmd)\n" if $debug>1;
#
#  my $kid;
#  my $pid = fork;
#  die "fork() failed: $!" unless defined $pid;
#  if ($pid) {
#    # parent
#    do {
#      $kid = waitpid(-1, WNOHANG);
#      print "parent waits...\n";
#      repaint();
#      sleep(1);
#    } until $kid > 0;
#
#    waitpid($pid, 0);
#  } else {
#    # child
#    $result = system( $cmd );
#    $result = exec( $cmd );
#    exit;
#  }
#  return $result;


#  sub loop {
#    my $thread = shift;
#    my $foo = 5;
#    while($foo--) { print "in thread $thread\n" }
#    threads->yield;
#    $foo = 5;
#    while($foo--) { print "in thread $thread\n" }
#  }
#  my $thread1 = threads->new(\&loop, 'first');
#  my $thread2 = threads->new(\&loop, 'second');
#  my $thread3 = threads->new(\&loop, 'third');

#  $thread1->join;
#  $thread2->join;
#  $thread3->join;

#  my $sys = threads->new(\&threadSystem, $cmd);
#  $sys->join;

#  system( $cmd );
#  print "<-- exec($cmd)\n" if $debug>1;

}

sub CB_device_activated
{

  print "--> CB_device_activated()\n" if $debug>1;

  initValues();

  print "<-- CB_device_activated()\n" if $debug>1;

}

sub PB_scanCD_clicked
{

  print "--> PB_scanCD_clicked()\n" if $debug>1;

  PB_scanCD->setFocus();

  my $tmpFile = "/tmp/kRip.tmp";
  my $rc = 0;

  # Figure out how many tracks there are on the CD ($numTracks)
  $command = "cdda2wav -JHg -v toc -D $dev 2>$tmpFile";
  print "DEBUG: scan command: $command\n" if $debug;

  setCursor(C_wait);
  system( $command );
  setCursor(C_arrow);
  if ($? == -1) {
    print "failed to execute: $command\n";
    die;
  } elsif ($? & 127) {
    printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
    die;
  } else {
    $rc = $? >> 8;
    printf "child exited with value $rc\n" if $debug;
#|       0 | no errors encountered, successful operation.                       |
#|       1 | usage or syntax error. cdda2wav got inconsistent arguments.        |
#|       2 | permission (un)set errors. permission changes failed.              |
#|       3 | read errors on the cdrom/burner device encountered.                |
#|       4 | write errors while writing one of the output files encountered.    |
#|       5 | errors with soundcard handling (initialization/write).             |
#|       6 | errors with stat() system call on the read device (cooked ioctl).  |
#|       7 | pipe communication errors encountered (in forked mode).            |
#|       8 | signal handler installation errors encountered.                    |
#|       9 | allocation of shared memory failed (in forked mode).               |
#|      10 | dynamic heap memory allocation failed.                             |
#|      11 | errors on the audio cd medium encountered.                         |
#|      12 | device open error in ioctl handling detected.                      |
#|      13 | race condition in ioctl interface handling detected.               |
#|      14 | error in ioctl() operation encountered.                            |
#|      15 | internal error encountered. Please report back!!!                  |
#|      16 | error in semaphore operation encountered (install / request).      |
#|      17 | could not get the scsi transfer buffer.                            |
#|      18 | could not create pipes for process communication (in forked mode). |
    if( $rc != 0 ) {        # XXX add special handling for some RCs
    }
  }


  if( $rc == 0 ) {
    open(TEMP, "<$tmpFile") or print STDERR "cannot open temp file $tmpFile\n" and exit(3);

    $_ = " ";

    # This code is HIGHLY DEPENDENT on the output format from cdda2wav!
    # If you have a better suggestion for how to do this, I am all ears.

    # Find the start of the track listing output
    while( not(eof TEMP) and not($_ =~ /Album title:/) ) {
      $_ = <TEMP>;
      if( $_ =~ /^CDDB discid:/  ) {
        $cddb_id = $_;
        $cddb_id =~ s/^CDDB discid: 0x([0-9a-fA-F]*)\s*/$1/;
      }
    }

    $numTracks = 0;
    # Start counting the lines in the file (output from cdda2wav)
    while( not(eof TEMP) and not($_ =~ /Leadout:/) ) {
      $_ = <TEMP>;

      if( not($_ =~ /Leadout:/ ) ) {
        $trackNameList  [ $numTracks ] = "Track".($numTracks+1);
        $trackNameFile  [ $numTracks ] = "Track".($numTracks+1).".wav";

        s/T..:\s+\d*\s+(\d*:\d*\.\d*).*/$1/;
        $trackNameLength[ $numTracks ] = $_;
        $trackNameLevel [ $numTracks ] = "";
        $trackNamePeak  [ $numTracks ] = "";

        $numTracks++;
      }
    }
    close( TEMP );
    system( "rm $tmpFile" ) if( -e "$tmpFile") ;

    TL_cddbid->setText("CDDB id: ".$cddb_id);
    CB_live->setChecked(0);
    CB_sampler->setChecked(0);

    $artist = "";
    LE_artist->setText($artist);
    $title = "";
    LE_album->setText($title);

    SB_year->setValue($defaultYear);
    CB_genre->setCurrentItem($defaultGenre);
    $coverfilename = "";
    PL_cover->setPixmap( Qt::Pixmap() );
    PL_cover->setScaledContents( 0 );
    TL_coverinfo->setText("");

    for( my $i=0; $i<$numTracks; $i++) {
      T_tracks->setText( $i, 0, $trackNameList[ $i ] );
      T_tracks->setText( $i, 1, col1name($trackNameList[ $i ]) );
      T_tracks->setText( $i, 2, col2name($trackNameList[ $i ]) );
      T_tracks->showRow( $i );
      T_tracks_2->setText( $i, 0, $trackNameList  [ $i ] );
      T_tracks_2->setText( $i, 1, $trackNameLength[ $i ] );
      T_tracks_2->setText( $i, 2, $trackNameLevel [ $i ] );
      T_tracks_2->setText( $i, 3, $trackNamePeak  [ $i ] );
      T_tracks_2->showRow( $i );
      T_tracks_2->selectCells( 0, 0, $numTracks-1, 3 ) if $numTracks>0;
    }
    for( my $i=$numTracks; $i<$maxTracks; $i++) {
      T_tracks->hideRow($i);
      T_tracks_2->hideRow($i);
    }
    $numHiddenTracks = 0;

    TL_levelValAvg->setText("...");
    TL_levelValMin->setText("...");
    TL_levelValMax->setText("...");
    TL_peakValAvg->setText("...");
    TL_peakValMin->setText("...");
    TL_peakValMax->setText("...");

    $state = 1;
    refreshPanel();
    if(($localCDDBpath ne "") and ($cddb_id ne "") and
      ( -e "$localCDDBpath$cddb_id")) {
      PB_queryLocalCDDB->setEnabled( 1>0 );
      PB_queryLocalCDDB->setFocus();
    } else {
      PB_queryLocalCDDB->setEnabled( 1<0 );
      PB_queryCDDB->setFocus();
    }
  }

  print "<-- PB_scanCD_clicked()\n" if $debug>1;

}

sub CB_sampler_stateChanged
{

  print "--> CB_sampler_stateChanged()\n" if $debug>1;

  my $trArtist;
  my $trTitle;

  if( !CB_sampler->isChecked() ) {
    # normal Album: show only the Trackname
    T_tracks->setColumnWidth(0, T_tracks->visibleWidth()-1);
    T_tracks->setColumnWidth(1, 0);
    T_tracks->setColumnWidth(2, 0);
    T_tracks->showColumn(0);
    T_tracks->hideColumn(1);
    T_tracks->hideColumn(2);
  } else {
    # Sampler: split Trackname in 'Artist: Title' and show
    T_tracks->setColumnWidth(0, 0);
    T_tracks->setColumnWidth(1, T_tracks->visibleWidth()/2);
    T_tracks->setColumnWidth(2, (T_tracks->visibleWidth()-1)/2);
    T_tracks->hideColumn(0);
    T_tracks->showColumn(1);
    T_tracks->showColumn(2);
  }

  print "<-- CB_sampler_stateChanged()\n" if $debug>1;

}

sub T_tracks_valueChanged
{

  my($row, $col) = @_;
  print "--> T_tracks_valueChanged($row, $col)\n" if $debug>2;

  # update the other columns in the table
  if( $col == 0 ) {
    $trackNameList[ $row ] = T_tracks->text($row, 0);
    T_tracks->setText( $row, 1, col1name($trackNameList[ $row ]) );
    T_tracks->setText( $row, 2, col2name($trackNameList[ $row ]) );
  } else {
    $trackNameList[ $row ] = col0name(T_tracks->text($row, 1), T_tracks->text($row, 2));
    T_tracks->setText( $row, 0, $trackNameList[ $row ] );
  }
  T_tracks_2->setText( $row, 0 , $trackNameList[$row] );

#  my $item = T_tracks->item($row+1, $col);
#  if( $item ) {
#    print "Text: ".$item->text();
#    T_tracks->editCell($row+1, $col);
##    $item->setFocus();
#  } else {
#    print "item (".($row+1).",".($col).") == 0\n";
#  }


  # and set the focus to the next field
#  if( T_tracks->hasFocus() ) {
#print ">>>> hasFocus()\n";
#    if( $col == 0 ) {
#      if($row < ($numTracks-1)) {
#print ">>>> editCell(".($row+1).", 0)\n";
#        T_tracks->setEditMode(&Qt::Table::Editing, ($row+1), 0);
#        T_tracks->setCurrentCell(($row+1), 0);
#        T_tracks->editCell(($row+1), 0);
#      }
#    } else {
#      if($col == 1) {
#print ">>>> editCell(".$row.", 2)\n";
#        T_tracks->setEditMode(&Qt::Table::Editing, $row, 2);
#        T_tracks->setCurrentCell($row, 2);
#        T_tracks->editCell($row, 2);
#      } elsif($row < ($numTracks-1)) {
#print ">>>> editCell(".($row+1).", 1)\n";
#        T_tracks->setEditMode(&Qt::Table::Editing, ($row+1), 1);
#        T_tracks->setCurrentCell(($row+1), 1);
#        T_tracks->editCell(($row+1), 1);
#      }
#    }
#  } else {
#print ">>>> has NO Focus()\n";
#  }

  print "<-- T_tracks_valueChanged()\n" if $debug>2;

}

sub PB_openCover_clicked
{

  print "--> PB_openCover_clicked()\n" if $debug>1;

  $coverfilename = "";

  $coverfilename = Qt::FileDialog::getOpenFileName(".", "Covers (*.jpg;*.jpeg;*.bmp)", this);

##  FD_preview = Qt::Label(this, "PL_preview");

#  FD_open = Qt::FileDialog;
#  FD_open->setViewMode( &Qt::FileDialog::Detail );
##  FD_open->addRightWidget(FD_preview);
#  $coverfilename = FD_open->getOpenFileName(".", "Covers (*.jpg *.bmp *.png)", this,
#                                            "Open Cover",
#                                            "",
#                                            "");

  print "DEBUG: coverfilename:$coverfilename\n" if $debug;
  my $image0 = Qt::Pixmap($coverfilename);
  PL_cover->setPixmap( $image0 );
  PL_cover->setScaledContents( $coverfilename ne "" );

  if( ( $coverfilename ne "" ) and ( $image0 ) ) {
    my $tmpDir  = "";
    my $tmpFile = "";
    ($tmpDir, $tmpFile) = $coverfilename =~ m/(.*[^\\]\/)(.*)$/;
    my $tmp = $tmpFile."\n".$image0->width()." x ".$image0->height();
    TL_coverinfo->setText($tmp);
  } else {
    TL_coverinfo->setText("");
  }

  print "<-- PB_openCover_clicked()\n" if $debug>1;

}

sub PB_searchCover_clicked
{

  print "--> PB_searchCover_clicked()\n" if $debug>1;

  my $filename = "";
  my $ua;

  $coverfilename = "";

  print "Set Amazon token : $amazonToken\n" if $debug;
  if($defaultAmazonLocale == 0) {
    $ua = Net::Amazon->new(token => $amazonToken);
  } else {
    my $locale = CB_amazonLocale->text($defaultAmazonLocale);
    print "Set Amazon locale: $locale\n" if $debug;
    $ua = Net::Amazon->new(token => $amazonToken,
                           locale => $locale);
  }

  # Strip out info in "()"'s and "[]"'s which tend to be disc numbers or other info which throws off amazons database.
  my $cover_title = $title;
  my $cover_artist = $artist;
  $cover_title =~ s/\((.*)\)//;
  $cover_title =~ s/\[(.*)\]//;
  $cover_title =~ s/\-/\ /;
  $cover_title =~ s/\&/\ /;
  $cover_title =~ s/^\s+//;            # Remove leading white-space
  $cover_title =~ s/\s+$//;            # Remove trailing white-space
  $cover_artist =~ s/\((.*)\)//;
  $cover_artist =~ s/\[(.*)\]//;
  $cover_artist =~ s/\-/\ /;
  $cover_artist =~ s/\&/\ /;
  $cover_artist =~ s/^\s+//;           # Remove leading white-space
  $cover_artist =~ s/\s+$//;           # Remove trailing white-space

  print "Search for >$cover_artist< >$cover_title<\n" if $debug;

  my $req = Net::Amazon::Request::Keyword->new(keyword  => "$cover_artist, $cover_title",
                                               mode => "music");
  my $resp = $ua->request($req);
  my $n = 0;
  if($resp->is_success()) {
    for ($resp->properties) {

      print "Found ASIN ".$_->Asin() if $debug;
      my $req2 = Net::Amazon::Request::ASIN->new(asin => $_->Asin());
      my $resp2 = $ua->request($req2);

      my @Sizes = ("ImageUrlLarge", "ImageUrlMedium", "ImageUrlSmall");
      my $size;
      foreach $size(@Sizes) {
        my $image = Image::Magick->new;
        my $x = $image->Read($resp2->properties->{$size});
        warn "$x" if "$x";
        my ($width,$height) = $image->Get("width","height");
        if ( $width > 1 ) {
          $n++;

          # write 120x120 color $artist - $title.jpg
          $filename = "$pwd/found_".$n.".jpg";
          open(IMAGE, ">$filename");
          $image->Write(file=>\*IMAGE, filename=>"$filename");
          close(IMAGE);
          print " with cover. Saved as $filename ($width,$height)" if $debug;

          last;
        }
      }
      print "\n" if $debug;
    }
    print "\nFound $n cover(s).\n" if $debug;

    if( $n > 1 ) {
      system ("./select_cover.pl");
      $coverfilename = "$pwd/found.jpg";
    } elsif( $n == 1 ) {
      system ("cp \"$pwd/found_1.jpg\" \"$pwd/found.jpg\"");
      $coverfilename = "$pwd/found.jpg";
    } else {
      $coverfilename = "";
    }

  } else {
    print STDERR "Amazon-Error: ".$resp->message()."\n";
  }

  print "DEBUG: coverfilename:$coverfilename\n" if $debug;
  my $image0 = Qt::Pixmap($coverfilename);
  PL_cover->setPixmap( $image0 );
  PL_cover->setScaledContents( $coverfilename ne "" );

  if( ( $coverfilename ne "" ) and ( $image0 ) ) {
    my $tmpDir  = "";
    my $tmpFile = "";
    ($tmpDir, $tmpFile) = $coverfilename =~ m/(.*[^\\]\/)(.*)$/;
    my $tmp = $tmpFile."\n".$image0->width()." x ".$image0->height();
    TL_coverinfo->setText($tmp);
  } else {
    TL_coverinfo->setText("");
  }

  print "<-- PB_searchCover_clicked()\n" if $debug>1;

}

sub CB_amazonLocale_activated
{

  print "--> CB_amazonLocale_activated()\n" if $debug>1;

  $defaultAmazonLocale = CB_amazonLocale->currentItem();

  print "<-- CB_amazonLocale_activated()\n" if $debug>1;

}

sub PB_queryCDDB_clicked
{

  print "--> PB_queryCDDB_clicked()\n" if $debug>1;

  my $applyChanges = 1;

  PB_queryCDDB->setFocus();

  if( $queryTypeCDDB==0) {
    # use CDDB_get function for querying CDDB
    my %cd;
    my %config;                                       # Configuration passed to CDDB/CDDB_get
    $config{CD_DEVICE}  = $dev;                       # Device that has the audio CD
    $config{CDDB_HOST}  = $CDDB_HOST;                 # CDDB host to find the server on
    $config{CDDB_PORT}  = $CDDB_PORT;                 # CDDB port the CDDB server is using
    $config{CDDB_MODE}  = "cddb";                     # CDDB mode can be: cddb, http
    $config{input}      = 0;                          # User interaction: 1 = true, 0 = false
    $config{HELLO_ID}   = "me no.net kRippl $version"; # HELLO string to give CDDB server
    $config{HTTP_PROXY} = $ENV{HTTP_PROXY} if $ENV{HTTP_PROXY}; # Use HTTP proxy if it exists in environ

    # Get the names via CDDB

    # Use CDDB_get/CDDB to do the dirty work here.
    %cd = get_cddb( \%config );

    # Assume no title means CDDB lookup failed and we need to go to manual mode.
    if( !defined $cd{title} ) {
#      $numTracks = 0;
      $applyChanges = 0;
    } else {
      $artist    = $cd{artist};
      $title     = $cd{title};
      if( $cd{year} ) {
        $year    = $cd{year};
      } else {
        $year    = $defaultYear;
      }
      if( $cd{cat} ) {
        $genre   = $cd{cat};
      } else {
        $genre   = CB_genre->text($defaultGenre);
      }

      $numTracks=0;
      foreach my $track ( @{$cd{track}} ) {
        $track =~ s/^\s+//;  $track =~ s/\s+$//;
        $trackNameList  [ $numTracks ] = $track;
#        $trackNameLength[ $numTracks ] = "";
#        $trackNameLevel [ $numTracks ] = "";
#        $trackNamePeak  [ $numTracks ] = "";
        $numTracks++;
      }
#      $state = 1;
    }

  } else {
    # use cdda2wav for querying CDDB
    my $tmpFile = "/tmp/kRip.tmp";
    my $rc = 0;
    my $track1;
    my $track2;


    # ask cdda2wav
    $command = "cdda2wav -JHg -v toc -L 1 -cddbp-server=$CDDB_HOST -cddbp-port=$CDDB_PORT -D $dev 2>$tmpFile";
    print "DEBUG: query cddb command: $command\n" if $debug;

    setCursor(C_wait);
    system( $command );
    setCursor(C_arrow);
    if ($? == -1) {
      print "failed to execute: $command\n";
      die;
    } elsif ($? & 127) {
      printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
      die;
    } else {
      $rc = $? >> 8;
      printf "child exited with value $rc\n" if $debug;
  #|       0 | no errors encountered, successful operation.                       |
  #|       1 | usage or syntax error. cdda2wav got inconsistent arguments.        |
  #|       2 | permission (un)set errors. permission changes failed.              |
  #|       3 | read errors on the cdrom/burner device encountered.                |
  #|       4 | write errors while writing one of the output files encountered.    |
  #|       5 | errors with soundcard handling (initialization/write).             |
  #|       6 | errors with stat() system call on the read device (cooked ioctl).  |
  #|       7 | pipe communication errors encountered (in forked mode).            |
  #|       8 | signal handler installation errors encountered.                    |
  #|       9 | allocation of shared memory failed (in forked mode).               |
  #|      10 | dynamic heap memory allocation failed.                             |
  #|      11 | errors on the audio cd medium encountered.                         |
  #|      12 | device open error in ioctl handling detected.                      |
  #|      13 | race condition in ioctl interface handling detected.               |
  #|      14 | error in ioctl() operation encountered.                            |
  #|      15 | internal error encountered. Please report back!!!                  |
  #|      16 | error in semaphore operation encountered (install / request).      |
  #|      17 | could not get the scsi transfer buffer.                            |
  #|      18 | could not create pipes for process communication (in forked mode). |
      if( $rc != 0 ) {        # XXX add special handling for some RCs
      }
    }


    if( $rc != 0 ) {
      $applyChanges = 0;
    } else {
      open(TEMP, "<$tmpFile") or print STDERR "cannot open temp file $tmpFile\n" and exit(3);

      $_ = " ";

      # This code is HIGHLY DEPENDENT on the output format from cdda2wav!
      # If you have a better suggestion for how to do this, I am all ears.

      # Find the start of the track listing output
      while( not(eof TEMP) and not($_ =~ /^Album title:/) ) {
        $_ = <TEMP>;
      }
      if($_ =~ /^Album title:/) {
        $artist = $_;
        $artist =~ s/^Album title: '(.*)' from '(.*)'\s*/$2/;
        $title  = $_;
        $title  =~ s/^Album title: '(.*)' from '(.*)'\s*/$1/;
      }

      $numTracks = 0;
      # Start counting the lines in the file (output from cdda2wav)
      while( not(eof TEMP) and not($_ =~ /Leadout:/) ) {
        $_ = <TEMP>;

        if( not($_ =~ /Leadout:/ ) ) {
          $track1 = $_;
          $track1 =~ s/T..:\s+\d*\s+\d*:\d*\.\d*.*title '\s*(.*)\s*' from '\s*(.*)\s*'/$1/;
          $track1 =~ s/^\s+//;  $track1 =~ s/\s+$//;
          $track1 =~ s/\\'/'/;
          $track2 = $_;
          $track2 =~ s/T..:\s+\d*\s+\d*:\d*\.\d*.*title '\s*(.*)\s*' from '\s*(.*)\s*'/$2/;
          $track2 =~ s/^\s+//;  $track2 =~ s/\s+$//;
          $track2 =~ s/\\'/'/;

          if( $track2 ne "" ) {
            $trackNameList  [ $numTracks ] = $track2." - ".$track1;
          } else {
            $trackNameList  [ $numTracks ] = $track1;
          }
          $numTracks++;
        }
      }
#      $state = 1;
      close( TEMP );
      system( "rm $tmpFile" ) if( -e "$tmpFile") ;

      $year    = $defaultYear;
      $genre   = CB_genre->text($defaultGenre);
    } # system($command) was OK

  } # use cdda2wav


  if( $applyChanges == 1) {
    CB_live->setChecked(0);
    CB_sampler->setChecked(0);

    $artist  =~ s/^\s+//;  $artist  =~ s/\s+$//;
    $title   =~ s/^\s+//;  $title   =~ s/\s+$//;
    $year    =~ s/^\s+//;  $year    =~ s/\s+$//;
    $genre   =~ s/^\s+//;  $genre   =~ s/\s+$//;

    print "\$artist  = $artist\n" if $debug;
    print "\$title   = $title\n" if $debug;
    print "\$year    = $year\n" if $debug;
    print "\$genre   = $genre\n" if $debug;

    LE_artist->setText($artist);
    LE_album->setText($title);
    SB_year->setValue($year);
    CB_genre->setCurrentText($genre);

    for( my $i=0; $i<CB_genre->count(); $i++) {
      if( uc($genre) eq uc(CB_genre->text($i))) {
        CB_genre->setCurrentItem($i);
        $genre = CB_genre->text($i);
        CB_genre->setCurrentText(CB_genre->text($i));
      }
    }

    for( my $i=0; $i<$numTracks; $i++) {
      if( $i<10 ) {
        print "\$trackNameList[$i]  = $trackNameList[ $i ]\n" if $debug;
      } else {
        print "\$trackNameList[$i] = $trackNameList[ $i ]\n" if $debug;
      }
    }
    for( my $i=0; $i<$numTracks; $i++) {
      T_tracks->setText( $i, 0, $trackNameList[ $i ] );
      T_tracks->setText( $i, 1, col1name($trackNameList[ $i ]) );
      T_tracks->setText( $i, 2, col2name($trackNameList[ $i ]) );
      T_tracks->showRow( $i );
      T_tracks_2->setText( $i, 0, $trackNameList[ $i ] );
      T_tracks_2->showRow( $i );
      T_tracks_2->selectCells( 0, 0, $numTracks-1, 3 ) if $numTracks>0;
    }
    for( my $i=$numTracks; $i<$maxTracks; $i++) {
      T_tracks->hideRow($i);
      T_tracks_2->hideRow($i);
    }
  }




#  refreshPanel();
  PB_autoCorrection->setFocus();

  print "<-- PB_queryCDDB_clicked()\n" if $debug>1;

}

sub PB_queryLocalCDDB_clicked
{

  print "--> PB_queryLocalCDDB_clicked()\n" if $debug>1;

  PB_queryLocalCDDB->setFocus();

  my $variable;
  my $value;
  my $tmp;
  my $dtlive;

  if(($localCDDBpath ne "") and ($cddb_id ne "")) {
    if( -e "$localCDDBpath$cddb_id") {
      # Open the local CDDB file for reading
      open( cddbFile, "<$localCDDBpath/$cddb_id" ) or print STDERR "Cannot open $localCDDBpath/$cddb_id for reading: $!\n" and exit(3);

      CB_live->setChecked(0);
      CB_sampler->setChecked(0);

      $numTracks=0;
      while( <cddbFile> ) {
        chomp;
        s/#.*//;
        s/^\s+//;
        s/\s+$//;
        s/\"//g;
        next unless length;
        ($variable, $value) = split( /\s*=\s*/, $_, 2 );
        print "DEBUG: $variable = $value\n" if $debug;

        if( $variable eq "DISCID" ) {
          print "\$cddb_id = $value\n" if $debug;
          if( $value ne $cddb_id ) {
            print STDERR "$localCDDBpath/$cddb_id has not the correct CDDB id ($cddb_id)\n";
            last;
          }
        }
        if( $variable eq "DTITLE" ) {
          ($artist, $title) = split( /\s*\/\s*/, $value, 2 );
          print "\$artist  = $artist\n" if $debug;
          print "\$title   = $title\n" if $debug;
        }
        if( $variable eq "DYEAR" ) {
          $year = int($value);
          print "\$year    = $year\n" if $debug;
        }
        if( $variable eq "DGENRE" ) {
          $genre = $value;
          print "\$genre   = $genre\n" if $debug;
        }

        if( $_ =~ /^TTITLE/  ) {
          $tmp = $_;
          $tmp =~ s/^TTITLE(\d+)\s*=.*/$1/;
          print "\$trackNameList[ $numTracks ]   = $value\n" if $debug;
          $trackNameList  [ $numTracks ] = $value;
#          $trackNameLength[ $numTracks ] = "";
#          $trackNameLevel [ $numTracks ] = "";
#          $trackNamePeak  [ $numTracks ] = "";
          $numTracks++;
        }

        if( $variable eq "DTYPE" ) {
          my @list = split /\s+/, $value;
          foreach (@list) {
            if( $_ eq "LIVE" ) {
              CB_live->setChecked(1);
            } elsif ( $_ eq "SAMPLER" ) {
              CB_sampler->setChecked(1);
            }
          }
        }
      }

      close( cddbFile );
    }

    LE_artist->setText($artist);
    LE_album->setText($title);
    SB_year->setValue($year);

    for( my $i=0; $i<CB_genre->count(); $i++) {
      if( uc($genre) eq uc(CB_genre->text($i))) {
        CB_genre->setCurrentItem($i);
        $genre = CB_genre->text($i);
        CB_genre->setCurrentText(CB_genre->text($i));
      }
    }

    for( my $i=0; $i<$numTracks; $i++) {
      T_tracks->setText( $i, 0, $trackNameList[ $i ] );
      T_tracks->setText( $i, 1, col1name($trackNameList[ $i ]) );
      T_tracks->setText( $i, 2, col2name($trackNameList[ $i ]) );
      T_tracks->showRow( $i );
      T_tracks_2->setText( $i, 0, $trackNameList[ $i ] );
      T_tracks_2->showRow( $i );
      T_tracks_2->selectCells( 0, 0, $numTracks-1, 3 ) if $numTracks>0;
    }
    for( my $i=$numTracks; $i<$maxTracks; $i++) {
      T_tracks->hideRow($i);
      T_tracks_2->hideRow($i);
    }
#    $state = 1;
  }
#  refreshPanel();
  PB_nextTo2->setFocus();

  print "<-- PB_queryLocalCDDB_clicked()\n" if $debug>1;

}

sub PB_saveLocalCDDB_clicked
{

  print "--> PB_saveLocalCDDB_clicked()\n" if $debug>1;

  my $item;

  if(($localCDDBpath ne "") and ($cddb_id ne "")) {
    # Open the local CDDB file for writing
    open( cddbFile, ">$localCDDBpath/$cddb_id" ) or print STDERR "Cannot open $localCDDBpath/$cddb_id for writing: $!\n" and exit(3);

    print cddbFile "DISCID=$cddb_id\n";
    print cddbFile "DTITLE=".$artist." / ".$title."\n";
    print cddbFile "DYEAR =$year\n";
    print cddbFile "DGENRE=$genre\n";
    my $l = "";
    my $s = "";
    $l = "LIVE" if CB_live->isChecked();
    $s = "SAMPLER" if CB_sampler->isChecked();
    print cddbFile "DTYPE =$l $s\n";

    for( my $i=0; $i<$numTracks; $i++ ) {
      $item = $trackNameList[$i];
      if( $item ) {
        $item =~ s/\\\"/\"/g;
        $item =~ s/'\\''/'/g;

        if( $i<10 ) {
          print cddbFile ( "TTITLE$i =$item\n");
        } else {
          print cddbFile ( "TTITLE$i=$item\n");
        }
      }
    }
    close( cddbFile );
  }

  print "<-- PB_saveLocalCDDB_clicked()\n" if $debug>1;

}

sub PB_autoCorrection_clicked
{

  print "--> PB_autoCorrection_clicked()\n" if $debug>1;

  PB_autoCorrection->setFocus();
  $artist = autoCorrect(LE_artist->text());
  LE_artist->setText($artist);

  $title = autoCorrect(LE_album->text());
  # replace (disc 1) with (CD1)
  $title =~ s/[\(\[\{]\s*disc\s*(\d+)\s*[\)\]\}]/\(CD$1\)/ig;
  # replace ( cd 1 ) with (CD1)
  $title =~ s/[\(\[\{]\s*cd\s*(\d+)\s*[\)\]\}]/\(CD$1\)/ig;
  LE_album->setText($title);

  for( my $i=0; $i<$numTracks; $i++ ) {
    $trackNameList[$i] = autoCorrect(T_tracks->text($i, 0));

    if( CB_sampler->isChecked() and (col1name($trackNameList[ $i ]) eq "")) {
      # check for 'a / b' and replace with 'a - b'
      $trackNameList[$i] =~ s/\s+\/\s+/ - /ig;
      # check for 'a: b' and replace with 'a - b'
      $trackNameList[$i] =~ s/\s*:\s+/ - /ig;
    }
    T_tracks->setText( $i, 0, $trackNameList[ $i ] );
    T_tracks->setText( $i, 1, col1name($trackNameList[ $i ]));
    T_tracks->setText( $i, 2, col2name($trackNameList[ $i ]));
    T_tracks_2->setText( $i, 0, $trackNameList[ $i ] );
  }
  PB_nextTo2->setFocus();

  print "<-- PB_autoCorrection_clicked()\n" if $debug>1;

}

sub T_tracks_2_selectionChanged
{

  print "--> T_tracks_2_selectionChanged()\n" if $debug>2;
  my $anythingSelected = 0;
  for( my $i=0; $i<$numTracks; $i++ ) {
    if(T_tracks_2->isRowSelected($i, 0)) {
      $anythingSelected = 1;
      last;
    }
  }
  PB_rip->setEnabled( $anythingSelected != 0 );
  CB_removeSilence->setEnabled( $anythingSelected != 0 );
  CB_checkHiddenTracks->setEnabled( $anythingSelected != 0 );

  print "<-- T_tracks_2_selectionChanged()\n" if $debug>2;

}

sub PB_rip_clicked
{

  print "--> PB_rip_clicked()\n" if $debug>1;

  my $tmpFile = "/tmp/kRip.tmp";
  my $level = "";
  my $peak  = "";
  my $rest  = "";
  my $avgnum = 0;
  my $avglevel = 0;
  my $minlevel = 0;
  my $maxlevel = -99.0;
  my $avgpeak = 0;
  my $minpeak = 0;
  my $maxpeak = -99.0;
  my $file;


  # XXX
  for( my $i=0; $i<$numTracks+$numHiddenTracks; $i++ ) {
    print "DEBUG: Begin rip: \$trackNameList[$i] = \"$trackNameList[$i]\"\n" if $debug;
    print "DEBUG: Begin rip: \$trackNameFile[$i] = \"$trackNameFile[$i]\"\n" if $debug;
  }

  for( my $i=0; $i<$numTracks; $i++ ) {
    if(T_tracks_2->isRowSelected($i, 0)) {
#      use integer;

      # initialize the filename
      $trackNameFile[$i] = "Track".($i+1).".wav";
      # and check, if there are old normalized/cut/splitted wave files
      if( -e "$trackNameFile[$i]") {
        $command = "rm ".$trackNameFile[$i]." 2>/dev/null";
        print "DEBUG: command: $command\n" if $debug;
# XXX only for test
        system( $command );
      }
      if( -e "Track".($i+1)."_01.wav") {
        $command = "rm Track".($i+1)."_01.wav 2>/dev/null";
        print "DEBUG: command: $command\n" if $debug;
# XXX only for test
        system( $command );
      }
      # check for splitted files...
      foreach $file (glob("Track".($i+1)."_??.wav")) {
        $command = "rm $file";
        print "DEBUG: command: $command\n" if $debug;
# XXX only for test
        system( $command );
        # and remove them from the list
        for( my $j=0; $j<$numHiddenTracks; $j++ ) {
          if( $file eq $trackNameFile[$numTracks+$j]) {
            for( my $k=$j+1; $k<$numHiddenTracks; $k++ ) {
              $trackNameFile[$numTracks+$k-1] = $trackNameFile[$numTracks+$k];
            }
            $numHiddenTracks--;
            T_tracks->setText( $numTracks+$j, 0, "" );
            T_tracks->setText( $numTracks+$j, 1, "" );
            T_tracks->setText( $numTracks+$j, 2, "" );
            T_tracks->hideRow( $numTracks+$j );
            T_tracks_2->setText( $numTracks+$j, 0, "" );
            T_tracks_2->setText( $numTracks+$j, 1, "" );
            T_tracks_2->hideRow( $numTracks+$j );
            last;
          }
        }
      }


      # rip the track
      $command = "cdda2wav $ripperDEVICE $dev $ripperOTHER $ripperTRACK ".($i+1)." \"$trackNameFile[$i]\"";
      print "DEBUG: rip command: $command\n" if $debug;

      setCursor(C_wait);
# XXX only for test
      system( $command );
      setCursor(C_arrow);

      if ($? == -1) {
        print "failed to execute: $command\n";
        die;
      } elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
        die;
      } else {
        my $rc = $? >> 8;
        printf "child exited with value $rc\n" if $debug;
#|       0 | no errors encountered, successful operation.                       |
#|       1 | usage or syntax error. cdda2wav got inconsistent arguments.        |
#|       2 | permission (un)set errors. permission changes failed.              |
#|       3 | read errors on the cdrom/burner device encountered.                |
#|       4 | write errors while writing one of the output files encountered.    |
#|       5 | errors with soundcard handling (initialization/write).             |
#|       6 | errors with stat() system call on the read device (cooked ioctl).  |
#|       7 | pipe communication errors encountered (in forked mode).            |
#|       8 | signal handler installation errors encountered.                    |
#|       9 | allocation of shared memory failed (in forked mode).               |
#|      10 | dynamic heap memory allocation failed.                             |
#|      11 | errors on the audio cd medium encountered.                         |
#|      12 | device open error in ioctl handling detected.                      |
#|      13 | race condition in ioctl interface handling detected.               |
#|      14 | error in ioctl() operation encountered.                            |
#|      15 | internal error encountered. Please report back!!!                  |
#|      16 | error in semaphore operation encountered (install / request).      |
#|      17 | could not get the scsi transfer buffer.                            |
#|      18 | could not create pipes for process communication (in forked mode). |
        if( $rc == 0 ) {
        } elsif ( $rc == 3 ) {  # XXX add special handling for some RCs
          die;
        } else {
          die;
        }
      }
    }
  }


  if( CB_removeSilence->isChecked() or CB_checkHiddenTracks->isChecked() or CB_live->isChecked() ) {

    my $wavutilOpts = "-t 2 -g 3000 ";
    if( CB_removeSilence->isChecked() or CB_live->isChecked() ) {
      $wavutilOpts .= "-be ";
    }
    if( CB_checkHiddenTracks->isChecked() or CB_live->isChecked() ) {
      $wavutilOpts .= "-s ";
    }
    if( CB_removeSilence->isChecked() or CB_checkHiddenTracks->isChecked() or CB_live->isChecked() ) {
      if( CB_live->isChecked() ) {
        $wavutilOpts .= "-B 0 -E 0 ";
      } else {
        $wavutilOpts .= "-B 100 -E 400 ";
      }
    }

    for( my $i=0; $i<$numTracks; $i++ ) {
      if(T_tracks_2->isRowSelected($i, 0)) {

        # check for silence, cut silence and split track
        $command = "wavutil $wavutilOpts \"$trackNameFile[$i]\" 2>/dev/null >$tmpFile";
        print "DEBUG: wavutil command: $command\n" if $debug;

        setCursor(C_wait);
# XXX only for test
        system( $command );
# XXX        execThread( $command );
        setCursor(C_arrow);
        if ($? == -1) {
          print "failed to execute: $command\n";
          die;
        } elsif ($? & 127) {
          printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
          die;
        } else {
          my $rc = $? >> 8;
          printf "child exited with value $rc\n" if $debug;
#define RC_INPUT_FILENAME_MISSING               -10
#define RC_VERSION_DISPLAYED                    -11
#define RC_USAGE_DISPLAYED                      -12
#define RC_INVALID_PARAMETER                    -13
#define RC_CANNOT_HANDLE_COMPRESSED_FILES       -20
#define RC_CANNOT_HANDLE_MORE_THAN_2_CHANNELS   -21
#define RC_DATA_CHUNK_BEFORE_FMT_CHUNK          -22
#define RC_FREAD_ERROR                          -23
#define RC_INVALID_START_END_FRAMES             -24
#define RC_READ_UNKNOWN_DATA                    -25
          if( $rc != 0 ) {  # XXX add special handling for some RCs
            die;
          }
        }
# XXX only for debug purposes
        system( "cat $tmpFile" );

        open(TEMP, "<$tmpFile") or print "cannot open temp file $tmpFile\n" and exit(3);
        $_ = " ";

        while( not(eof TEMP) ) {
          $_ = <TEMP>;

          if( $_ =~ /^T\d\d/ ) {
            # T01      13908 -    9997296 (00:00:00.315374-00:03:46.696054) L:00:03:46.380680 F:Track12_01.wav

            my $partNr;
            my $partLen;
            my $partFile;

            $partNr = $_;
            $partNr =~ s/^T(\d{2}).*/$1/;
            $partLen = $_;
            $partLen =~ s/^T.* L:\d{2}:(\d{2}:\d{2}.\d{2})\d{4}.*/$1/;
            $partLen =~ s/^0(.*)/$1/;
            $partFile = $_;
            $partFile =~ s/^T.* F:(.*)\s*/$1/;

            if( $_ =~ /^T01/ ) {
              $trackNameLength[$i] = $partLen;
              $trackNameFile[$i] = $partFile;
              T_tracks_2->setText( $i, 1, $trackNameLength[ $i ] );
            } else {
              my $tmp = $numTracks+$numHiddenTracks;
              $trackNameList[$tmp] = "Hidden Track";
              $trackNameLength[$tmp] = $partLen;
              $trackNameFile[$tmp] = $partFile;
              T_tracks->setText( $tmp, 0, $trackNameList[$tmp] );
              T_tracks->setText( $tmp, 1, col1name($trackNameList[$tmp]) );
              T_tracks->setText( $tmp, 2, col2name($trackNameList[$tmp]) );
              T_tracks->showRow( $tmp );
              T_tracks_2->setText( $tmp, 0, $trackNameList[$tmp] );
              T_tracks_2->setText( $tmp, 1, $trackNameLength[$tmp] );
              T_tracks_2->showRow( $tmp );

              T_tracks_2->addSelection(Qt::TableSelection( $tmp, 0, $tmp, 3));

              print "Found hidden track and added it to the list.\n";
              $numHiddenTracks++;
            }
          }
        }
        close( TEMP );

#print ">>> now, try to get the display refreshed\n";
#T_tracks->repaint();
#T_tracks_2->repaint();

      }
    }
  } else {
    print "DEBUG: do not call wavutil\n" if $debug;
  }

  # XXX
  for( my $i=0; $i<$numTracks+$numHiddenTracks; $i++ ) {
    print "DEBUG: \$trackNameList[$i] = \"$trackNameList[$i]\"\n" if $debug;
    print "DEBUG: \$trackNameFile[$i] = \"$trackNameFile[$i]\"\n" if $debug;
  }


  for( my $i=0; $i<$numTracks+$numHiddenTracks; $i++ ) {
    if(T_tracks_2->isRowSelected($i, 0)) {

      # normalize track
      $command = "normalize -n \"$trackNameFile[$i]\" 2>/dev/null >$tmpFile";
      print "DEBUG: normalize command: $command\n" if $debug;

      setCursor(C_wait);
# XXX only for test
      system( $command );
      setCursor(C_arrow);
      if ($? == -1) {
        print "failed to execute: $command\n";
        die;
      } elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
        die;
      } else {
        my $rc = $? >> 8;
        printf "child exited with value $rc\n" if $debug;
        if( $rc != 0 ) {  # XXX add special handling for some RCs
          die;
        }
      }

      open(TEMP, "<$tmpFile") or print "cannot open temp file $tmpFile\n" and exit(3);
      $_ = " ";

      while( not(eof TEMP) ) {
        $_ = <TEMP>;
        ($level, $peak, $rest) = split( /\s+/, $_, 3 );
        $level =~ s/dBFS//i;
        $peak  =~ s/dBFS//i;
        print "DEBUG: level: $level    peak: $peak\n" if $debug>3;
        $trackNameLevel [ $i ] = $level;
        $trackNamePeak  [ $i ] = $peak;
        $avglevel = $avglevel + $level;
        $minlevel = $level if($level < $minlevel);
        $maxlevel = $level if($level > $maxlevel);
        print "DEBUG: level avg: $avglevel    min: $minlevel   max: $maxlevel\n" if $debug>3;
        $avgpeak  = $avgpeak + $peak;
        $minpeak  = $peak  if($peak  < $minpeak );
        $maxpeak  = $peak  if($peak  > $maxpeak );
        print "DEBUG: peak  avg: $avgpeak    min: $minpeak   max: $maxpeak\n" if $debug>3;
        $avgnum++;

        T_tracks_2->setText( $i, 2, $trackNameLevel[ $i ] );
        T_tracks_2->setText( $i, 3, $trackNamePeak [ $i ] );
      }
      close( TEMP );
    }
  }
  system( "rm $tmpFile" ) if( -e "$tmpFile") ;

  if( $avgnum > 0) {
    $avglevel = int($avglevel / $avgnum * 10000 + 0.5) / 10000.0;
    $avgpeak  = int($avgpeak  / $avgnum * 10000 + 0.5) / 10000.0;
    TL_levelValAvg->setText($avglevel);
    TL_peakValAvg->setText($avgpeak);
  } else {
    TL_levelValAvg->setText("n/a");
    TL_peakValAvg->setText("n/a");
  }
  TL_levelValMin->setText($minlevel);
  TL_levelValMax->setText($maxlevel);
  TL_peakValMin->setText($minpeak);
  TL_peakValMax->setText($maxpeak);

  $state = 2;
  refreshPanel();
  PB_normalizeTo->setFocus();

  print "<-- PB_rip_clicked()\n" if $debug>1;

}

sub PB_normalizeTo_clicked
{

  print "--> PB_normalizeTo_clicked\n" if $debug>1;

  my $tmpFile = "/tmp/kRip.tmp";
  my $level = "";
  my $peak  = "";
  my $rest  = "";
  my $avgnum = 0;
  my $avglevel = 0;
  my $minlevel = 0;
  my $maxlevel = -99.0;
  my $avgpeak = 0;
  my $minpeak = 0;
  my $maxpeak = -99.0;


  $normalizeToLevel = SB_normalizeToLevel->value();
  $normalizeOpts = LE_normalizeOpts->text();

  for( my $i=0; $i<$numTracks+$numHiddenTracks; $i++ ) {
    if(T_tracks_2->isRowSelected($i, 0)) {
      if( (CB_normalizeIgnoreLouder->isChecked()) and
          ($trackNameLevel [ $i ] >= $normalizeToLevel ) ) {
        print "DEBUG: no normalization ( ".$trackNameLevel [ $i ]." >= ".$normalizeToLevel." )\n" if $debug;
      } else {
        $command = "normalize -a ".$normalizeToLevel."dBFS $normalizeOpts \"$trackNameFile[$i]\"";
        print "DEBUG: normalize command: $command\n" if $debug;

        setCursor(C_wait);
        system( $command );
        setCursor(C_arrow);
        if ($? == -1) {
          print "failed to execute: $command\n";
          die;
        } elsif ($? & 127) {
          printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
          die;
        } else {
          my $rc = $? >> 8;
          printf "child exited with value $rc\n" if $debug;
          if( $rc != 0 ) {  # XXX add special handling for some RCs
            die;
          }
        }

        $command = "normalize -n \"$trackNameFile[$i]\" 2>/dev/null >$tmpFile";
        print "DEBUG: normalize command: $command\n" if $debug;

        setCursor(C_wait);
        system( $command );
        setCursor(C_arrow);
        if ($? == -1) {
          print "failed to execute: $command\n";
          die;
        } elsif ($? & 127) {
          printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
          die;
        } else {
          my $rc = $? >> 8;
          printf "child exited with value $rc\n" if $debug;
          if( $rc != 0 ) {  # XXX add special handling for some RCs
            die;
          }
        }

        open(TEMP, "<$tmpFile") or print "cannot open temp file $tmpFile\n" and exit(3);
        $_ = " ";

        while( not(eof TEMP) ) {
          $_ = <TEMP>;
          ($level, $peak, $rest) = split( /\s+/, $_, 3 );
          $level =~ s/dBFS//i;
          $peak  =~ s/dBFS//i;
          $trackNameLevel [ $i ] = $level;
          $trackNamePeak  [ $i ] = $peak;
          $avglevel = $avglevel + $level;
          $minlevel = $level if($level < $minlevel);
          $maxlevel = $level if($level > $maxlevel);
          $avgpeak  = $avgpeak + $peak;
          $minpeak  = $peak  if($peak  < $minpeak );
          $maxpeak  = $peak  if($peak  > $maxpeak );
          $avgnum++;

          T_tracks_2->setText( $i, 2, $trackNameLevel[ $i ] );
          T_tracks_2->setText( $i, 3, $trackNamePeak [ $i ] );
        }
        close( TEMP );
      }
    }
  }
  system( "rm $tmpFile" ) if( -e "$tmpFile") ;

  if( $avgnum > 0) {
    $avglevel = int($avglevel / $avgnum * 10000 + 0.5) / 10000.0;
    $avgpeak  = int($avgpeak  / $avgnum * 10000 + 0.5) / 10000.0;
    TL_levelValAvg->setText($avglevel);
    TL_peakValAvg->setText($avgpeak);
  } else {
    TL_levelValAvg->setText("n/a");
    TL_peakValAvg->setText("n/a");
  }
  TL_levelValMin->setText($minlevel);
  TL_levelValMax->setText($maxlevel);
  TL_peakValMin->setText($minpeak);
  TL_peakValMax->setText($maxpeak);

  PB_nextTo3->setFocus();

  print "<-- PB_normalizeTo_clicked\n" if $debug>1;

}

sub PB_normalizeBy_clicked
{

  print "--> PB_normalizeBy_clicked\n" if $debug>1;

  my $tmpFile = "/tmp/kRip.tmp";
  my $level = "";
  my $peak  = "";
  my $rest  = "";
  my $avgnum = 0;
  my $avglevel = 0;
  my $minlevel = 0;
  my $maxlevel = -99.0;
  my $avgpeak = 0;
  my $minpeak = 0;
  my $maxpeak = -99.0;
  my $tmpNormLevel = 0;


  $normalizeByLevel = SB_normalizeByLevel->value();
  $normalizeOpts = LE_normalizeOpts->text();

  for( my $i=0; $i<$numTracks+$numHiddenTracks; $i++ ) {
    if(T_tracks_2->isRowSelected($i, 0)) {
      $tmpNormLevel = $trackNameLevel [ $i ] + $normalizeByLevel;
      if( $tmpNormLevel >= 0 ) {
        $tmpNormLevel = 0;
      }
      $command = "normalize -a ".$tmpNormLevel."dBFS $normalizeOpts \"$trackNameFile[$i]\"";
      print "DEBUG: normalize command: $command\n" if $debug;

      setCursor(C_wait);
      system( $command );
      setCursor(C_arrow);
      if ($? == -1) {
        print "failed to execute: $command\n";
        die;
      } elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
        die;
      } else {
        my $rc = $? >> 8;
        printf "child exited with value $rc\n" if $debug;
        if( $rc != 0 ) {  # XXX add special handling for some RCs
          die;
        }
      }

      $command = "normalize -n \"$trackNameFile[$i]\" 2>/dev/null >$tmpFile";
      print "DEBUG: normalize command: $command\n" if $debug;

      setCursor(C_wait);
      system( $command );
      setCursor(C_arrow);
      if ($? == -1) {
        print "failed to execute: $command\n";
        die;
      } elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
        die;
      } else {
        my $rc = $? >> 8;
        printf "child exited with value $rc\n" if $debug;
        if( $rc != 0 ) {  # XXX add special handling for some RCs
          die;
        }
      }

      open(TEMP, "<$tmpFile") or print "cannot open temp file $tmpFile\n" and exit(3);
      $_ = " ";

      while( not(eof TEMP) ) {
          $_ = <TEMP>;
          ($level, $peak, $rest) = split( /\s+/, $_, 3 );
          $level =~ s/dBFS//i;
          $peak  =~ s/dBFS//i;
          $trackNameLevel [ $i ] = $level;
          $trackNamePeak  [ $i ] = $peak;
          $avglevel = $avglevel + $level;
          $minlevel = $level if($level < $minlevel);
          $maxlevel = $level if($level > $maxlevel);
          $avgpeak  = $avgpeak + $peak;
          $minpeak  = $peak  if($peak  < $minpeak );
          $maxpeak  = $peak  if($peak  > $maxpeak );
          $avgnum++;

          T_tracks_2->setText( $i, 2, $trackNameLevel[ $i ] );
          T_tracks_2->setText( $i, 3, $trackNamePeak [ $i ] );
      }
      close( TEMP );
    }
  }
  system( "rm $tmpFile" ) if( -e "$tmpFile") ;

  if( $avgnum > 0) {
    $avglevel = int($avglevel / $avgnum * 10000 + 0.5) / 10000.0;
    $avgpeak  = int($avgpeak  / $avgnum * 10000 + 0.5) / 10000.0;
    TL_levelValAvg->setText($avglevel);
    TL_peakValAvg->setText($avgpeak);
  } else {
    TL_levelValAvg->setText("n/a");
    TL_peakValAvg->setText("n/a");
  }
  TL_levelValMin->setText($minlevel);
  TL_levelValMax->setText($maxlevel);
  TL_peakValMin->setText($minpeak);
  TL_peakValMax->setText($maxpeak);

  PB_nextTo3->setFocus();

  print "<-- PB_normalizeBy_clicked\n" if $debug>1;

}

sub PB_jobNew_clicked
{

  print "--> PB_jobNew_clicked()\n" if $debug>1;

  $countItems = LB_jobs->count();
  $currentItem = LB_jobs->currentItem();

  for( my $i=$countItems; $i>$currentItem+1; $i--) {
    $AoA[$i] = $AoA[$i-1];
  }

  $AoA[$currentItem+1][$JOB_NAME     ] = "new job";
  $AoA[$currentItem+1][$JOB_ENABLED  ] = 1;
  $AoA[$currentItem+1][$JOB_ENCODER  ] = 0;
  $AoA[$currentItem+1][$JOB_ENCOPTS  ] = "-h --preset standard";
  $AoA[$currentItem+1][$JOB_ENCFORMAT] = "\%A/\%Y_\%B/\%N_\%T";
  $AoA[$currentItem+1][$JOB_TAG      ] = 1;
  $AoA[$currentItem+1][$JOB_TAGCOVER ] = 1;
  $AoA[$currentItem+1][$JOB_COVERJPG ] = 1;
  $AoA[$currentItem+1][$JOB_COVERBMP ] = 1;
  $AoA[$currentItem+1][$JOB_ALBUMTXT ] = 1;

  LB_jobs->insertItem($AoA[$currentItem+1][$JOB_NAME], $currentItem+1);
  LB_jobs->setSelected($currentItem+1, 1);

  refreshJobs();

  print "<-- PB_jobNew_clicked()\n\n\n" if $debug>1;

}

sub PB_jobDelete_clicked
{

  print "--> PB_jobDelete_clicked()\n" if $debug>1;

  $countItems = LB_jobs->count();
  $currentItem = LB_jobs->currentItem();

  $currItemLast = -1;

  for( my $i=$currentItem; $i<$countItems-1; $i++) {
    $AoA[$i] = $AoA[$i+1];
  }

  LB_jobs->removeItem($currentItem);

  refreshJobs();

  print "<-- PB_jobDelete_clicked()\n\n\n" if $debug>1;

}

sub PB_jobMoveUp_clicked
{

  print "--> PB_jobMoveUp_clicked()\n" if $debug>1;

  $currentItem = LB_jobs->currentItem();
  if($currentItem > 0) {
    $currItemLast = $currentItem;
    saveJobProperties();

    $AoA[$numJobs]       = $AoA[$currentItem-1];
    $AoA[$currentItem-1] = $AoA[$currentItem];
    $AoA[$currentItem]   = $AoA[$numJobs];
    $currentItem--;
    $currItemLast = $currentItem;

    LB_jobs->clear();
    for( my $i=0; $i<$numJobs; $i++) {
      LB_jobs->insertItem($AoA[$i][0], $i);
    }
    LB_jobs->setSelected($currentItem, 1);
  }

  print "<-- PB_jobMoveUp_clicked()\n" if $debug>1;

}

sub PB_jobMoveDown_clicked
{

  print "--> PB_jobMoveDown_clicked()\n" if $debug>1;

  $currentItem = LB_jobs->currentItem();
  if($currentItem < $numJobs-1) {
    $currItemLast = $currentItem;
    saveJobProperties();

    $AoA[$numJobs]       = $AoA[$currentItem+1];
    $AoA[$currentItem+1] = $AoA[$currentItem];
    $AoA[$currentItem]   = $AoA[$numJobs];
    $currentItem++;
    $currItemLast = $currentItem;

    LB_jobs->clear();
    for( my $i=0; $i<$numJobs; $i++) {
      LB_jobs->insertItem($AoA[$i][$JOB_NAME], $i);
    }
    LB_jobs->setSelected($currentItem, 1);
  }

  print "<-- PB_jobMoveDown_clicked()\n" if $debug>1;

}

sub LB_jobs_currentChanged
{

  print "--> LB_jobs_currentChanged()\n" if $debug>2;

  if($initialFill == 0) {

    $currItemLast = $currentItem;
    print "    currentItem : $currentItem\n" if $debug>4;
    print "    currItemLast: $currItemLast\n" if $debug>4;
    $countItems = LB_jobs->count();
    $currentItem = LB_jobs->currentItem();
    print "    currentItem2: $currentItem\n" if $debug>4;

    if(($currItemLast > -1) and ($currentItem != $currItemLast)) {

      saveJobProperties();

      my $jobName = LE_jobName->text();
      if(LB_jobs->text($currItemLast) ne $jobName) {
        LB_jobs->changeItem($jobName, $currItemLast);
      }
    }

    refreshJobs();

    $currItemLast = $currentItem;
  }

  print "<-- LB_jobs_currentChanged()\n\n" if $debug>2;

}

sub LB_jobTag_stateChanged
{

  print "--> LB_jobTag_stateChanged()\n" if $debug>2;

  if($initialFill == 0) {
    print "    currentItem : $currentItem\n" if $debug>4;
    my $jobTagEnabled = 0;
    if( CB_jobTag->isChecked() ) {
      $jobTagEnabled = 1;
    }
    $AoA[$currentItem][$JOB_TAG] = $jobTagEnabled;
    $currItemLast = $currentItem;
    saveJobProperties();

    refreshJobs();
  }

  print "<-- LB_jobTag_stateChanged()\n" if $debug>2;

}

sub PB_jobsRun_clicked
{

  print "--> PB_jobsRun_clicked()\n" if $debug>1;

  # save field data in structure
  $currItemLast = $currentItem;
  saveJobProperties();

  # read data first
  $artist = LE_artist->text();
  $title = LE_album->text();
  $year = SB_year->value();
  $genre = CB_genre->currentText();
  for( my $i=0; $i<$numTracks; $i++ ) {
    $trackNameList[$i] = T_tracks->text($i, 0);
  }


  # create local CDDB entry...
  PB_saveLocalCDDB_clicked();

  # create 'artist - album.jpg' cover
  my $filename = "";
  if($coverfilename ne "") {
    $filename = $coverPath."/".$artist." - ".$title.".jpg";
    $filename =~ s/\s*\(CD\d+\)//g;
    my $image = Image::Magick->new;
    my $x = $image->Read($coverfilename);
    warn "$x" if "$x";
    my ($width,$height) = $image->Get("width","height");
    if( ( $width == 120 ) and ( $height == 120 ) ) {
      print "DEBUG: cover already 120x120. Do NOT scale !\n" if $debug;
      if( $coverfilename ne $filename ) {
        system( "cp \"$coverfilename\" \"$filename\"" );
      }
    } elsif ( $width > 1 ) {
      print "DEBUG: resize cover to 120x120\n" if $debug;
      # write 120x120 color 'artist - album.jpg'
      open(IMAGE, ">$filename");
      $image->Resize(geometry=>"120x120",
                     width=>"120", height=>"120",
                     filter=>"Cubic",
                     blur=>"0.5");
      $image->Write(file=>\*IMAGE, filename=>"$filename");
      close(IMAGE);
    } else {
      $filename = "";
    }
  }
  # create cover.jpg if at least one job requires it...
  for( my $job=0; $job<$numJobs; $job++ ) {
    if(( $AoA[$job][$JOB_ENABLED ] != 0 ) and
       (($AoA[$job][$JOB_TAGCOVER] != 0) or
        ($AoA[$job][$JOB_COVERJPG] != 0)) and
       ( $filename ne "")) {
      system( "cp \"$filename\" \"$pwd/$COVERJPGFILE\"" );
      last;
    }
  }
  # create cover.bmp if at least one job requires it...
  for( my $job=0; $job<$numJobs; $job++ ) {
    if(( $AoA[$job][$JOB_ENABLED ] != 0 ) and
       ( $AoA[$job][$JOB_COVERBMP] != 0 ) and
       ( $filename ne "" )) {
      my $image = Image::Magick->new;
      my $x = $image->Read($coverfilename);
      warn "$x" if "$x";
      my ($width,$height) = $image->Get("width","height");
      if ( $width > 1 ) {

        # write 48x48 grayscale $COVERTMP
        open(IMAGE, ">$pwd/$COVERTMP");
        $image->Resize(geometry=>"48x48",
                       width=>"48", height=>"48",
                       filter=>"Cubic",
                       blur=>"0.5");
        $image->Quantize(colorspace=>"Gray");
        $image->Write(file=>\*IMAGE, filename=>$COVERTMP);
        close(IMAGE);

        # convert $COVERTMP to $COVERBMPFILE
        setCursor(C_wait);
        system( "jpegtopnm $pwd/$COVERTMP      2\>\/dev\/null \| ".
                "pnmquant \'16\'               2\>\/dev\/null \| ".
                "ppmtobmp \>$pwd/$COVERBMPFILE 2\>\/dev\/null" );
        system( "rm $pwd/$COVERTMP" ) if( -e "$pwd/$COVERTMP" );
        setCursor(C_arrow);
        last;
      }
    }
  }


  my $targetDir   = "";
  my $targetFile  = "";
  my $extension   = "";

  my $tag_cddb_id;
  my $tag_artist;
  my $tag_title;
  my $tag_year;
  my $tag_genre;
  my $tag_track;
  my $tag_song;

  # now do the encoding
  for( my $job=0; $job<$numJobs; $job++ ) {
    if( $AoA[$job][$JOB_ENABLED ] != 0 ) {

      print ">>> run Job: $AoA[$job][$JOB_NAME]\n";

      if( $AoA[$job][$JOB_ENCODER] == 0 ) {
        # lame
        $extension = "mp3";
      } else {
        # oggenc
        $extension = "ogg";
      }


      for( my $track=1; $track<=$numTracks+$numHiddenTracks; $track++ ) {
        if(T_tracks_2->isRowSelected(($track-1), 0)) {
          $tag_cddb_id = $cddb_id;
          $tag_cddb_id =~ s/\"/\\\"/g;
          if( !CB_sampler->isChecked() ) {
            $tag_artist = $artist;
            $tag_artist =~ s/\"/\\\"/g;
            $tag_song = $trackNameList[$track-1];
            $tag_song =~ s/\"/\\\"/g;
          } else {
            $tag_artist = T_tracks->text(($track-1), 1);
            $tag_artist =~ s/\"/\\\"/g;
            $tag_song = T_tracks->text(($track-1), 2);
            $tag_song =~ s/\"/\\\"/g;
          }
          $tag_title = $title;
          $tag_title =~ s/\"/\\\"/g;
          $tag_year = $year;
          $tag_year =~ s/\"/\\\"/g;
          $tag_genre = $genre;
          $tag_genre =~ s/\"/\\\"/g;
          $tag_track = $track;
          $tag_track =~ s/\"/\\\"/g;



          $targetDir = "";
          $targetFile = "";
          my $tmp;

          if( $AoA[$job][$JOB_ENCFORMAT] ) {
            ($targetDir, $targetFile) = $AoA[$job][$JOB_ENCFORMAT] =~ m/(.*[^\\]\/)(.*)$/;

            $targetDir =~ s/\%C/$cddb_id/g;
            $targetDir =~ s/\%A/$artist/g;
            $targetDir =~ s/\%B/$title/g;
            $targetDir =~ s/\%Y/$year/g;
            $targetDir =~ s/\%G/$genre/g;
            if( $track<10 ) {
              $targetDir =~ s/\%N/0$track/g;
            } else {
              $targetDir =~ s/\%N/$track/g;
            }
#           if( !CB_sampler->isChecked() ) {
              $tmp = $trackNameList[$track-1];
              $targetDir =~ s/\%T/$tmp/g;
#           } else {
#             $tmp = T_tracks->text(($track-1), 2);
#             $targetDir =~ s/\%T/$tmp/g;
#           }


            $targetFile =~ s/\%C/$cddb_id/g;
            $targetFile =~ s/\%A/$artist/g;
            $targetFile =~ s/\%B/$title/g;
            $targetFile =~ s/\%Y/$year/g;
            $targetFile =~ s/\%G/$genre/g;
            if( $track<10 ) {
              $targetFile =~ s/\%N/0$track/g;
            } else {
              $targetFile =~ s/\%N/$track/g;
            }
#           if( !CB_sampler->isChecked() ) {
              $tmp = $trackNameList[$track-1];
              $targetFile =~ s/\%T/$tmp/g;
#           } else {
#             $tmp = T_tracks->text(($track-1), 2);
#             $targetFile =~ s/\%T/$tmp/g;
#           }
            $targetFile = $targetFile.".".$extension;
          }

          if( $targetFile eq "" ) {
            if( $track<10 ) {
              $targetFile = "Track_0".$track.".".$extension;
            } else {
              $targetFile = "Track_".$track.".".$extension;
            }
          }

#         $encFilename =~ s/\/\//\//g;
#         $encFilename =~ s/\/\//\//g;

          if(  not( -e "$pwd/$targetDir" ) ) {
            print "DEBUG: try to create dir $pwd/$targetDir\n" if $debug;
            system( "mkdir -p \"$pwd/$targetDir\"" );

            if(  not( -e "$pwd/$targetDir" ) or not( -w "$pwd/$targetDir" )  ) {
              print STDERR "Either the output directory $pwd/$targetDir cannot be created\n";
              $targetDir = "";
              print STDERR "or it is not writable. Output will be to $pwd/$targetDir instead.\n";
              print STDERR "Likely there is a file with the name $pwd/$targetDir already in $pwd.\n";
            }
          }

          print "DEBUG: File: $targetFile\n" if $debug;
          print "DEBUG: Path: $pwd/$targetDir\n" if $debug;

          if( $AoA[$job][$JOB_COVERJPG] != 0 ) {
            if( (! -e "$pwd/$targetDir/$COVERJPGFILE") and (-e "$pwd/$COVERJPGFILE") ) {
              system( "cp \"$pwd/$COVERJPGFILE\" \"$pwd/$targetDir/$COVERJPGFILE\"" );
            }
          }
          if( $AoA[$job][$JOB_COVERBMP] != 0 ) {
            if( (! -e "$pwd/$targetDir/$COVERBMPFILE") and (-e "$pwd/$COVERBMPFILE") ) {
              system( "cp \"$pwd/$COVERBMPFILE\" \"$pwd/$targetDir/$COVERBMPFILE\"" );
            }
          }
          if( $AoA[$job][$JOB_ALBUMTXT] != 0 ) {
            if( (! -e "$pwd/$targetDir/$ALBUMTXTFILE") and (-e "$localCDDBpath/$cddb_id") ) {
              system( "cp \"$localCDDBpath/$cddb_id\" \"$pwd/$targetDir/$ALBUMTXTFILE\"" );
            }
          }

          # remember the original filename
          my $file = "$pwd/$targetDir/$targetFile";
          $targetFile =~ s/\"/\\\"/g;

          if( $AoA[$job][$JOB_ENCODER] == 0 ) {
            # lame
            $command = "lame $AoA[$job][$JOB_ENCOPTS] \"$trackNameFile[$track-1]\" \"$pwd/$targetDir/$targetFile\"";
          } else {
            # oggenc
            my $tagstring = "";
            my $tagentry = "";
            if( $AoA[$job][$JOB_TAG] != 0 ) {
              $tagstring = " -c \"discid=$tag_cddb_id\"".
                           " -a \"$tag_artist\"" .
                           " -l \"$tag_title\"" .
                           " -d \"$tag_year\"" .
                           " -G \"$tag_genre\"" .
#                          " -c \"comment=$comment\"" .
                           " -N \"$tag_track\"" .
                           " -t \"$tag_song\"";
            }
            $command = "oggenc $AoA[$job][$JOB_ENCOPTS] $tagstring -o \"$pwd/$targetDir/$targetFile\" \"$trackNameFile[$track-1]\"";
          }
          print "DEBUG: encode command: $command\n" if $debug;

          setCursor(C_wait);
          system( $command );
          setCursor(C_arrow);
          if ($? == -1) {
            print "failed to execute: $command\n";
            die;
          } elsif ($? & 127) {
            printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
            die;
          } else {
            my $rc = $? >> 8;
            printf "child exited with value $rc\n" if $debug;
            if( $rc != 0 ) {  # XXX add special handling for some RCs
              die;
            }
          }

          # tag LAME encoded mp3 file
          if( ($AoA[$job][$JOB_ENCODER] == 0 ) and ( $AoA[$job][$JOB_TAG] != 0 )) {

            print "DEBUG: tagging \"$pwd/$targetDir/$targetFile\"\n" if $debug;
            print "DEBUG: \$song     :\"$tag_song\"\n".
                  "       \$artist   :\"$tag_artist\"\n".
                  "       \$title    :\"$tag_title\"\n".
                  "       \$year     :\"$tag_year\"\n".
#                 "       \$comment  :\"$comment\"\n".
                  "       \$genre    :\"$tag_genre\"\n".
                  "       \$track    :\"$tag_track\"\n" if $debug;

            my $mp3totag = MP3::Tag->new($file);

            # remove any existing ID3 tags first
            $mp3totag->get_tags();
            if (exists $mp3totag->{ID3v1}) {
              $mp3totag->{ID3v1}->remove_tag();
            }
            if (exists $mp3totag->{ID3v2}) {
              $mp3totag->{ID3v2}->remove_tag();
            }

            # create a new id3v1 tag
            my $id3v1 = $mp3totag->new_tag("ID3v1");
            $id3v1->title($tag_song);
            $id3v1->artist($tag_artist);
            $id3v1->album($tag_title);
            $id3v1->year($tag_year);
            $id3v1->genre($tag_genre);
#           $id3v1->comment($comment);
            $id3v1->track($tag_track);
            $id3v1->write_tag();

            # create a new id3v2 tag
            my $id3v2 = $mp3totag->new_tag("ID3v2");
            if ($tag_song) {
              $id3v2->add_frame("TIT2", $tag_song);
            }
            if ($tag_artist) {
              $id3v2->add_frame("TPE1", $tag_artist);
            }
            if ($tag_title) {
              $id3v2->add_frame("TALB", $tag_title);
            }
            if ($tag_year) {
              $id3v2->add_frame("TYER", $tag_year);
            }
            if ($tag_genre) {
              $id3v2->add_frame("TCON", $tag_genre);
            }
#           if ($comment) {
#             $id3v2->add_frame("COMM", $comment);
#           }
            if ($tag_track) {
              $id3v2->add_frame("TRCK", $tag_track);
            }

            if(($AoA[$job][$JOB_TAGCOVER] != 0 ) and
               ($filename ne "") and
               (-e "$pwd/$COVERJPGFILE")) {
              my $imagefile = "$pwd/$COVERJPGFILE";
              my $coverimage = new Image::Magick;
              if (my $x = $coverimage->Read($imagefile)) {
                print "Couldn't read the image $imagefile: $x\n";
              } else {
                my $imagedata = $coverimage->ImageToBlob(magick => "jpg" );
                $id3v2->add_frame("APIC", 0, "image/jpeg", "\x03", "", $imagedata);
              }
            }
            $id3v2->write_tag();
            $mp3totag->close();
          }

        } # if track selected
      } # for selected tracks
    } # if job enabled
  } # for all $jobs


  TW_tabs->setCurrentPage(0);
  initValues();

  print "<-- PB_jobsRun_clicked()\n" if $debug>1;

}

sub PB_saveSettings_clicked
{

  print "--> PB_saveSettings_clicked()\n" if $debug>1;

  if($currItemLast > -1)  {
    saveJobProperties();
  }

  $actDevice     = CB_device->currentItem();
  $defaultYear   = SB_year->value();
  $defaultGenre  = CB_genre->currentItem();
  $removeSilence = CB_removeSilence->isChecked() ? 1:0;
  $checkHiddenTracks = CB_checkHiddenTracks->isChecked() ? 1:0;
  $normalizeToLevel  = SB_normalizeToLevel->value();
  $normalizeIgnL = CB_normalizeIgnoreLouder->isChecked() ? 1:0;
  $normalizeByLevel  = SB_normalizeByLevel->value();
  $normalizeOpts = LE_normalizeOpts->text();
  $numJobs       = LB_jobs->count();

  rcFileWrite();
  print "<-- PB_saveSettings_clicked()\n" if $debug>1;

}

sub PB_nextTab_clicked
{

  my $currPage = TW_tabs->currentPageIndex();
  if( $currPage+1 < TW_tabs->count() ) {
    TW_tabs->setCurrentPage($currPage+1);
    if($currPage == 0) {
      # switch to tab 2
      PB_rip->setFocus();
    } else {
      # switch to tab 3
      PB_jobsRun->setFocus();
    }
  }

}

sub PB_prevTab_clicked
{

  my $currPage = TW_tabs->currentPageIndex();
  if( $currPage > 0 ) {
    TW_tabs->setCurrentPage($currPage-1);
    if($currPage == 1) {
      # switch to tab 1
      PB_scanCD->setFocus();
    } else {
      # switch to tab 2
      PB_rip->setFocus();
    }
  }

}

sub readFieldData
{

  print "--> readFieldData()\n" if $debug>3;

  if( LE_artist->hasFocus() ) {
    $artist = LE_artist->text();
  }
  if( LE_album->hasFocus() ) {
    $title = LE_album->text();
  }
  if( SB_year->hasFocus() ) {
    $year = SB_year->value();
  }
  if( CB_genre->hasFocus() ) {
    $genre = CB_genre->currentText();
  }

  PB_searchCover->setEnabled(($state > 0) and ($artist ne "") and ($title ne ""));
  CB_amazonLocale->setEnabled(($state > 0) and ($artist ne "") and ($title ne ""));

  print "<-- readFieldData()\n" if $debug>3;

}

1;


package main;

use Qt;
use kRip;

my $a = Qt::Application(\@ARGV);
my $w = kRip;
$a->setMainWidget($w);
$w->show;
exit $a->exec;
