/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <iostream>

#ifdef XSID_WB_DEBUG
#include <iomanip>
#include <iomanip>
using namespace std;
#endif

#ifdef XSID_HAVE_NOTHROW
#include <new>
#endif
using namespace std;

#include <qcheckbox.h>
#include <qlabel.h>
#include <qlcdnumber.h>
#include <qslider.h>
#include <qspinbox.h>

#ifdef XSID_HAVE_TSID
#include <tsid/tsid.h>
#endif

#ifdef XSID_HAVE_LIRC
#include <unistd.h>
#include <fcntl.h>
#include <qsocketnotifier.h>
#endif

#include "MainDialog.h"
#include "AudioDialog.h"
#include "EmuDialog.h"
#include "FilterDialog.h"
#include "HistoryDialog.h"
#include "HVSC_Dialog.h"
#include "MixerDialog.h"
#include "PlaylistDialog.h"
#include "PlaylistEditDialog.h"
#include "PlaylistOptDialog.h"
#include "StilDialog.h"
#include "StilDialog.h"

#include "AudioDrivers.h"
#include "ConfigC.h"
#include "ConfigFile.h"
#include "HVSC_Config.h"
#include "filenames.h"
#include "Playlist.h"
#include "PlaylistCheck.h"
#include "Player.h"
#include "wrapper/SongLength.h"
#include "wrapper/EmuWrapper.h"
#include "wrapper/SidTuneWrapper.h"

#include "widgets/PicButton.h"
#include "widgets/TimeLCD.h"

#include "images/prev.xpm"
#include "images/stop.xpm"
#include "images/play.xpm"
#include "images/pause.xpm"
#include "images/ff.xpm"
#include "images/next.xpm"

#include "images/stop_p.xpm"
#include "images/pause_p.xpm"
#include "images/ff_p.xpm"

#include "images/prev_l.xpm"
#include "images/stop_l.xpm"
#include "images/play_l.xpm"
#include "images/pause_l.xpm"
#include "images/ff_l.xpm"
#include "images/next_l.xpm"

#include "images/prev_l_p.xpm"
#include "images/stop_l_p.xpm"
#include "images/play_l_p.xpm"
#include "images/next_l_p.xpm"

// --------------------------------------------------------------------------

MainDialog::MainDialog(QWidget* parent, const char* name)
: MainDialogData(parent,name)
{
    // Init pixmap pointers.
    
    prevPic = new QPixmap((const char**)prev_xpm);
    stopPic = new QPixmap((const char**)stop_xpm);
    playPic = new QPixmap((const char**)play_xpm);
    pausePic = new QPixmap((const char**)pause_xpm);
    ffPic = new QPixmap((const char**)ff_xpm);
    nextPic = new QPixmap((const char**)next_xpm);

    stop_pPic = new QPixmap((const char**)stop_p_xpm);
    pause_pPic = new QPixmap((const char**)pause_p_xpm);
    ff_pPic = new QPixmap((const char**)ff_p_xpm);
    
    prev_lPic = new QPixmap((const char**)prev_l_xpm);
    stop_lPic = new QPixmap((const char**)stop_l_xpm);
    play_lPic = new QPixmap((const char**)play_l_xpm);
    pause_lPic = new QPixmap((const char**)pause_l_xpm);
    ff_lPic = new QPixmap((const char**)ff_l_xpm);
    next_lPic = new QPixmap((const char**)next_l_xpm);
    
    prev_l_pPic = new QPixmap((const char**)prev_l_p_xpm);
    stop_l_pPic = new QPixmap((const char**)stop_l_p_xpm);
    play_l_pPic = new QPixmap((const char**)play_l_p_xpm);
    next_l_pPic = new QPixmap((const char**)next_l_p_xpm);

    // Finish dialog setup.
    
    setMinimumSize(width(),99);
    setIcon(*mainIcon);

    QFont infoField_font( infoField->font() );
    infoField_font.setFixedPitch( true );
//    infoField_font.setFamily( "Monospace" );
//    infoField_font.setStyleHint( QFont::Courier );
//    infoField_font.setPointSize( 10 );
    infoField->setFont( infoField_font ); 
    infoField->setText("");

    QPopupMenu *fileMenu = new QPopupMenu(this);
    Q_CHECK_PTR( fileMenu );
    fileMenu->insertItem( "&Open file(s)", this, SLOT(openFile()), CTRL+Key_O );
    fileMenu->insertItem( "&Save as...", this, SLOT(saveAs()), CTRL+Key_S );
    fileMenu->insertSeparator();
    fileMenu->insertItem( "Exit", this, SLOT(quit()), CTRL+Key_Q );

    QPopupMenu *configMenu = new QPopupMenu(this);
    Q_CHECK_PTR( configMenu );
    configMenu->insertItem( "Audio", this, SLOT(openAudioDialog()) );
    configMenu->insertItem( "Emulator", this, SLOT(openEmulatorDialog()) );
#ifndef SID_WITH_SIDPLAY2
    configMenu->insertItem( "SID filter", this, SLOT(openFilterDialog()) );
#endif
    configMenu->insertItem( "HVSC", this, SLOT(openHVSC_Dialog()) );

    QPopupMenu *playlistAddMenu = new QPopupMenu(this);
    Q_CHECK_PTR( playlistAddMenu );
    playlistAddMenu->insertItem( "Directory", this, SLOT(addDirToPlaylist()) );
    playlistAddMenu->insertItem( "Tree", this, SLOT(addTreeToPlaylist()) );
    playlistAddMenu->insertItem( "Hotlist", this, SLOT(addHotlistToPlaylist()) );

    QPopupMenu *playlistMenu = new QPopupMenu(this);
    Q_CHECK_PTR( playlistMenu );
    playlistMenu->insertItem( "&View", this, SLOT(openPlaylistDialog()), CTRL+Key_V );
    playlistMenu->insertItem( "&Edit", this, SLOT(openPlaylistEditDialog()), CTRL+Key_E );
    playlistMenu->insertSeparator();
    playlistMenu->insertItem( "&Load...", this, SLOT(loadPlaylist()), CTRL+Key_L );
    playlistMenu->insertItem( "Save as...", this, SLOT(savePlaylistAs()) );
    playlistMenu->insertSeparator();
    playlistMenu->insertItem( "&Add tune", this, SLOT(addTuneToPlaylist()), CTRL+Key_A );
    playlistMenu->insertItem( "Add...", playlistAddMenu );
    playlistMenu->insertSeparator();
    playlistMenu->insertItem( "Update", this, SLOT(updatePlaylist()) );
    playlistMenu->insertSeparator();
    playlistMenu->insertItem( "Options", this, SLOT(openPlaylistOptions()) );

    QPopupMenu *hotlistMenu = new QPopupMenu(this);
    Q_CHECK_PTR( hotlistMenu );
    hotlistMenu->insertItem( "&Mark tune", this, SLOT(markTune()), CTRL+Key_M );
    hotlistMenu->insertItem( "Mark all", this, SLOT(markPlaylist()) );
    hotlistMenu->insertSeparator();
    hotlistMenu->insertItem( "Save as...", this, SLOT(saveHotlist()) );

    extraMenu = new QPopupMenu(this);
    Q_CHECK_PTR( extraMenu );
    extraMenu->setCheckable(true);
    stilID = extraMenu->insertItem( "STIL view", this, SLOT(toggleStilDialog()) );
    songLenDbID = extraMenu->insertItem( "Songlen DB", this, SLOT(toggleSongLengthDB()) );
    extraMenu->insertItem( "History", this, SLOT(openHistoryDialog()) );
#ifndef SID_WITH_SIDPLAY2
    extraMenu->insertItem( "Mixer", this, SLOT(openMixerDialog()) );
#endif
    
    QPopupMenu *helpMenu = new QPopupMenu(this);
    Q_CHECK_PTR( helpMenu );
    helpMenu->insertItem( "SIDPLAY", this, SLOT(about()), CTRL+Key_H );
    helpMenu->insertItem( "Qt", this, SLOT(aboutQt()) );

    menuBar = new QMenuBar(this);
    Q_CHECK_PTR( menuBar );
    menuBar->insertItem( "&File", fileMenu );
    menuBar->insertItem( "&Config", configMenu );
    menuBar->insertItem( "&Playlist", playlistMenu );
    menuBar->insertItem( "&Hotlist", hotlistMenu );
    menuBar->insertItem( "&Extra", extraMenu );
    menuBar->insertSeparator();
    menuBar->insertItem( "&About", helpMenu );

    timeLCD->setSmallDecimalPoint( FALSE );
    timeLCD->setNumDigits( 5 );
    timeLCD->setMode( QLCDNumber::DEC );
    timeLCD->setSegmentStyle( QLCDNumber::Filled );
    timeLCD->setFrameShadow( QLCDNumber::Sunken );
    timeLCD->setFrameShape( QLCDNumber::Panel );

#ifdef XSID_HAVE_TSID
    myTSID = new TSID;  // Time SID Manager
#endif
    
    // The currently active file name.
    sidFileNameFull = sidFileNameHVSC = "";

    hotlist = new Playlist;

    myFileDlg = 0;  // do that when we get access to the config file
    
    myAudioDlg = new AudioDialog(this);
    myEmuDlg = new EmuDialog(this);
    myFilterDlg = new FilterDialog(this);
    myMixerDlg = new MixerDialog(this);
    myHVSC_Dlg = new HVSC_Dialog(this);
    myHistoryDlg = new HistoryDialog(this);
    myPlaylistDlg = new PlaylistDialog(this);
    myPlaylistEditDlg = new PlaylistEditDialog(this);
    
    myStilDlg = new StilDialog();
    
    myFilterDlg->connect(myEmuDlg,SIGNAL(changed(const emuConfig&)),myFilterDlg,SLOT(receiveConfig(const emuConfig&)));
    myEmuDlg->connect(myFilterDlg,SIGNAL(changed(const emuConfig&)),myEmuDlg,SLOT(receiveConfig(const emuConfig&)));
    connect(myEmuDlg,SIGNAL(changed(const emuConfig&)),this,SLOT(receiveEmuConfig(const emuConfig&)));
    connect(myFilterDlg,SIGNAL(changed(const emuConfig&)),this,SLOT(receiveEmuConfig(const emuConfig&)));
    connect(myHVSC_Dlg,SIGNAL(changed(const HVSC_Config&)),this,SLOT(receiveHVSC_Config(const HVSC_Config&)));
    connect(myMixerDlg,SIGNAL(changed(const MixerConfig&)),this,SLOT(receiveMixerConfig(const MixerConfig&)));
    connect(myHistoryDlg,SIGNAL(playHistoryItem(const QString&)),this,SLOT(playHistoryFile(const QString&)));

    playerButtonsOff();
    subSongSlider->setFocus();

#ifdef XSID_HAVE_LIRC
  // set up device
  lircfh=lirc_init("xsidplay", 1);
  if(lircfh < 0)
    {
      qWarning("could not init LIRC");
    }
  else
    {
      if(fcntl(lircfh, F_SETFL, O_NONBLOCK) < 0)
	qFatal("could not set O_NONBLOCK for LIRC");
      else
	{
	  if(lirc_readconfig(NULL /*configfile*/, &lircConfig, NULL) < 0)
	    qFatal("could not read LIRC config");
	  else
	    {
	      QSocketNotifier *s=new QSocketNotifier(lircfh, QSocketNotifier::Read, this);
	      Q_CHECK_PTR(s);
	      connect(s, SIGNAL(activated(int)), this, SLOT(lircEvent(int)));
	    }
	}
    } 
#endif

}

