/* -*- c++ -*-
 *
 * kmldonkey.cpp
 *
 * Copyright (C) 2003 Petter Stokke <ummo@hellokitty.com>
 * Copyright (C) 2003 Sebastian Sauer <mail@dipe.org>
 * Copyright (C) 2006 Christian Muehlhaeuser <chris@chris.de>
 *
 * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "kmldonkey.h"
#include "infolist.h"
#include "prefs.h"
#include "fontselector.h"
#include "serverpage.h"
#include "downloadpage.h"
#include "sharepage.h"
#include "friendpage.h"
#include "roompage.h"
#include "statspage.h"
#include "consolepage.h"
#include "search.h"
#include "debugpage.h"
#include "mlconfig.h"
#include "submitdialog.h"
#include "systemtray.h"
#include "setupwizard.h"

#include <qdragobject.h>
#include <kprinter.h>
#include <qpainter.h>
#include <qpaintdevicemetrics.h>
#include <qclipboard.h>
#include <qcheckbox.h>
#include <qsignalmapper.h>
#include <qtimer.h>
#include <qtooltip.h>

#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kmenubar.h>
#include <kstatusbar.h>
#include <kkeydialog.h>
#include <kaccel.h>
#include <kconfig.h>
#include <kurl.h>
#include <kmessagebox.h>
#include <knuminput.h>
#include <kcolorbutton.h>
#include <kcombobox.h>
#include <kcharsets.h>
#include <kedittoolbar.h>
#include <kstdaccel.h>
#include <kaction.h>
#include <kstdaction.h>
#include <kapplication.h>
#include <knotifyclient.h>
#include <knotifydialog.h>
#include <ktabwidget.h>
#include <kinputdialog.h>

#include <dcopclient.h>

#include <donkeyprotocol.h>
#include <hostmanager.h>
#include <donkeyhost.h>

#include <kdeversion.h>

KMLDonkey *KMLDonkey::App=0; // global access to the static instance-pointer

void enableActionList(QPtrList<KAction> list, bool enable)
{
    QPtrListIterator<KAction> it(list);
    for (; it.current(); ++it)
        it.current()->setEnabled(enable);
}


KMLDonkey::KMLDonkey()
    : DCOPObject("KMLDonkeyIface"),
      KMainWindow( 0, "KMLDonkey", Qt::WType_TopLevel )
{
    App = this;
    showStatusbarLabels = true;
    persistentReconnect = true;
    reconnect = 0;
    prefs = 0;
    donkey = new DonkeyProtocol(false, this);
    hostManager = new HostManager(this);
    lastHost = hostManager->defaultHostName();
    m_currentPage = 0;
    coreVersion = i18n("Core version string not (yet) available", "Not Available");

    connect(kapp, SIGNAL(aboutToQuit()), SLOT(finalise()));

    setAcceptDrops(true);
    setCentralWidget(buildInterface());
    setupActions();
    resize( QSize(607, 621).expandedTo(minimumSizeHint()) );
    setAutoSaveSettings();
    restoreState( KGlobal::config() );

    // Register with DCOP
    if ( !kapp->dcopClient()->isRegistered() ) {
        kapp->dcopClient()->registerAs( "kmldonkey" );
        kapp->dcopClient()->setDefaultObject( objId() );
    }

    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(checkReconnect()));
    timer->start(5000);

    connect(donkey, SIGNAL(signalConnected()), this, SLOT(coreConnected()));
    connect(donkey, SIGNAL(signalDisconnected(int)), this, SLOT(coreDisconnected(int)));
    connect(donkey, SIGNAL(clientStats(int64, int64, int64, int, int, int, int, int, int, int, QMap<int,int>*)),
            this, SLOT(updateStatus(int64, int64, int64, int, int, int, int, int, int, int, QMap<int,int>*)));
    connect(donkey, SIGNAL(coreVersion(const QString&)), this, SLOT(coreVersionReceived(const QString&)));

    connect(hostManager, SIGNAL(hostListUpdated()), SLOT(hostListUpdated()));

    if (!factory()->container("download_actions", this)) {
        showBadInstallDialog();
        deleteLater();
    }

    tray = new SystemTray(this);

    connectToCore();
}

KMLDonkey::~KMLDonkey()
{
}

void KMLDonkey::finalise()
{
    if (donkey->isConnected()) {
        if (searchPage) searchPage->stopAllSearches();
        donkey->disconnectFromCore(); // needed to get the sock.flush() and in general a nicer beaviour to "logout" correct
    }

    saveState( KGlobal::config() );
}

bool KMLDonkey::queryExit()
{
    // We need to handle the isHidden()-state at queryExit() cause if Quit
    // was choosen from the trayicon-popupmenu then we've called the
    // functions in that order; queryExit(), hide() (yes, hide() is always
    // called here *grr*), finalise(), ...
    hideMainWin = isHidden();
    return true;
}

void KMLDonkey::show()
{
    QByteArray args;
    QDataStream stream(args, IO_WriteOnly);
    if (hideMainWin && (showTrayIcon || closeToTrayIcon)) { // eval the hideMainWin only if we're able to restore the mainwindow ;-)
        stream << false;
    }
    else {
        KMainWindow::show();
        stream << true;
    }
    hideMainWin = false;
    updateTrayIcon();
    emitDCOPSignal("kmldonkeyAppeared(bool)", args);
}

void KMLDonkey::hide()
{
    KMainWindow::hide();
    updateTrayIcon();
    QByteArray args;
    QDataStream stream(args, IO_WriteOnly);
    stream << false;
    emitDCOPSignal("kmldonkeyAppeared(bool)", args);
}

void KMLDonkey::addPage(KMLDonkeyPage* page, const QIconSet& iconset, const QString& label)
{
    QWidget* p = dynamic_cast<QWidget*>(page);
    if (!p) return;
    pageTab->addTab(p, iconset, label);
    pages.append(page);
    page->plugGenericActions(this, SLOT(pageActionsChanged(KMLDonkeyPage*)));
}

void KMLDonkey::removePage(KMLDonkeyPage* page)
{
    QWidget* p = dynamic_cast<QWidget*>(page);
    if (!p) return;
    pageTab->removePage(p);
    pages.remove(page);
}

void KMLDonkey::activatePage(KMLDonkeyPage* page)
{
    QWidget* p = dynamic_cast<QWidget*>(page);
    if (!p) return;
    pageTab->showPage(p);
}

void KMLDonkey::activatePage(int page)
{
    pageTab->setCurrentPage(page);
}

void KMLDonkey::activateNextPage()
{
    int page = pageTab->currentPageIndex() + 1;
    if (page >= pageTab->count()) page = 0;
    activatePage(page);
}

void KMLDonkey::activatePreviousPage()
{
    int page = pageTab->currentPageIndex() - 1;
    if (page < 0) page = pageTab->count() - 1;
    activatePage(page);
}

QWidget* KMLDonkey::buildInterface()
{
    pageTab = new KTabWidget(this, "pages");
    pageTab->setTabReorderingEnabled(true);

    serverPage = new ServerPage(pageTab);
    searchPage = new SearchPage(pageTab);
    downloadPage = new DownloadPage(pageTab);
    sharePage = new SharePage(pageTab);
    statsPage = new StatsPage(pageTab);
    friendPage = new FriendPage(pageTab);
    roomPage = new RoomPage(pageTab);
    consolePage = new ConsolePage(pageTab);
    debugPage = 0;

    // Statusbar
    statInfo = new QLabel(this);
    statServer = new QLabel(this);
    statFiles = new QLabel(this);
    statShare = new QLabel(this);
    statTransfer = new QLabel(this);
    statRate = new QLabel(this);

    QToolTip::add(statServer, i18n("Server connected/total"));
    QToolTip::add(statFiles, i18n("Files finished/downloading"));
    QToolTip::add(statShare, i18n("Shared files total"));
    QToolTip::add(statTransfer, i18n("Transfer up/down"));
    QToolTip::add(statRate, i18n("Speed up/down"));

    statusBar()->addWidget(statInfo,1);
    statusBar()->addWidget(statServer);
    statusBar()->addWidget(statFiles);
    statusBar()->addWidget(statShare);
    statusBar()->addWidget(statTransfer);
    statusBar()->addWidget(statRate);

    // Interface done, we now need to restore some state that needs to be done before anything else is called.

    KConfig* conf = KGlobal::config();
    KIconLoader* icons = KGlobal::iconLoader();
    conf->setGroup("State");
    QStringList pageOrder = conf->readListEntry("PageOrder");
    QStringList::Iterator it;
    it = pageOrder.find("debugPage");
    if (it != pageOrder.end())
        pageOrder.remove(it);
    if (pageOrder.empty() || pageOrder.count() != 8) // Update this to reflect number of pages when adding one!
        pageOrder = QStringList::split(",", "serverPage,searchPage,downloadPage,sharePage,statsPage,friendPage,roomPage,consolePage");
    for (it = pageOrder.begin(); it != pageOrder.end(); ++it) {
        if (*it == "serverPage")
            addPage(serverPage, icons->loadIconSet("connect_creating", KIcon::Small), i18n("Servers"));
        else if (*it == "searchPage")
            addPage(searchPage, icons->loadIconSet("find", KIcon::Small), i18n("Search"));
        else if (*it == "downloadPage")
            addPage(downloadPage, icons->loadIconSet("down", KIcon::Small), i18n("Downloads"));
        else if (*it == "sharePage")
            addPage(sharePage, icons->loadIconSet("up", KIcon::Small), i18n("Uploads"));
        else if (*it == "friendPage")
            addPage(friendPage, icons->loadIconSet("personal", KIcon::Small), i18n("Friends"));
        else if (*it == "roomPage")
            addPage(roomPage, icons->loadIconSet("contents", KIcon::Small), i18n("Rooms"));
        else if (*it == "statsPage")
            addPage(statsPage, icons->loadIconSet("info", KIcon::Small), i18n("Statistics"));
        else if (*it == "consolePage")
            addPage(consolePage, icons->loadIconSet("openterm", KIcon::Small), i18n("Console"));
    }

    connect(pageTab, SIGNAL(currentChanged(QWidget*)), SLOT(currentPageChanged(QWidget*)));
    int page = conf->readNumEntry("CurrentPage");
    if(page >= 0 && page < pageTab->count())
        pageTab->setCurrentPage(page);

    return (QWidget*) pageTab;
}

void KMLDonkey::setupActions()
{
    KStdAction::quit(kapp, SLOT(quit()), actionCollection());

    m_toolbarAction = KStdAction::showToolbar(this, SLOT(optionsShowToolbar()), actionCollection(), "toolBar");
    m_statusbarAction = KStdAction::showStatusbar(this, SLOT(optionsShowStatusbar()), actionCollection(), "statusBar");

    KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection(), "configure_keyBindings");
    KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection(), "configure_Toolbars");
    KStdAction::preferences(this, SLOT(optionsPreferences()), actionCollection(), "configure_kmldonkey");
    KStdAction::configureNotifications(this, SLOT(optionsNotifications()), actionCollection(), "configure_Notifications");

    (void)new KAction(i18n("KMLDonkey Setup Wizard..."), "wizard", 0, this, SLOT(launchSetupWizard()),
                      actionCollection(), "wizard_setup");
    (void)new KAction(i18n("Configure Co&nnection..."), "connect_established", 0, this, SLOT(optionsConfigureConnection()),
                      actionCollection(), "configure_connection");
    KMLDonkey::App->addCoreAction( new KAction(i18n("Configure &MLDonkey..."), "kmldonkey", 0, this, SLOT(optionsConfigureMLDonkey()),
                                   actionCollection(), "configure_mldonkey") );

    m_connectAction = new HostSelectAction(i18n("&Connect to Core"), "connect_creating", hostManager,
                                           actionCollection(), "connect_core");
    connect(m_connectAction, SIGNAL(activated()), SLOT(actionConnectCore()));
    connect(m_connectAction, SIGNAL(hostSelected(const QString&)), SLOT(actionConnectCore(const QString&)));

    addCoreAction(new KAction(i18n("&Disconnect From Core"), "connect_no", 0, this, SLOT(actionDisconnectCore()),
                              actionCollection(), "disconnect_core"));
    addCoreAction(new KAction(i18n("&Kill Core"), "stop", 0, this, SLOT(actionKillCore()),
                              actionCollection(), "kill_core"));
    addCoreAction(new KAction(i18n("&Submit URLs..."), "fileopen", 0, this, SLOT(actionSubmitURL()),
                              actionCollection(), "submit_url"));

    pageMapper = new QSignalMapper(this);
    connect(pageMapper, SIGNAL(mapped(int)), SLOT(activatePage(int)));
    for (int i=1; i<10; i++) {
        KAction* action = new KAction(i18n("Activate Page %1").arg(i), 0, QString("Ctrl+%1").arg(i), pageMapper, SLOT(map()),
                                      actionCollection(), QString("activate_page_%1").arg(i).local8Bit());
        pageMapper->setMapping(action, i - 1);
    }

    (void)new KAction(i18n("Next Page"), "next", KStdAccel::tabNext(), this, SLOT(activateNextPage()),
                      actionCollection(), "activate_next_page");
    (void)new KAction(i18n("Previous Page"), "previous", KStdAccel::tabPrev(), this, SLOT(activatePreviousPage()),
                      actionCollection(), "activate_previous_page");

    actionMapper = new QSignalMapper(this);
    connect(actionMapper, SIGNAL(mapped(const QString&)), SLOT(handleGenericAction(const QString&)));

    KAction* copy = KStdAction::copy(0, 0, actionCollection(), "copy_url");
    copy->setText(i18n("Copy to Clipboard as &URL"));
    addGenericAction("copy_url", copy);
    addGenericAction("copy_html", i18n("Copy to Clipboard as H&TML"), "editcopy", 0);
    addGenericAction("copy_hash", i18n("Copy to Clipboard as &Hash"), "editcopy", 0);
    addGenericAction("search_download", i18n("&Download"), "down", 0);
    addGenericAction("search_force_download", i18n("&Force Download"), "down", 0);
    addGenericAction("search_download_as", i18n("Download &As..."), "down", 0);
    addGenericAction("file_information", i18n("File &Information..."), "viewmag", 0);

    QPtrListIterator<KMLDonkeyPage> it(pages);
    for ( ; it.current() ; ++it ) (*it)->setupActions(actionCollection());

    enableActionList(coreActions, false);

    createGUI();
}

bool KMLDonkey::sortDownloadingFirst()
{
    return downloadPage ? downloadPage->sortDownloadingFirstAction->isChecked() : false;
}

bool KMLDonkey::sortPausedLast()
{
    return downloadPage ? downloadPage->sortPausedLastAction->isChecked() : false;
}

void KMLDonkey::addGenericAction(const char* name, KAction* action)
{
    genericActions.replace(name, action);
    connect(action, SIGNAL(activated()), actionMapper, SLOT(map()));
    actionMapper->setMapping(action, name);
}

void KMLDonkey::addGenericAction(const char* name, const QString& label, const QString& icon, const KShortcut& shortcut)
{
    addGenericAction(name, new KAction(label, icon, shortcut, actionCollection(), name));
}

void KMLDonkey::handleGenericAction(const QString& action)
{
    m_currentPage->handleGenericAction(action);
}

void KMLDonkey::updateSupportedActions(KMLDonkeyPage* page)
{
    QDictIterator<KAction> it(genericActions);
    if (page) {
        QStringList supported = page->supportedGenericActions();
        for (; it.current(); ++it) {
            if (supported.contains(it.currentKey()))
                it.current()->setEnabled(true);
            else
                it.current()->setEnabled(false);
        }
    } else {
        for (; it.current(); ++it)
            it.current()->setEnabled(false);
    }
}

void KMLDonkey::updateTrayIcon()
{
    if (showTrayIcon || (isHidden() && closeToTrayIcon)) {
        if (tray->isHidden()) tray->show();
    }
    else {
        if (tray->isVisible()) tray->hide();
    }
}

void KMLDonkey::pageActionsChanged(KMLDonkeyPage* newPage)
{
    if (m_currentPage != newPage) return;
    updateSupportedActions(m_currentPage);
}

void KMLDonkey::currentPageChanged(QWidget* widget)
{
    KMLDonkeyPage* page = dynamic_cast<KMLDonkeyPage*>(widget);
    if (!page) return;
    if (m_currentPage && m_currentPage != page)
        m_currentPage->deactivatePageActions();
    m_currentPage = page;
    updateSupportedActions(page);
}

void KMLDonkey::addCoreAction(KAction* action)
{
    coreActions.append(action);
}

void KMLDonkey::enableDebugConsole(bool enable)
{
    if (enable && !debugPage) {
        debugPage = new DebugPage(pageTab);
        addPage(debugPage, KGlobal::iconLoader()->loadIconSet("gear", KIcon::Small), i18n("Debug"));
    } else if (!enable && debugPage) {
        removePage(debugPage);
        delete debugPage;
        debugPage = 0;
    }
}

void KMLDonkey::restoreState( KConfig *conf )
{
    conf->setGroup("Options");
    m_toolbarAction->setChecked( conf->readBoolEntry("ShowToolbar", true) );
    emit optionsShowToolbar();
    m_statusbarAction->setChecked( conf->readBoolEntry("ShowStatusbar", true) );
    emit optionsShowStatusbar();
    showStatusbarLabels = conf->readBoolEntry("ShowStatusbarLabels", showStatusbarLabels);
    showTrayIcon = conf->readBoolEntry("ShowTrayIcon", false);
    closeToTrayIcon = conf->readBoolEntry("CloseToTrayIcon", false);
    hideMainWin = conf->readBoolEntry("HideMainWin", false);
    humanReadableSizes = conf->readBoolEntry("humanReadableSizes", true);
    displayProgressbar = conf->readBoolEntry("displayProgressbar", false);
    persistentReconnect = conf->readBoolEntry("persistentReconnect", persistentReconnect);
    debugEnabled = conf->readBoolEntry("EnableDebugConsole", false);
    enableDebugConsole(debugEnabled);
    advancedConfig = conf->readBoolEntry("AdvancedConfig", false);
    coreCharset = conf->readEntry("CoreCharset", "iso 8859-1");
    DonkeyMessage::setStringCodec(KGlobal::charsets()->codecForName(coreCharset));

    conf->setGroup("Fonts");
    consoleFont = KGlobalSettings::fixedFont();
    consoleFont = conf->readFontEntry("ConsoleFont", &consoleFont);
    listFont = KGlobalSettings::generalFont();
    listFont = conf->readFontEntry("ListFont", &listFont);

    conf->setGroup("Colors");
    QColor color;

    coloredViews = conf->readBoolEntry("coloredViews", false);

    color = QColor(0x99, 0x00, 0x00);
    colorServerNotConnected = conf->readColorEntry("NotConnected", &color);
    color = QColor(0xAA, 0xAA, 0xAA);
    colorServerBlacklisted = conf->readColorEntry("Blacklisted", &color);
    color = QColor(0x00, 0x00, 0x80);
    colorServerConnecting = conf->readColorEntry("Connecting", &color);
    color = QColor(0x15, 0x70, 0x15);
    colorServerConnected = conf->readColorEntry("Connected", &color);

    color = QColor(0x99, 0x00, 0x00);
    colorSearchFewSources = conf->readColorEntry("SearchFewSources", &color);
    color = QColor(0x15, 0x70, 0x15);
    colorSearchManySources = conf->readColorEntry("SearchManySources", &color);
    color = QColor(0xAA, 0xAA, 0xAA);
    colorSearchAlreadyDone = conf->readColorEntry("SearchAlreadyDone", &color);
    searchThreshold = conf->readNumEntry("SearchThreshold", 5);

    color = QColor(0x15, 0x70, 0x15);
    colorDownloadDownloading = conf->readColorEntry("Downloading", &color);
    color = QColor(0xAA, 0xAA, 0xAA);
    colorDownloadPaused = conf->readColorEntry("Paused", &color);
    color = QColor(0x99, 0x00, 0x00);
    colorDownloadLooking = conf->readColorEntry("Looking", &color);
    color = QColor(0xFF, 0x00, 0x00);
    colorDownloadNotAvailable = conf->readColorEntry("NotAvailable", &color);
    color = QColor(0x00, 0x00, 0x00);
    colorDownloadQueued = conf->readColorEntry("Queued", &color);

    color = Qt::red;
    availabilityColours.setColor(QColorGroup::Background, conf->readColorEntry("NoSources", &color));
    color = Qt::green;
    availabilityColours.setColor(QColorGroup::Foreground, conf->readColorEntry("Complete", &color));
    color = QColor(240, 255, 128, QColor::Hsv);
    availabilityColours.setColor(QColorGroup::Dark, conf->readColorEntry("FewSources", &color));
    color = QColor(240, 128, 255, QColor::Hsv);
    availabilityColours.setColor(QColorGroup::Light, conf->readColorEntry("ManySources", &color));

    availabilityThreshold = conf->readNumEntry("Threshold", 25);
    availabilityShadingDepth = conf->readNumEntry("ShadingDepth", 250);
    availabilityShading = conf->readBoolEntry("Shading", true);

    color = QColor(0x99, 0x00, 0x00);
    colorSourceNotConnected = conf->readColorEntry("SourceNotConnected", &color);
    color = QColor(0xAA, 0xAA, 0xAA);
    colorSourceBlacklisted = conf->readColorEntry("SourceBlacklisted", &color);
    color = QColor(0x00, 0x00, 0x80);
    colorSourceConnecting = conf->readColorEntry("SourceConnecting", &color);
    color = QColor(0xA0, 0x64, 0x09);
    colorSourceQueued = conf->readColorEntry("SourceQueued", &color);
    color = QColor(0x15, 0x70, 0x15);
    colorSourceDownloading = conf->readColorEntry("SourceDownloading", &color);

    // Pages
    QPtrListIterator<KMLDonkeyPage> pit( pages );
    for ( ; pit.current() ; ++pit )
        (*pit)->restoreState( conf );
}

void KMLDonkey::saveState( KConfig *conf )
{
    conf->setGroup("State");
    conf->writeEntry("FirstRun", false);
    conf->writeEntry("CurrentPage", pageTab->currentPageIndex());
    int i;
    QStringList pageOrder;
    for (i=0; i<pageTab->count(); i++)
        pageOrder.append(pageTab->page(i)->name());
    conf->writeEntry("PageOrder", pageOrder);
    conf->setGroup("Options");
    conf->writeEntry("ShowToolbar", m_toolbarAction->isChecked());
    conf->writeEntry("ShowStatusbar", m_statusbarAction->isChecked());
    conf->writeEntry("ShowStatusbarLabels", showStatusbarLabels);
    conf->writeEntry("ShowTrayIcon", showTrayIcon);
    conf->writeEntry("CloseToTrayIcon", closeToTrayIcon);
    conf->writeEntry("HideMainWin", hideMainWin);
    conf->writeEntry("humanReadableSizes", humanReadableSizes);
    conf->writeEntry("displayProgressbar", displayProgressbar);
    conf->writeEntry("persistentReconnect", persistentReconnect);
    conf->writeEntry("EnableDebugConsole", debugEnabled);
    conf->writeEntry("AdvancedConfig", advancedConfig);
    conf->writeEntry("CoreCharset", coreCharset);

    // Pages
    QPtrListIterator<KMLDonkeyPage> it(pages);
    for ( ; it.current() ; ++it )
        (*it)->saveState(conf);
}

void KMLDonkey::actionConnectCore()
{
    reconnect = 0;
    connectToCore();
}

void KMLDonkey::actionConnectCore(const QString& host)
{
    if (hostManager->validHostName(host)) {
        lastHost = host;
        reconnect = 0;
        connectToCore();
    }
}

void KMLDonkey::actionDisconnectCore()
{
    reconnect = 0;
    disconnectFromCore();
}

void KMLDonkey::actionKillCore()
{
    if (KMessageBox::warningYesNo(this, i18n("Really kill the MLDonkey-Core?\n\nBe warned, that you cannot restart the core with KMLDonkey!"), i18n("Kill Core")) == KMessageBox::Yes)
        KMLDonkey::App->donkey->killCore();
}

void KMLDonkey::actionSubmitURL()
{
    SubmitDialog *dlg = new SubmitDialog(this);
    if (dlg) dlg->show();
}

bool KMLDonkey::queryClose()
{
    if (closeToTrayIcon && QObject::sender() == 0) {
        hide();
        return false;
    }
    return true;
}

void KMLDonkey::dragEnterEvent(QDragEnterEvent* event)
{
    event->accept(QUriDrag::canDecode(event));
}

void KMLDonkey::dropEvent(QDropEvent* event)
{
    QStringList uri;

    if (QUriDrag::decodeToUnicodeUris(event, uri))
    {
        QStringList::Iterator it;
        for (it = uri.begin(); it != uri.end(); ++it)
            donkey->submitURL(*it);
    }
}

void KMLDonkey::optionsShowToolbar()
{
    // this is all very cut and paste code for showing/hiding the
    // toolbar
    if (m_toolbarAction->isChecked())
        toolBar()->show();
    else
        toolBar()->hide();
}

void KMLDonkey::optionsShowStatusbar()
{
    // this is all very cut and paste code for showing/hiding the
    // statusbar
    if (m_statusbarAction->isChecked())
        statusBar()->show();
    else
        statusBar()->hide();
}

void KMLDonkey::optionsConfigureKeys()
{
    KKeyDialog::configure(actionCollection());
}

void KMLDonkey::optionsConfigureToolbars()
{
    saveMainWindowSettings(KGlobal::config(), autoSaveGroup());
    KEditToolbar dlg(actionCollection());
    connect(&dlg, SIGNAL(newToolbarConfig()), this, SLOT(newToolbarConfig()));
    if (dlg.exec()) {
        createGUI();
    }
}

void KMLDonkey::newToolbarConfig()
{
    // ...if you use any action list, use plugActionList on each here...
    applyMainWindowSettings(KGlobal::config(), autoSaveGroup());
}

void KMLDonkey::optionsPreferences()
{
   if (! prefs) { // on first access create the KMLDonkeyPreferences instance
        prefs = new KMLDonkeyPreferences(this);
        connect(prefs, SIGNAL(applyClicked()), this, SLOT(applyPreferences()));
        connect(prefs, SIGNAL(okClicked()), this, SLOT(applyPreferences()));
    }

    prefs->generalPage->showToolbarCheckbox->setChecked( m_toolbarAction->isChecked() );
    prefs->generalPage->showStatusbarCheckbox->setChecked( m_statusbarAction->isChecked() );
    prefs->generalPage->statusbarLabelsCheckbox->setChecked( showStatusbarLabels );
    prefs->generalPage->persistentReconnectCheckbox->setChecked( persistentReconnect );
    prefs->generalPage->debugConsoleCheckbox->setChecked( debugEnabled );
    prefs->generalPage->advancedConfigCheckbox->setChecked( advancedConfig );
    prefs->generalPage->setEncoding(coreCharset);
    prefs->generalPage->showTrayiconCheckBox->setChecked( showTrayIcon );
    prefs->generalPage->showTrayOnCloseCheckBox->setChecked( closeToTrayIcon );

    prefs->listsPage->humanReadableSizesCheckbox->setChecked( humanReadableSizes );
    prefs->listsPage->progressbarCheckBox->setChecked( displayProgressbar );

    prefs->colorPage->coloredListsCheckbox->setChecked(coloredViews);
    emit prefs->colorPage->coloredLists(coloredViews);

    prefs->colorPage->colorServerNotConnected->setColor(colorServerNotConnected);
    prefs->colorPage->colorServerBlacklisted->setColor(colorServerBlacklisted);
    prefs->colorPage->colorServerConnecting->setColor(colorServerConnecting);
    prefs->colorPage->colorServerConnected->setColor(colorServerConnected);

    prefs->colorPage->colorSearchFewSources->setColor(colorSearchFewSources);
    prefs->colorPage->colorSearchManySources->setColor(colorSearchManySources);
    prefs->colorPage->colorSearchAlreadyDone->setColor(colorSearchAlreadyDone);
    prefs->colorPage->searchSourcesThresholdSlider->setValue(searchThreshold);

    prefs->colorPage->downloadingColorSelect->setColor(colorDownloadDownloading);
    prefs->colorPage->pausedColorSelect->setColor(colorDownloadPaused);
    prefs->colorPage->lookingColorSelect->setColor(colorDownloadLooking);
    prefs->colorPage->notavailableColorSelect->setColor(colorDownloadNotAvailable);
    prefs->colorPage->queuedColorSelect->setColor(colorDownloadQueued);

    prefs->colorPage->noSourcesColorSelect->setColor(availabilityColours.background());
    prefs->colorPage->fewSourcesColorSelect->setColor(availabilityColours.dark());
    prefs->colorPage->manySourcesColorSelect->setColor(availabilityColours.light());
    prefs->colorPage->completeColorSelect->setColor(availabilityColours.foreground());
    prefs->colorPage->availabilityThresholdSlider->setValue(availabilityThreshold);
    prefs->colorPage->availabilityShadingSlider->setValue(availabilityShadingDepth);
    prefs->colorPage->availabilityShadingCheckbox->setChecked(availabilityShading);

    prefs->colorPage->colorSourceNotConnected->setColor(colorSourceNotConnected);
    prefs->colorPage->colorSourceBlacklisted->setColor(colorSourceBlacklisted);
    prefs->colorPage->colorSourceConnecting->setColor(colorSourceConnecting);
    prefs->colorPage->colorSourceQueued->setColor(colorSourceQueued);
    prefs->colorPage->colorSourceDownloading->setColor(colorSourceDownloading);

    prefs->fontPage->consoleFontSelect->setFont(consoleFont);
    prefs->fontPage->listFontSelect->setFont(listFont);

    QPtrListIterator<KMLDonkeyPage> it(pages);
    for ( ; it.current() ; ++it ) (*it)->configurePrefsDialog(prefs);

    prefs->show();
}

void KMLDonkey::applyPreferences()
{
    if (! prefs) return; // just to be sure ;-)

    m_toolbarAction->setChecked( prefs->generalPage->showToolbarCheckbox->isChecked() );
    emit optionsShowToolbar();
    m_statusbarAction->setChecked( prefs->generalPage->showStatusbarCheckbox->isChecked() );
    emit optionsShowStatusbar();
    showStatusbarLabels = prefs->generalPage->statusbarLabelsCheckbox->isChecked();
    persistentReconnect = prefs->generalPage->persistentReconnectCheckbox->isChecked();
    debugEnabled = prefs->generalPage->debugConsoleCheckbox->isChecked();
    advancedConfig = prefs->generalPage->advancedConfigCheckbox->isChecked();
    enableDebugConsole(debugEnabled);

    if (prefs->generalPage->showTrayiconCheckBox->isChecked() != showTrayIcon ||
        prefs->generalPage->showTrayOnCloseCheckBox->isChecked() != closeToTrayIcon) {
            closeToTrayIcon = prefs->generalPage->showTrayOnCloseCheckBox->isChecked();
            showTrayIcon = prefs->generalPage->showTrayiconCheckBox->isChecked();
            updateTrayIcon();
    }

    QString newCharset = prefs->generalPage->getEncoding();
    if (newCharset != coreCharset) {
        coreCharset = newCharset;
        DonkeyMessage::setStringCodec(KGlobal::charsets()->codecForName(coreCharset));
        connectToCore();
    }

    humanReadableSizes = prefs->listsPage->humanReadableSizesCheckbox->isChecked();
    displayProgressbar = prefs->listsPage->progressbarCheckBox->isChecked();

    KConfig* conf = KGlobal::config();
    conf->setGroup("Colors");

    coloredViews = prefs->colorPage->coloredListsCheckbox->isChecked();
    conf->writeEntry("coloredViews", coloredViews);

    colorServerNotConnected = prefs->colorPage->colorServerNotConnected->color();
    colorServerBlacklisted = prefs->colorPage->colorServerBlacklisted->color();
    colorServerConnecting = prefs->colorPage->colorServerConnecting->color();
    colorServerConnected = prefs->colorPage->colorServerConnected->color();
    conf->writeEntry("NotConnected", colorServerNotConnected);
    conf->writeEntry("Blacklisted", colorServerBlacklisted);
    conf->writeEntry("Connecting", colorServerConnecting);
    conf->writeEntry("Connected", colorServerConnected);

    colorSearchFewSources = prefs->colorPage->colorSearchFewSources->color();
    colorSearchManySources = prefs->colorPage->colorSearchManySources->color();
    colorSearchAlreadyDone = prefs->colorPage->colorSearchAlreadyDone->color();
    searchThreshold = prefs->colorPage->searchSourcesThresholdSlider->value();
    conf->writeEntry("SearchFewSources", colorSearchFewSources);
    conf->writeEntry("SearchManySources", colorSearchManySources);
    conf->writeEntry("SearchAlreadyDone", colorSearchAlreadyDone);
    conf->writeEntry("SearchThreshold", searchThreshold);

    colorDownloadDownloading = prefs->colorPage->downloadingColorSelect->color();
    colorDownloadPaused = prefs->colorPage->pausedColorSelect->color();
    colorDownloadLooking = prefs->colorPage->lookingColorSelect->color();
    colorDownloadNotAvailable = prefs->colorPage->notavailableColorSelect->color();
    colorDownloadQueued = prefs->colorPage->queuedColorSelect->color();
    conf->writeEntry("Downloading", colorDownloadDownloading);
    conf->writeEntry("Paused", colorDownloadPaused);
    conf->writeEntry("Looking", colorDownloadLooking);
    conf->writeEntry("NotAvailable", colorDownloadNotAvailable);
    conf->writeEntry("Queued", colorDownloadQueued);

    availabilityColours.setColor(QColorGroup::Background, prefs->colorPage->noSourcesColorSelect->color());
    availabilityColours.setColor(QColorGroup::Dark, prefs->colorPage->fewSourcesColorSelect->color());
    availabilityColours.setColor(QColorGroup::Light, prefs->colorPage->manySourcesColorSelect->color());
    availabilityColours.setColor(QColorGroup::Foreground, prefs->colorPage->completeColorSelect->color());
    availabilityThreshold = prefs->colorPage->availabilityThresholdSlider->value();
    availabilityShadingDepth = prefs->colorPage->availabilityShadingSlider->value();
    availabilityShading = prefs->colorPage->availabilityShadingCheckbox->isChecked();
    conf->writeEntry("NoSources", availabilityColours.background());
    conf->writeEntry("FewSources", availabilityColours.dark());
    conf->writeEntry("ManySources", availabilityColours.light());
    conf->writeEntry("Complete", availabilityColours.foreground());
    conf->writeEntry("Threshold", availabilityThreshold);
    conf->writeEntry("ShadingDepth", availabilityShadingDepth);
    conf->writeEntry("Shading", availabilityShading);

    colorSourceNotConnected = prefs->colorPage->colorSourceNotConnected->color();
    colorSourceBlacklisted = prefs->colorPage->colorSourceBlacklisted->color();
    colorSourceConnecting = prefs->colorPage->colorSourceConnecting->color();
    colorSourceQueued = prefs->colorPage->colorSourceQueued->color();
    colorSourceDownloading = prefs->colorPage->colorSourceDownloading->color();
    conf->writeEntry("SourceNotConnected", colorSourceNotConnected);
    conf->writeEntry("SourceBlacklisted", colorSourceBlacklisted);
    conf->writeEntry("SourceConnecting", colorSourceConnecting);
    conf->writeEntry("SourceQueued", colorSourceQueued);
    conf->writeEntry("SourceDownloading", colorSourceDownloading);

    conf->setGroup("Fonts");

    listFont = prefs->fontPage->listFontSelect->font();
    consoleFont = prefs->fontPage->consoleFontSelect->font();

    conf->writeEntry("ConsoleFont", consoleFont);
    conf->writeEntry("ListFont", listFont);

    QPtrListIterator<KMLDonkeyPage> it(pages);
    for ( ; it.current() ; ++it ) (*it)->applyPreferences(prefs);

    saveState( conf ); // finally save everything
}

void KMLDonkey::optionsConfigureConnection()
{
    QStringList foo;
    foo.append("kcmdonkey");
    QString err;
    if (KApplication::kdeinitExec("kcmshell", foo, &err))
        KMessageBox::detailedError(this, i18n("Failed to launch the MLDonkey control panel applet."), err);
}

void KMLDonkey::optionsConfigureMLDonkey()
{
    if (!donkey->isConnected()) {
        KMessageBox::error(this, i18n("You must connect to a running MLDonkey core before you can configure it!"));
        return;
    }
    MLDonkeyConfigDialog* cfg = new MLDonkeyConfigDialog(this);
    if (cfg) cfg->show();
}

void KMLDonkey::launchSetupWizard()
{
    SetupWizard* w = new SetupWizard(this);
    w->show();
}

void KMLDonkey::optionsNotifications()
{
    KNotifyDialog::configure(this);
}

void KMLDonkey::changeStatusbar(const QString& text)
{
    // display the text on the statusbar
    statInfo->setText(text);
    QToolTip::remove(statInfo);
}

void KMLDonkey::changeCaption(const QString& text)
{
    // display the text on the caption
    setCaption(text);
}

void KMLDonkey::hostListUpdated()
{
    if (!hostManager->validHostName(lastHost))
        lastHost = hostManager->defaultHostName();
    connectToCore();
}

void KMLDonkey::connectToCore()
{
    donkey->setHost(hostManager->hostProperties(lastHost));
    donkey->connectToCore();
    changeStatusbar(i18n("Connecting..."));
}

void KMLDonkey::disconnectFromCore()
{
    donkey->disconnectFromCore();
}

void KMLDonkey::coreDisconnected(int err)
{
    tray->tooltip = i18n("Disconnected.");
    changeStatusbar(tray->tooltip);
    QString statInfoTooltip = QString::null;

    statServer->setText(QString::null);
    statFiles->setText(QString::null);
    statShare->setText(QString::null);
    statTransfer->setText(QString::null);
    statRate->setText(QString::null);

    switch (err) {
    case ProtocolInterface::AuthenticationError:
        statInfoTooltip = i18n("Authentication error. Incorrect name or password.");
        KMessageBox::error(this, statInfoTooltip);
        reconnect = 0;
        break;
    case ProtocolInterface::ConnectionRefusedError:
        if (!reconnect) {
            statInfoTooltip = i18n("Connection to the core was refused. "
                                   "Are you sure it is running?");
            if(isVisible())
                KMessageBox::error(this, statInfoTooltip);
            reconnect = 0;
        }
        else
            reconnect = persistentReconnect;
        break;
    case ProtocolInterface::HostNotFoundError: {
        HostInterface* host = hostManager->hostProperties(lastHost);
        if (!reconnect) {
            statInfoTooltip = host ? i18n("Host '%1' not found!").arg(host->address()) : i18n("No Host defined!");
            if (isVisible())
                KMessageBox::error(this, statInfoTooltip);
            reconnect = 0;
        }
        else
            reconnect = persistentReconnect;
    } break;
    case ProtocolInterface::CommunicationError:
        if (!reconnect) {
            statInfoTooltip = i18n("A read error occurred when communicating "
                                   "with the core. The connection has been terminated.");
            if (isVisible())
                KMessageBox::error(this, statInfoTooltip);
            reconnect = 0;
        }
        else
            reconnect = persistentReconnect;
        break;
    case ProtocolInterface::IncompatibleProtocolError:
        statInfoTooltip = i18n("Your mldonkey core uses an obsolete communications protocol. "
                               "Please upgrade it to a more recent version!");
        if (isVisible())
            KMessageBox::error(this, statInfoTooltip);
        reconnect = 0;
        break;
    default:
        reconnect = persistentReconnect;
        break;
    }

    if (statInfoTooltip.isEmpty())
        KNotifyClient::event(winId(), "KMLDonkeyDisconnected", tray->tooltip);
    else {
        QToolTip::add(statInfo, statInfoTooltip);
        KNotifyClient::event(winId(), "KMLDonkeyDisconnected", tray->tooltip + " " + statInfoTooltip);
    }

    QPtrListIterator<KMLDonkeyPage> it(pages);
    for ( ; it.current() ; ++it ) (*it)->clear();
    enableActionList(coreActions, false);
    updateSupportedActions(0);
}

void KMLDonkey::updateStatusBarTooltip()
{
    DonkeyHost* host = (DonkeyHost*)hostManager->hostProperties(lastHost);
    QToolTip::add(statInfo, i18n("Hostname: %1<br>Host Address: %2<br>Host Port: %3<br>Username: %4<br>"
            "Core Version: %7<br>"
                    "Current GUI Protocol: %5<br>Core's GUI Protocol: %6")
                    .arg(host->name())
                    .arg(host->address())
                    .arg(QString::number(host->port()))
                    .arg(host->username())
                    .arg(donkey->protocolVersion())
                    .arg(donkey->coreProtocolVersion())
                    .arg(coreVersion));
}

void KMLDonkey::coreConnected()
{
    DonkeyHost* host = (DonkeyHost*)hostManager->hostProperties(lastHost);
    tray->tooltip = i18n("Connected username@server (symbolic name)", "Connected %1@%2 (%3)")
                    .arg(host->username())
                    .arg(host->address())
                    .arg(host->name());
    changeStatusbar(tray->tooltip);
    updateStatusBarTooltip();
    reconnect = 0;
    enableActionList(coreActions, true);
    updateSupportedActions(m_currentPage);
    KNotifyClient::event(winId(), "KMLDonkeyConnected", tray->tooltip);
}

void KMLDonkey::checkReconnect()
{
    if (persistentReconnect && reconnect && (!donkey || !donkey->isConnected()))
        connectToCore();
}

void KMLDonkey::sendConsoleMessage(const QString& txt)
{
    consolePage->sendConsoleMessage(txt);
}

void KMLDonkey::updateStatus(int64 ul, int64 dl, int64, int nsh, int tul, int tdl, int, int, int ndl, int ncp, QMap<int,int>*)
{
    QString server = i18n("Server %1/%2").arg(donkey->connectedServerCount()).arg(donkey->totalServerCount());
    QString files = i18n("Files %1/%2").arg(ncp).arg(ndl);
    QString share = i18n("Shared %1").arg(nsh);
    QString transfer = i18n("Transfer %1/%2").arg(humanReadableSize(ul)).arg(humanReadableSize(dl));
    QString rate = i18n("Speed %1/%2").arg( QString::number((double)tul / 1024.0,'f',1) ).arg( QString::number((double)tdl / 1024.0,'f',1) );

    if (showStatusbarLabels) {
        statServer->setText(server);
        statFiles->setText(files);
        statShare->setText(share);
        statTransfer->setText(transfer);
        statRate->setText(rate);
    }
    else {
        statServer->setText( QString("%1/%2").arg(donkey->connectedServerCount()).arg(donkey->totalServerCount()) );
        statFiles->setText( QString("%1/%2").arg(ncp).arg(ndl) );
        statShare->setText( QString("%1").arg(nsh) );
        statTransfer->setText( QString("%1/%2").arg(humanReadableSize(ul)).arg(humanReadableSize(dl)) );
        statRate->setText( QString("%1/%2").arg( QString::number((double)tul / 1024.0,'f',1) ).arg( QString::number((double)tdl / 1024.0,'f',1) ) );
    }

    tray->tooltip = QString("%1<br>%2<br>%3<br>%4<br>%5<br>%6")
                    .arg(statInfo->text()).arg(server).arg(files).arg(share).arg(transfer).arg(rate);
    //delete map;
}

void KMLDonkey::coreVersionReceived(const QString& v)
{
    coreVersion = v;
    updateStatusBarTooltip();
}

void KMLDonkey::showBadInstallDialog()
{
    KMessageBox::detailedError(this, i18n("Unable to construct a GUI element. Your KMLDonkey installation is "
                                          "incomplete."),
                               i18n("Unable to construct a requested GUI element. This is most likely "
                                    "because the GUI specification data file (kmldonkeyui.rc) has been "
                                    "installed in the wrong location, or has not been installed at all. If you "
                                    "installed KMLDonkey from a binary package, please report this to the "
                                    "packager at once. If you installed from source, check that you configured "
                                    "KMLDonkey to use the same install prefix as your KDE installation "
                                    "(see the README file accompanying the KMLDonkey source for details), and "
                                    "that your system does not keep KDE files in non-standard locations "
                                    "(Mandriva Linux systems, in particular, are known to do this)."));
}



/*
 * DCOP methods (as defined in kmldonkeyiface.h)
 */

void KMLDonkey::submitURL(QString url)
{
    donkey->submitURL(url);
}

void KMLDonkey::consoleCommand(QString command)
{
    sendConsoleMessage(command);
}



#include "kmldonkey.moc"
