// -*- indent-tabs-mode: nil -*-
/*
    kCalculator, a simple scientific calculator for KDE

    Copyright (C) 1996-2000 Bernd Johannes Wuebben
                            wuebben@kde.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.

    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 Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/

#include "kcalc.h"

#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <QApplication>
#include <QCursor>
#include <QKeyEvent>
#include <QShortcut>
#include <QStyle>

#include <kaboutdata.h>
#include <kacceleratormanager.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <kapplication.h>
#include <kbuttongroup.h>
#include <kcmdlineargs.h>
#include <kcolorbutton.h>
#include <kcolormimedata.h>
#include <kconfig.h>
#include <kconfigdialog.h>
#include <kdialog.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kmenu.h>
#include <kmenubar.h>
#include <knumvalidator.h>
#include <kpushbutton.h>
#include <kstatusbar.h>
#include <kstandardaction.h>
#include <ktoggleaction.h>
#include <ktoolbar.h>
#include <kxmlguifactory.h>

#include "kcalcdisplay.h"
#include "kcalc_const_menu.h"
#include "version.h"
#include "kcalc_settings.h"
#include "kcalc_bitset.h"

class QActionGroup;

static const char description[] = I18N_NOOP("KDE Calculator");
static const char version[] = KCALCVERSION;

static const int maxprecision = 1000;

KCalculator::KCalculator(QWidget *parent)
        : KXmlGuiWindow(parent), shift_mode(false), hyp_mode(false),
        memory_num(0.0), constants(0), core()
{
    /* central widget to contain all the elements */
    QWidget *central = new QWidget(this);
    central->setLayoutDirection(Qt::LeftToRight);
    setCentralWidget(central);
    KAcceleratorManager::setNoAccel(central);

    // load science constants from xml-file
    KCalcConstMenu::init_consts();

    // setup interface (order is critical)

    setupUi(central);

    setupMainActions();

    setupStatusbar();

    createGUI();

    setupKeys();

    toolBar()->hide(); // hide by default

    // create button groups

    BaseChooseGroup = new QButtonGroup(this);
    BaseChooseGroup->setExclusive(true);
    BaseChooseGroup->addButton(hexRadio, HexMode);
    BaseChooseGroup->addButton(decRadio, DecMode);
    BaseChooseGroup->addButton(octRadio, OctMode);
    BaseChooseGroup->addButton(binRadio, BinMode);
    connect(BaseChooseGroup, SIGNAL(buttonClicked(int)),
            SLOT(slotBaseSelected(int)));

    AngleChooseGroup =  new QButtonGroup(this);
    AngleChooseGroup->setExclusive(true);
    AngleChooseGroup->addButton(degRadio, DegMode);
    AngleChooseGroup->addButton(radRadio, RadMode);
    AngleChooseGroup->addButton(gradRadio, GradMode);
    connect(AngleChooseGroup, SIGNAL(buttonClicked(int)),
            SLOT(slotAngleSelected(int)));

    // additional menu setup

    KCalcConstMenu *tmp_menu = new KCalcConstMenu(i18n("&Constants"), this);
    menuBar()->insertMenu((menuBar()->actions)()[2], tmp_menu);
    connect(tmp_menu, SIGNAL(triggeredConstant(science_constant const &)),
            this, SLOT(slotConstantToDisplay(science_constant const &)));

    // misc setup

    setColors();
    setFonts();

    // Show the result in the app's caption in taskbar (wishlist - bug #52858)
    if (KCalcSettings::captionResult() == true)
        connect(calc_display, SIGNAL(changedText(const QString &)),
                SLOT(setCaption(const QString &)));
    calc_display->changeSettings();
    setPrecision();

    updateGeometry();

    setFixedSize(minimumSize());

    updateDisplay(true);

    // misc settings
    KCalcSettings::EnumCalculatorMode::type calculatorMode = KCalcSettings::calculatorMode();
    switch (calculatorMode) {
    case KCalcSettings::EnumCalculatorMode::science:
        actionModeScience->setChecked(true);
        break;
    case KCalcSettings::EnumCalculatorMode::statistics:
        actionModeStatistic->setChecked(true);
        break;
    case KCalcSettings::EnumCalculatorMode::numeral:
        actionModeNumeral->setChecked(true);
        break;
    case KCalcSettings::EnumCalculatorMode::simple:
    default:
        actionModeSimple->setChecked(true);
    }

    setAngle();
    setBase();

    // connections

    connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
            SLOT(setColors()));
    connect(KGlobalSettings::self(), SIGNAL(kdisplayFontChanged()),
            SLOT(setFonts()));

    calc_display->setFocus();
}

KCalculator::~KCalculator()
{
    KCalcSettings::self()->writeConfig();
}

void KCalculator::setupMainActions(void)
{
    // file menu
    KStandardAction::quit(this, SLOT(close()), actionCollection());

    // edit menu
    KStandardAction::undo(calc_display, SLOT(slotHistoryBack()), actionCollection());
    KStandardAction::redo(calc_display, SLOT(slotHistoryForward()), actionCollection());
    KStandardAction::cut(calc_display, SLOT(slotCut()), actionCollection());
    KStandardAction::copy(calc_display, SLOT(slotCopy()), actionCollection());
    KStandardAction::paste(calc_display, SLOT(slotPaste()), actionCollection());

    // mode menu
    QActionGroup *modeGroup = new QActionGroup(this);
    actionModeSimple = actionCollection()->add<KToggleAction>("mode_simple");
    actionModeSimple->setActionGroup(modeGroup);
    actionModeSimple->setText(i18n("Simple Mode"));
    connect(actionModeSimple, SIGNAL(toggled(bool)), SLOT(slotSetSimpleMode()));
    actionModeScience = actionCollection()->add<KToggleAction>("mode_science");
    actionModeScience->setActionGroup(modeGroup);
    actionModeScience->setText(i18n("Science Mode"));
    connect(actionModeScience, SIGNAL(toggled(bool)), SLOT(slotSetScienceMode()));
    actionModeStatistic = actionCollection()->add<KToggleAction>("mode_statistics");
    actionModeStatistic->setActionGroup(modeGroup);
    actionModeStatistic->setText(i18n("Statistic Mode"));
    connect(actionModeStatistic, SIGNAL(toggled(bool)), SLOT(slotSetStatisticMode()));
    actionModeNumeral = actionCollection()->add<KToggleAction>("mode_numeral");
    actionModeNumeral->setActionGroup(modeGroup);
    actionModeNumeral->setText(i18n("Numeral System Mode"));
    connect(actionModeNumeral, SIGNAL(toggled(bool)), SLOT(slotSetNumeralMode()));

    // settings menu
    actionConstantsShow = actionCollection()->add<KToggleAction>("show_constants");
    actionConstantsShow->setText(i18n("&Constants Buttons"));
    actionConstantsShow->setChecked(true);
    connect(actionConstantsShow, SIGNAL(toggled(bool)),
            SLOT(slotConstantsShow(bool)));

    actionBitsetshow = actionCollection()->add<KToggleAction>("show_bitset");
    actionBitsetshow->setText(i18n("Show B&it Edit"));
    actionBitsetshow->setChecked(true);
    connect(actionBitsetshow, SIGNAL(toggled(bool)),
            SLOT(slotBitsetshow(bool)));

    KStandardAction::preferences(this, SLOT(showSettings()), actionCollection());
    KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()),
                                 actionCollection());
}