MainDialog::~MainDialog()
{
    delete hotlist;
    
#ifdef XSID_HAVE_TSID
    delete myTSID;
#endif
    
    delete next_l_pPic;
    delete play_l_pPic;
    delete stop_l_pPic;
    delete prev_l_pPic;

    delete next_lPic;
    delete ff_lPic;
    delete pause_lPic;
    delete play_lPic;
    delete stop_lPic;
    delete prev_lPic;

    delete ff_pPic;
    delete pause_pPic;
    delete stop_pPic;

    delete nextPic;
    delete ffPic;
    delete pausePic;
    delete playPic;
    delete stopPic;
    delete prevPic;

#ifdef XSID_HAVE_LIRC
  if(lircfh >= 0)
    {
      if(lircConfig)
	lirc_freeconfig(lircConfig);
      lirc_deinit();
    }
#endif
}

void MainDialog::setup(ConfigFile* cfg, const QString& path, Player* p)
{
    config = cfg;
    configPath = path;
    player = p;
    
    // Receive 'play' and 'stop' signals from player and playlist dialogs.
    // We are responsible for starting/stopping songs.
    connect(player,SIGNAL( playListItem(const PlaylistItem&) ),this,SLOT( playPlaylistFile(const PlaylistItem&) ));
    connect(player,SIGNAL( stopPlaylistPlay() ),this,SLOT( playerButtonsForceStop() ));
    connect(myPlaylistDlg,SIGNAL( playlistPlayRequest(const PlaylistItem&) ),this,SLOT( playPlaylistFile(const PlaylistItem&) ));
    connect(myPlaylistEditDlg,SIGNAL( playlistEditPlayRequest(const PlaylistItem&) ),this,SLOT( playPlaylistFile(const PlaylistItem&) ));
    // Let player receive 'playnewpos' signals from playlist dialog.
    connect(myPlaylistDlg,SIGNAL( playlistPlayNewPosRequest(int) ),player,SLOT( playNextFromList(int) ));
    // Let playlist dialog receive 'play' signal from player.
    // That way it can update the current item.
    connect(player,SIGNAL( playerPlayRequest(uint) ),myPlaylistDlg,SLOT( newListPos(uint) ));
    // Let playlist dialog receive signals from playlist editor.
    // That way it can update the current item or listbox.
    connect(myPlaylistEditDlg,SIGNAL( playlistEditPlayRequest(uint) ),myPlaylistDlg,SLOT( newListPos(uint) ));
    connect(myPlaylistEditDlg,SIGNAL( playlistEditDelRequest(uint) ),myPlaylistDlg,SLOT( deleteListPos(uint) ));
    connect(myPlaylistEditDlg,SIGNAL( playlistEditClearRequest() ),myPlaylistDlg,SLOT( clear() ));
    connect(myPlaylistEditDlg,SIGNAL( playlistEditUpdateRequest(uint) ),myPlaylistDlg,SLOT( updateListPos(uint) ));
    
    // Give player access to a few widgets.
    player->link(this,timeLCD,myPlaylistDlg);
    // Give playlist dialogs access to list in player.
    myPlaylistEditDlg->takeList( player->getPlaylist() );
    myPlaylistDlg->takeList( player->getPlaylist() );
    
    myFilterDlg->setConfigDefault( player->getEmuEngine()->getConfigDefault() );  // *not* redundant
    // Order is important with current implementation.
    setEmuConfig( config->getEmuConfig() );
    myHVSC_Dlg->setConfig( config->getHVSC_Config() );
    myStilDlg->setConfig( config->getHVSC_Config() );
    config->setGroup( ConfigC::groups[ConfigC::Audio] );
    QString audioDriver = config->getString( ConfigC::keys[ConfigC::Driver] );
    if ( audioDriver.isEmpty() )
        audioDriver = "No Sound";
    AudioDrivers::setDriver( audioDriver );
#ifdef XSID_WB_DEBUG
    cout << AudioDrivers::getName() << endl;
#endif
    setAudioConfig( config->getAudioConfig() );
    
    // HVSC root playlist basedir hack.
    player->getPlaylist()->setMaxBasePrefix( config->getHVSC_Config().hvscRootPath.absPath() );

    // Mixer - Since we do not save the entire MixerConfig to disk,
    // we copy relevant values from the emuConfig.
    // The mixer knows the emuConfig constants.
    MixerConfig newMixerConfig = config->getMixerConfig();
    newMixerConfig.channels = player->getEmuEngine()->getConfig().channels;
    newMixerConfig.mixingMode = player->getEmuEngine()->getConfig().volumeControl;
    newMixerConfig.panningMode = player->getEmuEngine()->getConfig().autoPanning;
    myMixerDlg->setConfig(newMixerConfig);
    // Make sure volume levels get set as well.
    receiveMixerConfig(newMixerConfig);

    // Playlist dialog
    config->setGroup(ConfigC::groups[ConfigC::Playlist]);
    config->restoreGeom(myPlaylistDlg);
    playtimeDefault = 0;
    fadeoutDefault = 3;
    config->getValue(ConfigC::keys[ConfigC::PlaytimeDefault],playtimeDefault);
    config->getValue(ConfigC::keys[ConfigC::FadeOutDefault],fadeoutDefault);
    if ( config->getBool( ConfigC::keys[ConfigC::SongLenDB] ))
        setSongLenDbEnabled( initSongLengthDB() );

    // Playlist editor
    config->setGroup(ConfigC::groups[ConfigC::PlaylistEditor]);
    config->restoreGeom(myPlaylistEditDlg);
    
    myHistoryDlg->load(configPath+'/'+HISTORY_FILE_NAME);

    // Misc
    config->setGroup(ConfigC::groups[ConfigC::Misc]);
    if ( config->getBool( ConfigC::keys[ConfigC::STIL_Viewer] ))
        setStilInfoEnabled( initStilViewClass() );
    config->getValue( ConfigC::keys[ConfigC::Usage], usage );

    // Main window
    config->setGroup(ConfigC::groups[ConfigC::MainWindow]);
    config->restoreGeom(this);

    // Create file selector with defaults from config file.
    config->setGroup(ConfigC::groups[ConfigC::Selector]);
    QString cwd = config->getString(ConfigC::keys[ConfigC::Dir]);
    QString nameFilter = config->getString(ConfigC::keys[ConfigC::NameFilter]);
    myFileDlg = new QFileDialog(cwd,nameFilter,this,"Selector_file_dialog",false);
    config->restoreGeom(myFileDlg);
    myFileDlg->setCaption("SIDPLAY: Sidtune selector");
    myFileDlg->setMode(QFileDialog::ExistingFile);
    myFileDlg->setIcon(*mainIcon);
    connect(myFileDlg,SIGNAL(fileHighlighted(const QString&)),this,SLOT(playFile(const QString&)));
    if ( config->getBool(ConfigC::keys[ConfigC::Open]) )
        openFile();
}

