/***************************************************************************
 *   Copyright (C) 2004 by Fred Schaettgen <kdebluetooth@schaettgen.de>    *
 *   Copyright (C) 2004 by Alex Ibrado <alex@kdex.org>                     *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <qpixmap.h>
#include <kglobal.h>
#include <kconfig.h>
#include <kiconloader.h>

#include <qobject.h>
#include <qtextedit.h>
#include <qcheckbox.h>
#include <qpushbutton.h>
#include <qbuttongroup.h>
#include <qspinbox.h>
#include <qdatastream.h>
#include <qtable.h>
#include <qtabwidget.h>
#include <qregexp.h>
#include <kguiitem.h>
#include <klocale.h>
#include <klineedit.h>
#include <kaction.h>
#include <kpopupmenu.h>
#include <kmessagebox.h>
#include <kapplication.h>
#include <kstdaction.h>
#include <kdialogbase.h>
#include <klistview.h>
#include <kaboutapplication.h>
#include <kurlrequester.h>
#include <kurlcompletion.h>
#include <krun.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <kstandarddirs.h>
#include <kdesktopfile.h>
#include <kdebug.h>
#include <unistd.h>

#include <algorithm>

#include "kbemusedsrv.h"
#include "xmmscontroller.h"
#include "noatuncontroller.h"
#include "amarokcontroller.h"
#include "scriptscontroller.h"
#include "kurltableitem.h"
#include "maindialogbase.h"
#include "logdialogbase.h"
#include "dcopcall.h"

#define VERSION_MAJOR 1
#define VERSION_MINOR 73 

using namespace KBluetooth;

MainDialog::MainDialog(int s, QString addr, QString name) : 
    DCOPObject( "kbemusedsrv" ), KSystemTray( 0, "KBemusedSrv" )
{
    isStandalone = (s == 0);
    acceptClose = true;

    cfg = KGlobal::config();

    this->configDlgFrame = new KDialogBase(this, "config", true,
        "Bemused Server Configuration", 
        KDialogBase::Ok | KDialogBase::Apply | KDialogBase::User1 |
            (isStandalone? KDialogBase::Close : KDialogBase::Cancel),
        KDialogBase::Ok, false, KGuiItem(i18n("&Reset"), "reset")
    );

    this->configDlg = new MainDialogBase(configDlgFrame);
    this->configDlgFrame->setMainWidget(configDlg);
    connect(configDlgFrame, SIGNAL(applyClicked()),
        this, SLOT(slotApplyConfig()));
    connect(configDlgFrame, SIGNAL(okClicked()),
        this, SLOT(slotApplyConfigOk()));
    connect(configDlg->addButton, SIGNAL(clicked()),
        this, SLOT(slotAddDir()));
    connect(configDlg->deleteButton, SIGNAL(clicked()),
        this, SLOT(slotDeleteDir()));
    connect(this, SIGNAL(quitSelected()),
        this, SLOT(slotQuitSelected()));

    configDlgIsSetup = false;

    if(!isStandalone) {
        cfg->setGroup("main");
        curControllerIndex = cfg->readNumEntry("controllerIndex", 0);

        this->logDlgFrame = new KDialogBase(this, "protocollog", false,
            "Protocol Log", KDialogBase::Close);
        this->logDlg = new LogDialogBase(logDlgFrame);
        logDlgFrame->setMainWidget(logDlg);

        this->aboutDlg = new KAboutApplication(this);

        setPixmap(KGlobal::iconLoader()->loadIcon("kbemusedsrv", KIcon::Small, 16));

        socket = new RfcommSocket(this, "rfcommsocket");
        socket->setSocket(s);

        configAction = new KAction(i18n("&Configuration..."), 0,
            this, SLOT(slotShowConfig()), actionCollection(), "config");
        configAction->plug(contextMenu());

        logAction = new KAction(i18n("&Protocol Log..."), 0,
            this, SLOT(slotShowLog()), actionCollection(), "log");
        logAction->plug(contextMenu());

        KStdAction::aboutApp(this, SLOT(slotShowAbout()),
            actionCollection(), "about")->plug(contextMenu());

        connect(socket, SIGNAL(readyRead()),
            this, SLOT(slotSocketReadyRead()));
        connect(socket, SIGNAL(error(int)),
            this, SLOT(slotClose()));
        connect(socket, SIGNAL(connectionClosed()),
            this, SLOT(slotClose()));

        QString text = logDlg->textEdit->text();
        logDlg->textEdit->setText(text + "<br>" + 
            i18n("Incoming connection from %1 (%2)").arg(name).arg(addr));

    }
    // Keep alphabetical for the config screens
    // edit by Patrick Steiner
    controllers.append(new AmarokController(this));
    controllers.append(new NoatunController(this));
    controllers.append(new ScriptsController(this));
#ifdef HAVE_XMMS
    controllers.append(new XmmsController(this));
#endif

    // Make sure that the controller index is in the correct range
    int numControllers=controllers.count();
    if (curControllerIndex >= numControllers) {
        curControllerIndex = numControllers-1;
    }
    if (curControllerIndex < 0) {
        curControllerIndex = 0;
    }
    curController = controllers.at(curControllerIndex);
    loadConfig();
    if(isStandalone) {
        connect(configDlgFrame, SIGNAL(closeClicked()),
            this, SLOT(slotExitStandalone()));
        configure();
    } else {
        cfg->setGroup("main");
        QString startupController = cfg->readEntry("startupController", "Last used");
        if(startupController != "Last used") {
            Controller *controller = getControllerByName(startupController);
            if(controller) curController = controller;
        }

        launchApp(STARTUP); // When server starts up
    }
}

void MainDialog::addSendLogTxt(QString text)
{
    logDlg->textEdit->setText(logDlg->textEdit->text() +
    QString("<font color=#008800>%1</font>").arg(text));
    logDlg->textEdit->scrollToBottom();
}

void MainDialog::addRecvLogTxt(QString text)
{
    logDlg->textEdit->setText(logDlg->textEdit->text() +
    QString("<font color=#880000>%1</font>").arg(text));
    logDlg->textEdit->scrollToBottom();
}

void MainDialog::configure()
{
    slotShowConfig();
}


void MainDialog::slotSocketReadyRead()
{
    while (socket->size() > 0) {
        handlePacket();
    }
}

void MainDialog::handlePacket()
{
    char cmdBuf[6];
    char fileBuf[1024];

    socket->readBlock(cmdBuf, 1);
    socket->readBlock(cmdBuf+1, 1);
    socket->readBlock(cmdBuf+2, 1);
    socket->readBlock(cmdBuf+3, 1);

    QString cmd = QString(QCString(cmdBuf, 5));
    QDataStream stream(socket);
    stream.setByteOrder(QDataStream::BigEndian);

    addRecvLogTxt("<br/>" + cmd + "... ");

    if(activeControllers == 0) {
        addRecvLogTxt("No active controllers!");
        return;
    }
        
    if (cmd == "CHCK") {
        // Checks that the server is running OK
        stream.writeBytes("Y", 1);
        addSendLogTxt("Y");
    }
    else if (cmd == "DINF") {
        // Gets detailed information about the current song
        unsigned int bitrate;
        unsigned int sampleRate;
        unsigned int channels;
        curController->commandDINF(bitrate, sampleRate, channels);
        stream.writeBytes("DINFACK", 7);
        stream << Q_UINT32(bitrate) << Q_UINT32(sampleRate) << Q_UINT32(channels);

        addSendLogTxt(QString("DINFACK: br=%1 sr=%2 ch=%3...")
            .arg(bitrate).arg(sampleRate).arg(channels));
    }
    else if (cmd == "DLST") {
        // Lists a specific directory
        QString dirname = readFilenameStructure();
        addRecvLogTxt(QString(" dir=%1<br>").arg(dirname));

        QStringList list = getDirList(dirname, NONRECURSIVE);

        cfg->setGroup("lists");
        if(dirname.isEmpty() && cfg->readBoolEntry("appendControllers",true))
            list += getControllerList();

        socket->writeBlock("LISTACK", 7);
        sendPathList(list, UNEXPANDED_DIR_NODE);
    }
    else if (cmd == "DOWN") {
        // Downloads the specified file
        QString filename = readFilenameStructure();
        filename = filename.replace("\\", "/");
        QString expandedName = expandFilename(filename);

        socket->writeBlock("DOWNACK", 7);
        if (expandedName != QString::null) {
            addRecvLogTxt(QString("filename: [%1]").arg(expandedName));

            QFile f(expandedName);
            if(f.open( IO_ReadOnly )) {
                int r;
                do {
                    r=f.readBlock(fileBuf, 1024);
                    socket->writeBlock(fileBuf, 1024);
                } while(r>0);
                f.close();
            } else {
                addRecvLogTxt(QString("Warning: couldn't open filename: [%1]")
                .arg(filename));
            }
        }
        else {
            addRecvLogTxt(QString("Warning: couldn't expand filename: [%1]")
                .arg(filename));
        }
    }
    else if (cmd == "FADE") {
        // Stops the current song by fading it out
        curController->commandFADE();
    }
    else if (cmd == "FFWD") {
        // Fast-forwards by five seconds (hold NEXT down at least 2s)
        curController->commandFFWD();
    }
    else if (cmd == "FINF") {
        // Gets information about the specified file
        QString filename = readFilenameStructure();
        filename = filename.replace("\\", "/");
        QString expandedName = expandFilename(filename);
        if (expandedName != QString::null) {
            unsigned int size=0;
            QFile f(expandedName);
            if(f.open( IO_ReadOnly )) {
                size=f.size();
                f.close();
            }
            addRecvLogTxt(QString("filename: [%1] length: [%2]").arg(expandedName).arg(size));
            socket->writeBlock("FINFACK", 7);
            stream << Q_UINT32(size);
        }
        else {
            addRecvLogTxt(QString("Warning: couldn't expand filename: [%1]")
                .arg(filename));
        }
    }
    else if (cmd == "INFO" || cmd == "INF2") {
        // Gets information from XMMS
        Controller::SongInfo info;
        if(cmd == "INF0") {
            info=curController->commandINFO();
        } else {
            info=curController->commandINF2();
        }
        socket->writeBlock("INFOACK", 7);
        stream << Q_UINT8(info.playState) << Q_UINT32(info.length)
            << Q_UINT32(info.currentTime) << Q_UINT8(info.shuffle) << Q_UINT8(info.repeat);

        socket->writeBlock(info.trackName.utf8(), info.trackName.length());
        if(cmd == QString("INF2"))
            stream << Q_UINT8(0); 

        addSendLogTxt(QString("INFOACK st%1 l%2 t%3 sh%4 re%5 n%6...")
            .arg(info.playState).arg(info.length).arg(info.currentTime)
            .arg(info.shuffle).arg(info.repeat).arg(info.trackName));
    }
    else if (cmd == QString("LIST")) {
        // Lists the entire directory tree
        QStringList list;
        cfg->setGroup("lists");
        if(cfg->readBoolEntry("appendControllers",true))
            list = getControllerList();
        list += getDirList("", RECURSIVE);

        socket->writeBlock("LISTACK", 7);
        sendPathList(list, DIR_NODE);
    }
    else if (cmd == QString("NEXT")) {
        // Plays the next song in the playlist
        curController->commandNEXT();
    }
    else if (cmd == QString("PAUS")) {
        // Pauses the current song
        curController->commandPAUS();
    }
    else if (cmd == QString("PLAY") || cmd == QString("LADD")) {
        // Plays the specified file

        QString filename = readFilenameStructure();
        QString appname = filename.mid(1, filename.find(".mp3")-1);
        int controllerIndex;
        Controller *controller = getControllerByName(appname, controllerIndex);
        if (controller) {
            if((Controller *)curController == controller) {
                launchApp(RESELECT); // When controller is re-selected
            } else {
                curControllerIndex = controllerIndex;
                addRecvLogTxt(QString("appname [%1] ").arg(appname));
                curController = controller;
            }
        }
        else {
            QString aliasController;
            // TODO: Playlists should store aliased entries since locations may change
            QString expandedName = expandFilename(filename, &aliasController);
            if(!aliasController.isEmpty() && aliasController != "Default") {
                Controller *controller = getControllerByName(aliasController);
                if(controller)
                    curController = controller;
            }
            
            if(curController && curController->initialCommand) {
                launchApp(INITIALCMD);
            }

            if (expandedName != QString::null) {
                addRecvLogTxt(QString("filename: [%1]").arg(expandedName));
                
                cfg->setGroup("main");
                if(cfg->readBoolEntry("useFileExts", false)) {
                    curControllerIndex = cfg->readNumEntry("controllerIndex", 0);
                    // Find appropriate controller if necessary
                    
                    // 1. Get the extension
                    QFileInfo fi(expandedName);
                    QString ext = fi.extension(FALSE).lower();
                    
                    // 2. Find the first controller which contains the file's extension
                    bool ctrlChanged = false;
                    for(unsigned int ci=0; ci < controllers.count(); ++ci) {
                        Controller *controller=controllers.at(ci);
                        QString ctrlName=controller->name();
                        cfg->setGroup(ctrlName);
                        
                        QString exts = cfg->readEntry("fileExts", controller->fileExts());
                        if(exts.contains(QRegExp(".*" + ext + "[,;]*")) > 0) {
                            if((int) ci != curControllerIndex) {
                                QString text = logDlg->textEdit->text();
                                logDlg->textEdit->setText(text + "<br>" + 
                                    i18n("Set controller to %1 ").arg(ctrlName));

                                curControllerIndex = ci;
                                ctrlChanged = true;
                            }
                            break;
                        }
                    }
                    curController = controllers.at(curControllerIndex);
                    
                    if(ctrlChanged)
                        launchApp(USERPREFS); // Controller changes due to prefs
                        
                } else {
                    addRecvLogTxt(QString("useFileExts set to FALSE"));
                }
        
                if(cmd == "PLAY" ) {
                    cfg->setGroup("lists");
                    curController->commandPLAY(expandedName, 
                        cfg->readBoolEntry("playClears", false));
                } else
                    curController->commandLADD(expandedName);
            }
            else {
                addRecvLogTxt(QString("Warning: couldn't expand filename: [%1]")
                    .arg(filename));
            }
        }
    }
    else if (cmd == QString("PLST")) {
        // Writes the current playlist to the phone
        socket->writeBlock("PLSTACK", 7);

        if(curController && curController->initialCommand) {
            launchApp(INITIALCMD);
        }

        Q_UINT16 pos;
        int tempPos; 
        QStringList playlist = curController->commandPLST(tempPos);
        pos=tempPos;

        QString playlistbuf = playlist.join("\n")+"\n";
        stream << pos;
        socket->writeBlock("#\n", 2);
        socket->writeBlock(playlistbuf.utf8(), playlistbuf.length()+1);
    }
    else if (cmd == QString("PREV")) {
        // Plays the previous song in the playlist
        curController->commandPREV();
    }
    else if (cmd == QString("REPT")) {
        // Enables or disables repeat mode
        Q_UINT8 v;
        socket->readBlock((char*)&v, 1);
        addRecvLogTxt(QString(" v=%1...").arg(v));
        curController->commandREPT(v>0);
    }
    else if (cmd == QString("RMAL")) {
        // Removes all songs from the playlist
        curController->commandRMAL();
    }
    else if (cmd == QString("RWND")) {
        // Rewinds by five seconds (hold PREV down at least 2s)
        curController->commandRWND();
    }
    else if (cmd == QString("SHFL")) {
        // Enables or disables shuffle mode
        Q_UINT8 v;
        socket->readBlock((char*)&v, 1);
        addRecvLogTxt(QString(" v=%1...").arg(v));
        curController->commandSHFL(v>0);
    }
    else if (cmd == QString("SHUT")) {
        // "Shuts down" the PC
        cfg->setGroup("main");
        int shutFx = cfg->readNumEntry("shutFx",
            ( cfg->readBoolEntry("allowShutdown", false) ? 3 : 0)
        );

        switch( shutFx ) {
            case 1: {
                // Lock screen
                DCOPCall lockScreen(KApplication::dcopClient(),
                    "kdesktop", "KScreensaverIface");
                lockScreen.call("lock()");
                addRecvLogTxt("Locked screen");
                break;
            }
            case 2: {
                // Execute command
                runShutCommand("shutCmd");
               break;
            }
            case 3:
                // Logout
                kapp->requestShutDown (
                    KApplication::ShutdownConfirmNo,
                    KApplication::ShutdownTypeNone,
                    KApplication::ShutdownModeForceNow
                );
                break;

            case 5:
                // Reboot
                kapp->requestShutDown (
                    KApplication::ShutdownConfirmNo,
                    KApplication::ShutdownTypeReboot,
                    KApplication::ShutdownModeForceNow
                );
                break;

            case 6:
                // Shutdown 
                kapp->requestShutDown (
                    KApplication::ShutdownConfirmNo,
                    KApplication::ShutdownTypeHalt,
                    KApplication::ShutdownModeForceNow
                );
                break;
        }
    }
    else if (cmd == QString("SLCT")) {
        // Selects song at [index] in playlist
        Q_UINT16 index;
        stream >> index;
        addRecvLogTxt(QString(" index=%1...").arg(index));
        curController->commandSLCT(index);
    }
    else if (cmd == QString("STEN")) {
        // Stops playing at the end of the current song
        curController->commandSTEN();
    }
    else if (cmd == QString("STOP")) {
        // Stops the current song immediately
        curController->commandSTOP();
    }
    else if (cmd == QString("STRT")) {
        // Starts playing the current song
        curController->commandSTRT();
    }
    else if (cmd == QString("VOLM")) {
        // Sets the volume to the value specified
        Q_UINT8 vol;
        socket->readBlock((char*)&vol, 1);
        addRecvLogTxt(QString(" vol=%1...").arg(vol));
        curController->commandVOLM(vol);
    }
    else if (cmd == QString("FULL")) {
        // Toggle fullscreen state
        cfg->setGroup("main");

        int fullFx = cfg->readNumEntry("fullFx", 0);
        bool handled = curController->commandFULL();
        if(fullFx == 1) { // Execute command
            bool execUnhandled = cfg->readBoolEntry("fullExecUnhandled", true);
            if((!execUnhandled) || (execUnhandled && !handled)) {
                runShutCommand("fullCmd");
            }
        }
    }
    else if (cmd == QString("SEEK")) {
        // Seek to position in seconds (e.g. for btAmp)
        Q_UINT32 sec;
        stream >> sec;
        addRecvLogTxt(QString(" sec=%1...").arg(sec));
        curController->commandSEEK(sec);
    }
    else if (cmd == QString("VERS")) {
        // Send server version
        addSendLogTxt(QString("VERSACK vers=%1.%2 ...").arg(VERSION_MAJOR).arg(VERSION_MINOR));
        socket->writeBlock("VERSACK", 7);
        stream << Q_UINT8(VERSION_MAJOR) << Q_UINT8(VERSION_MINOR);
    }
    else if (cmd == QString("PLEN")) {
        // Return playlist length
        Q_UINT32 plen=curController->commandPLEN();
        addSendLogTxt(QString("PLENACK len=%1...").arg(plen));
        socket->writeBlock("PLENACK", 7);
        stream << plen;
    }
    else if (cmd == QString("GVOL")) {
        // Get current volume
        Q_UINT8 curVol=0;
        if(curController->commandGVOL(curVol)) {
            // Controller supports GVOL
            addSendLogTxt(QString("GVOLACK vol=%1...").arg(curVol));
            socket->writeBlock("GVOLACK", 7);
            stream << curVol;
        } else {
            addSendLogTxt(QString("GVOLNAK..."));
            socket->writeBlock("GVOLNAK", 7);
        }
    }
    else {
        addRecvLogTxt(QString("Unknown Command (%1)...").arg(cmd));
    }
    addRecvLogTxt(" done<br/>");
}

QString MainDialog::expandFilename(QString filename, QString* controller)
{

    filename = filename.replace("\\", "/");
    QString alias = filename.left(filename.find("/"));
    std::vector<MediaDirInfo>::iterator it;
    for (it = mediaDirInfos.begin(); it!=mediaDirInfos.end(); ++it) {
        if (alias == it->alias) {
            if(controller) *controller = it->controllerName;
            return it->path+filename.right(filename.length()-filename.find("/"));
        }
    }
    return QString::null;
}

QStringList MainDialog::getDirList(QString clientpath, bool recurse)
{
    QStringList ret;
    QStringList paths;
    std::vector<MediaDirInfo>::iterator it;
    QString alias;

    clientpath = clientpath.replace("\\", "/");
    if(!recurse && !clientpath.isEmpty()) {
        QString serverpath = "";
        // ... First, expand it
        // TODO: Determine why expandFilename has problems in this case
        for (it = mediaDirInfos.begin(); it!=mediaDirInfos.end(); ++it) {
            if (clientpath.startsWith(it->alias)) {
                serverpath=QDir(it->path).absPath() +
                    clientpath.right(clientpath.length()-it->alias.length());
            }
        }

        // Get its subdirectories and add them
        QDir serverDir = QDir(serverpath);
        QStringList files = serverDir.entryList(QDir::Dirs, QDir::DefaultSort);
        for (unsigned int n=0; n<files.count(); ++n) {
            QString path = files[n];
            if (!path.startsWith(".")) 
                ret.append(path+"/");
        }

        // Get the files and add them
        files  = serverDir.entryList(QDir::Files, QDir::DefaultSort);
        for (unsigned int n=0; n<files.count(); ++n) {
            QString file = files[n];
            ret.append(file);
        } 
    } else {
        for (it = mediaDirInfos.begin(); it!=mediaDirInfos.end(); ++it) {
            if(recurse) 
                paths = getRecursiveDirList(it->path);
            else
                paths.append(it->path);

            for (unsigned int n=0; n<paths.count(); ++n) {
                QString path = paths[n];
                if (path.startsWith(it->path)) {
                    ret.append(it->alias + "/" +
                        path.right(path.length()-it->path.length()));
                }
            }
        }
    }

    if(ret.count()==0  &&  clientpath.isEmpty()) {
        ret.append("  No aliases found!");
        ret.append("  Please add some");
        ret.append("  then Refresh list.");
    }

    return ret;
}

QStringList MainDialog::getRecursiveDirList(QDir dir)
{
    QStringList subDirs = dir.entryList(QDir::Dirs, QDir::DefaultSort);
    QStringList ret;
    for (unsigned int n=0; n<subDirs.count(); ++n) {
        if (!subDirs[n].startsWith(".")) {
            ret += getRecursiveDirList(QDir(dir.absPath()+"/"+subDirs[n]));
        }
    }
    QStringList files = dir.entryList(QDir::Files, QDir::DefaultSort);
    for (unsigned int n=0; n<files.count(); ++n) {
        QString filepath = dir.absPath()+"/"+files[n];
        ret.append(filepath);
    }
    return ret;
}

QStringList MainDialog::getControllerList()
{
    QStringList ret;
    for (unsigned int n=0; n<controllers.count(); ++n) {
        // Add the mp3 extension so they shows up in 
        //  "Music files only" and "MP3s only"
        ret.append(QString("#%1.mp3").arg(controllers.at(n)->name()));
    }
    return ret;
}

Controller* MainDialog::getControllerByName(QString name)
{
    int temp;
    return getControllerByName(name, temp);
}

Controller* MainDialog::getControllerByName(QString name, int& index)
{
    Controller* ret = NULL;
    index = -1;
    for (unsigned int n=0; n<controllers.count(); ++n) {
        if (controllers.at(n)->name() == name) {
            ret = controllers.at(n);
            index = n;
        }
    }
    return ret;
}

QString MainDialog::readFilenameStructure()
{
    char len[2];
    socket->readBlock(len, 2);
    uint buflen = (len[0]<<8)+len[1];
    QByteArray buf(buflen);
    char *cbuf = buf.data();
    socket->readBlock(cbuf, buflen);
    return QString(QCString(buf.data(), buflen+1));
}

void MainDialog::sendDirNode(Q_UINT8 nodeType, QString name)
{
    socket->writeBlock((char*)(&nodeType), 1);
    socket->writeBlock(name.utf8(), name.length()+1);
}

void MainDialog::sendPathList(QStringList pathlist, Q_UINT8 dir_type)
{
    // Send root node
    sendDirNode(ROOT_NODE | DIR_NODE, "");
    sendSubPathList(pathlist, dir_type);

    // Send terminator node
    sendDirNode(0xFF, "");
}

void MainDialog::sendSubPathList(QStringList pathlist, Q_UINT8 dir_type)
{
    // Sort the file list
    pathlist.sort();

    QStringList filenames;
    QStringList dirnames;
    // Iterate over all paths and collect a list
    //   of top directory names and filenames
    for (unsigned int n=0; n<pathlist.size(); ++n) {
        int slashPos = pathlist[n].find("/");
        if (slashPos == -1) {
            QString filename = pathlist[n];
            if (filenames.find(filename) == filenames.end()) {
                filenames.append(filename);
            }

        }
        else {
            QString dir = pathlist[n].left(slashPos);
            if (dirnames.find(dir) == dirnames.end()) {
                dirnames.append(dir);
            }
        }
    }

    // Add the directories
    for (unsigned int n=0; n<dirnames.count(); ++n) {
        QString dirname = dirnames[n];
        if(dirname.isEmpty()) continue;

        Q_UINT8 nodeType;
        if (dirnames.count() == 1 && (filenames.count() == 0)) {
            nodeType = (ONLY_CHILD_NODE | dir_type );
        }
        else if (n == 0) {
            nodeType = (CHILD_NODE | dir_type );
        }
        else if ((n == dirnames.count()-1) && (filenames.count() == 0)) {
            nodeType = (LAST_SIBLING_NODE | dir_type );
        }
        else {
            nodeType = (SIBLING_NODE | dir_type );
        }
        sendDirNode(nodeType, dirname.left(dirname.length()));

        QStringList subpaths;
        for (unsigned int m=0; m<pathlist.count(); ++m) {
            if (pathlist[m].startsWith(dirname)) {
                QString subpath = pathlist[m].right(
                    pathlist[m].length()-dirname.length()-1);
                if (subpath.length() > 0) {
                    subpaths.append(subpath);
                }
            }
        }

        sendSubPathList(subpaths, dir_type);
    }

    // Add the files
    for (unsigned int n=0; n<filenames.count(); ++n) {
        QString filename = filenames[n];

        Q_UINT8 nodeType;
        if ((filenames.count() == 1) && (dirnames.count() == 0)) {
            nodeType = (ONLY_CHILD_NODE | FILE_NODE);
        }
        else if ((n == 0) && (dirnames.count() == 0)) {
            nodeType = (CHILD_NODE | FILE_NODE);
        }
        else if (n == filenames.count()-1) {
            nodeType = (LAST_SIBLING_NODE | FILE_NODE);
        }
        else {
            nodeType = (SIBLING_NODE | FILE_NODE);
        }

        sendDirNode(nodeType, filename);
    }
}

void MainDialog::loadConfig()
{
    cfg->setGroup("main");
    QStringList pathList = cfg->readListEntry("pathList");
    QStringList aliasList = cfg->readListEntry("aliasList");
    QStringList aliasControllerList = cfg->readListEntry("aliasControllerList");

    mediaDirInfos.clear();
    int numRows=std::min(pathList.count(), aliasList.count());
    configDlg->dirTable->setNumRows(numRows);

    for (int n=0; n<numRows; ++n) {
        MediaDirInfo info;
        info.alias = aliasList[n];
        info.path = pathList[n];

        QString ctrlName = aliasControllerList[n];
        if(ctrlName.isEmpty())
            ctrlName = "Default";

        info.controllerName = ctrlName;
        mediaDirInfos.push_back(info);

        QTableItem *cellAlias= new QTableItem(configDlg->dirTable,
            QTableItem::WhenCurrent, info.alias);
        configDlg->dirTable->setItem(n, 0, cellAlias);

        KURLTableItem *cellAliasPath = new KURLTableItem(configDlg->dirTable, 
            info.path, KURLCompletion::DirCompletion, KFile::Directory);
        configDlg->dirTable->setItem(n, 1, cellAliasPath);
        
        if(ctrlName != "Default") {
            Controller *controller = getControllerByName(ctrlName);
            if((controller && !controller->isActive()) || !controller)  {
                ctrlName="Default";
            } else {
                ctrlName="#"+ctrlName;
            }
        }

        QTableItem *cellController = new QTableItem(configDlg->dirTable,
            QTableItem::Never, ctrlName);
        configDlg->dirTable->setItem(n, 2, cellController);
 
    }

    // Controllers

    configDlg->ctrlStartup->clear();
    configDlg->ctrlStartup->insertItem("Last used");

    QString startupController= cfg->readEntry("startupController", "Last used");

    // Do it this way so that new controllers don't mess up the config
    int startupIndex=0;
    int activeCtr=0;

    configDlg->ctrlTable->setNumRows(controllers.count());
    for(unsigned int n=0; n<controllers.count(); n++) {
        Controller *controller=controllers.at(n);
        QString ctrlName=controller->name();
        cfg->setGroup(ctrlName);

        bool active = cfg->readBoolEntry("active", controller->isActive());
        QString fileExts = cfg->readEntry("fileExts", controller->fileExts());
        QString pathToApp = cfg->readEntry("appPath", controller->pathToApp());
        controller->setActive(active);

        QCheckTableItem *cellActive = new QCheckTableItem(configDlg->ctrlTable,"#"+ctrlName);
        cellActive->setChecked(active);
        configDlg->ctrlTable->setItem(n, 0, cellActive);
        
        QTableItem *cellExts = new QTableItem(configDlg->ctrlTable,
            QTableItem::WhenCurrent, fileExts);
        configDlg->ctrlTable->setItem(n, 1, cellExts);
        
        KURLTableItem *cellAppPath = new KURLTableItem(configDlg->ctrlTable,
            pathToApp, KURLCompletion::ExeCompletion);
        configDlg->ctrlTable->setItem(n, 2, cellAppPath);

        if(active) {
            activeCtr++;
            configDlg->ctrlStartup->insertItem("#"+ctrlName);
            if(ctrlName == startupController) 
                startupIndex=n+1; 
        }
    }
    
    activeControllers = activeCtr;
    refreshControllersTab(activeCtr, startupIndex);

}

void MainDialog::setupConfigDlg() 
{
    kdf = new KDesktopFile("kbluetoothd_kbemusedsrv.desktop", false, "services");
    kdf->setDesktopGroup();

    // Close standalone configuration dialog if present
    bool shutdownStandalone = false;
    DCOPCall shutdownConf(KApplication::dcopClient(), 
        "kbemusedsrv-config-", "MainApplication-Interface");
    shutdownStandalone=(shutdownConf.broadcast("quit()").count()>0);

    if(shutdownStandalone) {
        KMessageBox::information(NULL,
            i18n("The standalone configuration dialog that was running \
            has been closed."), 
            i18n("Bemused Server"), 
            "notice_shutdown_standalone"
        );
        refreshWarningsButton();
    }

    // Why is this cast required?!
    connect((QButton *)configDlg->shutExec, SIGNAL(toggled(bool)),
        configDlg->shutExecCmd, SLOT(setEnabled(bool)));
    connect((QButton *)configDlg->fullExec, SIGNAL(toggled(bool)),
        configDlg->fullExecCmd, SLOT(setEnabled(bool)));
    connect((QButton *)configDlg->fullExec, SIGNAL(toggled(bool)),
        configDlg->fullExecUnhandled, SLOT(setEnabled(bool)));

    connect(configDlg->restoreWarnings, SIGNAL(clicked()),
        this, SLOT(slotRestoreWarnings()));

    // Reset
    connect(configDlgFrame, SIGNAL(user1Clicked()),
        this, SLOT(slotReloadConfig()));

    configDlg->dirTable->setLeftMargin(0);
    configDlg->dirTable->setColumnWidth(1, 200);
    configDlg->dirTable->setColumnStretchable(2, true);

    configDlg->ctrlTable->setLeftMargin(0);
    configDlg->ctrlTable->setColumnWidth(1, 200);
    configDlg->ctrlTable->setColumnStretchable(2, true);

    connect(configDlg->dirTable, SIGNAL(currentChanged (int, int)),
        this, SLOT(slotDirTableCellChanged(int, int)));
    connect(configDlg->shutGroup, SIGNAL(pressed(int)),
        this, SLOT(slotShutFxSelected(int)));

    configDlg->shutExecCmd->completionObject()->setMode(KURLCompletion::ExeCompletion);
    configDlg->fullExecCmd->completionObject()->setMode(KURLCompletion::ExeCompletion);

    configDlgIsSetup = true;
}

void MainDialog::refreshControllersTab() 
{
    int activeCnt = 0;
    int startupIndex = 0;

    cfg->setGroup("main");
    QString startupController = cfg->readEntry("startupController", "Last used");
    for(unsigned int n=0; n<controllers.count(); ++n) {
        if(controllers.at(n)->isActive()) {
            activeCnt++;
            if(controllers.at(n)->name() == startupController) {
                startupIndex = n+1;
            }
        }
    }

    refreshControllersTab(activeCnt,startupIndex);
}

void MainDialog::refreshControllersTab(int activeCnt, int startupIndex) 
{
    if(activeCnt == 0) {
        // No active controllers
        configDlg->ctrlStatus->setText(i18n("<b>Warning:</b> No active controllers -- the server has been disabled."));
        configDlg->ctrlStartup->setEnabled(false);
    } else {
        configDlg->ctrlStartup->setEnabled(true);
        configDlg->ctrlStatus->setText(QString(i18n("Current controller: <b>#%1</b>")).
            arg(curController->name()));
        configDlg->ctrlStartup->setCurrentItem(startupIndex);
    } 
    configDlg->ctrlTable->setCurrentCell(configDlg->ctrlTable->currentRow(), -1);
}

void MainDialog::refreshAliasesTab() 
{
    if(configDlg->dirTable->numRows() == 0) {
        // No aliases
        configDlg->dirStatus->setText(i18n("<b>Warning:</b> No aliases -- the client can't add items to the playlist."));
        configDlg->deleteButton->setEnabled(false);
    } else {
        configDlg->deleteButton->setEnabled(true);
        configDlg->dirStatus->setText(i18n("Refresh the client's Browse list after applying changes."));
    } 
    configDlg->dirTable->setCurrentCell(configDlg->dirTable->currentRow(), -1);
    
    cfg->setGroup("main");
    configDlg->useFileExts->setChecked(cfg->readBoolEntry("useFileExts", false));
}

void MainDialog::refreshClientTab()
{
    cfg->setGroup("main");
    int choice = cfg->readNumEntry("shutFx", 
        ( cfg->readBoolEntry("allowShutdown", false) ? 3 : 0)
    );
    configDlg->shutGroup->setButton(choice);
    QString cmd = cfg->readEntry("shutCmd");
    configDlg->shutExecCmd->setURL(cmd);

    choice = cfg->readNumEntry("fullFx", 0);
    configDlg->fullGroup->setButton(choice);
    cmd = cfg->readEntry("fullCmd");
    configDlg->fullExecCmd->setURL(cmd);
    bool b = cfg->readBoolEntry("fullExecUnhandled", true);
    configDlg->fullExecUnhandled->setChecked(b);

    cfg->setGroup("lists");
    configDlg->appendControllers->setChecked(cfg->readBoolEntry("appendControllers", true));
    configDlg->autoSave->setChecked(cfg->readBoolEntry("autoSave", false));
    configDlg->playClears->setChecked(cfg->readBoolEntry("playClears", false));
}

void MainDialog::refreshServerTab()
{
    cfg->setGroup("main");
    
    QStringList launchList = cfg->readListEntry("launchList");
    configDlg->launchStartup->setChecked(launchList.contains(QString("%1").arg(STARTUP)));
    configDlg->launchPlayAddPlst->setChecked(launchList.contains(QString("%1").arg(RESELECT)));
    configDlg->launchPrefs->setChecked(launchList.contains(QString("%1").arg(INITIALCMD)));
    configDlg->launchReselect->setChecked(launchList.contains(QString("%1").arg(USERPREFS)));

    //savedChannel=kdf->readNumEntry("X-KDE-KBLUETOOTHD-port");
    int spinChannel = configDlg->rfcSpin->value();

    if(spinChannel == 0) {
        // Check if kbluetoothd is running
        if(!KApplication::dcopClient()->isApplicationRegistered("kbluetoothd")) {
            int runDaemon = KMessageBox::warningYesNo( 0,
                i18n("KBluetoothD does not seem to be running. Start it now?"),
                i18n("Bemused Server"), i18n("Start"), i18n("Do Not Start")
            );
            if (runDaemon == KMessageBox::Yes) {
                QString pathToKBtD = KGlobal::dirs()->findExe("kbluetoothd", getenv("PATH"));
                if (pathToKBtD.isEmpty()) {
                    KMessageBox::error( 0,
                        i18n("Cannot find kbluetoothd! Please check your installation."),
                        i18n("Bemused Server")
                    );
                } else {
                    KRun::runCommand(pathToKBtD);
                    unsigned int timeout = 0;
                    while (!KApplication::dcopClient()->isApplicationRegistered("kbluetoothd")
                        && ++timeout<=30) ::sleep(1);
                }
            }
        }

        // Get the actual channel as used by kbluetoothd
        DCOPCall getChannel(KApplication::dcopClient(), 
            "kbluetoothd", "MetaServer");
        getChannel.args() << QString("kbemusedsrv") << QString("RfcommChannel");
        if(getChannel.call("resources(QString,QString)") == "QString") {
            curRfcommChannel = 0;
            QString rfcString;
            getChannel.ret() >> rfcString;
            curRfcommChannel = (rfcString.isNull()) ?  rfcString.toInt() : 27; // original default
        } else {
            configDlg->rfcSpin->setEnabled(false);
        }
        spinChannel = curRfcommChannel; 
    }

    configDlg->rfcSpin->setValue(spinChannel);
    refreshWarningsButton();
}

void MainDialog::refreshWarningsButton()
{
    configDlg->restoreWarnings->setEnabled(
        cfg->hasGroup("Notification Messages")
    );
}


void MainDialog::runShutCommand(QString entry)
{
    QString cmd = cfg->readEntry(entry,"").stripWhiteSpace();
    if(!cmd.isEmpty()) {
        KRun::runCommand(cmd);
        addRecvLogTxt(QString("<br/>Executed %1").arg(cmd));
    }
}

void MainDialog::launchApp(LaunchTrigger trigger)
{
    cfg->setGroup("main");
    QStringList triggers = cfg->readListEntry("launchList");

    if(triggers.contains(QString("%1").arg(trigger)) > 0) {
        curController->initialCommand = false;

        QString pathToApp = curController->launchApp();

        addRecvLogTxt(QString("<br/>Executed %1 at location %2")
            .arg(pathToApp).arg(trigger));
    }
    if(curController) {
        cfg->setGroup("lists");
        if(cfg->readBoolEntry("autoSave", false)) {
            curController->loadPlaylist();
        }
    }
}

void MainDialog::slotApplyConfigOk()
{
    slotApplyConfig();
    if(isStandalone) {
        configDlgFrame->delayedDestruct();
        kapp->quit();
    }
}

void MainDialog::slotApplyConfig()
{
    // Controllers
    cfg->setGroup("main");
    QString comboStartup = configDlg->ctrlStartup->currentText();
    if(comboStartup.startsWith("#")) 
        comboStartup=comboStartup.mid(1);

    QString savedStartup = cfg->readEntry("startupController", comboStartup);

    QString targetStartup = (comboStartup == savedStartup) ? 
        savedStartup : comboStartup;

    configDlg->ctrlStartup->clear();
    configDlg->ctrlStartup->insertItem("Last used");

    int activeCtr=0;
    int startupIndex=0;

    QTable *ct=configDlg->ctrlTable;

    // Save current ctrlTable entries if still editing
    ct->setCurrentCell(ct->currentRow(), -1);

    for(int n=0; n<ct->numRows(); ++n) {
        QString ctrlName=ct->item(n,0)->text();
        if(ctrlName.startsWith("#"))
            ctrlName = ctrlName.mid(1);

        bool active = ((QCheckTableItem *)ct->item(n,0))->isChecked();
        cfg->setGroup(ctrlName);

        QString fileExts = ct->item(n,1)->text();
        QString pathToApp = ct->item(n,2)->text();
        //QString playlistMgr = ct->item(n,2)->text();

        Controller *controller = getControllerByName(ctrlName);
        controller->setActive(active);
        // If inactive, don't add to ctrlStartup ComboBox
        if(active) {
            activeCtr++;
            configDlg->ctrlStartup->insertItem("#"+ctrlName);
            // Compare strings since names may move around due to deactivations
            if(ctrlName == targetStartup) {
                startupIndex=activeCtr;
            }
        } 
        cfg->writeEntry("active", active);
        cfg->writeEntry("appPath", pathToApp);
        cfg->writeEntry("fileExts", fileExts);
        //cfg->writeEntry("playlistMgr", playlistMgr);
    }

    cfg->setGroup("main");
    cfg->writeEntry("useFileExts", configDlg->useFileExts->isChecked());
    cfg->writeEntry("startupController", targetStartup);

    // Aliases
    QStringList pathList;
    QStringList aliasList;
    QStringList aliasControllerList;
    mediaDirInfos.clear();

    ct=configDlg->dirTable;
    // Moving to column -1 saves current ctrlTable entries if still editing
    ct->setCurrentCell(ct->currentRow(), -1);

    for(int n=0; n<ct->numRows(); ++n) {
        MediaDirInfo info;
        info.alias = ct->item(n,0)->text();
        info.path = ct->item(n,1)->text();

        QString ctrlName = ct->item(n,2)->text();
        if(ctrlName.startsWith("#")) {
            ctrlName=ctrlName.mid(1);
            Controller *controller = getControllerByName(ctrlName);
            if(controller && !controller->isActive())  {
                // If controller is inactive, revert to Default
                if(ct->item(n,2)->rtti()==0) {
                    ctrlName="Default";
                    ct->item(n,2)->setText(ctrlName);
                } else
                    ((QComboTableItem *)ct->item(n,2))->setCurrentItem(0);
            }
        }
        info.controllerName = ctrlName;
        
        pathList.append(info.path);
        aliasList.append(info.alias);
        aliasControllerList.append(info.controllerName);
        mediaDirInfos.push_back(info);
    }

    cfg->writeEntry("pathList", pathList);
    cfg->writeEntry("aliasList", aliasList);
    cfg->writeEntry("aliasControllerList", aliasControllerList);
    refreshAliasesTab();
    
    // Server
    cfg->setGroup("main");
#if (QT_VERSION >= 0x030200)
    cfg->writeEntry("fullFx", configDlg->fullGroup->selectedId());
    cfg->writeEntry("shutFx", configDlg->shutGroup->selectedId());
#else
    cfg->writeEntry("fullFx", configDlg->fullGroup->id(
        configDlg->fullGroup->selected() 
    ));
    cfg->writeEntry("shutFx", configDlg->fullGroup->id(
        configDlg->shutGroup->selected() 
    ));
#endif


    QStringList launchList;

    if(configDlg->launchStartup->isChecked())
        launchList.append(QString("%1").arg(STARTUP));

    if(configDlg->launchReselect->isChecked())
        launchList.append(QString("%1").arg(RESELECT));

    if(configDlg->launchPlayAddPlst->isChecked())
        launchList.append(QString("%1").arg(INITIALCMD));

    if(configDlg->launchPrefs->isChecked())
        launchList.append(QString("%1").arg(USERPREFS));

    cfg->writeEntry("launchList", launchList);
    
    cfg->writeEntry("fullCmd", configDlg->fullExecCmd->url());
    cfg->writeEntry("fullExecUnhandled", configDlg->fullExecUnhandled->isChecked());
    cfg->writeEntry("shutCmd", configDlg->shutExecCmd->url());

    // Client: Lists
    cfg->setGroup("lists");
    cfg->writeEntry("appendControllers", configDlg->appendControllers->isChecked());
    cfg->writeEntry("autoSave", configDlg->autoSave->isChecked());
    cfg->writeEntry("playClears", configDlg->playClears->isChecked());

    // Save new RfcommChannel setting
    int newRfcommChannel = configDlg->rfcSpin->value();
    if (newRfcommChannel > 0 && newRfcommChannel != curRfcommChannel) {
        kdf->writeEntry("X-KDE-KBLUETOOTHD-port", newRfcommChannel);
        kdf->writeEntry("X-KDE-KBLUETOOTHD-autoPortRange", 0); // Don't deviate (?)
        kdf->sync();

        // Rebeuild the system configuration cache, since
        // kbluetoothd won't see the new channel, even though
        // the data was already written to disk,
        DCOPCall rebuildSyCoCa(KApplication::dcopClient(), 
            "kded", "kbuildsycoca");
        rebuildSyCoCa.call("recreate()");
        
        DCOPCall reloadKbtd(KApplication::dcopClient(), 
            "kbluetoothd", "MetaServer");
        reloadKbtd.call("reload()");

        /*KMessageBox::information(this, i18n("You need to restart KBemusedSrv " \
            "and/or KBluetoothD for the new RFCOMM Channel setting to take effect."), 
            i18n("Bemused Server"), 
            "new_rfcomm_channel"
        );*/
        curRfcommChannel = newRfcommChannel;
        refreshWarningsButton();
    }

    // Make the display reflect what has actually been saved (extraneous?)
    cfg->sync();
    loadConfig();
}

