#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; SavedStack = new TQValueList; 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 *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::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::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::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::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 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 ComboAction::combo() { return m_combo; } #include "mathemagics.moc"