bool MainDialog::writeConfig()
{
    // Misc
    config->setGroup(ConfigC::groups[ConfigC::Misc]);
    long int mileage = usage+player->getEmuEngine()->getSecondsTotal();
    config->setEntry( ConfigC::keys[ConfigC::Usage], mileage);
    config->setEntry( ConfigC::keys[ConfigC::STIL_Viewer], isStilInfoEnabled() );

    AudioConfig thisAudioConfig = myAudioDlg->getConfig();
    // Always save user`s sane fragmentation settings.
    extern AudioConfig bakAudioConfig;
    thisAudioConfig.bufSize = bakAudioConfig.bufSize;
    thisAudioConfig.maxFrags = bakAudioConfig.maxFrags;
    thisAudioConfig.fragSize = bakAudioConfig.fragSize;
    
    config->setAudioConfig(thisAudioConfig);
    config->setGroup( ConfigC::groups[ConfigC::Audio] );
    config->setEntry( ConfigC::keys[ConfigC::Driver], AudioDrivers::getName() );
        
    config->setEmuConfig( player->getEmuEngine()->getConfig() );
    config->setHVSC_Config( myHVSC_Dlg->getConfig() );
    config->setMixerConfig( myMixerDlg->getConfig() );

    // Playlist
    config->setGroup( ConfigC::groups[ConfigC::Playlist] );
    config->saveGeom(myPlaylistDlg);
    config->setEntry( ConfigC::keys[ConfigC::PlaytimeDefault], playtimeDefault);
    config->setEntry( ConfigC::keys[ConfigC::FadeOutDefault], fadeoutDefault);
    config->setEntry( ConfigC::keys[ConfigC::SongLenDB], isSongLenDbEnabled() );
    // Playlist editor
    config->setGroup(ConfigC::groups[ConfigC::PlaylistEditor]);
    config->saveGeom(myPlaylistEditDlg);
    
    // Selector
    config->setGroup(ConfigC::groups[ConfigC::Selector]);
    config->saveGeom(myFileDlg);
    // See playFile(..).
    //config->setEntry( ConfigC::keys[ConfigC::Dir], myFileDlg->dirPath() );
    //qDebug( myFileDlg->dirPath() );
    config->setEntry( ConfigC::keys[ConfigC::NameFilter], myFileDlg->selectedFilter() );
    config->setEntry( ConfigC::keys[ConfigC::Open], myFileDlg->isVisible() );

    // Dialog geometries.
    config->setGroup(ConfigC::groups[ConfigC::MainWindow]);
    config->saveGeom(this);
        
    bool ret = config->save();
    if (!ret)
    {
        QMessageBox error;
        error.setIcon(QMessageBox::Warning);
        error.setText("Could not save configuration file!");
        error.adjustSize();
        error.exec();
    }
    return ret;
}

/*
 * [ms]: If this way of receiving config data from dialogs (and sending
 *       data to other dialogs) on the fly turns out to be buggy, it
 *       could be changed at any time by turning the dialogs into modal
 *       dialogs and adding common OK/Apply/Cancel buttons.
 *       Currently the worst thing that can happen is that Qt signals
 *       pass on to the next dialog until all dialogs have the same
 *       config.
 */

void MainDialog::setAudioConfig(const AudioConfig& inConfig)
{
    // Let the audio dialog check/adjust the config.
    myAudioDlg->setConfig(inConfig);
    if ( !player->init( myAudioDlg->getConfig() ) )
        infoField->setText( player->getErrorStr() );
        
    // Now retrieve the possibly changed config from the driver.
    AudioConfig goodAudioConfig = player->getAudioConfig();
    myAudioDlg->setConfig(goodAudioConfig);
    
    // Set dialogs that directly depend on the emuConfig.
    myEmuDlg->setConfig( player->getEmuEngine()->getConfig() );
    myFilterDlg->setConfig( player->getEmuEngine()->getConfig() );
    
    // Here update the dialog buttons according to the settings the
    // audio driver accepted.
    myAudioDlg->setButtons();

    MixerConfig newMixerConfig = myMixerDlg->getConfig();
    // The mixer knows the emuConfig constants.
    newMixerConfig.channels = player->getEmuEngine()->getConfig().channels;
    newMixerConfig.mixingMode = player->getEmuEngine()->getConfig().volumeControl;
    newMixerConfig.panningMode = player->getEmuEngine()->getConfig().autoPanning;
    myMixerDlg->setConfig(newMixerConfig);
    // Make sure volume levels get set as well.
    receiveMixerConfig(newMixerConfig);
}

void MainDialog::setEmuConfig(const emuConfig& inConfig)
{
    player->getEmuEngine()->setConfig(inConfig);
    emuConfig myEmuConfig = player->getEmuEngine()->getConfig();  // get a good copy
    
    // Set dialogs that directly depend on the emuConfig.
    // Not: myMixerDlg.
    myEmuDlg->setConfig(myEmuConfig);
    myFilterDlg->setConfig(myEmuConfig);
}

void MainDialog::receiveEmuConfig(const emuConfig& inConfig)
{
    // Save current player state to allow for paused playback.
    bool wasPlaying = player->isPlaying();
    bool wasPaused = player->isPaused();
    player->pause(true);
    
    player->getEmuEngine()->setConfig(inConfig);
    emuConfig myEmuConfig = player->getEmuEngine()->getConfig();  // get a good copy

    // Do not write back emuConfig to any dialog.
    // But:
    myFilterDlg->paintFilterFunc();
    
    MixerConfig newMixerConfig = myMixerDlg->getConfig();
    // The mixer knows the emuConfig constants.
    newMixerConfig.channels = myEmuConfig.channels;
    newMixerConfig.mixingMode = myEmuConfig.volumeControl;
    newMixerConfig.panningMode = myEmuConfig.autoPanning;
    myMixerDlg->setConfig(newMixerConfig);
    
    if ( wasPlaying && !wasPaused )
        player->resume();
}

void MainDialog::receiveHVSC_Config(const HVSC_Config& inConfig)
{
    myStilDlg->setConfig(inConfig);
    
    if ( isStilInfoEnabled() )
        setStilInfoEnabled( initStilViewClass() );
    
    // Hide a possibly open STIL dialog.
    if ( myStilDlg->isVisible() && !isStilInfoEnabled() )
        myStilDlg->hide();
    
    if ( isSongLenDbEnabled() )
        setSongLenDbEnabled( initSongLengthDB() );
    
    // HVSC root playlist basedir hack.
    player->getPlaylist()->setMaxBasePrefix( inConfig.hvscRootPath.absPath() );
}