void KCalculator::setupStatusbar(void)
{
    // Status bar contents
    statusBar()->insertPermanentFixedItem(" NORM ", ShiftField);
    statusBar()->setItemAlignment(ShiftField, Qt::AlignCenter);

    statusBar()->insertPermanentFixedItem(" HEX ", BaseField);
    statusBar()->setItemAlignment(BaseField, Qt::AlignCenter);

    statusBar()->insertPermanentFixedItem(" DEG ", AngleField);
    statusBar()->setItemAlignment(AngleField, Qt::AlignCenter);

    statusBar()->insertPermanentFixedItem(" \xa0\xa0 ", MemField);   // nbsp
    statusBar()->setItemAlignment(MemField, Qt::AlignCenter);
}

// additional setup for button keys

void KCalculator::setupKeys()
{
    // NOTE: all alphanumeric shorts set in ui file

    // numbers

    NumButtonGroup = new QButtonGroup(this);
    connect(NumButtonGroup, SIGNAL(buttonClicked(int)),
            SLOT(slotNumberclicked(int)));

    NumButtonGroup->addButton(pb0, 0);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb0, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb1, 1);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb1, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb2, 2);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb2, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb3, 3);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb3, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb4, 4);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb4, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb5, 5);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb5, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb6, 6);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb6, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb7, 7);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb7, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb8, 8);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb8, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pb9, 9);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pb9, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbA, 0xA);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbA, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbB, 0xB);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbB, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbC, 0xC);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbD, 0xD);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbD, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbE, 0xE);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbE, SLOT(slotSetAccelDisplayMode(bool)));
    NumButtonGroup->addButton(pbF, 0xF);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbF, SLOT(slotSetAccelDisplayMode(bool)));

    // right keypad

    connect(pbShift, SIGNAL(toggled(bool)),
            SLOT(slotShifttoggled(bool)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbShift, SLOT(slotSetAccelDisplayMode(bool)));

    pbClear->setShortcut(QKeySequence(Qt::Key_Escape));
    new QShortcut(Qt::Key_PageUp, pbClear, SLOT(animateClick()));
    connect(pbClear, SIGNAL(clicked(void)),
            SLOT(slotClearclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbClear, SLOT(slotSetAccelDisplayMode(bool)));

    pbAllClear->setShortcut(QKeySequence(Qt::Key_Delete));
    new QShortcut(Qt::Key_PageDown, pbAllClear, SLOT(animateClick()));
    connect(pbAllClear, SIGNAL(clicked(void)),
            SLOT(slotAllClearclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbAllClear, SLOT(slotSetAccelDisplayMode(bool)));

    pbParenOpen->setShortcut(QKeySequence(Qt::Key_ParenLeft));
    connect(pbParenOpen, SIGNAL(clicked(void)),
            SLOT(slotParenOpenclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbParenOpen, SLOT(slotSetAccelDisplayMode(bool)));

    pbParenClose->setShortcut(QKeySequence(Qt::Key_ParenRight));
    connect(pbParenClose, SIGNAL(clicked(void)),
            SLOT(slotParenCloseclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbParenClose, SLOT(slotSetAccelDisplayMode(bool)));

    pbMemRecall->setDisabled(true);   // nothing in memory at start
    connect(pbMemRecall, SIGNAL(clicked(void)),
            SLOT(slotMemRecallclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMemRecall, SLOT(slotSetAccelDisplayMode(bool)));

    connect(pbMemClear, SIGNAL(clicked(void)),
            SLOT(slotMemClearclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMemClear, SLOT(slotSetAccelDisplayMode(bool)));

    pbMemPlusMinus->addMode(ModeNormal, i18nc("Add display to memory", "M+"), i18n("Add display to memory"));
    pbMemPlusMinus->addMode(ModeShift, i18nc("Subtract from memory", "M−"), i18n("Subtract from memory"));
    connect(pbMemPlusMinus, SIGNAL(clicked(void)),
            SLOT(slotMemPlusMinusclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMemPlusMinus, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbMemPlusMinus, SLOT(slotSetMode(ButtonModeFlags, bool)));

    connect(pbMemStore, SIGNAL(clicked(void)),
            SLOT(slotMemStoreclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMemStore, SLOT(slotSetAccelDisplayMode(bool)));

    pbPercent->setShortcut(QKeySequence(Qt::Key_Percent));
    connect(pbPercent, SIGNAL(clicked(void)),
            SLOT(slotPercentclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbPercent, SLOT(slotSetAccelDisplayMode(bool)));

    pbPlusMinus->setShortcut(QKeySequence(Qt::Key_Backslash));
    connect(pbPlusMinus, SIGNAL(clicked(void)),
            SLOT(slotPlusMinusclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbPlusMinus, SLOT(slotSetAccelDisplayMode(bool)));

    // numeric keypad

    pbCube->addMode(ModeNormal, i18nc("Third power", "x<sup>3</sup>"), i18n("Third power"));
    pbCube->addMode(ModeShift, "<sup>3</sup>&radic;x", i18n("Cube root"));
    connect(pbCube, SIGNAL(clicked(void)),
            SLOT(slotCubeclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbCube, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbCube, SLOT(slotSetMode(ButtonModeFlags, bool)));

    pbDivision->setShortcut(QKeySequence(Qt::Key_Slash));
    new QShortcut(Qt::Key_division, pbDivision, SLOT(animateClick()));
    connect(pbDivision, SIGNAL(clicked(void)),
            SLOT(slotDivisionclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbDivision, SLOT(slotSetAccelDisplayMode(bool)));

    pbMultiplication->setShortcut(QKeySequence(Qt::Key_Asterisk));
    new QShortcut(Qt::Key_X, pbMultiplication, SLOT(animateClick()));
    new QShortcut(Qt::Key_multiply, pbMultiplication, SLOT(animateClick()));
    connect(pbMultiplication, SIGNAL(clicked(void)),
            SLOT(slotMultiplicationclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMultiplication, SLOT(slotSetAccelDisplayMode(bool)));

    pbMinus->setShortcut(QKeySequence(Qt::Key_Minus));
    connect(pbMinus, SIGNAL(clicked(void)),
            SLOT(slotMinusclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMinus, SLOT(slotSetAccelDisplayMode(bool)));

    pbPlus->setShortcut(QKeySequence(Qt::Key_Plus));
    connect(pbPlus, SIGNAL(clicked(void)),
            SLOT(slotPlusclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbPlus, SLOT(slotSetAccelDisplayMode(bool)));

    pbPeriod->setText(KGlobal::locale()->decimalSymbol());
    pbPeriod->setShortcut(KGlobal::locale()->decimalSymbol());
    if (KGlobal::locale()->decimalSymbol() == ".")
        new QShortcut(Qt::Key_Comma, pbPeriod, SLOT(animateClick()));
    else if (KGlobal::locale()->decimalSymbol() == ",")
        new QShortcut(Qt::Key_Period, pbPeriod, SLOT(animateClick()));
    connect(pbPeriod, SIGNAL(clicked(void)),
            SLOT(slotPeriodclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbPeriod, SLOT(slotSetAccelDisplayMode(bool)));

    pbEqual->setShortcut(QKeySequence(Qt::Key_Enter));
    new QShortcut(Qt::Key_Equal, pbEqual, SLOT(animateClick()));
    new QShortcut(Qt::Key_Return, pbEqual, SLOT(animateClick()));
    connect(pbEqual, SIGNAL(clicked(void)),
            SLOT(slotEqualclicked(void)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbEqual, SLOT(slotSetAccelDisplayMode(bool)));

    // logic keys

    logicButtons.append(pbAND);
    logicButtons.append(pbOR);
    logicButtons.append(pbXOR);
    logicButtons.append(pbLsh);
    logicButtons.append(pbRsh);
    logicButtons.append(pbCmp);

    pbAND->setShortcut(QKeySequence(Qt::Key_Ampersand));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbAND, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbAND, SIGNAL(clicked(void)), SLOT(slotANDclicked(void)));

    pbOR->setShortcut(QKeySequence(Qt::Key_Bar));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbOR, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbOR, SIGNAL(clicked(void)), SLOT(slotORclicked(void)));

    connect(this, SIGNAL(switchShowAccels(bool)),
            pbXOR, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbXOR, SIGNAL(clicked(void)), SLOT(slotXORclicked(void)));

    pbLsh->setShortcut(QKeySequence(Qt::Key_Less));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbLsh, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbLsh, SIGNAL(clicked(void)),
            SLOT(slotLeftShiftclicked(void)));

    pbRsh->setShortcut(QKeySequence(Qt::Key_Greater));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbRsh, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbRsh, SIGNAL(clicked(void)),
            SLOT(slotRightShiftclicked(void)));

    pbCmp->setShortcut(QKeySequence(Qt::Key_AsciiTilde));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbCmp, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbCmp, SIGNAL(clicked(void)), SLOT(slotNegateclicked(void)));

    // scientific keys

    scientificButtons.append(pbHyp);
    scientificButtons.append(pbSin);
    scientificButtons.append(pbCos);
    scientificButtons.append(pbTan);
    scientificButtons.append(pbLog);
    scientificButtons.append(pbLn);

    connect(this, SIGNAL(switchShowAccels(bool)),
            pbHyp, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbHyp, SIGNAL(toggled(bool)), SLOT(slotHyptoggled(bool)));

    pbSin->addMode(ModeNormal, i18nc("Sine", "Sin"), i18n("Sine"));
    pbSin->addMode(ModeShift, i18nc("Arc sine", "Asin"), i18n("Arc sine"));
    pbSin->addMode(ModeHyperbolic, i18nc("Hyperbolic sine", "Sinh"), i18n("Hyperbolic sine"));
    pbSin->addMode(ButtonModeFlags(ModeShift | ModeHyperbolic),
                   i18nc("Inverse hyperbolic sine", "Asinh"), i18n("Inverse hyperbolic sine"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbSin, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbSin, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbSin, SIGNAL(clicked(void)), SLOT(slotSinclicked(void)));

    pbCos->addMode(ModeNormal, i18nc("Cosine", "Cos"), i18n("Cosine"));
    pbCos->addMode(ModeShift, i18nc("Arc cosine", "Acos"), i18n("Arc cosine"));
    pbCos->addMode(ModeHyperbolic, i18nc("Hyperbolic cosine", "Cosh"), i18n("Hyperbolic cosine"));
    pbCos->addMode(ButtonModeFlags(ModeShift | ModeHyperbolic),
                   i18nc("Inverse hyperbolic cosine", "Acosh"), i18n("Inverse hyperbolic cosine"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbCos, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbCos, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbCos, SIGNAL(clicked(void)), SLOT(slotCosclicked(void)));

    pbTan->addMode(ModeNormal, i18nc("Tangent", "Tan"), i18n("Tangent"));
    pbTan->addMode(ModeShift, i18nc("Arc tangent", "Atan"), i18n("Arc tangent"));
    pbTan->addMode(ModeHyperbolic, i18nc("Hyperbolic tangent", "Tanh"), i18n("Hyperbolic tangent"));
    pbTan->addMode(ButtonModeFlags(ModeShift | ModeHyperbolic),
                   i18nc("Inverse hyperbolic tangent", "Atanh"), i18n("Inverse hyperbolic tangent"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbTan, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbTan, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbTan, SIGNAL(clicked(void)), SLOT(slotTanclicked(void)));

    pbLog->addMode(ModeNormal, i18nc("Logarithm to base 10", "Log"), i18n("Logarithm to base 10"));
    pbLog->addMode(ModeShift, i18nc("10 to the power of x", "10<sup>x</sup>"), i18n("10 to the power of x"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbLog, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbLog, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbLog, SIGNAL(clicked(void)), SLOT(slotLogclicked(void)));
    pbLn->addMode(ModeNormal, i18nc("Natural log", "Ln"), i18n("Natural log"));
    pbLn->addMode(ModeShift, i18nc("Exponential function", "e<sup>x</sup>"), i18n("Exponential function"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbLn, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbLn, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbLn, SIGNAL(clicked(void)), SLOT(slotLnclicked(void)));

    // statistic buttons

    statButtons.append(pbNData);
    statButtons.append(pbMean);
    statButtons.append(pbSd);
    statButtons.append(pbMed);
    statButtons.append(pbDat);
    statButtons.append(pbCSt);

    pbNData->addMode(ModeNormal, i18nc("Number of data entered", "N"), i18n("Number of data entered"));
    pbNData->addMode(ModeShift, QString::fromUtf8("\xce\xa3")
                     + 'x', i18n("Sum of all data items"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbNData, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbNData, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbNData, SIGNAL(clicked(void)), SLOT(slotStatNumclicked(void)));

    pbMean->addMode(ModeNormal, i18nc("Mean", "Mea"), i18n("Mean"));
    pbMean->addMode(ModeShift, QString::fromUtf8("\xce\xa3")
                    + "x<sup>2</sup>",
                    i18n("Sum of all data items squared"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMean, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbMean, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbMean, SIGNAL(clicked(void)), SLOT(slotStatMeanclicked(void)));

    pbSd->addMode(ModeNormal, QString::fromUtf8("σ", -1) + "<sub>N</sub>",
                  i18n("Standard deviation"));
    pbSd->addMode(ModeShift, QString::fromUtf8("σ", -1) + "<sub>N-1</sub>",
                  i18n("Sample standard deviation"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbSd, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbSd, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbSd, SIGNAL(clicked(void)), SLOT(slotStatStdDevclicked(void)));

    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMed, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbMed, SIGNAL(clicked(void)), SLOT(slotStatMedianclicked(void)));

    pbDat->addMode(ModeNormal, i18nc("Enter data", "Dat"), i18n("Enter data"));
    pbDat->addMode(ModeShift, i18nc("Delete last data item", "CDat"), i18n("Delete last data item"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbDat, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbDat, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbDat, SIGNAL(clicked(void)), SLOT(slotStatDataInputclicked(void)));

    connect(this, SIGNAL(switchShowAccels(bool)),
            pbCSt, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbCSt, SIGNAL(clicked(void)), SLOT(slotStatClearDataclicked(void)));

    // constants keys

    constButtons.append(pbC1);
    constButtons.append(pbC2);
    constButtons.append(pbC3);
    constButtons.append(pbC4);
    constButtons.append(pbC5);
    constButtons.append(pbC6);

    pbC1->setButtonNumber(0);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC1, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC1, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC1, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    pbC2->setButtonNumber(1);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC2, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC2, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC2, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    pbC3->setButtonNumber(2);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC3, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC3, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC3, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    pbC4->setButtonNumber(3);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC4, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC4, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC4, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    pbC5->setButtonNumber(4);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC5, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC5, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC5, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    pbC6->setButtonNumber(5);
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbC6, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbC6, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbC6, SIGNAL(clicked(int)), this, SLOT(slotConstclicked(int)));

    changeButtonNames();

    // misc buttons

    pbMod->addMode(ModeNormal, i18nc("Modulo", "Mod"), i18n("Modulo"));
    pbMod->addMode(ModeShift, i18nc("Integer division", "IntDiv"), i18n("Integer division"));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbMod, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbMod, SLOT(slotSetAccelDisplayMode(bool)));
    pbMod->setShortcut(QKeySequence(Qt::Key_Colon));
    connect(pbMod, SIGNAL(clicked(void)), SLOT(slotModclicked(void)));

    pbReci->addMode(ModeNormal, i18nc("Reciprocal", "1/x"), i18n("Reciprocal"));
    pbReci->addMode(ModeShift, i18nc("n Choose m", "nCm"), i18n("n Choose m"));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbReci, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbReci, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbReci, SIGNAL(clicked(void)), SLOT(slotReciclicked(void)));

    pbFactorial->addMode(ModeNormal, i18nc("Factorial", "x!"), i18n("Factorial"));
    pbFactorial->setShortcut(QKeySequence(Qt::Key_Exclam));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbFactorial, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbFactorial, SIGNAL(clicked(void)), SLOT(slotFactorialclicked(void)));

    pbSquare->addMode(ModeNormal, i18nc("Square", "x<sup>2</sup>"), i18n("Square"));
    pbSquare->addMode(ModeShift, "&radic;x", i18n("Square root"));
    pbSquare->setShortcut(QKeySequence(Qt::Key_BracketLeft));
    new QShortcut(Qt::Key_twosuperior, pbSquare, SLOT(animateClick()));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbSquare, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbSquare, SLOT(slotSetMode(ButtonModeFlags, bool)));
    connect(pbSquare, SIGNAL(clicked(void)), SLOT(slotSquareclicked(void)));

    pbPower->addMode(ModeNormal, i18nc("x to the power of y", "x<sup>y</sup>"), i18n("x to the power of y"));
    pbPower->addMode(ModeShift, i18nc("x to the power of 1/y", "x<sup>1/y</sup>"), i18n("x to the power of 1/y"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbPower, SLOT(slotSetAccelDisplayMode(bool)));
    connect(this, SIGNAL(switchMode(ButtonModeFlags, bool)),
            pbPower, SLOT(slotSetMode(ButtonModeFlags, bool)));
    pbPower->setShortcut(QKeySequence(Qt::Key_AsciiCircum));
    connect(pbPower, SIGNAL(clicked(void)), SLOT(slotPowerclicked(void)));

    pbEE->addMode(ModeNormal, "x<small>" "\xb7" "10</small><sup>y</sup>",
                  i18n("Exponent"));
    connect(this, SIGNAL(switchShowAccels(bool)),
            pbEE, SLOT(slotSetAccelDisplayMode(bool)));
    connect(pbEE, SIGNAL(clicked(void)), SLOT(slotEEclicked(void)));

    // other button lists

    mFunctionButtonList.append(pbHyp);
    mFunctionButtonList.append(pbShift);
    mFunctionButtonList.append(pbEE);
    mFunctionButtonList.append(pbSin);
    mFunctionButtonList.append(pbPlusMinus);
    mFunctionButtonList.append(pbCos);
    mFunctionButtonList.append(pbReci);
    mFunctionButtonList.append(pbTan);
    mFunctionButtonList.append(pbFactorial);
    mFunctionButtonList.append(pbLog);
    mFunctionButtonList.append(pbSquare);
    mFunctionButtonList.append(pbLn);
    mFunctionButtonList.append(pbPower);

    mMemButtonList.append(pbMemRecall);
    mMemButtonList.append(pbMemPlusMinus);
    mMemButtonList.append(pbMemStore);
    mMemButtonList.append(pbMemClear);
    mMemButtonList.append(pbClear);
    mMemButtonList.append(pbAllClear);

    mOperationButtonList.append(pbMultiplication);
    mOperationButtonList.append(pbParenOpen);
    mOperationButtonList.append(pbParenClose);
    mOperationButtonList.append(pbAND);
    mOperationButtonList.append(pbDivision);
    mOperationButtonList.append(pbOR);
    mOperationButtonList.append(pbXOR);
    mOperationButtonList.append(pbPlus);
    mOperationButtonList.append(pbMinus);
    mOperationButtonList.append(pbLsh);
    mOperationButtonList.append(pbRsh);
    mOperationButtonList.append(pbPeriod);
    mOperationButtonList.append(pbEqual);
    mOperationButtonList.append(pbPercent);
    mOperationButtonList.append(pbCmp);
    mOperationButtonList.append(pbMod);
    mOperationButtonList.append(pbCube);
}

void KCalculator::updateGeometry(void)
{
    KCalcButton *button;
    QSize em = pbAND->fontMetrics().size(0, "M");
    int margin =
        QApplication::style()->pixelMetric(QStyle::PM_ButtonMargin, 0, 0);
    margin = qMax(qMin(margin / 2, 3), 3);

    // left pad

    foreach(QObject *obj, leftPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            button->setFixedWidth(em.width()*4 + margin*2);
            button->installEventFilter(this);
        }
    }

    // right pad

    foreach(QObject *obj, rightPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            button->setFixedWidth(em.width()*3 + margin*2);
            button->installEventFilter(this);
        }
    }

    // numeric pad

    foreach(QObject *obj, numericPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            if (button != pb0) {   // let pb0 expand freely
                button->setFixedWidth(em.width()*3 + margin*2);
            }
            button->installEventFilter(this);
        }
    }
}

void KCalculator::slotConstantToDisplay(struct science_constant const &const_chosen)
{
    calc_display->setAmount(const_chosen.value);

    updateDisplay(false);
}

void KCalculator::slotBaseSelected(int base)
{
    int current_base;

    // set display & statusbar (if item exist in statusbar)
    switch (base) {
    case BinMode:
        current_base = calc_display->setBase(NumBase(2));
        statusBar()->changeItem("BIN", BaseField);
        calc_display->setStatusText(BaseField, "Bin");
        break;
    case OctMode:
        current_base = calc_display->setBase(NumBase(8));
        statusBar()->changeItem("OCT", BaseField);
        calc_display->setStatusText(BaseField, "Oct");
        break;
    case DecMode:
        current_base = calc_display->setBase(NumBase(10));
        statusBar()->changeItem("DEC", BaseField);
        calc_display->setStatusText(BaseField, "Dec");
        break;
    case HexMode:
        current_base = calc_display->setBase(NumBase(16));
        statusBar()->changeItem("HEX", BaseField);
        calc_display->setStatusText(BaseField, "Hex");
        break;
    default:
        statusBar()->changeItem("Error", BaseField);
        calc_display->setStatusText(BaseField, "Error");
        return;
    }

    // Enable the buttons not available in this base
    for (int i = 0; i < current_base; i++)
        (NumButtonGroup->buttons()[i])->setEnabled(true);

    // Disable the buttons not available in this base
    for (int i = current_base; i < 16; i++)
        (NumButtonGroup->buttons()[i])->setEnabled(false);

    // Only enable the decimal point in decimal
    pbPeriod->setEnabled(current_base == NB_DECIMAL);
    // Only enable the x*10^y button in decimal
    pbEE->setEnabled(current_base == NB_DECIMAL);

    // Disable buttons that make only sense with floating point numbers
    if (current_base != NB_DECIMAL)  {
        foreach(QAbstractButton *btn, scientificButtons) {
            btn->setEnabled(false);
        }
    } else {
        foreach(QAbstractButton *btn, scientificButtons) {
            btn->setEnabled(true);
        }
    }
    KCalcSettings::setBaseMode(base);
}

void KCalculator::keyPressEvent(QKeyEvent *e)
{
    if (((e->modifiers() & Qt::NoModifier) == 0) ||
            (e->modifiers() & Qt::ShiftModifier)) {
        switch (e->key()) {
        case Qt::Key_Backspace:
            calc_display->deleteLastDigit();
            break;
        }
    }

    if (e->key() == Qt::Key_Control)
        emit switchShowAccels(true);
}

void KCalculator::keyReleaseEvent(QKeyEvent *e)
{
    if (e->key() == Qt::Key_Control)
        emit switchShowAccels(false);
}

void KCalculator::slotAngleSelected(int mode)
{
    _angle_mode = mode;
    switch (mode) {
    case DegMode:
        statusBar()->changeItem("DEG", AngleField);
        calc_display->setStatusText(AngleField, "Deg");
        break;
    case RadMode:
        statusBar()->changeItem("RAD", AngleField);
        calc_display->setStatusText(AngleField, "Rad");
        break;
    case GradMode:
        statusBar()->changeItem("GRA", AngleField);
        calc_display->setStatusText(AngleField, "Gra");
        break;
    default: // we shouldn't ever end up here
        _angle_mode = RadMode;
    }
    KCalcSettings::setAngleMode(_angle_mode);
}

void KCalculator::slotEEclicked(void)
{
    calc_display->newCharacter('e');
}

void KCalculator::slotShifttoggled(bool flag)
{
    shift_mode = flag;

    emit switchMode(ModeShift, flag);

    if (shift_mode) {
        statusBar()->changeItem(i18nc("Second button functions are active", "SHIFT"), ShiftField);
        calc_display->setStatusText(ShiftField, i18n("Shift"));
    } else {
        statusBar()->changeItem(i18nc("Normal button functions are active", "NORM"), ShiftField);
        calc_display->setStatusText(ShiftField, QString());
    }
}

void KCalculator::slotHyptoggled(bool flag)
{
    // toggle between hyperbolic and standart trig functions
    hyp_mode = flag;

    emit switchMode(ModeHyperbolic, flag);
}



void KCalculator::slotMemRecallclicked(void)
{
    // temp. work-around
    calc_display->sendEvent(KCalcDisplay::EventReset);

    calc_display->setAmount(memory_num);
    updateDisplay(false);
}

void KCalculator::slotMemStoreclicked(void)
{
    EnterEqual();

    memory_num = calc_display->getAmount();
    calc_display->setStatusText(MemField, "M");
    statusBar()->changeItem("M", MemField);
    pbMemRecall->setEnabled(true);
}

void KCalculator::slotNumberclicked(int number_clicked)
{
    calc_display->enterDigit(number_clicked);
}

void KCalculator::slotSinclicked(void)
{
    if (hyp_mode) {
        // sinh or arsinh
        if (!shift_mode)
            core.SinHyp(calc_display->getAmount());
        else
            core.AreaSinHyp(calc_display->getAmount());
    } else {
        // sine or arcsine
        if (!shift_mode)
            switch (_angle_mode) {
            case DegMode:
                core.SinDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.SinRad(calc_display->getAmount());
                break;
            case GradMode:
                core.SinGrad(calc_display->getAmount());
                break;
            }
        else
            switch (_angle_mode) {
            case DegMode:
                core.ArcSinDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.ArcSinRad(calc_display->getAmount());
                break;
            case GradMode:
                core.ArcSinGrad(calc_display->getAmount());
                break;
            }
    }

    updateDisplay(true);
}

void KCalculator::slotPlusMinusclicked(void)
{
    // display can only change sign, when in input mode, otherwise we
    // need the core to do this.
    if (!calc_display->sendEvent(KCalcDisplay::EventChangeSign)) {
        core.InvertSign(calc_display->getAmount());
        updateDisplay(true);
    }
}

void KCalculator::slotMemPlusMinusclicked(void)
{
    bool tmp_shift_mode = shift_mode; // store this, because next command deletes shift_mode
    EnterEqual(); // finish calculation so far, to store result into MEM

    if (!tmp_shift_mode) memory_num += calc_display->getAmount();
    else    memory_num -= calc_display->getAmount();

    pbShift->setChecked(false);
    statusBar()->changeItem(i18n("M"), MemField);
    calc_display->setStatusText(MemField, i18n("M"));
    pbMemRecall->setEnabled(true);
}

void KCalculator::slotCosclicked(void)
{
    if (hyp_mode) {
        // cosh or arcosh
        if (!shift_mode)
            core.CosHyp(calc_display->getAmount());
        else
            core.AreaCosHyp(calc_display->getAmount());
    } else {
        // cosine or arccosine
        if (!shift_mode)
            switch (_angle_mode) {
            case DegMode:
                core.CosDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.CosRad(calc_display->getAmount());
                break;
            case GradMode:
                core.CosGrad(calc_display->getAmount());
                break;
            }
        else
            switch (_angle_mode) {
            case DegMode:
                core.ArcCosDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.ArcCosRad(calc_display->getAmount());
                break;
            case GradMode:
                core.ArcCosGrad(calc_display->getAmount());
                break;
            }
    }

    updateDisplay(true);
}

void KCalculator::slotReciclicked(void)
{
    if (shift_mode) {
        core.enterOperation(calc_display->getAmount(),
                            CalcEngine::FUNC_BINOM);
    }  else {
        core.Reciprocal(calc_display->getAmount());
        updateDisplay(true);
        return;
    }
    // temp. work-around
    KNumber tmp_num = calc_display->getAmount();
    calc_display->sendEvent(KCalcDisplay::EventReset);
    calc_display->setAmount(tmp_num);
    updateDisplay(false);

}

void KCalculator::slotTanclicked(void)
{
    if (hyp_mode) {
        // tanh or artanh
        if (!shift_mode)
            core.TangensHyp(calc_display->getAmount());
        else
            core.AreaTangensHyp(calc_display->getAmount());
    } else {
        // tan or arctan
        if (!shift_mode)
            switch (_angle_mode) {
            case DegMode:
                core.TangensDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.TangensRad(calc_display->getAmount());
                break;
            case GradMode:
                core.TangensGrad(calc_display->getAmount());
                break;
            }
        else
            switch (_angle_mode) {
            case DegMode:
                core.ArcTangensDeg(calc_display->getAmount());
                break;
            case RadMode:
                core.ArcTangensRad(calc_display->getAmount());
                break;
            case GradMode:
                core.ArcTangensGrad(calc_display->getAmount());
                break;
            }
    }

    updateDisplay(true);
}

void KCalculator::slotFactorialclicked(void)
{
    // Set WaitCursor, as this operation may take looooong
    // time and UI frezes with large numbers. User needs some
    // visual feedback.
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    core.Factorial(calc_display->getAmount());
    QApplication::restoreOverrideCursor();
    updateDisplay(true);
}

void KCalculator::slotLogclicked(void)
{
    if (!shift_mode)
        core.Log10(calc_display->getAmount());
    else
        core.Exp10(calc_display->getAmount());

    updateDisplay(true);
}


void KCalculator::slotSquareclicked(void)
{
    if (!shift_mode)
        core.Square(calc_display->getAmount());
    else
        core.SquareRoot(calc_display->getAmount());

    updateDisplay(true);
}


void KCalculator::slotCubeclicked(void)
{
    if (!shift_mode)
        core.Cube(calc_display->getAmount());
    else
        core.CubeRoot(calc_display->getAmount());

    updateDisplay(true);
}


void KCalculator::slotLnclicked(void)
{
    if (!shift_mode)
        core.Ln(calc_display->getAmount());
    else
        core.Exp(calc_display->getAmount());

    updateDisplay(true);
}

void KCalculator::slotPowerclicked(void)
{
    if (shift_mode) {
        core.enterOperation(calc_display->getAmount(),
                            CalcEngine::FUNC_PWR_ROOT);
        pbShift->setChecked(false);
    } else {
        core.enterOperation(calc_display->getAmount(),
                            CalcEngine::FUNC_POWER);
    }
    // temp. work-around
    KNumber tmp_num = calc_display->getAmount();
    calc_display->sendEvent(KCalcDisplay::EventReset);
    calc_display->setAmount(tmp_num);
    updateDisplay(false);
}

void KCalculator::slotMemClearclicked(void)
{
    memory_num  = 0;
    statusBar()->changeItem(" \xa0\xa0 ", MemField);
    calc_display->setStatusText(MemField, QString());
    pbMemRecall->setDisabled(true);
}

void KCalculator::slotClearclicked(void)
{
    calc_display->sendEvent(KCalcDisplay::EventClear);
}

void KCalculator::slotAllClearclicked(void)
{
    core.Reset();
    calc_display->sendEvent(KCalcDisplay::EventReset);

    updateDisplay(true);
}

void KCalculator::slotParenOpenclicked(void)
{
    core.ParenOpen(calc_display->getAmount());

    // What behavior, if e.g.: "12(6*6)"??
    //updateDisplay(true);
}

void KCalculator::slotParenCloseclicked(void)
{
    core.ParenClose(calc_display->getAmount());

    updateDisplay(true);
}

void KCalculator::slotANDclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_AND);

    updateDisplay(true);
}

void KCalculator::slotMultiplicationclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_MULTIPLY);

    updateDisplay(true);
}

void KCalculator::slotDivisionclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_DIVIDE);

    updateDisplay(true);
}

void KCalculator::slotORclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_OR);

    updateDisplay(true);
}

void KCalculator::slotXORclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_XOR);

    updateDisplay(true);
}

void KCalculator::slotPlusclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_ADD);

    updateDisplay(true);
}

void KCalculator::slotMinusclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_SUBTRACT);

    updateDisplay(true);
}

