
/***************************************************************************
 *                                                                         *
 *   KCPULoad and KNetLoad are copyright (c) 1999-2000, Markus Gustavsson  *
 *                                       (c) 2002, Ben Burton              *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "icontoggleaction.h"
#include "speeddialog.h"
#include "statdock.h"
#include "statpopup.h"

#include <qpainter.h>
#include <qtimer.h>
#include <kaction.h>
#include <kcolordialog.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kglobal.h>
#include <khelpmenu.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kwin.h>

#define DEFAULT_SPEED 1000
#define TEXT_EXPANSION_HORIZONTAL 10
#define TEXT_EXPANSION_VERTICAL 3

const QColor StatPopup::colorBorder(0, 0, 0);

StatPopup::StatPopup(bool useSupportSplit, QWidget *parent, const char *name) :
        QWidget(parent, name, Qt::WStyle_Customize | Qt::WStyle_NoBorder |
        Qt::WStyle_StaysOnTop | Qt::WDestructiveClose | Qt::WType_TopLevel),
        supportSplit(useSupportSplit) {
    // Window management.
    KWin::setState(winId(), NET::SkipTaskbar | NET::SkipPager);

    // TODO: Get all-desktops working, even after a hide/reshow.
    //KWin::setOnAllDesktops(winId(), true);

    // Basic variable initialisation.
    relX = relY = 0;
    dock[0] = dock[1] = 0;
    isDragged = closing = false;

    // Initialise the text contents of this pop-up.
    fullReading = i18n("Inactive.");
    resizeToText();

    // Prepare for actions and the config file, but don't actually add
    // any actions or read the configuration.
    config = KGlobal::config();
    coll = new KActionCollection(this);

    // Set up a timer for our periodic updates.
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(takeReading()));
}

StatPopup::~StatPopup() {
}

void StatPopup::readPopupState() {
    config->setGroup("Popup Options");

    // Read the position.
    int useX = config->readNumEntry("PopupX", 0);
    int useY = config->readNumEntry("PopupY", 0);
    if (useX || useY)
        move(useX, useY);

    // Show the pop-up if appropriate.
    if (config->readBoolEntry("PopupActive", false))
        show();
    else
        hide();
}

void StatPopup::savePopupState() {
    config->setGroup("Popup Options");
    config->writeEntry("PopupActive", isVisible());
    config->writeEntry("PopupX", x());
    config->writeEntry("PopupY", y());
    config->sync();
}

void StatPopup::initDock(StatDock* target, KPopupMenu* menu, int whichDock) {
    // Initialise the menus.
    actActive->plug(menu);
    actClearHistory->plug(menu);
    menu->insertSeparator();
    actSpeed->plug(menu);
    if (supportSplit)
        actSplit->plug(menu);
    menu->insertSeparator();

    insertCustomItems(menu);

    KPopupMenu* fillMenu = new KPopupMenu(menu);
    actFillLines->plug(fillMenu);
    actFillBars->plug(fillMenu);
    actFillShaded->plug(fillMenu);
    menu->insertItem(SmallIcon("style"), i18n("St&yle"), fillMenu);

    actSoft->plug(menu);
    actLabelled->plug(menu);
    actGrid->plug(menu);
    actColor0->plug(menu);
    actColor1->plug(menu);
    menu->insertSeparator();
    menu->insertItem(SmallIcon("help"), i18n("&Help"),
        (new KHelpMenu(0, KGlobal::instance()->aboutData(), false))->menu());

    // Set up display properties for the dock.
    target->setActive(actActive->isChecked());
    target->setSplit(isSplit());
    target->setFill(fillStyle);
    target->setSoft(actSoft->isChecked());
    target->setLabelled(actLabelled->isChecked());
    target->setGrid(actGrid->isChecked());
    if (whichDock == 0) {
        target->setColor(color[0]);
        actColor0->setEnabled(true);
    } else if (whichDock == 1) {
        target->setColor(color[1]);
        actColor1->setEnabled(true);
    }
    setCustomProperties(target);
}

void StatPopup::cleanUpDock(int whichDock) {
    if (whichDock == 0)
        actColor0->setEnabled(false);
    else if (whichDock == 1)
        actColor1->setEnabled(false);
}

bool StatPopup::isActive() const {
    return actActive->isChecked();
}

bool StatPopup::isSplit() const {
    return (supportSplit ? actSplit->isChecked() : false);
}

void StatPopup::setActive(bool set) {
    if (set)
        startUpdates();
    else
        stopUpdates();

    if (dock[0])
        dock[0]->setActive(set);
    if (dock[1])
        dock[1]->setActive(set);

    config->setGroup("General Options");
    config->writeEntry("Active", set);
    config->sync();
}

void StatPopup::clearHistory() {
    if (dock[0])
        dock[0]->clearHistory();
    if (dock[1])
        dock[1]->clearHistory();
}

void StatPopup::selectSpeed() {
    SpeedDialog dlg(speed, firstDock());
    if (dlg.exec()) {
        int newSpeed = dlg.getSpeed();
        if (newSpeed > 0) {
            speed = newSpeed;
            if (timer->isActive())
                timer->changeInterval(speed);

            config->setGroup("General Options");
            config->writeEntry("Speed", speed);
            config->sync();
        }
    }
}

void StatPopup::setSplit(bool set) {
    if (! supportSplit)
        return;

    if (dock[0])
        dock[0]->setSplit(set);
    if (dock[1])
        dock[1]->setSplit(set);

    requestResize();

    config->setGroup("General Options");
    config->writeEntry("Split", set);
    config->sync();
}

void StatPopup::setFillLines() {
    fillStyle = StatDock::fillLines;

    actFillLines->setChecked(true);
    actFillBars->setChecked(false);
    actFillShaded->setChecked(false);

    if (dock[0])
        dock[0]->setFill(StatDock::fillLines);
    if (dock[1])
        dock[1]->setFill(StatDock::fillLines);

    config->setGroup("General Options");
    config->writeEntry("StyleID", StatDock::fillLines);
    config->sync();
}

void StatPopup::setFillBars() {
    fillStyle = StatDock::fillBars;

    actFillLines->setChecked(false);
    actFillBars->setChecked(true);
    actFillShaded->setChecked(false);

    if (dock[0])
        dock[0]->setFill(StatDock::fillBars);
    if (dock[1])
        dock[1]->setFill(StatDock::fillBars);

    config->setGroup("General Options");
    config->writeEntry("StyleID", StatDock::fillBars);
    config->sync();
}

void StatPopup::setFillShaded() {
    fillStyle = StatDock::fillShaded;

    actFillLines->setChecked(false);
    actFillBars->setChecked(false);
    actFillShaded->setChecked(true);

    if (dock[0])
        dock[0]->setFill(StatDock::fillShaded);
    if (dock[1])
        dock[1]->setFill(StatDock::fillShaded);

    config->setGroup("General Options");
    config->writeEntry("StyleID", StatDock::fillShaded);
    config->sync();
}

void StatPopup::setSoft(bool set) {
    if (dock[0])
        dock[0]->setSoft(set);
    if (dock[1])
        dock[1]->setSoft(set);

    config->setGroup("General Options");
    config->writeEntry("Soft", set);
    config->sync();
}

void StatPopup::setLabelled(bool set) {
    if (dock[0])
        dock[0]->setLabelled(set);
    if (dock[1])
        dock[1]->setLabelled(set);

    config->setGroup("General Options");
    config->writeEntry("Labelled", set);
    config->sync();
}

void StatPopup::setGrid(bool set) {
    if (dock[0])
        dock[0]->setGrid(set);
    if (dock[1])
        dock[1]->setGrid(set);

    config->setGroup("General Options");
    config->writeEntry("Grid", set);
    config->sync();
}

void StatPopup::selectColor0() {
    if (dock[0]) {
        QColor ans;
        if (KColorDialog::getColor(ans, color[0], firstDock()) ==
                QDialog::Accepted) {
            color[0] = ans;
            dock[0]->setColor(ans);

            config->setGroup("General Options");
            config->writeEntry("Color0", ans);
            config->sync();
        }
    }
}

void StatPopup::selectColor1() {
    if (dock[1]) {
        QColor ans;
        if (KColorDialog::getColor(ans, color[1], firstDock()) ==
                QDialog::Accepted) {
            color[1] = ans;
            dock[1]->setColor(ans);

            config->setGroup("General Options");
            config->writeEntry("Color1", ans);
            config->sync();
        }
    }
}

StatDock* StatPopup::firstDock() {
    if (dock[0])
        return dock[0];
    else
        return dock[1];
}

void StatPopup::startUpdates() {
    takeReading();
    timer->start(speed);
}

void StatPopup::stopUpdates() {
    timer->stop();
    fullReading = i18n("Inactive.");
}

void StatPopup::setupActions() {
    config->setGroup("General Options");
    bool bVal;
    int iVal;

    bVal = config->readBoolEntry("Active", true);
    actActive = new KToggleAction(i18n("&Active"), 0, coll, "active");
    actActive->setChecked(bVal);
    connect(actActive, SIGNAL(toggled(bool)), this, SLOT(setActive(bool)));

    actClearHistory = new KAction(i18n("&Clear"), "editdelete", 0,
        this, SLOT(clearHistory()), coll, "clear");

    speed = config->readNumEntry("Speed", DEFAULT_SPEED);
    actSpeed = new KAction(i18n("&Speed..."), "speedarrow", 0,
        this, SLOT(selectSpeed()), coll, "speed");

    if (supportSplit) {
        bVal = config->readBoolEntry("Split", true);
        actSplit = new IconToggleAction(i18n("Sp&lit Graph"), "split",
            i18n("Graph Sp&litting Enabled"), "spliton", 0, coll, "split");
        actSplit->setChecked(bVal);
        connect(actSplit, SIGNAL(toggled(bool)), this, SLOT(setSplit(bool)));
    }

    iVal = config->readNumEntry("StyleID", StatDock::fillShaded);
    actFillLines = new IconToggleAction(i18n("&Lines"), "lines", "lineson", 0,
        this, SLOT(setFillLines()), coll, "filllines");
    actFillBars = new IconToggleAction(i18n("&Bars"), "bars", "barson", 0,
        this, SLOT(setFillBars()), coll, "fillbars");
    actFillShaded = new IconToggleAction(i18n("&Shaded"), "shaded", "shadedon",
        0, this, SLOT(setFillShaded()), coll, "fillshaded");
    if (iVal == StatDock::fillLines)
        setFillLines();
    else if (iVal == StatDock::fillBars)
        setFillBars();
    else
        setFillShaded();

    bVal = config->readBoolEntry("Soft", false);
    actSoft = new IconToggleAction(i18n("So&ft Curves"), "soft",
        i18n("So&ft Curves Enabled"), "softon", 0, coll, "soft");
    actSoft->setChecked(bVal);
    connect(actSoft, SIGNAL(toggled(bool)), this, SLOT(setSoft(bool)));

    bVal = config->readBoolEntry("Labelled", true);
    actLabelled= new IconToggleAction(i18n("Show &Labels"), "labels",
        i18n("&Labels Enabled"), "labelson", 0, coll, "labelled");
    actLabelled->setChecked(bVal);
    connect(actLabelled, SIGNAL(toggled(bool)), this, SLOT(setLabelled(bool)));

    bVal = config->readBoolEntry("Grid", true);
    actGrid = new IconToggleAction(i18n("Show &Grid"), "grid",
        i18n("&Grid Enabled"), "gridon", 0, coll, "grid");
    actGrid->setChecked(bVal);
    connect(actGrid, SIGNAL(toggled(bool)), this, SLOT(setGrid(bool)));

    color[0] = defaultDockColor(0);
    color[0] = config->readColorEntry("Color0", color);
    actColor0 = new KAction(QString(i18n("Color (%1)...")).arg(dockName(0)),
        "color0", 0, this, SLOT(selectColor0()), coll, "color0");
    actColor0->setEnabled(false);

    color[1] = defaultDockColor(1);
    color[1] = config->readColorEntry("Color1", color + 1);
    actColor1 = new KAction(QString(i18n("Color (%1)...")).arg(dockName(1)),
        "color1", 0, this, SLOT(selectColor1()), coll, "color1");
    actColor1->setEnabled(false);

    setupCustomActions();
}

void StatPopup::paintEvent(QPaintEvent*) {
    QPainter p(this);

    // Draw the border.
    p.setPen(colorBorder);
    p.drawLine(0, 0, width(), 0);
    p.drawLine(0, 1, width(), 1);
    p.drawLine(0, 2, 0, height());
    p.drawLine(1, 2, 1, height());
    p.drawLine(width() - 2, 2, width() - 2, height());
    p.drawLine(width() - 1, 2, width() - 1, height());
    p.drawLine(2, height() - 2, width() - 2, height() - 2);
    p.drawLine(2, height() - 1, width() - 2, height() - 1);

    // Draw the text.
    p.setFont(font());
    p.setPen(colorGroup().foreground());
    p.drawText(rect(), AlignHCenter | AlignVCenter, fullReading);
}

void StatPopup::mousePressEvent(QMouseEvent* e) {
    if(e->button() == RightButton) {
        // Hide the pop-up.
        hide();
    } else {
        // Begin a drag operation.
        isDragged = true;
        relX = e->x();
        relY = e->y();
        repaint();
    }
}

void StatPopup::mouseMoveEvent(QMouseEvent* e) {
    // In the middle of a drag operation.
    move(e->globalX() - relX, e->globalY() - relY);
}

void StatPopup::mouseReleaseEvent(QMouseEvent* e) {
    // The end of a drag operation.
    move(e->globalX() - relX, e->globalY() - relY);
    isDragged = false;
    repaint();
}

void StatPopup::closeEvent(QCloseEvent* e) {
    // We're about to close.  Save the current state for the last time.
    savePopupState();
    closing = true;
    QWidget::closeEvent(e);
}

void StatPopup::hideEvent(QHideEvent* e) {
    // We're about to hide.  Save the current state if we're not
    // closing altogether.
    if (! closing)
        savePopupState();
    QWidget::hideEvent(e);
}

void StatPopup::showEvent(QShowEvent* e) {
    // Make sure we're up-to-date and properly resized.
    if (isActive())
        takeReading();
    else
        resizeToText();

    QWidget::showEvent(e);
}

void StatPopup::takeReading() {
    takeReadingInternal();

    if (dock[0])
        dock[0]->addPercentReading(upper[0], lower[0]);
    if (dock[1])
        dock[1]->addPercentReading(upper[1], lower[1]);

    if (isVisible()) {
        // Only resize if the pop-up is visible; otherwise fullReading
        // might be stale anyway so we'll do it when we show the pop-up.
        if (resizeRequested)
            resizeToText();

        repaint();
    }
}

void StatPopup::resizeToText() {
    resizeRequested = false;

    QSize size = fontMetrics().size(0, fullReading);
    resize(size.width() + 2 * TEXT_EXPANSION_HORIZONTAL,
        size.height() + 2 * TEXT_EXPANSION_VERTICAL);
    repaint();
}


#include "statpopup.moc"