void MainDialog::slotAddDir()
{
    QTableItem *cellAlias= new QTableItem(configDlg->dirTable,
        QTableItem::WhenCurrent, "");

    KURLTableItem *cellAliasPath = new KURLTableItem(configDlg->dirTable, 
        "", KURLCompletion::DirCompletion, KFile::Directory);

    QTableItem *cellController = new QTableItem(configDlg->dirTable,
        QTableItem::Never, "Default");

    configDlg->dirTable->setNumRows(configDlg->dirTable->numRows()+1);
    int newRow = configDlg->dirTable->numRows()-1;

    configDlg->dirTable->setItem(newRow, 0, cellAlias);
    configDlg->dirTable->setItem(newRow, 1, cellAliasPath);
    configDlg->dirTable->setItem(newRow, 2, cellController);
    configDlg->dirTable->setCurrentCell(newRow, 0);
}

void MainDialog::slotDeleteDir()
{
    int curRow = configDlg->dirTable->currentRow();
    configDlg->dirTable->removeRow(curRow);
    int newRows = configDlg->dirTable->numRows();
    if(newRows < curRow) curRow--;
    if(curRow < 0) curRow = 0;
    configDlg->dirTable->setCurrentCell(curRow, -1);
    configDlg->deleteButton->setEnabled(newRows > 0);
}