void KCalculator::slotLeftShiftclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_LSH);

    updateDisplay(true);
}

void KCalculator::slotRightShiftclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_RSH);

    updateDisplay(true);
}

void KCalculator::slotPeriodclicked(void)
{
    calc_display->newCharacter('.');
}

void KCalculator::EnterEqual()
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_EQUAL);

    updateDisplay(true, true);
}

void KCalculator::slotEqualclicked(void)
{
    EnterEqual();
}

void KCalculator::slotPercentclicked(void)
{
    core.enterOperation(calc_display->getAmount(),
                        CalcEngine::FUNC_PERCENT);

    updateDisplay(true);
}

void KCalculator::slotNegateclicked(void)
{
    core.Complement(calc_display->getAmount());

    updateDisplay(true);
}

void KCalculator::slotModclicked(void)
{
    if (shift_mode)
        core.enterOperation(calc_display->getAmount(),
                            CalcEngine::FUNC_INTDIV);
    else
        core.enterOperation(calc_display->getAmount(),
                            CalcEngine::FUNC_MOD);

    updateDisplay(true);
}

void KCalculator::slotStatNumclicked(void)
{
    if (!shift_mode) {
        core.StatCount(0);
    } else {
        pbShift->setChecked(false);
        core.StatSum(0);
    }

    updateDisplay(true);
}

