You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mathemagics/mathemagics/mathemagics.cpp

1975 lines
41 KiB

#include <tqcombobox.h>
#include <tqevent.h>
#include <tqfile.h>
#include <tqguardedptr.h>
#include <tqlayout.h>
#include <tqlineedit.h>
#include <tqmap.h>
#include <tqpushbutton.h>
#include <tqradiobutton.h>
#include <tqscrollview.h>
#include <tqsizepolicy.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tqtextstream.h>
#include <tqtimer.h>
#include <tqtooltip.h>
#include <tqvaluelist.h>
#include <tqwhatsthis.h>
#include <tqwidget.h>
#include <tdeaccel.h>
#include <tdeaction.h>
#include <tdeconfig.h>
#include <kdebug.h>
#include <kedittoolbar.h>
#include <kdialog.h>
#include <tdefiledialog.h>
#include <tdeglobal.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <kkeydialog.h>
#include <tdemainwindow.h>
#include <knotifyclient.h>
#include <kstandarddirs.h>
#include <kstatusbar.h>
#include <kstdaction.h>
#include <tdetoolbar.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "keypad.h"
#include "kparanoidline.h"
#include "mathemagics.h"
Mathemagics::Mathemagics(TQWidget *parent, const char *name, WFlags f)
: TDEMainWindow(parent, name, f)
{
noSave = false;
enterMode = false;
numStackLevels = 0;
stackLevels.setAutoDelete(true);
TQWidget *dummy = new TQWidget(this);
setCentralWidget(dummy);
TQVBoxLayout *topLayout = new TQVBoxLayout(dummy, 0, KDialog::spacingHint());
topLevel = new TQScrollView(dummy);
topLayout->addWidget(topLevel);
boxParent = new TQWidget(topLevel->viewport());
topLevel->addChild(boxParent);
topLevel->setResizePolicy(TQScrollView::AutoOneFit);
bigBox = new TQVBoxLayout(boxParent, KDialog::marginHint(), KDialog::spacingHint());
LineEdit = new EditAction(i18n("Values"), 0, this, SLOT(slotEnter()), actionCollection(), "lineedit");
HistoryBox = new ComboAction(i18n("History"), 0, 0, 0, actionCollection(), "history");
keypadAct = new TDEToggleAction(i18n("Show Keypad"), "Ctrl+K", 0, 0, actionCollection(), "keypad");
connect(keypadAct, SIGNAL(toggled(bool)), SLOT(toggleKeypad(bool)));
keypad = new MathKeypad(dummy, "Keypad");
topLayout->addWidget(keypad);
connect(keypad, SIGNAL(closing()), this, SLOT(keypadClosing()));
connect(keypad, SIGNAL(insertChar(const TQString &)), this, SLOT(insertChar(const TQString &)));
connect(keypad, SIGNAL(add()), this, SLOT(slotAdd()));
connect(keypad, SIGNAL(subtract()), this, SLOT(slotSubtract()));
connect(keypad, SIGNAL(multiply()), this, SLOT(slotMultiply()));
connect(keypad, SIGNAL(divide()), this, SLOT(slotDivide()));
connect(keypad, SIGNAL(enter()), this, SLOT(slotEnter()));
connect(keypad, SIGNAL(backspace()), this, SLOT(slotKeypadBackspace()));
connect(keypad, SIGNAL(eex()), this, SLOT(slotEEX()));
keypad->hide();
TQStringList options(i18n("Degrees"));
options.append(i18n("Radians"));
options.append(i18n("Grads"));
angGroup = new TDEListAction(i18n("Angle"), 0, 0, 0, actionCollection(), "angle");
angGroup->setItems(options);
options.clear();
options.append(i18n("Hexadecimal"));
options.append(i18n("Decimal"));
options.append(i18n("Octal"));
options.append(i18n("Binary"));
baseGroup = new TDEListAction(i18n("Base"), 0, 0, 0, actionCollection(), "base");
baseGroup->setItems(options);
TQStringList defaultFormulae;
formulae = new TDEListAction(i18n("&Formulae"), 0, 0, 0, actionCollection(), "formulae");
#include "formulae"
formulae->setItems(defaultFormulae);
formulae->setCurrentItem(-1);
(void) KStdAction::quit(this, SLOT(close()), actionCollection());
(void) KStdAction::open(this, SLOT(slotOpen()), actionCollection());
(void) KStdAction::preferences(this, SLOT(slotConfigure()), actionCollection());
(void) KStdAction::undo(this, SLOT(slotRestoreStack()), actionCollection())->setText(i18n("Restore stack levels"));
(void) KStdAction::configureToolbars(this, SLOT(configureToolBars()), actionCollection());
(void) KStdAction::keyBindings(this, SLOT(keyBindings()), actionCollection());
(void) new TDEAction(i18n("+/-"), "Alt+-", this, SLOT(slotNegative()), actionCollection(), "+/-");
(void) new TDEAction("x!", "Alt+!", this, SLOT(slotFactorial()), actionCollection(), "factorial");
(void) new TDEAction("1/x", "Alt+i", this, SLOT(slotInverse()), actionCollection(), "1/x");
(void) new TDEAction("10^x", 0, this, SLOT(slotRaiseTen()), actionCollection(), "10^x");
(void) new TDEAction("e^x", "Alt+E", this, SLOT(slotRaiseE()), actionCollection(), "e^x");
(void) new TDEAction("Mod", "Alt+M", this, SLOT(slotModulo()), actionCollection(), "modulo");
(void) new TDEAction("Ln", "Alt+L", this, SLOT(slotLn()), actionCollection(), "ln");
(void) new TDEAction("%", "Alt+%", this, SLOT(slotPercent()), actionCollection(), "percent");
(void) new TDEAction("Sqrt", "Alt+R", this, SLOT(slotSqrt()), actionCollection(), "sqrt");
(void) new TDEAction("x^2", "Alt+Shift+S", this, SLOT(slotSquared()), actionCollection(), "x^2");
(void) new TDEAction("y^x", "Alt+^", this, SLOT(slotPower()), actionCollection(), "y^x");
InvButton = new TDEToggleAction("Inv", "Ctrl+I", 0, 0, actionCollection(), "inverse");
HypButton = new TDEToggleAction("Hyp", "Ctrl+H", 0, 0, actionCollection(), "hyperbolic");
// bitwise
(void) new TDEAction(i18n("And"), "Alt+A", this, SLOT(slotAnd()), actionCollection(), "bitwise_and");
(void) new TDEAction(i18n("Or"), "Alt+O", this, SLOT(slotOr()), actionCollection(), "bitwise_or");
(void) new TDEAction(i18n("XOr"), "Alt+X", this, SLOT(slotXOr()), actionCollection(), "bitwise_xor");
(void) new TDEAction(i18n("Left Shift"), "back", 0, this, SLOT(slotLsh()), actionCollection(), "bitwise_lsh");
(void) new TDEAction(i18n("Right Shift"), "forward", 0, this, SLOT(slotRsh()), actionCollection(), "bitwise_rsh");
// trig
(void) new TDEAction("Sin", "Alt+S", this, SLOT(slotSine()), actionCollection(), "sin");
(void) new TDEAction("Cos", "Alt+C", this, SLOT(slotCosine()), actionCollection(), "cos");
(void) new TDEAction("Tan", "Alt+T", this, SLOT(slotTangent()), actionCollection(), "tan");
// edit
(void) new TDEAction(i18n("&Revert Stack Changes"), 0, this, SLOT(slotRevert()), actionCollection(), "revert");
(void) new TDEAction(i18n("&Drop"), 0, this, SLOT(slotDrop()), actionCollection(), "drop");
setStandardToolBarMenuEnabled(true);
statusBar();
createGUI();
resize(430, 500);
applyMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
// signals and slots connections
connect(baseGroup, SIGNAL(activated(int)), this, SLOT(slotBaseChanged(int)));
connect(angGroup, SIGNAL(activated(int)), this, SLOT(slotAngleChanged(int)));
connect(formulae, SIGNAL(activated(const TQString &)), this, SLOT(runFormula(const TQString &)));
connect(HistoryBox->combo(), SIGNAL(activated(const TQString&)), this, SLOT(slotPushHighlighted(const TQString&)));
connect(LineEdit->edit(), SIGNAL(backspacePressed()), this, SLOT(slotBackspace()));
pi = asin(1L) * 2L;
variables["pi"] = pi;
defVariables.append("pi");
variables["e"] = exp(1);
defVariables.append("e");
variables["true"] = 1;
defVariables.append("true");
variables["false"] = 0;
defVariables.append("false");
variables["degkey"] = 0;
defVariables.append("degkey");
variables["radkey"] = 1;
defVariables.append("radkey");
variables["gradkey"] = 2;
defVariables.append("gradkey");
Stack = new TQValueList<double>;
SavedStack = new TQValueList<double>;
optionDialog = 0;
// how long do status msgs stay up? in ms
dispTime = 1500;
kapp->config()->setGroup("App");
bool showkeys = kapp->config()->readBoolEntry("show keypad", true);
toggleKeypad(showkeys);
keypadAct->setChecked(showkeys);
slotUpdateConfiguration();
kapp->config()->setGroup("Session");
TQStringList stackList = kapp->config()->readListEntry("Stack", ',');
int i;
TQStringList historyList = kapp->config()->readListEntry("History", ',');
int count = historyList.count();
if (historyList.first() != "Nothing" && count > 0)
{
if (count > histNum)
count = histNum;
for (i = 0; i < count; i++)
HistoryBox->combo()->insertItem(*historyList.at(i));
HistoryBox->combo()->setCurrentItem(HistoryBox->combo()->count() - 1);
}
base = 10; // this necessary so we can
// setbase properly :P
// default config choices
runCommand("$degkey"); runCommand("setangle");
runCommand("10"); runCommand("setbase");
runCommand("$false"); runCommand("sethyp");
updateStack();
// load session file
openFile(TDEGlobal::dirs()->saveLocation("appdata") + "session");
saveStack();
LineEdit->edit()->setFocus();
statusBar()->message(i18n("Welcome to Mathemagics"), dispTime);
}
Mathemagics::~Mathemagics()
{
delete Stack;
delete SavedStack;
delete optionDialog;
delete keypad;
}
void Mathemagics::keypadClosing()
{
keypadAct->setChecked(false);
}
void Mathemagics::insertChar(const TQString &s)
{
LineEdit->insert(s);
}
void Mathemagics::updateStack()
{
StackLevel *level;
const int count = Stack->count();
int i = 1;
for (level = stackLevels.first(); level; level = stackLevels.next(), ++i)
{
if (i > count)
{
for (; level; level = stackLevels.next())
{
level->edit()->clear();
level->setEnabled(false);
}
break;
}
level->setEnabled(true);
level->edit()->setText(format(*Stack->at(i - 1)));
}
TQTimer::singleShot(100, this, SLOT(scrollToBottom()));
}
void Mathemagics::enter()
{
const bool notDec = base != 10;
bool noClear = false;
saveStack();
bool oldNoSave = noSave;
noSave = true;
TQStringList entryNums = this->entryNums;
this->entryNums.clear();
TQString oldLineText = LineEdit->edit()->text();
LineEdit->clear();
for (TQStringList::Iterator it = entryNums.begin(); it != entryNums.end(); ++it)
{
int eIndex;
int eqIndex = (*it).find('=');
int dolIndex = (*it).find('$');
bool ok;
double val;
if (eqIndex >= 0 && (dolIndex < 0 || dolIndex > eqIndex)) // add equation
{
TQString formulaName = (*it).left(eqIndex);
if (formulaName.isEmpty())
continue;
TQString formula = (*it).right((*it).length() - eqIndex - 1);
for (++it; it != entryNums.end(); ++it)
{
formula.append(" ");
formula.append(*it);
}
formulas[formulaName] = formula;
TQStringList newList(formulae->items());
newList.remove(formulaName);
if (!formulaName.isEmpty())
newList.append(formulaName);
formulae->setItems(newList);
statusBar()->message(i18n("Formula %1 added").arg(formulaName), dispTime);
break;
}
else if ((eIndex = (*it).find('E')) != -1 && !notDec) // E found
{
TQString firstArg = (*it).left(eIndex);
if (firstArg.isEmpty())
continue;
TQString secondArg = (*it).right((*it).length() - eIndex - 1);
val = firstArg.toDouble(&ok) * pow(10, secondArg.toDouble(&ok));
if (ok)
{
Stack->prepend(val);
continue;
}
// else try something else below
}
// plain
val = format(*it, &ok);
if (ok)
{
Stack->prepend(val);
continue;
}
if (enterMode)
if (!runCommand((*it).lower()))
break;
}
if (noClear)
LineEdit->edit()->setText(oldLineText);
noSave = oldNoSave;
}
void Mathemagics::slotEnter()
{
enterMode = true;
if (LineEdit->text().isEmpty() && Stack->count() >= 1)
// dup
Stack->prepend(Stack->first());
else
parseArgs(false, true, false);
updateStack();
enterMode = false;
}
bool Mathemagics::parseArgs(unsigned int reqArgs, bool autoEnter, bool save)
{
int entryNumsCount = 0;
if (!LineEdit->text().isEmpty())
{
entryNums = TQStringList::split(TQChar(' '), LineEdit->text());
entryNumsCount = entryNums.count();
}
if (Stack->count() + entryNumsCount < reqArgs)
{
changeStatusError(i18n("At least %1 arguments required").arg(reqArgs));
return false;
}
if (save)
saveStack();
if (autoEnter)
enter();
return true;
}
int Mathemagics::testArgs()
{
int entryNumsCount = 0;
if (!LineEdit->text().isEmpty())
{
entryNums = TQStringList::split(TQChar(' '), LineEdit->text());
entryNumsCount = entryNums.count();
}
return entryNumsCount;
}
void Mathemagics::saveStack()
{
if (noSave)
return;
*SavedStack = *Stack;
}
void Mathemagics::slotRestoreStack()
{
// a simple swap
TQValueList<double> *SavedStackPtr = Stack;
Stack = SavedStack;
SavedStack = SavedStackPtr;
updateStack();
}
void Mathemagics::slotAdd()
{
if (!parseArgs(2))
return;
double theResult = (* Stack->at(0)) + (* Stack->at(1));
changeStatus(& theResult, '+');
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotSubtract()
{
if (!parseArgs(2))
return;
double theResult = (* Stack->at(1)) - (* Stack->at(0));
changeStatus(& theResult, '-');
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotMultiply()
{
if (!parseArgs(2))
return;
double theResult = (* Stack->at(0)) * (* Stack->at(1));
changeStatus(& theResult, '*');
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotDivide()
{
getArgs(1);
if (args.first() == 0)
{
error();
return;
}
if (!parseArgs(2))
return;
double theResult = (* Stack->at(1)) / (* Stack->at(0));
changeStatus(& theResult, '/');
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotRaiseE()
{
if (!parseArgs(1))
return;
double theResult;
theResult = exp(Stack->first());
changeStatus(& theResult, i18n("e ^ "), 1);
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotInverse()
{
if (!parseArgs(1))
return;
double theResult;
theResult = 1 / Stack->first();
changeStatus(& theResult, i18n("1 / "), 1);
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotSquared()
{
if (!parseArgs(1))
return;
double level1 = Stack->first();
level1 *= level1;
changeStatus(&level1, i18n(" ^ 2"));
Stack->pop_front();
Stack->prepend(level1);
updateStack();
}
void Mathemagics::slotSqrt()
{
getArgs(1);
if (args.first() < 0)
{
changeStatusError(i18n("Complex number"));
return;
}
if (!parseArgs(1))
return;
double theResult = sqrt(Stack->first());
changeStatus(&theResult, i18n("Square root of "), 1);
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotSine()
{
double theResult;
if (InvButton->isChecked())
{
if (HypButton->isChecked())
{
if (!parseArgs(1))
return;
theResult = asinh(Stack->first());
changeStatus(& theResult, i18n("Hyp ArcSine of "), 1);
}
else
{
if (!getArgs(1))
return;
double lev1 = args.first();
if (lev1 < -1 || lev1 > 1)
{
error();
return;
}
parseArgs(1);
theResult = angOut(asin(Stack->first()));
changeStatus(& theResult, i18n("ArcSine of "), 1);
}
InvButton->setChecked(false);
}
else
{
if (!parseArgs(1))
return;
if (HypButton->isChecked())
{
theResult = sinh(Stack->first());
changeStatus(& theResult, i18n("Hyp Sine of "), 1);
}
else
{
theResult = sin(angConv(Stack->first()));
changeStatus(& theResult, i18n("Sine of "), 1);
}
}
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotCosine()
{
double theResult;
if (InvButton->isChecked())
{
if (!getArgs(1))
return;
if (HypButton->isChecked())
{
if (args.first() < 1)
{
error();
return;
}
parseArgs(1);
theResult = acosh(Stack->first());
changeStatus(& theResult, i18n("Hyp ArcCosine of "), 1);
}
else
{
double lev1 = args.first();
if (lev1 < -1 || lev1 > 1)
{
error();
InvButton->setChecked(false);
return;
}
parseArgs(1);
theResult = angOut(acos(Stack->first()));
changeStatus(& theResult, i18n("ArcCosine of "), 1);
}
InvButton->setChecked(false);
}
else
{
if (!parseArgs(1))
return;
if (HypButton->isChecked())
{
theResult = cosh(Stack->first());
changeStatus(& theResult, i18n("Hyp Cosine of "), 1);
}
else
{
theResult = cos(angConv(Stack->first()));
changeStatus(& theResult, i18n("Cosine of "), 1);
}
}
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotTangent()
{
double theResult;
if (InvButton->isChecked())
{
if (HypButton->isChecked())
{
if (!getArgs(1))
return;
if (fabs(args.first()) > 1)
{
error();
return;
}
parseArgs(1);
theResult = atanh(Stack->first());
changeStatus(& theResult, i18n("Hyp ArcTangent of "), 1);
}
else
{
if (!parseArgs(1))
return;
theResult = angOut(atan(Stack->first()));
changeStatus(& theResult, i18n("ArcTangent of "), 1);
}
InvButton->setChecked(false);
}
else
{
if (!parseArgs(1))
return;
if (HypButton->isChecked())
{
theResult = tanh(Stack->first());
changeStatus(& theResult, i18n("Hyp Tangent of "), 1);
}
else
{
theResult = tan(angConv(Stack->first()));
changeStatus(& theResult, i18n("Tangent of "), 1);
}
}
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotLn()
{
if (!getArgs(1))
return;
if (args.first() <= 0)
{
error();
return;
}
double theResult;
parseArgs(1);
theResult = log(Stack->first());
changeStatus(& theResult, i18n("ln "), 1);
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotFactorial()
{
if (!getArgs(1))
return;
double lev1 = args.first();
if ((int)lev1 != lev1 || lev1 <= 0)
{
changeStatusError(i18n("Not implemented"));
return;
}
if (parseArgs(1))
{
int n = fact(Stack->first());
double theDouble = (double)n;
changeStatus(& theDouble, "!");
Stack->first() = n;
updateStack();
}
}
int Mathemagics::fact(int n)
{
if (n <= 0)
return 1;
else
return n * fact(n-1);
}
void Mathemagics::slotRaiseTen()
{
if (!parseArgs(1))
return;
double theResult = pow(10, Stack->first());
changeStatus(&theResult, i18n("10 ^ "), 1);
Stack->first() = theResult;
updateStack();
}
void Mathemagics::slotEEX()
{
if (base == 10 || base >= 15)
LineEdit->insert(TQString("E"));
}
void Mathemagics::slotPercent()
{
double theResult;
if (!parseArgs(2))
return;
theResult = (*Stack->at(1) / Stack->first()) * 100;
changeStatus(&theResult, '%');
Stack->pop_front();
Stack->first() = theResult;
updateStack();
}
void Mathemagics::slotModulo()
{
double theResult;
if (!getArgs(1))
return;
if (args.first() == 0)
{
error();
return;
}
if (!parseArgs(2))
return;
theResult = fmod(*Stack->at(1), Stack->first());
changeStatus(&theResult, TQString(i18n("%1 mod ")).arg(*Stack->at(1)), 1);
Stack->pop_front();
Stack->first() = theResult;
updateStack();
}
void Mathemagics::slotPower()
{
double theResult;
if (!getArgs(2))
return;
if (InvButton->isChecked())
{
if (*args.at(1) < 0)
{
changeStatusError(i18n("Complex number"));
return;
}
parseArgs(2);
theResult = pow(*Stack->at(1), 1 / Stack->first());
changeStatus(&theResult, TQString(i18n("%1 root of ")).arg(format(*Stack->at(1))), true);
InvButton->setChecked(false);
}
else
{
double lev1 = args.first();
if (*args.at(1) < 0 && (int)lev1 != lev1)
{
changeStatusError(i18n("Complex number"));
return;
}
parseArgs(2);
theResult = pow(*Stack->at(1), Stack->first());
changeStatus(&theResult, TQString(i18n("%1 ^ ")).arg(format(*Stack->at(1))), 1);
}
Stack->pop_front();
Stack->first() = theResult;
updateStack();
}
void Mathemagics::slotNegative()
{
if (LineEdit->text().isEmpty() && Stack->count() >= 1)
{
saveStack();
Stack->first() *= -1;
updateStack();
}
else
{
TQString text = LineEdit->text();
unsigned int cursPos = LineEdit->edit()->cursorPosition();
unsigned int length = text.length();
int eindex = text.findRev('E', cursPos - length - 1);
int negIndex = text.findRev(' ', cursPos - length - 1) + 1;
if (base == 10 && eindex != -1)
negIndex = eindex + 1;
if (text.at(negIndex) == TQChar('-'))
{
text.remove(negIndex, 1);
LineEdit->edit()->setText(text);
LineEdit->edit()->setCursorPosition(cursPos - 1);
}
else
{
text.insert(negIndex, '-');
LineEdit->edit()->setText(text);
LineEdit->edit()->setCursorPosition(cursPos + 1);
}
}
}
void Mathemagics::slotAnd()
{
long double theResult;
if (parseArgs(2))
{
theResult = (long int)Stack->first() & (long int)*Stack->at(1);
double pass = (double)theResult;
changeStatus(&pass, TQString(i18n("%1 AND ")).arg(format(*Stack->at(1))), 1);
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
}
void Mathemagics::slotXOr()
{
if (!parseArgs(2))
return;
double theResult;
theResult = (long int)Stack->first() ^ (long int)*Stack->at(1);
changeStatus(&theResult, TQString(i18n("%1 XOR ")).arg(format(*Stack->at(1))), 1);
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
}
void Mathemagics::slotOr()
{
if (!parseArgs(2))
return;
double theResult;
theResult = (long int)Stack->first() | (long int)*Stack->at(1);
changeStatus(&theResult, TQString(i18n("%1 OR ")).arg(format(*Stack->at(1))), 1);
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotRsh()
{
if (!parseArgs(2))
return;
double theResult;
theResult = (long int)*Stack->at(1) >> (long int)Stack->first();
changeStatus(&theResult, TQString(i18n("%1 RSH ")).arg(format(*Stack->at(1))), true);
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotLsh()
{
if (!parseArgs(2))
return;
double theResult;
theResult = (long int)*Stack->at(1) << (long int)Stack->first();
changeStatus(&theResult, TQString(i18n("%1 LSH ")).arg(format(*Stack->at(1))), true);
Stack->pop_front();
Stack->pop_front();
Stack->prepend(theResult);
updateStack();
}
void Mathemagics::slotDup2()
{
if (!parseArgs(2))
return;
Stack->prepend(* Stack->at(1));
Stack->prepend(* Stack->at(1));
updateStack();
}
void Mathemagics::slotDrop()
{
if (!parseArgs(1))
return;
Stack->pop_front();
updateStack();
}
void Mathemagics::slotKeypadBackspace()
{
slotBackspace();
LineEdit->edit()->backspace();
}
void Mathemagics::slotBackspace()
{
if (InvButton->isChecked())
{
saveStack();
Stack->clear();
updateStack();
InvButton->setChecked(false);
return;
}
if (LineEdit->text().isEmpty())
{
if (!Stack->isEmpty() && delDrops)
{
saveStack();
Stack->pop_front();
updateStack();
}
}
}
void Mathemagics::closeEvent(TQCloseEvent * /*e*/)
{
TQString list;
unsigned int i;
kapp->config()->setGroup("Session");
// save history for next time
int count = HistoryBox->combo()->count();
if (count != 0)
{
list = HistoryBox->combo()->text(0);
for (i = 1; i < (unsigned int)count; i++)
{
list.append(',');
list.append(HistoryBox->combo()->text(i));
}
kapp->config()->writeEntry("History", list);
}
else
kapp->config()->writeEntry("History", "Nothing");
kapp->config()->setGroup("App");
kapp->config()->writeEntry("show keypad", keypadAct->isChecked());
kapp->config()->sync();
saveFile(TDEGlobal::dirs()->saveLocation("appdata") + "session");
saveMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
kapp->quit();
}
void Mathemagics::slotConfigure()
{
if (optionDialog == 0)
{
optionDialog = new ConfigureDialog(topLevelWidget());
if (optionDialog == 0)
return;
connect(optionDialog, SIGNAL(hidden()),this,SLOT(slotConfigureHide()));
connect(optionDialog, SIGNAL(valueChanged()), this, SLOT(slotUpdateConfiguration()));
connect(optionDialog, SIGNAL(clearHistory()), this, SLOT(clearHistory()));
}
optionDialog->show();
}
void Mathemagics::slotConfigureHide()
{
TQTimer::singleShot(0, this, SLOT(slotConfigureDestroy()));
}
void Mathemagics::slotConfigureDestroy()
{
if (optionDialog != 0 && optionDialog->isVisible() == 0)
{
delete optionDialog;
optionDialog = 0;
}
}
void Mathemagics::slotBaseChanged(int id)
{
switch (id)
{
case 0:
base = 16;
break;
case 1:
base = 10;
break;
case 2:
base = 8;
break;
case 3:
base = 2;
break;
}
updateStack();
statusBar()->message(i18n("Base changed to %1").arg(base), dispTime);
}
void Mathemagics::slotAngleChanged(int id)
{
angle = id;
statusBar()->message(i18n("Angle changed to %1").arg(theAngle()), dispTime);
}
void Mathemagics::recreateStackLevels()
{
stackLevels.clear();
for (int i = numStackLevels; i > 0; --i)
{
StackLevel *level = new StackLevel(i, boxParent);
connect(level, SIGNAL(buttonPressed(int)), this, SLOT(slotRoll(int)));
connect(level, SIGNAL(returnPressed(int)), this, SLOT(slotUpdateStackLevel(int)));
bigBox->addWidget(level);
stackLevels.prepend(level);
level->show();
}
}
void Mathemagics::scrollToBottom()
{
topLevel->ensureVisible(1, boxParent->height() - 1);
}
void Mathemagics::slotUpdateConfiguration()
{
kapp->config()->setGroup("App");
int oldNum = numStackLevels;
numStackLevels = kapp->config()->readNumEntry("numStackLevels", 12);
if (oldNum != numStackLevels)
recreateStackLevels();
fixedPrec = kapp->config()->readBoolEntry("fixedPrec", false);
toSaveStack = kapp->config()->readBoolEntry("saveStack", true);
showPeriod = kapp->config()->readBoolEntry("showPeriod", false);
formatPrec = kapp->config()->readNumEntry("formatPrec", 8);
histNum = kapp->config()->readNumEntry("histNum", 15);
delDrops = kapp->config()->readBoolEntry("delDrops", true);
histDetail = kapp->config()->readBoolEntry("histDetail", false);
beep = kapp->config()->readBoolEntry("beep", true);
updateStack();
while (histNum < HistoryBox->combo()->count())
HistoryBox->combo()->removeItem(0);
HistoryBox->combo()->setCurrentItem(HistoryBox->combo()->count() - 1);
}
void Mathemagics::slotUpdateStackLevel(int level)
{
saveStack();
const TQString stackText(stackLevels.at(level - 1)->edit()->text());
if (stackText.isEmpty())
{
Stack->remove(Stack->at(level - 1));
updateStack();
return;
}
bool ok;
double val = format(stackText.section(' ', 0, 0), &ok);
if (ok)
{
(*Stack->at(level - 1)) = val;
stackLevels.at(level - 1)->edit()->setText(format(val));
statusBar()->message(i18n("Change to level %1 applied").arg(level), dispTime);
}
else
changeStatusError(i18n("Bad number"));
}
void Mathemagics::slotRevert()
{
updateStack();
statusBar()->message(i18n("Changes destroyed"), dispTime);
}
double Mathemagics::format(TQString theString, bool *ok)
{
return (base != 10 ? (double)theString.toInt(ok, base) : theString.toDouble(ok));
}
TQString Mathemagics::format(double theDouble)
{
if (base != 10)
{
TQString convStr;
convStr.setNum((int)theDouble, base);
if (showPeriod)
convStr.append('.');
return convStr;
}
TQString decimal = TQString::number(theDouble, 'f', formatPrec);
// leave zeroes on
if (fixedPrec)
return decimal;
//this algorithm will find the last non-zero digits's index
int i;
for (i = decimal.length() - 1; i >= 0; i--)
{
if (decimal.at(i) != '0')
{
if (decimal.at(i) == '.')
{
if (!showPeriod) // get rid of the period
i--;
}
break;
}
}
// return the stuff that has no zeroes
return decimal.mid(0, i + 1);
}
double Mathemagics::angConv(const double theDouble)
{
switch (angle)
{
case 0: // deg2rad
return (((2L*pi)/360L) * theDouble);
case 1: // rad2rad
return (theDouble);
case 2: // grad2rad
return ((pi/200L) * theDouble);
}
return (theDouble);
}
double Mathemagics::angOut(const double theDouble)
{
switch (angle)
{
case 0: // rad2deg
return ((360L/(2L*pi)) * theDouble);
case 1: // rad2rad
return (theDouble);
case 2: // rad2grad
return ((200L/pi) * theDouble);
}
return (theDouble);
}
bool Mathemagics::getArgs(unsigned int num)
{
args.clear();
unsigned int i;
unsigned int lineArgs = testArgs();
unsigned int stackArgs = Stack->count();
if (lineArgs + stackArgs < num)
{
changeStatusError(i18n("Too few arguments"));
return false;
}
for (i = lineArgs; i > 0; i--)
{
args.prepend(format(*entryNums.at(i - 1)));
if (args.count() == num) return true;
}
for (i = 0; i < (num - lineArgs); i++)
{
args.prepend(*Stack->at(i));
if (args.count() == num) return true;
}
return false; // won't get here
}
void Mathemagics::slotRoll(int level)
{
saveStack();
if (level <= 1)
return;
if (InvButton->isChecked())
{
rolld(level);
InvButton->setChecked(false);
}
else
roll(level);
}
void Mathemagics::roll(unsigned int index)
{
if (Stack->count() < index || index <= 1)
return;
Stack->prepend(* Stack->at(index - 1));
Stack->remove(Stack->at(index));
updateStack();
}
void Mathemagics::rolld(unsigned int index)
{
if (Stack->count() < index || index <= 1)
return;
Stack->insert(Stack->at(index), Stack->first());
Stack->pop_front();
updateStack();
}
void Mathemagics::changeStatus(TQString text)
{
const unsigned int maxLength = 30;
// keep hisNnum history spots, should never go over histNum
// this removes last item..
if (HistoryBox->combo()->count() >= histNum) HistoryBox->combo()->removeItem(0);
if (histDetail)
text = TQString(text + i18n("Base: %2; %3")).arg(base).arg(theAngle());
if (text.length() > maxLength)
{
text = text.right(maxLength - 4);
text.prepend("... ");
}
HistoryBox->combo()->insertItem(text, -1);
HistoryBox->combo()->setCurrentItem(HistoryBox->combo()->count() - 1);
}
void Mathemagics::clearHistory()
{
HistoryBox->combo()->clear();
}
TQString Mathemagics::theAngle()
{
switch (angle)
{
case (0):
return "Deg";
case (1):
return "Rad";
case (2):
return "Grad";
}
return "Deg";
}
void Mathemagics::slotPushHighlighted(const TQString& text)
{
saveStack();
int pipeIndex = text.findRev('|') - 1;
int spaceIndex = text.findRev(' ', pipeIndex);
if (pipeIndex == -2) // no pipe
{
Stack->prepend(format(text.mid(spaceIndex, text.length() - spaceIndex)));
}
else
{
int precSpaceIndex = text.findRev(' ', spaceIndex - 1);
Stack->prepend(format(text.mid(precSpaceIndex, spaceIndex - precSpaceIndex)));
}
updateStack();
}
void Mathemagics::error()
{
changeStatusError("Bad arguments");
}
void Mathemagics::changeStatusError(TQString text)
{
text.prepend(i18n("Error: "));
statusBar()->message(text, dispTime);
if (beep)
KNotifyClient::beep("Generic Error");
}
void Mathemagics::changeStatus(double * res, char op)
{
changeStatus(TQString(i18n("%1 %2 %3 = %4")).arg(format(*Stack->at(1))).arg(op).arg(format(Stack->first())).arg(format(*res)));
}
void Mathemagics::changeStatus(double * res, TQString op, bool prepend)
{
if (prepend)
changeStatus(TQString(i18n("%1%2 = %3")).arg(op).arg(format(Stack->first())).arg(format(*res)));
else
changeStatus(TQString(i18n("%1%2 = %3")).arg(format(Stack->first())).arg(op).arg(format(*res)));
}
void Mathemagics::toggleKeypad(bool on)
{
if (on)
keypad->show();
else
keypad->hide();
}
void Mathemagics::slotOpen()
{
TQString filename = KFileDialog::getOpenFileName();
if (filename.isNull())
return;
openFile(filename);
}
void Mathemagics::openFile(const TQString &filename)
{
TQFile f(filename);
if (!f.open(IO_ReadOnly))
return;
enterMode = true;
TQTextStream t(&f);
while (!t.eof())
{
TQString s = t.readLine();
// comments
if (s.at(0) == '#')
continue;
if (!s.isEmpty())
{
entryNums = TQStringList::split(TQChar(' '), s);
enter();
}
}
enterMode = false;
updateStack();
}
void Mathemagics::saveFile(const TQString &filename)
{
TQFile f(filename);
if (!f.open(IO_WriteOnly))
return;
TQTextStream t(&f);
t << "# Saved by mathemagics" << endl;
t.precision(15);
if (toSaveStack)
{
// this is the reverse
t << "# stack" << endl;
bool already = false;
TQValueList<double>::Iterator it = Stack->end();
it--;
while (1)
{
if (already)
t << " ";
t << *it;
already = true;
if (it == Stack->begin())
break;
else
it--;
}
}
t << endl;
t << "# functions" << endl;
for (TQMap<TQString, TQString>::Iterator it = formulas.begin(); it != formulas.end(); ++it)
{
if (defFormulas.contains(it.key()) <= 0)
t << it.key() << "=" << it.data() << endl;
}
t << "# variables" << endl;
for (TQMap<TQString, double>::Iterator it = variables.begin(); it != variables.end(); ++it)
{
if (defVariables.contains(it.key()) <= 0)
t << it.data() << " $" << it.key() << "=" << endl;;
}
t << "# settings" << endl;
t << base << " setbase" << endl;
t << (angle == 0? "$degkey" : angle == 1? "$radkey" : "$gradkey") << " setangle" << endl;
t << (HypButton->isChecked() ? "$true" : "$false") << " sethyp" << endl;
}
void Mathemagics::runFormula(const TQString &name)
{
formulae->setCurrentItem(-1);
TQString s = formulas[name];
saveStack();
bool oldNoSave = noSave;
noSave = true;
TQStringList l = TQStringList::split(' ', s);
for (TQStringList::Iterator it = l.begin(); it != l.end(); ++it)
{
TQString command = (*it);
bool ok;
double num = format(command, &ok);
if (ok)
{
Stack->prepend(num);
continue;
}
if (!runCommand(command))
break;
}
noSave = oldNoSave;
}
// tests and returns startswith, and removes the search string from string if matches
bool removeStartsWith(TQString &s, const TQString &search)
{
if (s.startsWith(search))
{
s = s.right(s.length() - search.length());
return true;
}
else
return false;
}
bool Mathemagics::runCommand(const TQString &command)
{
// case insensitive
TQString operateOn = command.lower();
// allows operators to be embedded in numbers
// this finds a number at beginning of command
while (!operateOn.isEmpty())
{
TQChar firstChar(operateOn.at(0));
if (firstChar.isNumber())
{
for (int i = operateOn.length(); i > 0; --i)
{
bool ok;
double num = format(operateOn.left(i), &ok);
if (ok)
{
Stack->prepend(num);
operateOn = operateOn.right(operateOn.length() - i);
continue;
}
}
}
if (firstChar == '$')
{
TQString varName = operateOn.right(operateOn.length() - 1);
if (varName.isEmpty())
return false;
int eqIndex = varName.find('=');
if (eqIndex >= 0)
{
// assign
if (Stack->count() <= 0)
return false;
varName = varName.left(eqIndex);
const double val = Stack->first();
variables[varName] = val;
Stack->pop_front();
statusBar()->message(i18n("Variable %1 set to %2").arg(varName).arg(val), dispTime);
operateOn = operateOn.right(operateOn.length() - eqIndex - 2);
}
else
{
if (variables.contains(varName))
{
const double val = variables[varName];
Stack->prepend(val);
}
else
{
changeStatusError(i18n("Undefined variable %1").arg(varName));
return false;
}
break;
}
}
else if (removeStartsWith(operateOn, "reqargs"))
{
if (Stack->count() < 1)
return false;
double first = Stack->first();
Stack->pop_front();
if (!parseArgs(first))
return false;
}
else if (removeStartsWith(operateOn, "stopif"))
{
if (Stack->count() <= 0)
return false;
double first = Stack->first();
Stack->pop_front();
if (first == 1)
return false;
}
else if (removeStartsWith(operateOn, "+")) slotAdd();
else if (removeStartsWith(operateOn, "-")) slotSubtract();
else if (removeStartsWith(operateOn, "*")) slotMultiply();
else if (removeStartsWith(operateOn, "/")) slotDivide();
else if (removeStartsWith(operateOn, "mod")) slotModulo();
else if (removeStartsWith(operateOn, "inverse")) slotInverse();
else if (removeStartsWith(operateOn, "e^")) slotRaiseE();
else if (removeStartsWith(operateOn, "sqrt")) slotSqrt();
else if (removeStartsWith(operateOn, "^2")) slotSquared();
else if (removeStartsWith(operateOn, "sin")) slotSine();
else if (removeStartsWith(operateOn, "cos")) slotCosine();
else if (removeStartsWith(operateOn, "tan")) slotTangent();
else if (removeStartsWith(operateOn, "10^")) slotRaiseTen();
else if (removeStartsWith(operateOn, "ln")) slotLn();
else if (removeStartsWith(operateOn, "!")) slotFactorial();
else if (removeStartsWith(operateOn, "^")) slotPower();
else if (removeStartsWith(operateOn, "and")) slotAnd();
else if (removeStartsWith(operateOn, "xor")) slotXOr();
else if (removeStartsWith(operateOn, "or")) slotOr();
else if (removeStartsWith(operateOn, "lsh") || removeStartsWith(operateOn, "<<")) slotLsh();
else if (removeStartsWith(operateOn, "rsh") || removeStartsWith(operateOn, ">>")) slotRsh();
else if (removeStartsWith(operateOn, "at"))
{
if (Stack->count() < 1)
return false;
double index = Stack->first();
Stack->pop_front();
if (index < 0 || index > Stack->count() - 1)
return false;
Stack->prepend(*Stack->at(index));
}
else if (removeStartsWith(operateOn, "level"))
{
if (Stack->count() < 1)
return false;
double index = Stack->first() - 1;
Stack->pop_front();
if (index < 0 || index > Stack->count() - 1)
return false;
Stack->prepend(*Stack->at(index));
}
else if (removeStartsWith(operateOn, "curbase"))
Stack->prepend((double)base);
else if (removeStartsWith(operateOn, "curangle"))
Stack->prepend((double)angle);
else if (removeStartsWith(operateOn, "curangle"))
Stack->prepend((double)(HypButton->isChecked()? 1 : 0));
else if (removeStartsWith(operateOn, "clear")) Stack->clear();
else if (removeStartsWith(operateOn, "drop")) Stack->pop_front();
else if (removeStartsWith(operateOn, "swap")) roll(2);
else if (removeStartsWith(operateOn, "rolld"))
{
if (Stack->count() < 1)
return false;
double first = Stack->first();
Stack->pop_front();
rolld(first);
}
else if (removeStartsWith(operateOn, "roll"))
{
if (Stack->count() < 1)
return false;
double first = Stack->first();
Stack->pop_front();
roll(first);
}
else if (removeStartsWith(operateOn, "setbase"))
{
if (Stack->count() <= 0)
return false;
base = (int)Stack->first();
switch (base)
{
case 16:
baseGroup->setCurrentItem(0);
slotBaseChanged(0);
break;
case 10:
baseGroup->setCurrentItem(1);
slotBaseChanged(1);
break;
case 8:
baseGroup->setCurrentItem(2);
slotBaseChanged(2);
break;
case 2:
baseGroup->setCurrentItem(3);
slotBaseChanged(3);
break;
}
Stack->pop_front();
}
else if (removeStartsWith(operateOn, "setangle"))
{
if (Stack->count() <= 0)
return false;
angle = (int)Stack->first();
angGroup->setCurrentItem(angle);
Stack->pop_front();
}
else if (removeStartsWith(operateOn, "sethyp"))
{
if (Stack->count() <= 0)
return false;
HypButton->setChecked(Stack->first() == 1);
Stack->pop_front();
}
else if (removeStartsWith(operateOn, "dup2"))
slotDup2();
else if (removeStartsWith(operateOn, "dup"))
{
if (Stack->count() > 0)
Stack->prepend(Stack->first());
}
else
{
bool found = false;
// go through commands
for (TQMap<TQString, TQString>::Iterator it = formulas.begin(); it != formulas.end() && !found; ++it)
{
TQString name = it.key();
if (removeStartsWith(operateOn, name))
{
runFormula(name);
found = true;
}
}
if (!found)
{
changeStatusError(i18n("Unknown command %1").arg(operateOn));
return false;
}
}
}
return true;
}
void Mathemagics::keyBindings()
{
KKeyDialog::configure(actionCollection());
}
void Mathemagics::configureToolBars()
{
saveMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
KEditToolbar dlg(actionCollection());
connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolBarConfig()));
if (dlg.exec())
createGUI();
}
void Mathemagics::newToolBarConfig()
{
applyMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
}
////////////////////////////////////////////////
EditAction::EditAction(const TQString& text, int accel, const TQObject *receiver, const char *member, TQObject* parent, const char* name)
: TDEAction(text, accel, parent, name)
{
m_receiver = receiver;
m_member = member;
}
EditAction::~EditAction()
{
}
int EditAction::plug(TQWidget *w, int index)
{
TDEToolBar *toolBar = (TDEToolBar *)w;
int id = TDEAction::getToolButtonID();
KParanoidLine *comboBox = new KParanoidLine(toolBar, "search edit");
toolBar->insertWidget(id, 70, comboBox, index);
if (m_receiver)
connect(comboBox, SIGNAL(returnPressed()), m_receiver, m_member);
addContainer(toolBar, id);
connect(toolBar, SIGNAL(destroyed()), this, SLOT(slotDestroyed()));
toolBar->setItemAutoSized(id, true);
m_combo = comboBox;
emit plugged();
return containerCount() - 1;
}
void EditAction::unplug(TQWidget *w)
{
TDEToolBar *toolBar = (TDEToolBar *)w;
int idx = findContainer(w);
toolBar->removeItem(menuId(idx));
removeContainer(idx);
m_combo = 0L;
}
void EditAction::clear()
{
m_combo->clear();
}
void EditAction::append(TQString text)
{
m_combo->setText(this->text() + text);
}
void EditAction::insert(TQString text)
{
m_combo->insert(text);
}
TQGuardedPtr<KParanoidLine> EditAction::edit()
{
return m_combo;
}
////////////////////////////////////////////////
ComboAction::ComboAction(const TQString& text, int accel, const TQObject *receiver, const char *member, TQObject* parent, const char* name)
: TDEAction(text, accel, parent, name)
{
m_receiver = receiver;
m_member = member;
}
ComboAction::~ComboAction()
{
}
int ComboAction::plug(TQWidget *w, int index)
{
TDEToolBar *toolBar = (TDEToolBar *)w;
int id = TDEAction::getToolButtonID();
TQComboBox *comboBox = new TQComboBox(toolBar, "search edit");
toolBar->insertWidget(id, 70, comboBox, index);
if (m_receiver)
connect(comboBox, SIGNAL(returnPressed()), m_receiver, m_member);
addContainer(toolBar, id);
connect(toolBar, SIGNAL(destroyed()), this, SLOT(slotDestroyed()));
toolBar->setItemAutoSized(id, true);
m_combo = comboBox;
emit plugged();
return containerCount() - 1;
}
void ComboAction::unplug(TQWidget *w)
{
TDEToolBar *toolBar = (TDEToolBar *)w;
int idx = findContainer(w);
toolBar->removeItem(menuId(idx));
removeContainer(idx);
m_combo = 0L;
}
TQGuardedPtr<TQComboBox> ComboAction::combo()
{
return m_combo;
}
#include "mathemagics.moc"