void MainDialog::receiveMixerConfig(const MixerConfig& inConfig)
{
    emuConfig myEmuConfig = player->getEmuEngine()->getConfig();  // get a good copy
    
    if ((myEmuConfig.volumeControl!=inConfig.mixingMode) ||
        (myEmuConfig.autoPanning!=inConfig.panningMode))
    {
        myEmuConfig.volumeControl = inConfig.mixingMode;
        myEmuConfig.autoPanning = inConfig.panningMode;
        player->getEmuEngine()->setConfig(myEmuConfig);
        myEmuConfig = player->getEmuEngine()->getConfig();
        myEmuDlg->setConfig(myEmuConfig);
        myFilterDlg->setConfig(myEmuConfig);
    }

    for (int v=0; v < MixerDialog::voices; v++)
    {
        // In mono mode, set voice volume regardless of whether mixing
        // mode is active.
        if (myEmuConfig.channels == SIDEMU_MONO)
        {
            player->getEmuEngine()->setVoiceVolume(v+1,255,0,
                                       inConfig.volTotal[v]&inConfig.muteMask[v]);
        }
        else
        {
            if (myEmuConfig.volumeControl == SIDEMU_VOLCONTROL)
            {
                player->getEmuEngine()->setVoiceVolume(v+1,inConfig.volHQ[v].l,inConfig.volHQ[v].r,
                                           inConfig.volTotal[v]&inConfig.muteMask[v]);
            }
            else if ((myEmuConfig.volumeControl==SIDEMU_FULLPANNING) &&
                     (myEmuConfig.autoPanning==SIDEMU_NONE))
            {
                player->getEmuEngine()->setVoiceVolume(v+1,inConfig.volFP[v].l,inConfig.volFP[v].r,
                                           inConfig.volTotal[v]&inConfig.muteMask[v]);
            }
            else  // STEREOSURROUND or AUTOPANNING
            {
                // Adjust only the master volume level.
                // This is not clean because we don't use the emuEngine-defaults.
                if (myEmuConfig.volumeControl == SIDEMU_STEREOSURROUND)
                {
                    player->getEmuEngine()->setVoiceVolume(v+1,255,255,inConfig.volTotal[v]&inConfig.muteMask[v]);
                }
                else
                {
                    if ((v&1)==0)
                        player->getEmuEngine()->setVoiceVolume(v+1,255,0,inConfig.volTotal[v]&inConfig.muteMask[v]);
                    else
                        player->getEmuEngine()->setVoiceVolume(v+1,0,255,inConfig.volTotal[v]&inConfig.muteMask[v]);
                }
            }
        }
    }
}

// --------------------------------------------------------------------------

void MainDialog::closeEvent(QCloseEvent* e)
{
    programExitHook();
    e->accept();
    
    myStilDlg->close();
    delete myStilDlg;    // StilDialog is NOT a child
}

void MainDialog::resizeEvent(QResizeEvent*)
{
    int w = width();
    int h = height();
    // 5,80,350,110 (main 358,222)
    infoField->setGeometry(5,80,w-10,h-102);
    if ( (h-102)<8 )
        infoField->hide();
    else if ( !infoField->isVisible() )
        infoField->show();
    playlistCheckBox->move(16,h-22);  //  16,200
    loopCheckBox->move(142,h-22);     // 142,200
    randomCheckBox->move(245,h-22);   // 245,200
}

void MainDialog::playerButtonsOff()
{
    prevBtn->setPixmap(*prevPic);
    stopBtn->setPixmap(*stopPic);
    playBtn->setPixmap(*playPic);
    ffBtn->setPixmap(*ffPic);
    nextBtn->setPixmap(*nextPic);
}

void MainDialog::playerButtonsForceStart()
{
    // Make them look as if playing.
    setPrevBtnState();
    stopBtn->setPixmap(*stop_lPic);
    playBtn->setPixmap(*pause_lPic);
    ffBtn->setPixmap(*ff_lPic);
    setNextBtnState();
}

void MainDialog::playerButtonsForceStop()
{
    // Make them look as if stopped.
    setPrevBtnState();
    stopBtn->setPixmap(*stopPic);
    playBtn->setPixmap(*play_lPic);
    ffBtn->setPixmap(*ffPic);
    setNextBtnState();
}

void MainDialog::showSidTuneInfo()
{
    QString tmp;
    if ( player->getSidTuneWrapper()->getInfoStringsNum() == 3 )
    {
        tmp =    "Name      : ";
        tmp += player->getSidTuneWrapper()->getInfoString(0);
        tmp += "\nAuthor    : ";
        tmp += player->getSidTuneWrapper()->getInfoString(1);
        tmp += "\nCopyright : ";
        tmp += player->getSidTuneWrapper()->getInfoString(2);
    }
    else
    {
        for ( int infoi = 0; infoi < player->getSidTuneWrapper()->getInfoStringsNum(); infoi++ )
        {
            if (infoi > 0)
                tmp += "\n";
            tmp += "Info      : ";
            tmp += player->getSidTuneWrapper()->getInfoString(infoi);
        }
    }
    QString tmp2;
    tmp2.sprintf("\nSongs     : %d (Startsong: %d)",
             player->getSidTuneWrapper()->getSongs(), 
             player->getSidTuneWrapper()->getStartSong() );
    tmp += tmp2;
#ifdef SID_WITH_SIDPLAY2
    tmp2.sprintf("\nSong speed: %s",player->getEmuEngine()->getSpeedString() );
    tmp += tmp2;
    tmp2.sprintf("\nSID model : %s",player->getEmuEngine()->getSidModelString() );
#else
    tmp2.sprintf("\nSong speed: %s",player->getSidTuneWrapper()->getSpeedString() );
#endif
    tmp += tmp2;
    tmp2.sprintf("\nAddresses : $%04x, $%04x, $%04x",
             player->getSidTuneWrapper()->getLoadAddr(),
             player->getSidTuneWrapper()->getInitAddr(),
             player->getSidTuneWrapper()->getPlayAddr() );
    tmp += tmp2;
    tmp2.sprintf("\nFormat    : %s",player->getSidTuneWrapper()->getFormatString() );
    tmp += tmp2;
    infoField->setText(tmp);
            
    songLCD->display(player->getSidTuneWrapper()->getCurrentSong() );
}

void MainDialog::clearSidTuneInfo()
{
    QString tmp 
         = "Name      : \n";
    tmp += "Author    : \n";
    tmp += "Copyright : \n";
    tmp += "Addresses : \n";
    tmp += "Songs     : \n";
    tmp += "Song speed: \n";
    tmp += "Format    : \n";
    infoField->setText(tmp);
}

// --------------------------------------------------------------------------

/*
 * Called before application is quit.
 */
void MainDialog::programExitHook()
{
    player->stop();
#ifdef XSID_HAVE_TSID
    addTSID();  // add time last song has been listened to
#endif
    writeConfig();     // ignore return value
    myHistoryDlg->save();
    
    bool forceExit = false;
    while ( player->getPlaylist()->isModified() && !forceExit )
    {
        QMessageBox mb( "SIDPLAY",
                       "You are about to quit.\n\n"
                       "Playlist has not been saved yet!\n"
                       "Do you want to save it?",
                       QMessageBox::Information,
                       QMessageBox::Yes | QMessageBox::Default | QMessageBox::Escape,
                       QMessageBox::No,
                       0);
        mb.setButtonText( QMessageBox::Yes, "Yes" );
        mb.setButtonText( QMessageBox::No, "No" );
        switch( mb.exec() ) 
        {
         case QMessageBox::Yes:
            savePlaylistAs();
            break;
         case QMessageBox::No:
            forceExit = true;
            break;
        }
    }
    
    forceExit = false;
    while ( hotlist->isModified() && !forceExit )
    {
        QMessageBox mb( "SIDPLAY",
                       "You are about to quit.\n\n"
                       "Hotlist has not been saved yet!\n"
                       "Do you want to save it?",
                       QMessageBox::Information,
                       QMessageBox::Yes | QMessageBox::Default | QMessageBox::Escape,
                       QMessageBox::No,
                       0);
        mb.setButtonText( QMessageBox::Yes, "Yes" );
        mb.setButtonText( QMessageBox::No, "No" );
        switch( mb.exec() ) 
        {
         case QMessageBox::Yes:
            saveHotlist();
            break;
         case QMessageBox::No:
            forceExit = true;
            break;
        }
    }
}