void KCalculator::slotStatMeanclicked(void)
{
    if (!shift_mode)
        core.StatMean(0);
    else {
        pbShift->setChecked(false);
        core.StatSumSquares(0);
    }

    updateDisplay(true);
}

void KCalculator::slotStatStdDevclicked(void)
{
    if (shift_mode) {
        // std (n-1)
        core.StatStdDeviation(0);
        pbShift->setChecked(false);
    } else {
        // std (n)
        core.StatStdSample(0);
    }

    updateDisplay(true);
}

void KCalculator::slotStatMedianclicked(void)
{
    if (!shift_mode) {
        // std (n-1)
        core.StatMedian(0);
    } else {
        // std (n)
        core.StatMedian(0);
        pbShift->setChecked(false);
    }
    // it seems two different modes should be implemented, but...?
    updateDisplay(true);
}

void KCalculator::slotStatDataInputclicked(void)
{
    if (!shift_mode) {
        core.StatDataNew(calc_display->getAmount());
    } else {
        pbShift->setChecked(false);
        core.StatDataDel(0);
        statusBar()->showMessage(i18n("Last stat item erased"), 3000);
    }

    updateDisplay(true);
}

void KCalculator::slotStatClearDataclicked(void)
{
    if (!shift_mode) {
        core.StatClearAll(0);
        statusBar()->showMessage(i18n("Stat mem cleared"), 3000);
    } else {
        pbShift->setChecked(false);
        updateDisplay(false);
    }
}