void MainDialog::slotDirTableCellChanged(int row, int col)
{
    QStringList controllerList;
    controllerList.append("Default");
    for (unsigned int n=0; n<controllers.count(); ++n) {
        if(controllers.at(n)->isActive()) 
            controllerList.append("#"+controllers.at(n)->name());
    }

    for(int n=0; n<configDlg->dirTable->numRows(); ++n) {
        QTableItem *cellCheck = configDlg->dirTable->item(n, 2);
        if(!cellCheck) // as in Add
            continue;
        int cellType=cellCheck->rtti();
        QString celltext=cellCheck->text();

        if(n == row && col == 2 && cellType != 1) {
            // Show combo if there isn't one already
            QComboTableItem *cellController = 
                new QComboTableItem(configDlg->dirTable, controllerList);
            int ctrlIndex = 0;
            if(celltext != "Default") {
                getControllerByName(celltext.mid(1), ctrlIndex);
                ctrlIndex++;
            }
            cellController->setCurrentItem(ctrlIndex);
            configDlg->dirTable->setItem(row, 2, cellController); 
        } else if(cellType == 1) { 
            // Turn off combo
            QTableItem *cellPLMText = new QTableItem(configDlg->dirTable,
                QTableItem::Never, cellCheck->text());
            configDlg->dirTable->setItem(n, 2, cellPLMText);
        }
    }
}