void MainDialog::quit()
{
    programExitHook();
    qApp->quit();
}

void MainDialog::openFile()
{
    if ( myFileDlg!=0 )
    {
        myFileDlg->show();
    }
}

void MainDialog::saveAs()
{
    if ( player->getSidTuneWrapper()->getStatus() )
    {
        // Get last dir from config file.
        config->setGroup(ConfigC::groups[ConfigC::Selector]);
        QString lastSaveDir = config->getString(ConfigC::keys[ConfigC::SaveDir]);

        QString fileName = QFileDialog::getSaveFileName(lastSaveDir);
        if ( !fileName.isEmpty() && !player->getSidTuneWrapper()->savePSID(fileName,true) )
        {
            QMessageBox error;
            error.setIcon(QMessageBox::Warning);
            error.setText("Could not save file!");
            error.adjustSize();
            error.exec();
        }
        // Determine dir name and save it in config file.
        int slashPos = fileName.findRev( QDir::separator() );
        if ( slashPos >= 0 )
            fileName.truncate(slashPos);
        config->setGroup(ConfigC::groups[ConfigC::Selector]);
        config->setEntry(ConfigC::keys[ConfigC::SaveDir],fileName);
    }
}

// ----------------------------------------------------------- Playlist stuff

void MainDialog::loadPlaylist()
{
    bool forceLoad = false;
    while ( player->getPlaylist()->isModified() && !forceLoad )
    {
        QMessageBox mb( "SIDPLAY",
                       "Playlist has not been saved yet!\n"
                       "Do you want to save it?",
                       QMessageBox::Information,
                       QMessageBox::Yes | QMessageBox::Default | QMessageBox::Escape,
                       QMessageBox::No,
                       0);
        mb.setButtonText( QMessageBox::Yes, "Yes" );
        mb.setButtonText( QMessageBox::No, "No" );
        switch( mb.exec() ) 
        {
         case QMessageBox::Yes:
            savePlaylistAs();
            break;
         case QMessageBox::No:
            forceLoad = true;
            break;
        }
    }

    QString fileName = QFileDialog::getOpenFileName(fetchLastDir(ConfigC::groups[ConfigC::Playlist]),
                                                    "*.spl",this,0,"SIDPLAY: Load playlist");
    if ( !fileName.isEmpty() )
    {
        QApplication::setOverrideCursor( Qt::waitCursor );
        player->getPlaylist()->load(fileName);
        storeLastDir(ConfigC::groups[ConfigC::Playlist],fileName);
        QApplication::restoreOverrideCursor();
        bool goodList = PlaylistCheck::checkBaseDir( this, player->getPlaylist(),
                                                     myFileDlg );
        if ( !goodList )
            player->getPlaylist()->clear();
        myPlaylistEditDlg->takeList( player->getPlaylist() );
        myPlaylistDlg->takeList( player->getPlaylist() );
        if ( goodList )
            myPlaylistDlg->show();
    }
}

// slot
void MainDialog::savePlaylistAs()
{
    QString fileName = QFileDialog::getSaveFileName(fetchLastDir(ConfigC::groups[ConfigC::Playlist]),
                                                    "*.spl",this,0,"SIDPLAY: Save playlist");
    if ( !fileName.isEmpty() )
    {
        QApplication::setOverrideCursor( Qt::waitCursor );
        bool ret = player->getPlaylist()->save(fileName);
        storeLastDir(ConfigC::groups[ConfigC::Playlist],fileName);
        QApplication::restoreOverrideCursor();
        if (!ret)
        {
            QMessageBox error;
            error.setIcon(QMessageBox::Warning);
            error.setText("Could not save playlist!");
            error.adjustSize();
            error.exec();
        }
    }
}

const QString& MainDialog::fetchLastDir(const char* group)
{
    config->setGroup(group);
    return config->getString(ConfigC::keys[ConfigC::Dir]);
}
 
void MainDialog::storeLastDir(const char* group, const QString& fileName)
{
    QString path = fileName;
    // Determine dir name and save it in config file.
    int slashPos = path.findRev( QDir::separator() );
    if ( slashPos >= 0 )
        path.truncate(slashPos+1);
    else
        path = QDir::separator();
    config->setGroup(group);
    config->setEntry(ConfigC::keys[ConfigC::Dir],path);
}

void MainDialog::openPlaylistDialog()
{
    myPlaylistDlg->show();
}

void MainDialog::openPlaylistEditDialog()
{
    myPlaylistEditDlg->show();
}

void MainDialog::openPlaylistOptions()
{
    PlaylistOptDialog* myPlaylistOptDlg = new PlaylistOptDialog(this,"Playlist Options Dialog",true);
    myPlaylistOptDlg->playtimeSpinBox->setValue( playtimeDefault );
    myPlaylistOptDlg->fadeoutSpinBox->setValue( fadeoutDefault );
    bool oldIsSongLenDbEnabled = isSongLenDbEnabled();
    myPlaylistOptDlg->songlenDBcheckbox->setChecked( isSongLenDbEnabled() );
    int result = myPlaylistOptDlg->exec();
    if ( result==QDialog::Accepted )
    {
        playtimeDefault = myPlaylistOptDlg->playtimeSpinBox->text().toInt();
        fadeoutDefault = myPlaylistOptDlg->fadeoutSpinBox->text().toInt();
        if ( myPlaylistOptDlg->songlenDBcheckbox->isChecked() &&
             !oldIsSongLenDbEnabled )
        {
            setSongLenDbEnabled( initSongLengthDB() );
        }
        else if ( !myPlaylistOptDlg->songlenDBcheckbox->isChecked() )
        {
            setSongLenDbEnabled( false );
        }
    }
    delete myPlaylistOptDlg;
}

// slot
void MainDialog::addTuneToPlaylist()
{
    if ( !player->getSidTuneWrapper()->getStatus() )
        return;
    PlaylistItem* item = new PlaylistItem;
    item->fileNameString = sidFileNameFull;
    item->titleString = player->getSidTuneWrapper()->getInfoString(0);
    item->authorString = player->getSidTuneWrapper()->getInfoString(1);
    item->copyrightString = player->getSidTuneWrapper()->getInfoString(2);
    item->subtune = player->getSidTuneWrapper()->getCurrentSong();
    item->songs = player->getSidTuneWrapper()->getSongs();
    item->fadeout = fadeoutDefault;
    item->time = querySongLengthDB(player->getSidTuneWrapper(),
                                   player->getSidTuneWrapper()->getCurrentSong() );
    addTuneToPlaylistMain(item);
}

void MainDialog::addTuneToPlaylistMain(PlaylistItem* item)
{
    QApplication::setOverrideCursor( Qt::waitCursor );
    player->getPlaylist()->add(item);
    myPlaylistDlg->add(item);
    myPlaylistEditDlg->add(item);
    QApplication::restoreOverrideCursor();
}

void MainDialog::addDirToPlaylist()
{
    addDirMain(false);  // not recursive
}

void MainDialog::addTreeToPlaylist()
{
    addDirMain(true);  // recursive
}

void MainDialog::addDirMain(bool recurse)
{
    QString dirName = 
        QFileDialog::getExistingDirectory(fetchLastDir(ConfigC::groups[ConfigC::Selector]),this,0,
                                          "SIDPLAY: Open directory to be added");
    if ( dirName.isEmpty() )
    {
        return;  // User cancelled. 
    }
    storeLastDir(ConfigC::groups[ConfigC::Selector],dirName);
    SidTuneWrapper* mySidLoader = new SidTuneWrapper;  // unchecked alloc
    QApplication::setOverrideCursor( Qt::waitCursor );
    addDirTraverse(dirName,recurse,mySidLoader);
    QApplication::restoreOverrideCursor();
    delete mySidLoader;
}