void KCalculator::slotConstclicked(int button)
{
    KCalcConstButton *btn = qobject_cast<KCalcConstButton*>(constButtons[button]);
    if (!btn) return;

    if (!shift_mode) {
        // set the display to the configured value of constant button
        calc_display->setAmount(btn->constant());
    } else {
        pbShift->setChecked(false);
        KCalcSettings::setValueConstant(button, calc_display->text());
        // below set new tooltip
        btn->setLabelAndTooltip();
        // work around: after storing a number, pressing a digit should start
        // a new number
        calc_display->setAmount(calc_display->getAmount());
    }

    updateDisplay(false);
}

void KCalculator::showSettings()
{
    // Check if there is already a dialog and if so bring
    // it to the foreground.
    if (KConfigDialog::showDialog("settings"))
        return;

    // Create a new dialog with the same name as the above checking code.
    KConfigDialog *dialog = new KConfigDialog(this, "settings", KCalcSettings::self());
    dialog->showButtonSeparator(true);

    // general settings

    General *general = new General(0);
    general->kcfg_Precision->setMaximum(maxprecision);
    dialog->addPage(general, i18n("General"), "accessories-calculator", i18n("General Settings"));

    // font settings

    Fonts *fonts = new Fonts(0);
    dialog->addPage(fonts, i18n("Font"), "preferences-desktop-font", i18n("Select Display Font"));

    // color settings

    Colors *color = new Colors(0);
    dialog->addPage(color, i18n("Colors"), "format-fill-color", i18n("Button & Display Colors"));

    // constant settings

    if (!constants)
        constants = new Constants(0);

    KCalcConstMenu *tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst0(science_constant const &)));
    constants->pushButton0->setMenu(tmp_menu);

    tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst1(science_constant const &)));
    constants->pushButton1->setMenu(tmp_menu);

    tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst2(science_constant const &)));
    constants->pushButton2->setMenu(tmp_menu);

    tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst3(science_constant const &)));
    constants->pushButton3->setMenu(tmp_menu);

    tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst4(science_constant const &)));
    constants->pushButton4->setMenu(tmp_menu);

    tmp_menu = new KCalcConstMenu(this);
    connect(tmp_menu,
            SIGNAL(triggeredConstant(science_constant const &)),
            this,
            SLOT(slotChooseScientificConst5(science_constant const &)));
    constants->pushButton5->setMenu(tmp_menu);

    dialog->addPage(constants, i18n("Constants"), "preferences-kcalc-constants", i18n("Define Constants"));

    // When the user clicks OK or Apply we want to update our settings.
    connect(dialog, SIGNAL(settingsChanged(const QString &)), SLOT(updateSettings()));

    // Display the dialog.
    dialog->show();
}