void MainDialog::slotReloadConfig()
{
    loadConfig();
    refreshAliasesTab();
    refreshControllersTab();
    refreshClientTab();
    refreshServerTab();
}

void MainDialog::slotClose()
{
    cfg->setGroup("main");
    cfg->writeEntry("controllerIndex", curControllerIndex);
    close();
}

void MainDialog::slotShowConfig()
{
    loadConfig();

    if(!configDlgIsSetup) 
        setupConfigDlg(); 

    refreshAliasesTab();
    refreshControllersTab();
    refreshClientTab();
    refreshServerTab();

    configDlgFrame->show();

}

void MainDialog::slotShowLog()
{
    logDlgFrame->show();
}

void MainDialog::slotShowAbout()
{
    aboutDlg->show();
}


void MainDialog::slotShutFxSelected(int id) 
{
    if(id > 2) {
        // Logout, Reboot, or Shutdown
        int proceed = KMessageBox::warningContinueCancel(this,
            i18n("<p><b>This action will proceed without confirmation!</b>"
            "</b><br>You may lose any unsaved work at the time of %1</p>").arg(configDlg->shutGroup->find(id)->text()),
            i18n("Bemused Server"), 
            KStdGuiItem::cont(),
            "warning_noconfirm"
        );

        int shutFx = id;
        if(proceed == KMessageBox::Cancel) {
            // Reload old setting or disable
            cfg->setGroup("main");

            shutFx = cfg->readNumEntry("shutFx", 
                ( cfg->readBoolEntry("allowShutdown", false) ? 3 : 0)
            );

            if( shutFx == id )
                shutFx = 0;
        }
        configDlg->shutGroup->setButton(shutFx);
        refreshWarningsButton();
    }
}

void MainDialog::slotRestoreWarnings()
{
    cfg->deleteGroup("Notification Messages");
    configDlg->restoreWarnings->setEnabled(false);
}

void MainDialog::slotExitStandalone()
{
    configDlgFrame->delayedDestruct();
    kapp->quit();
}

void MainDialog::slotQuitSelected()
{
    int reallyQuit = KMessageBox::questionYesNo(this,
        i18n("Are you sure you want to quit?"),
        i18n("Bemused Server"), 
        KStdGuiItem::quit(),
        KStdGuiItem::cancel(),
        "warning_quit"
    );
    acceptClose = (reallyQuit == KMessageBox::Yes); 
    refreshWarningsButton();
}

void MainDialog::closeEvent(QCloseEvent *e) 
{
    if(acceptClose) {
        cfg->setGroup("lists");
        if(!isStandalone && cfg->readBoolEntry("autoSave", false)) {
            curController->savePlaylist();
        }
        e->accept();
    } else {
        e->ignore();
        acceptClose = true; // in case of close when client exits
    }
}

MainDialog::~MainDialog()
{
}

#include "kbemusedsrv.moc"