void MainDialog::addDirTraverse(QString dirName, bool recurse, SidTuneWrapper* mySidLoader)
{
    QDir* startDir = new QDir(dirName);
    Q_CHECK_PTR(startDir);

    const QFileInfoList* list = startDir->entryInfoList();
    QListIterator<QFileInfo> it( *list );  // create list iterator
   
    for ( it.toFirst(); it.current(); ++it )
    {
        QFileInfo* fi = it.current();
        if ( recurse && fi->isDir() && fi->fileName()!="." && fi->fileName()!=".." )
        {
            addDirTraverse(fi->absFilePath(),recurse,mySidLoader);
        }
        else if ( fi->isFile() && mySidLoader->open(fi->absFilePath()) )
        {
            PlaylistItem* item = new PlaylistItem;
            item->fileNameString = fi->absFilePath();
            item->titleString = mySidLoader->getInfoString(0);
            item->authorString = mySidLoader->getInfoString(1);
            item->copyrightString = mySidLoader->getInfoString(2);
            item->subtune = mySidLoader->getStartSong();
            item->songs = mySidLoader->getSongs();
            item->fadeout = fadeoutDefault;
            item->time = querySongLengthDB(mySidLoader,
                                           mySidLoader->getStartSong() );
            addTuneToPlaylistMain(item);
        }
    }
    delete startDir;
}

// slot
void MainDialog::updatePlaylist()
{
    PlaylistCheck::updateEntries( this, player->getPlaylist(), myPlaylistDlg, 
                                 myPlaylistEditDlg, myFileDlg );
}

// slot
void MainDialog::markTune()
{
    if ( player->getSidTuneWrapper()->getStatus() )
    {
        PlaylistItem* item = new PlaylistItem;
        item->fileNameString = sidFileNameFull;
        item->titleString = player->getSidTuneWrapper()->getInfoString(0);
        item->authorString = player->getSidTuneWrapper()->getInfoString(1);
        item->copyrightString = player->getSidTuneWrapper()->getInfoString(2);
        item->subtune = player->getSidTuneWrapper()->getCurrentSong();
        item->songs = player->getSidTuneWrapper()->getSongs();
        PlaylistItem i = player->getCurPlaylistParams();
        item->fadeout = i.fadeout;
        item->time = i.time;
        QApplication::setOverrideCursor( Qt::waitCursor );
        hotlist->add(item);
        QApplication::restoreOverrideCursor();
    }
}

// slot
void MainDialog::markPlaylist()
{
    QApplication::setOverrideCursor( Qt::waitCursor );
    // Add all playlist items to hotlist.
    QListIterator<PlaylistItem> it(player->getPlaylist()->list);
    for ( it.toFirst(); it.current(); ++it ) 
    {
        PlaylistItem* item = new PlaylistItem;
        *item = *it.current();
        hotlist->add(item);
    }
    QApplication::restoreOverrideCursor();
}

// slot
void MainDialog::addHotlistToPlaylist()
{
    QApplication::setOverrideCursor( Qt::waitCursor );
    // Add all hotlist list items to playlist.
    QListIterator<PlaylistItem> it(hotlist->list);
    for ( it.toFirst(); it.current(); ++it ) 
    {
        PlaylistItem* item = new PlaylistItem;
        *item = *it.current();
        player->getPlaylist()->add(item);
        myPlaylistDlg->add(item);
        myPlaylistEditDlg->add(item);
    }
    // Clear hotlist.
    hotlist->clear();
    QApplication::restoreOverrideCursor();
}
 
// slot
void MainDialog::saveHotlist()
{
    // We use the last playlist open/save dir.
    QString fileName = QFileDialog::getSaveFileName(fetchLastDir(ConfigC::groups[ConfigC::Playlist]),
                                                    "*.spl",this,0,"SIDPLAY: Save hotlist");
    if ( !fileName.isEmpty() )
    {
        QApplication::setOverrideCursor( Qt::waitCursor );
        bool ret = hotlist->save(fileName);
        storeLastDir(ConfigC::groups[ConfigC::Playlist],fileName);
        QApplication::restoreOverrideCursor();
        if (!ret)
        {
            QMessageBox error;
            error.setIcon(QMessageBox::Warning);
            error.setText("Could not save hotlist!");
            error.adjustSize();
            error.exec();
        }
    }
}

// --------------------------------------------------------------------------

void MainDialog::about()
{
    QString verCat = "SIDPLAY/X11   Version ";
    verCat += VERSION;
    verCat += "\nby Michael Schwendt\n\n";
    
    verCat += player->getEmuEngine()->getVersionString();
    verCat += "\n\n";
    
    verCat += mySTIL.getVersion();
    
#ifdef XSID_HAVE_TSID
    verCat += "\n";
    verCat += myTSID->getTSIDVersion();
#endif
    
    verCat += "\nListening mileage: ";
    long int mileage = usage+player->getEmuEngine()->getSecondsTotal();
    int days = mileage/(3600L*24);
    mileage -= days*(3600L*24);
    int hours = mileage/3600L;
    mileage -= hours*3600L;
    int minutes = mileage/60;
    mileage -= minutes*60;
    QString timeCat;
    timeCat.sprintf("%d days, %d hours, %d minutes",days,hours,minutes);
    verCat += timeCat;
    QMessageBox::about(this,"About SIDPLAY",verCat);
}

void MainDialog::aboutQt()
{
    QMessageBox::aboutQt(this);
}

void MainDialog::openAudioDialog()
{
    // Save current player state to allow for paused playback.
    bool wasPlaying = player->isPlaying();
    bool wasPaused = player->isPaused();
    
    player->pause(true);
    AudioDrivers::getDriver()->unload();

    myAudioDlg->setConfig( player->getAudioConfig() );
    if ( myAudioDlg->exec() == QDialog::Accepted )
    {    
        setAudioConfig( myAudioDlg->getConfig() );
    }
    if ( wasPlaying && !wasPaused )
        player->resume();
}

void MainDialog::openEmulatorDialog()
{
    myEmuDlg->show();
}

void MainDialog::openFilterDialog()
{
    myFilterDlg->show();
}

void MainDialog::openMixerDialog()
{
    myMixerDlg->show();
}

void MainDialog::openHVSC_Dialog()
{
    myHVSC_Dlg->show();
}

void MainDialog::openHistoryDialog()
{
    myHistoryDlg->show();
}

// --------------------------------------------------------------------------

/* slot */
void MainDialog::setPlaylistEnabled(bool value)
{
    player->enablePlaylist(value);
}

/* slot */
void MainDialog::setLoopPlayEnabled(bool value)
{
    player->enablePlaylistLoop(value);
}

/* slot */
void MainDialog::setRandomPlayEnabled(bool value)
{
    player->enablePlaylistRand(value);
}

/* slot */
void MainDialog::newSubSong(int newSong)
{
    if ( player->getSidTuneWrapper()->getStatus() && 
         newSong>=1 && newSong<=player->getSidTuneWrapper()->getSongs() )
    {
        player->pause();
#ifdef XSID_HAVE_TSID
        addTSID();  // add time last song has been listened to
#endif
        resetTimeDisplay();
        player->initSong(newSong);
        playlistCheckBox->setChecked(false);
        player->resume();
        if ( player->isReady() )
        {
            showSidTuneInfo();
            showStilInfo();
            setPrevBtnState();
            setNextBtnState();
        }
    }
    else
    {
        player->stop();
        playerButtonsOff();
    }
}

void MainDialog::updateSubSongSlider()
{
    if ( player->getSidTuneWrapper()->getStatus() )
    {
        // Block signals here, or otherwise the slider would emit
        // newValue(int) and activate the new newSubSong(int) slot.
        subSongSlider->blockSignals(true);
        subSongSlider->setRange(1,player->getSidTuneWrapper()->getSongs() );
        subSongSlider->setSteps(1,1);
        subSongSlider->setValue(player->getSidTuneWrapper()->getCurrentSong() );
        subSongSlider->blockSignals(false);
    }
}

void MainDialog::resetTimeDisplay()
{
    player->getEmuEngine()->resetSecondsThisSong();
    timeLCD->resetDisplay();
}

// ----------------------------------------------------------- Player buttons