// these 6 slots are just a quick hack, instead of setting the
// TextEdit fields in the configuration dialog, we are setting the
// Settingvalues themselves!!
void KCalculator::slotChooseScientificConst0(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant0->setText(chosen_const.value);

    constants->kcfg_nameConstant0->setText(chosen_const.label);
}

void KCalculator::slotChooseScientificConst1(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant1->setText(chosen_const.value);

    constants->kcfg_nameConstant1->setText(chosen_const.label);
}

void KCalculator::slotChooseScientificConst2(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant2->setText(chosen_const.value);

    constants->kcfg_nameConstant2->setText(chosen_const.label);
}

void KCalculator::slotChooseScientificConst3(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant3->setText(chosen_const.value);

    constants->kcfg_nameConstant3->setText(chosen_const.label);
}

void KCalculator::slotChooseScientificConst4(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant4->setText(chosen_const.value);

    constants->kcfg_nameConstant4->setText(chosen_const.label);
}

void KCalculator::slotChooseScientificConst5(struct science_constant const & chosen_const)
{
    constants->kcfg_valueConstant5->setText(chosen_const.value);

    constants->kcfg_nameConstant5->setText(chosen_const.label);
}

void KCalculator::slotSetSimpleMode()
{
    actionConstantsShow->setChecked(false);
    actionConstantsShow->setEnabled(false);
    actionBitsetshow->setChecked(false);
    actionBitsetshow->setEnabled(false);
    showScienceButtons(false);
    showStatButtons(false);
    showLogicButtons(false);
    KCalcSettings::setCalculatorMode(KCalcSettings::EnumCalculatorMode::simple);
}

void KCalculator::slotSetScienceMode()
{
    actionConstantsShow->setEnabled(true);
    actionConstantsShow->setChecked(KCalcSettings::showConstants());
    actionBitsetshow->setChecked(false);
    actionBitsetshow->setEnabled(false);
    showScienceButtons(true);
    showStatButtons(false);
    showLogicButtons(false);
    KCalcSettings::setCalculatorMode(KCalcSettings::EnumCalculatorMode::science);
}

void KCalculator::slotSetStatisticMode()
{
    actionConstantsShow->setEnabled(true);
    actionConstantsShow->setChecked(KCalcSettings::showConstants());
    actionBitsetshow->setChecked(false);
    actionBitsetshow->setEnabled(false);
    showScienceButtons(true);
    showStatButtons(true);
    showLogicButtons(false);
    KCalcSettings::setCalculatorMode(KCalcSettings::EnumCalculatorMode::statistics);
}

void KCalculator::slotSetNumeralMode()
{
    actionConstantsShow->setChecked(false);
    actionConstantsShow->setEnabled(false);
    actionBitsetshow->setEnabled(true);
    actionBitsetshow->setChecked(KCalcSettings::showBitset());
    showScienceButtons(false);
    showStatButtons(false);
    showLogicButtons(true);
    KCalcSettings::setCalculatorMode(KCalcSettings::EnumCalculatorMode::numeral);
}

void KCalculator::showStatButtons(bool toggled)
{
    if (toggled) {
        foreach(QAbstractButton *btn, statButtons) {
            btn->show();
        }
    } else {
        foreach(QAbstractButton *btn, statButtons) {
            btn->hide();
        }
    }
}

void KCalculator::showScienceButtons(bool toggled)
{
    if (toggled) {
        foreach(QAbstractButton* btn, scientificButtons) {
            btn->show();
        }
        foreach(QAbstractButton* btn, AngleChooseGroup->buttons()) {
            btn->show();
        }
        statusBar()->setItemFixed(AngleField, -1);
        setAngle();
    } else {
        foreach(QAbstractButton* btn, scientificButtons) {
            btn->hide();
        }
        foreach(QAbstractButton* btn, AngleChooseGroup->buttons()) {
            btn->hide();
        }
        statusBar()->changeItem(QString(), AngleField);
        statusBar()->setItemFixed(AngleField, 0);
        calc_display->setStatusText(AngleField, QString());
    }
}

void KCalculator::showLogicButtons(bool toggled)
{
    if (toggled) {
        mBitset->setEnabled(true);
        connect(mBitset, SIGNAL(valueChanged(unsigned long long)),
                this, SLOT(slotBitsetChanged(unsigned long long)));
        connect(calc_display, SIGNAL(changedAmount(const KNumber &)),
                SLOT(slotUpdateBitset(const KNumber &)));
        foreach(QAbstractButton* btn, logicButtons) {
            btn->show();
        }
        statusBar()->setItemFixed(BaseField, -1);
        setBase();

        foreach(QAbstractButton *btn, BaseChooseGroup->buttons()) {
            btn->show();
        }
        for (int i = 10; i < 16; i++)
            (NumButtonGroup->button(i))->show();
    } else {
        mBitset->setEnabled(false);
        disconnect(mBitset, SIGNAL(valueChanged(unsigned long long)),
                   this, SLOT(slotBitsetChanged(unsigned long long)));
        disconnect(calc_display, SIGNAL(changedAmount(const KNumber &)),
                   this, SLOT(slotUpdateBitset(const KNumber &)));
        foreach(QAbstractButton* btn, logicButtons) {
            btn->hide();
        }
        // Hide Hex-Buttons, but first switch back to decimal
        decRadio->animateClick(0);
        foreach(QAbstractButton *btn, BaseChooseGroup->buttons()) {
            btn->hide();
        }
        statusBar()->changeItem(QString(), BaseField);
        statusBar()->setItemFixed(BaseField, 0);
        calc_display->setStatusText(BaseField, QString());
        for (int i = 10; i < 16; i++)
            (NumButtonGroup->button(i))->hide();
    }
}

void KCalculator::slotConstantsShow(bool toggled)
{
    if (toggled) {
        foreach(QAbstractButton *btn, constButtons) {
            btn->show();
        }
    } else {
        foreach(QAbstractButton *btn, constButtons) {
            btn->hide();
        }
    }
    KCalcSettings::setShowConstants(toggled);
}

void KCalculator::slotBitsetshow(bool toggled)
{
    mBitset->setVisible(toggled);
    KCalcSettings::setShowBitset(toggled);
}

// This function is for setting the constant names configured in the
// kcalc settings menu. If the user doesn't enter a name for the
// constant C1 to C6 is used.
void KCalculator::changeButtonNames()
{
    KCalcConstButton *constbtn;
    foreach(QAbstractButton *btn, constButtons) {
        constbtn = qobject_cast<KCalcConstButton*>(btn);
        if (constbtn) constbtn->setLabelAndTooltip();
    }
}

void KCalculator::slotBitsetChanged(unsigned long long value)
{
    // note: sets display to *unsigned* value
    calc_display->setAmount(value);
    updateDisplay(false);
}

void KCalculator::slotUpdateBitset(const KNumber &nr)
{
    mBitset->setValue(static_cast<quint64>(nr));
}