void MainDialog::prevSongPressed()
{
    if ( player->havePrevSong() )
    {
        prevBtn->setPixmap(*prev_l_pPic);
        player->pause();
#ifdef XSID_HAVE_TSID
        addTSID();  // add time last song has been listened to
#endif
        resetTimeDisplay();
        player->initPrevSong();
        playlistCheckBox->setChecked(false);
        updateSubSongSlider();
        showSidTuneInfo();
        showStilInfo();
        if ( player->haveSidTune() )
            setNextBtnState();
        else
            playerButtonsOff();
        player->resume();
    }
}

void MainDialog::prevSongReleased()
{
    setPrevBtnState();
}

void MainDialog::setPrevBtnState()
{
    if ( player->havePrevSong() )
        prevBtn->setPixmap(*prev_lPic);
    else
        prevBtn->setPixmap(*prevPic);
}

void MainDialog::nextSongPressed()
{
    if ( player->haveNextSong() )
    {
        nextBtn->setPixmap(*next_l_pPic);
        player->pause();
#ifdef XSID_HAVE_TSID
        addTSID();  // add time last song has been listened to
#endif
        resetTimeDisplay();
        player->initNextSong();
        playlistCheckBox->setChecked(false);
        updateSubSongSlider();
        showSidTuneInfo();
        showStilInfo();
        if ( player->haveSidTune() )
            setPrevBtnState();
        else
            playerButtonsOff();
        player->resume();
    }
}

void MainDialog::nextSongReleased()
{
    setNextBtnState();
}

void MainDialog::setNextBtnState()
{
    if ( player->haveNextSong() )
        nextBtn->setPixmap(*next_lPic);
    else
        nextBtn->setPixmap(*nextPic);
}

void MainDialog::stopSongPressed()
{
    if ( player->isReady() && player->isPlaying() )
    {
        stopBtn->setPixmap(*stop_l_pPic);
        playBtn->setPixmap(*play_lPic);
        ffBtn->setPixmap(*ffPic);
        player->stop();
        playlistCheckBox->setChecked(false);
    }
}

void MainDialog::stopSongReleased()
{
    stopBtn->setPixmap(*stopPic);
}

void MainDialog::playSongPressed()
{
    if ( !player->haveSidTune() )
        return;
    if ( player->isStopped() )
    {
        playBtn->setPixmap(*play_l_pPic);
        stopBtn->setPixmap(*stop_lPic);
#ifdef XSID_HAVE_TSID
        addTSID();  // add time last song has been listened to
        // This catches repeated play back of the same sub-tune.
#endif
        resetTimeDisplay();
        player->start();
    }
    else if ( player->isPaused() )
    {
        playBtn->setPixmap(*play_l_pPic);
        player->resume();
    }
    else if ( player->isPlaying() && !player->isForwarding() )
    {
        playBtn->setPixmap(*pause_pPic);
        player->pause();
    }
}

void MainDialog::playSongReleased()
{
    if ( !player->haveSidTune() )
        return;
    if ( player->isPaused() )
    {
        playBtn->setPixmap(*play_lPic);
        ffBtn->setPixmap(*ffPic);
    }
    else if ( player->isPlaying() )
    {
        if ( !player->isForwarding() )
        {
            playBtn->setPixmap(*pause_lPic);
            ffBtn->setPixmap(*ff_lPic);
        }
    }
    else
    {
        playerButtonsForceStop();
        QMessageBox error;
        error.setIcon(QMessageBox::Critical);
        error.setText( player->getErrorStr() );
        error.adjustSize();
        error.exec();
    }
}

void MainDialog::forwardSongPressed()
{
    if ( player->isForwarding() )
    {
        playBtn->setPixmap(*pause_lPic);
        ffBtn->setPixmap(*ff_lPic);
        player->playNormalSpeed();
    }
    else if ( player->isPlaying() && !player->isPaused() )
    {
        playBtn->setPixmap(*pausePic);
        ffBtn->setPixmap(*ff_pPic);
        player->playFastForward();
    }
}

void MainDialog::forwardSongReleased()
{
    if ( player->isForwarding() )
        ffBtn->setPixmap(*ff_pPic);
    else if ( player->isPlaying() && !player->isPaused() )
        ffBtn->setPixmap(*ff_lPic);
}

// --------------------------------------------------------------- HVSC stuff

void MainDialog::createHVSCfileName(const QString& fileName)
{
    // Is filename anywhere in HVSC tree?
    if (fileName.find(myHVSC_Dlg->getConfig().hvscRootPath.absPath()) == 0)
    {
        // Create filename which is relative to the HVSC root.
        sidFileNameHVSC = 
            fileName.right(fileName.length()-
                           myHVSC_Dlg->getConfig().hvscRootPath.absPath().length());
    }
    else
    {
        sidFileNameHVSC = "";
    }
}

// --------------------------------------------------------------- STIL stuff

bool MainDialog::initStilViewClass()
{
    // Let STIL View class perform the check for the STIL database file.
    if ( mySTIL.setBaseDir( myHVSC_Dlg->getConfig().hvscRootPath.absPath() ) )
        return true;
    
    QMessageBox error(this, "initStilViewClass() error");
    error.setIcon( QMessageBox::Warning );
    error.setText( mySTIL.getErrorStr() );
    error.adjustSize();
    error.exec();
    return false;
}

void MainDialog::toggleStilDialog()
{
    if ( isStilInfoEnabled() )
    {
        // Disable STIL View.
        setStilInfoEnabled( false );
        // Hide a possibly open STIL View dialog.
        if ( myStilDlg->isVisible() )
            myStilDlg->hide();
    }
    else  // if !isStilInfoEnabled()
    {
        // Enabled STIL View (if init is successful).
        setStilInfoEnabled( initStilViewClass() );
        // Try to show STIL entry for currently active file.
        showStilInfo();
    }
}

void MainDialog::showStilInfo()
{
    // STIL View not enabled or no HVSC-relative file active?
    if ( !isStilInfoEnabled() || sidFileNameHVSC.isEmpty() )
    {
        // Nothing to show. Hence hide a possibly open STIL View dialog.
        myStilDlg->hide();
        return;
    }
    
    int song = player->getSidTuneWrapper()->getCurrentSong();
    
    bool stilIsOkay = true;    // we want to avoid successive errors
    // These should be converted to QString as well.
    char* globalComment;
    QString stilEntry;
    char* bugEntry;
    // Now retrieve the individual STIL entries.
    if (stilIsOkay)
    {
        globalComment = mySTIL.getGlobalComment(sidFileNameHVSC);
        stilIsOkay = !mySTIL.hasCriticalError();
    }
    if ( stilIsOkay && myHVSC_Dlg->getConfig().showForFile )
    {
        // Get file-specific comment and all entries (STIL::all).
        stilEntry = mySTIL.getEntry( sidFileNameHVSC, 0 );
        stilIsOkay = !mySTIL.hasCriticalError();
    }
    else if ( stilIsOkay )
    {
        // First get the file-global comment.
        stilEntry = mySTIL.getEntry( sidFileNameHVSC, 0, STIL::comment );
        if ( stilIsOkay = !mySTIL.hasCriticalError()  )
        {
            // Append STIL entry for current sub-tune 'song'.
            stilEntry += mySTIL.getEntry( sidFileNameHVSC, song );
            stilIsOkay = !mySTIL.hasCriticalError();
        }
    }
    if (stilIsOkay)
    {
        bugEntry = mySTIL.getBug(sidFileNameHVSC,song);
        stilIsOkay = !mySTIL.hasCriticalError();
    }
    if (stilIsOkay)
    {
        myStilDlg->setLabels(globalComment,stilEntry,bugEntry);
    }
    else
    {
        // Disable STIL View.
        // Protect the user from any further critical errors.
        setStilInfoEnabled(false);
        myStilDlg->hide();
        
        QMessageBox error;
        error.setIcon(QMessageBox::Critical);
        error.setText(mySTIL.getErrorStr());
        error.adjustSize();
        error.exec();
    }
}

// ------------------------------------------------------ Songlength DB stuff

bool MainDialog::initSongLengthDB()
{
    QString fileName = configPath + '/' + SONGLENGTHS_DB_FILE_NAME;
    
    if ( !QFile::exists(fileName) )
        fileName = myHVSC_Dlg->getConfig().hvscRootPath.absPath() + HVSC_DOC_DIR_NAME + '/' + SONGLENGTHS_DB_FILE_NAME;

    if ( SongLength::init(fileName) )
        return true;

    QMessageBox error;
    error.setIcon( QMessageBox::Warning );
    error.setText( SongLength::getErrorStr() );
    error.adjustSize();
    error.exec();
    return false;
}
 