void KCalculator::updateSettings()
{
    changeButtonNames();
    setColors();
    setFonts();
    setPrecision();
    // Show the result in the app's caption in taskbar (wishlist - bug #52858)
    disconnect(calc_display, SIGNAL(changedText(const QString &)),
               this, 0);
    if (KCalcSettings::captionResult()) {
        connect(calc_display,
                SIGNAL(changedText(const QString &)),
                SLOT(setCaption(const QString &)));
    } else {
        setCaption(QString());
    }
    calc_display->changeSettings();

    updateGeometry();
}

void KCalculator::updateDisplay(bool get_amount_from_core,
                                bool store_result_in_history)
{
    if (get_amount_from_core) {
        calc_display->updateFromCore(core, store_result_in_history);
    } else {
        calc_display->update();
    }

    pbShift->setChecked(false);

}

void KCalculator::setColors()
{
    calc_display->changeSettings();

    QString sheet = "KPushButton { background-color: %1 }";

    QColor numPal(KCalcSettings::numberButtonsColor());
    for (int i = 0; i < 10; i++) {
        (NumButtonGroup->button(i))->setStyleSheet(sheet.arg(numPal.name()));
    }

    QColor funcPal(KCalcSettings::functionButtonsColor());
    foreach(QAbstractButton *btn, mFunctionButtonList) {
        btn->setStyleSheet(sheet.arg(funcPal.name()));
    }

    QColor statPal(KCalcSettings::statButtonsColor());
    foreach(QAbstractButton *btn, statButtons) {
        btn->setStyleSheet(sheet.arg(statPal.name()));
    }

    QColor hexPal(KCalcSettings::hexButtonsColor());
    for (int i = 10; i < 16; i++) {
        (NumButtonGroup->button(i))->setStyleSheet(sheet.arg(hexPal.name()));
    }

    QColor memPal(KCalcSettings::memoryButtonsColor());
    foreach(QAbstractButton *btn, mMemButtonList) {
        btn->setStyleSheet(sheet.arg(memPal.name()));
    }

    QColor opPal(KCalcSettings::operationButtonsColor());
    foreach(QAbstractButton *btn, mOperationButtonList) {
        btn->setStyleSheet(sheet.arg(opPal.name()));
    }
}

void KCalculator::setFonts()
{
    KCalcButton *button;
    foreach(QObject *obj, leftPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            button->setFont(KCalcSettings::buttonFont());
        }
    }
    foreach(QObject *obj, numericPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            button->setFont(KCalcSettings::buttonFont());
        }
    }
    foreach(QObject *obj, rightPad->children()) {
        button = qobject_cast<KCalcButton*>(obj);
        if (button) {
            button->setFont(KCalcSettings::buttonFont());
        }
    }
    updateGeometry();
}

void KCalculator::setPrecision()
{
    KNumber:: setDefaultFloatPrecision(KCalcSettings::precision());
    updateDisplay(false);
}

void KCalculator::setAngle()
{
    QAbstractButton *btn;
    btn = AngleChooseGroup->button(KCalcSettings::angleMode());
    if (btn) btn->animateClick(0);
}

void KCalculator::setBase()
{
    QAbstractButton *btn;
    btn = BaseChooseGroup->button(KCalcSettings::baseMode());
    if (btn) btn->animateClick(0);
}

bool KCalculator::eventFilter(QObject *o, QEvent *e)
{
    switch (e->type()) {
    case QEvent::DragEnter: {
        QDragEnterEvent *ev = (QDragEnterEvent *)e;
        ev->setAccepted(KColorMimeData::canDecode(ev->mimeData()));
        return true;
    }
    case QEvent::DragLeave: {
        return true;
    }
    case QEvent::Drop: {
        KCalcButton* calcButton = qobject_cast<KCalcButton *>(o);
        if (!calcButton)
            return false;

        QDropEvent *ev = (QDropEvent *)e;
        QColor c = KColorMimeData::fromMimeData(ev->mimeData());
        if (c.isValid()) {
            QString cn = c.name();
            QString sheet = "background-color: %1";

            QList<QAbstractButton*> *list;
            int num_but;
            if ((num_but = NumButtonGroup->buttons().indexOf(calcButton)) != -1) {
                // Was it hex-button or normal digit??
                if (num_but < 10)
                    for (int i = 0; i < 10; i++)
                        (NumButtonGroup->buttons()[i])->setStyleSheet(sheet.arg(cn));
                else
                    for (int i = 10; i < 16; i++)
                        (NumButtonGroup->buttons()[i])->setStyleSheet(sheet.arg(cn));

                return true;
            } else if (mFunctionButtonList.contains(calcButton)) {
                list = &mFunctionButtonList;
            } else if (mStatButtonList.contains(calcButton)) {
                list = &mStatButtonList;
            } else if (mMemButtonList.contains(calcButton)) {
                list = &mMemButtonList;
            } else if (mOperationButtonList.contains(calcButton)) {
                list = &mOperationButtonList;
            } else
                return false;

            for (int i = 0; i < list->size(); i++)
                list->at(i)->setStyleSheet(sheet.arg(cn));
        }

        return true;
    }
    default:
        return KXmlGuiWindow::eventFilter(o, e);
    }
}




////////////////////////////////////////////////////////////////
// Include the meta-object code for classes in this file
//

#include "kcalc.moc"


extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
{
    KAboutData aboutData("kcalc",
                         0,
                         ki18n("KCalc"),
                         version,
                         ki18n(description),
                         KAboutData::License_GPL,
                         ki18n("(c) 2000-2008, The KDE Team\n"
                               "(c) 2003-2005, Klaus Niederkr" "\xc3\xbc" "ger\n"
                               "(c) 1996-2000, Bernd Johannes Wuebben"),
                         KLocalizedString(),
                         "http://utils.kde.org/projects/kcalc");

    /* Klaus Niederkrueger */
    aboutData.addAuthor(ki18n("Klaus Niederkr" "\xc3\xbc" "ger"), KLocalizedString(), "kniederk@math.uni-koeln.de");
    aboutData.addAuthor(ki18n("Bernd Johannes Wuebben"), KLocalizedString(), "wuebben@kde.org");
    aboutData.addAuthor(ki18n("Evan Teran"), KLocalizedString(), "eteran@alum.rit.edu");
    aboutData.addAuthor(ki18n("Espen Sand"), KLocalizedString(), "espen@kde.org");
    aboutData.addAuthor(ki18n("Chris Howells"), KLocalizedString(), "howells@kde.org");
    aboutData.addAuthor(ki18n("Aaron J. Seigo"), KLocalizedString(), "aseigo@olympusproject.org");
    aboutData.addAuthor(ki18n("Charles Samuels"), KLocalizedString(), "charles@altair.dhs.org");
    /* Rene Merou */
    aboutData.addAuthor(ki18n("Ren" "\xc3\xa9" " M" "\xc3\xa9"  "rou"), KLocalizedString(), "ochominutosdearco@yahoo.es");
    aboutData.addAuthor(ki18n("Michel Marti"), KLocalizedString(), "mma@objectxp.com");
    aboutData.addAuthor(ki18n("David Johnson"), ki18n("Maintainer"), "david@usermode.org");

    aboutData.setProgramIconName("accessories-calculator");

    KCmdLineArgs::init(argc, argv, &aboutData);

    KApplication app;

    // force system locale to "C" internally [bug 159168]
    setlocale(LC_NUMERIC, "C");

    KCalculator *calc = new KCalculator(0);
    app.setTopWidget(calc);

    calc->show();

    return app.exec();
}