void MainDialog::toggleSongLengthDB()
{
    if ( isSongLenDbEnabled() )
    {
        // Disable songlength database.
        setSongLenDbEnabled(false);  // menu
        player->enableSongLengthDB(false);
    }
    else
    {
        // Enable songlength database (if init is successful).
        setSongLenDbEnabled( player->enableSongLengthDB( initSongLengthDB() ));
    }
}

int MainDialog::querySongLengthDB(SidTuneWrapper* pSid, int song)
{
    if ( !isSongLenDbEnabled() )
        return playtimeDefault;
    SongLengthDBitem sli;
    // No other error handling yet.
    bool ret = SongLength::getItem( pSid, song, sli );
#ifdef XSID_WB_DEBUG
    if ( !ret )
        cerr << SongLength::getErrorStr() << endl;
#endif
    return sli.playtime;
}

// ------------------------------------------------------------------- Player

void MainDialog::playFile(const QString& fileName)
{
    // A fix for Qt 2.3.0 on Seawolf. QFileDialog::dirPath() cuts off
    // the last sub-dir, which messes up the recently used dir in
    // xsidplay.ini.
    storeLastDir(ConfigC::groups[ConfigC::Selector],fileName);
    //qDebug(fileName);
    //qDebug( fetchLastDir(ConfigC::groups[ConfigC::Selector]) );
    
    PlaylistItem pi;  // just a dummy
    playFileMain( fileName, 0, pi, true, false );
}

void MainDialog::playHistoryFile(const QString& fileName)
{
    PlaylistItem pi;  // just a dummy
    playFileMain( fileName, 0, pi, false, false );
}

void MainDialog::playPlaylistFile(const PlaylistItem& item)
{
    playFileMain( item.fileNameString, item.subtune, item, true, true );
}

void MainDialog::playFileMain(const QString& fileName, int song,
                              const PlaylistItem& item,
                              bool putInHistory, bool enablePlaylist)
{
    QFile testFile(fileName);
    // Who would use '-' for something else than stdin?
    if ( fileName.isEmpty() || ( fileName!="-" && !testFile.exists() ))
        return;
    
    player->stop();
#ifdef XSID_HAVE_TSID
    addTSID();  // add time last song has been listened to
#endif
    resetTimeDisplay();

    // Make a copy for later use.
    sidFileNameFull = fileName;
    createHVSCfileName(fileName);
    
    bool loadSucceeded = false;
    
    // sidTune::open() does not support standard input.
    if (fileName == "-")
    {
        unsigned char* fileBuf = new unsigned char[SidTuneWrapper::getMaxSidFileLen()];
        if (fileBuf == 0)
        {
            // If we are out of memory at this point,
            // it is very unlikely that anything else works.
            QMessageBox error;
            error.setIcon(QMessageBox::Critical);
            error.setText("Out of memory.");
            error.adjustSize();
            error.exec();
            return;
        }
        unsigned long int fileLen = 0;
        char datb;
        while (cin.get(datb) && fileLen<SidTuneWrapper::getMaxSidFileLen())
            fileBuf[fileLen++] = datb;
        loadSucceeded = player->getSidTuneWrapper()->load(fileBuf,fileLen);
        delete[] fileBuf;
    }
    else
    {
        loadSucceeded = player->getSidTuneWrapper()->open(fileName);
    }

    if ( enablePlaylist )
    {
        // Take params from playlist.
        player->setPlaylistParams( item );
    }
    else
    {
        // Not playlist mode. Put in defaults.
        PlaylistItem pi = player->getCurPlaylistParams();
        pi.fadeout = fadeoutDefault;
        pi.time = playtimeDefault;
        player->setPlaylistParams(pi);
    }

    if ( !loadSucceeded )
    {
        playerButtonsOff();
        clearSidTuneInfo();
        infoField->setText(player->getSidTuneWrapper()->getStatusString() );
        // With too many errors one has to click too much if
        // message boxes are used here.
    }
    else
    {
        if ( song == 0 )
            song = player->getSidTuneWrapper()->getStartSong();
        player->initSong(song);
        updateSubSongSlider();
        showSidTuneInfo();
        showStilInfo();
        // Add to history if not from there.
        if ( putInHistory )
        {
            QString tmp = player->getSidTuneWrapper()->getInfoString(0);
            tmp += " (";
            tmp += player->getSidTuneWrapper()->getInfoString(1);
            tmp += ")";
            myHistoryDlg->add(fileName,tmp);
        }
    }
    
    playlistCheckBox->setChecked(enablePlaylist);

    if ( player->getSidTuneWrapper()->getStatus() )
    {
        player->start();
        if ( player->isPlaying() )
            playerButtonsForceStart();
        else
            playerButtonsForceStop();
        if ( !player->isReady() )
        {
            QMessageBox error;
            error.setIcon(QMessageBox::Critical);
            error.setText( player->getErrorStr() );
            error.adjustSize();
            error.exec();
        }
    }
}

// --------------------------------------------------------------------- TSID

#ifdef XSID_HAVE_TSID
void MainDialog::addTSID()
{
    if ( sidFileNameHVSC.isEmpty() )
        return;
    static int prevTotalSecs = 0;
    int thisSecs = player->getEmuEngine()->getSecondsTotal() - prevTotalSecs;
    bool ret = myTSID->addTime( sidFileNameHVSC, thisSecs, player->getSidTuneWrapper()->getCurrentSong() );
#ifdef XSID_WB_DEBUG 
    cout << "TSID: " << sidFileNameHVSC << " / " << thisSecs << " / " << '#' << player->getSidTuneWrapper()->getCurrentSong() << " / return = " << ret << endl;
#endif
    prevTotalSecs = player->getEmuEngine()->getSecondsTotal();
    
}
#endif


// --------------------------------------------------------------------- LIRC

void MainDialog::lircEvent(int fh)
{
#ifdef XSID_HAVE_LIRC
  char *c=0;
  if(lirc_nextcode(&c) < 0)
    {
      qWarning("MainDialog::lircEvent(): error in lirc_nextcode()");
      return;
    }

  if(c==0)
    return;

  char *s=0;
  if(lirc_code2char(lircConfig, c, &s) < 0)
    {
      qWarning("MainDialog::lircEvent(): error in lirc_code2char()");
      return;
    }

  free(c);
  if(s==0)
    return;

  uint i;
  QString str=s;
  if(str=="Play")
    {
      emit playSongPressed();
      emit playSongReleased();
    }
  else if(str=="Stop")
    {
      emit stopSongPressed();
      emit stopSongReleased();
    }
  else if(str=="NextSong")
    {
      emit nextSongPressed();
      emit nextSongReleased();
    }
  else if(str=="PrevSong")
    {
      emit prevSongPressed();
      emit prevSongReleased();
    }
  else if(str=="Forward")
    {
      emit forwardSongPressed();
      emit forwardSongReleased();
    }
  else if(str=="Quit")
    {
      emit quit();
    }
  else if((i=str.toUInt()))
    {
      emit newSubSong(i);
    }

#if 0
    emit playerButtonsForceStop();
    
    emit openFile();
    emit saveAs();
    emit loadPlaylist();
    emit savePlaylistAs();
    emit saveHotlist();
    emit markTune();
    emit markPlaylist();
    
    emit openAudioDialog();
    emit openEmulatorDialog();
    emit openFilterDialog();
    emit openHVSC_Dialog();
    emit openHistoryDialog();
    emit openMixerDialog();
    
    emit openPlaylistDialog();
    emit openPlaylistEditDialog();
    emit openPlaylistOptions();
    emit addTuneToPlaylist();
    emit addDirToPlaylist();
    emit addTreeToPlaylist();
    emit addHotlistToPlaylist();
    emit updatePlaylist();
    
    emit toggleStilDialog();
    emit toggleSongLengthDB();

    emit updateSubSongSlider();

    emit setPlaylistEnabled(bool);
    emit setLoopPlayEnabled(bool);
    emit setRandomPlayEnabled(bool);
#endif

#endif  // XSID_HAVE_LIRC
}
