/* * mainwindow.cpp - part of abakus * Copyright (C) 2004, 2005 Michael Pyne * * 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 "mainwindow.h" #include "abakuscommon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "editor.h" #include "evaluator.h" #include "function.h" #include "resultlistview.h" #include "resultlistviewtext.h" #include "valuemanager.h" #include "node.h" #include "rpnmuncher.h" #include "dcopIface.h" #include "abakuslistview.h" #include "result.h" MainWindow::MainWindow() : TDEMainWindow(0, "abakus-mainwindow"), m_popup(0), m_insert(false) { m_mainSplitter = new TQSplitter(this); TQWidget *box = new TQWidget(m_mainSplitter); TQVBoxLayout *layout = new TQVBoxLayout(box); m_layout = layout; layout->setSpacing(6); layout->setMargin(6); TQWidget *configBox = new TQWidget(box); layout->addWidget(configBox); TQHBoxLayout *configLayout = new TQHBoxLayout(configBox); configLayout->addWidget(new TQWidget(configBox)); TQLabel *label = new TQLabel(i18n("History: "), configBox); label->setAlignment(AlignCenter); configLayout->addWidget(label); TQButtonGroup *buttonGroup = new TQButtonGroup(0); TQWidget *buttonGroupBox = new TQWidget(configBox); TQHBoxLayout *buttonGroupLayout = new TQHBoxLayout(buttonGroupBox); buttonGroupLayout->addStretch(0); configLayout->addWidget(buttonGroupBox); m_degrees = new TQRadioButton(i18n("&Degrees"), buttonGroupBox); buttonGroup->insert(m_degrees); buttonGroupLayout->addWidget(m_degrees); slotDegrees(); connect(m_degrees, TQ_SIGNAL(clicked()), TQ_SLOT(slotDegrees())); m_radians = new TQRadioButton(i18n("&Radians"), buttonGroupBox); buttonGroup->insert(m_radians); buttonGroupLayout->addWidget(m_radians); connect(m_radians, TQ_SIGNAL(clicked()), TQ_SLOT(slotRadians())); m_history = new TQVBox(box); layout->addWidget(m_history); m_history->setSpacing(6); m_history->setMargin(0); m_result = new ResultListView(m_history); m_result->setSelectionMode(TQListView::NoSelection); m_result->setHScrollBarMode(ResultListView::AlwaysOff); connect(m_result, TQ_SIGNAL(signalEntrySelected(const TQString &)), TQ_SLOT(slotEntrySelected(const TQString &))); connect(m_result, TQ_SIGNAL(signalResultSelected(const TQString &)), TQ_SLOT(slotResultSelected(const TQString &))); m_history->setStretchFactor(m_result, 1); layout->setStretchFactor(m_history, 1); TQHBox *editBox = new TQHBox(box); layout->addWidget(editBox); editBox->setSpacing(6); m_edit = new Editor(editBox); m_edit->setFocus(); editBox->setStretchFactor(m_edit, 1); KPushButton *evalButton = new KPushButton(i18n("&Evaluate"), editBox); connect(evalButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotEvaluate())); connect(m_edit, TQ_SIGNAL(returnPressed()), TQ_SLOT(slotReturnPressed())); connect(m_edit, TQ_SIGNAL(textChanged()), TQ_SLOT(slotTextChanged())); m_listSplitter = new TQSplitter(TQt::Vertical, m_mainSplitter); m_fnList = new FunctionListView(m_listSplitter); m_fnList->addColumn("Functions"); m_fnList->addColumn("Value"); m_varList = new VariableListView(m_listSplitter); m_varList->addColumn("Variables"); m_varList->addColumn("Value"); connect(FunctionManager::instance(), TQ_SIGNAL(signalFunctionAdded(const TQString &)), this, TQ_SLOT(slotNewFunction(const TQString &))); connect(FunctionManager::instance(), TQ_SIGNAL(signalFunctionRemoved(const TQString &)), this, TQ_SLOT(slotRemoveFunction(const TQString &))); connect(ValueManager::instance(), TQ_SIGNAL(signalValueAdded(const TQString &, Abakus::number_t)), this, TQ_SLOT(slotNewValue(const TQString &, Abakus::number_t))); connect(ValueManager::instance(), TQ_SIGNAL(signalValueChanged(const TQString &, Abakus::number_t)), this, TQ_SLOT(slotChangeValue(const TQString &, Abakus::number_t))); connect(ValueManager::instance(), TQ_SIGNAL(signalValueRemoved(const TQString &)), this, TQ_SLOT(slotRemoveValue(const TQString &))); setupLayout(); setCentralWidget(m_mainSplitter); #if KDE_IS_VERSION(3,4,89) setupGUI(TQSize(450, 400), Keys | StatusBar | Save | Create); #else setupGUI(Keys | StatusBar | Save | Create); #endif m_dcopInterface = new AbakusIface(); } bool MainWindow::inRPNMode() const { return action("toggleExpressionMode")->isChecked(); } bool MainWindow::eventFilter(TQObject *o, TQEvent *e) { return TDEMainWindow::eventFilter(o, e); } bool MainWindow::queryExit() { saveConfig(); return TDEMainWindow::queryExit(); } void MainWindow::contextMenuEvent(TQContextMenuEvent *e) { static TDEPopupMenu *popup = 0; if(!popup) { popup = new TDEPopupMenu(this); action("options_show_menubar")->plug(popup); } if(!action("options_show_menubar")->isChecked()) popup->popup(e->globalPos()); } void MainWindow::polish() { TDEMainWindow::polish(); loadConfig(); } void MainWindow::slotReturnPressed() { TQString text = m_edit->text(); text.replace("\n", ""); m_edit->appendHistory(text); // Item to insert after ResultListViewText *after = m_result->lastItem(); // Expand $foo references. TQString str = interpolateExpression(text, after); TQString resultVal; ResultListViewText *item; if(str.isNull()) return; // Error already has been posted m_insert = false; // Assume we failed if(inRPNMode()) { // We're in RPN mode. Abakus::number_t result = RPNParser::rpnParseString(str); if(!RPNParser::wasError()) { resultVal = result.toString(); ValueManager::instance()->setValue("ans", result); m_insert = true; } else { m_insert = false; resultVal = i18n("Error: %1").arg(RPNParser::errorString()); } // Skip creating list view items if in compact mode. if(!m_history->isShown()) { m_edit->setText(resultVal); TQTimer::singleShot(0, m_edit, TQ_SLOT(selectAll())); return; } item = new ResultListViewText(m_result, str, resultVal, after, false); } else { // Check to see if it's just a function name, if so, add (ans). if(FunctionManager::instance()->isFunction(str)) str += " ans"; // Add right parentheses as needed to balance out the expression. int parenLevel = getParenthesesLevel(str); for(int i = 0; i < parenLevel; ++i) str += ')'; Abakus::number_t result = parseString(str.latin1()); bool compact = !m_history->isShown(); switch(Result::lastResult()->type()) { case Result::Value: resultVal = result.toString(); ValueManager::instance()->setValue("ans", result); if(!compact) item = new ResultListViewText(m_result, str, result, after, false); m_insert = true; break; case Result::Null: // OK, no result to speak of resultVal = "OK"; if(!compact) item = new ResultListViewText(m_result, str, resultVal, after, true); break; default: resultVal = Result::lastResult()->message(); if(!compact) item = new ResultListViewText(m_result, str, resultVal, after, true); } // Skip creating list view items if in compact mode. if(compact) { m_edit->setText(resultVal); TQTimer::singleShot(0, m_edit, TQ_SLOT(selectAll())); return; } } m_edit->setText(text); m_result->setCurrentItem(item); m_result->ensureItemVisible(item); TQTimer::singleShot(0, m_edit, TQ_SLOT(selectAll())); } void MainWindow::slotTextChanged() { TQString str = m_edit->text(); if(str.length() == 1 && m_insert) { m_insert = false; // Don't do anything if in RPN Mode. if(inRPNMode()) return; if(str.find(TQRegExp("^[-+*/^]")) != -1) { m_edit->setText("ans " + str + " "); m_edit->moveCursor(TQTextEdit::MoveEnd, false); } } } void MainWindow::slotEvaluate() { slotReturnPressed(); } void MainWindow::slotUpdateSize() { if(m_newSize != TQSize(0, 0)) resize(m_newSize); else resize(width(), minimumSize().height()); } void MainWindow::slotDegrees() { setTrigMode(Abakus::Degrees); m_degrees->setChecked(true); if(action("setDegreesMode")) action("setDegreesMode")->setChecked(true); } void MainWindow::slotRadians() { setTrigMode(Abakus::Radians); m_radians->setChecked(true); if(action("setRadiansMode")) action("setRadiansMode")->setChecked(true); } int MainWindow::getParenthesesLevel(const TQString &str) { int level = 0; for(unsigned i = 0; i < str.length(); ++i) if(str[i] == '(') ++level; else if(str[i] == ')') --level; return level; } void MainWindow::loadConfig() { { TDEConfigGroup config(TDEGlobal::config(), "Settings"); TQString mode = config.readEntry("Trigonometric mode", "Degrees"); if(mode == "Degrees") { setTrigMode(Abakus::Degrees); m_degrees->setChecked(true); } else { setTrigMode(Abakus::Radians); m_radians->setChecked(true); } bool useRPN = config.readBoolEntry("Use RPN Mode", false); action("toggleExpressionMode")->setChecked(useRPN); int precision = config.readNumEntry("Decimal Precision", -1); if(precision < -1 || precision > 75) precision = -1; Abakus::m_prec = precision; selectCorrectPrecisionAction(); } { TDEConfigGroup config(TDEGlobal::config(), "Variables"); TQStringList list = config.readListEntry("Saved Variables"); for(TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { TQStringList values = TQStringList::split('=', *it); if(values.count() != 2) { kdWarning() << "Your configuration file has somehow been corrupted!\n"; continue; } ValueManager::instance()->setValue(values[0], Abakus::number_t(values[1].latin1())); } } { TDEConfigGroup config(TDEGlobal::config(), "GUI"); bool showHistory = config.readBoolEntry("ShowHistory", true); action("toggleHistoryList")->setChecked(showHistory); m_history->setShown(showHistory); bool showFunctions = config.readBoolEntry("ShowFunctions", true); action("toggleFunctionList")->setChecked(showFunctions); m_fnList->setShown(showFunctions); bool showVariables = config.readBoolEntry("ShowVariables", true); action("toggleVariableList")->setChecked(showVariables); m_varList->setShown(showVariables); bool compactMode = config.readBoolEntry("InCompactMode", false); compactMode = compactMode || !showHistory; action("toggleCompactMode")->setChecked(compactMode); if(compactMode) TQTimer::singleShot(0, this, TQ_SLOT(slotToggleCompactMode())); } { TDEConfigGroup config(TDEGlobal::config(), "Functions"); TQStringList fnList = config.readListEntry("FunctionList"); for(TQStringList::ConstIterator it = fnList.begin(); it != fnList.end(); ++it) parseString((*it).ascii()); // Run the function definitions through the parser } populateListViews(); } void MainWindow::saveConfig() { { TDEConfigGroup config(TDEGlobal::config(), "Settings"); config.writeEntry("Trigonometric mode", trigMode() == Abakus::Degrees ? "Degrees" : "Radians"); config.writeEntry("Use RPN Mode", inRPNMode()); config.writeEntry("Decimal Precision", Abakus::m_prec); } { TDEConfigGroup config(TDEGlobal::config(), "Variables"); TQStringList list; TQStringList values = ValueManager::instance()->valueNames(); TQStringList::ConstIterator it = values.begin(); // Set precision to max for most accuracy Abakus::m_prec = 75; for(; it != values.end(); ++it) { if(ValueManager::instance()->isValueReadOnly(*it)) continue; list += TQString("%1=%2") .arg(*it) .arg(ValueManager::instance()->value(*it).toString()); } config.writeEntry("Saved Variables", list); } { TDEConfigGroup config(TDEGlobal::config(), "GUI"); bool inCompactMode = action("toggleCompactMode")->isChecked(); config.writeEntry("InCompactMode", inCompactMode); if(!inCompactMode) { config.writeEntry("ShowHistory", m_history->isShown()); config.writeEntry("ShowFunctions", m_fnList->isShown()); config.writeEntry("ShowVariables", m_varList->isShown()); } else { config.writeEntry("ShowHistory", m_wasHistoryShown); config.writeEntry("ShowFunctions", m_wasFnShown); config.writeEntry("ShowVariables", m_wasVarShown); } } { TDEConfigGroup config(TDEGlobal::config(), "Functions"); FunctionManager *manager = FunctionManager::instance(); TQStringList userFunctions = manager->functionList(FunctionManager::UserDefined); TQStringList saveList; for(TQStringList::ConstIterator it = userFunctions.begin(); it != userFunctions.end(); ++it) { UnaryFunction *fn = dynamic_cast(manager->function(*it)->userFn->fn); TQString var = manager->function(*it)->userFn->varName; TQString expr = fn->operand()->infixString(); saveList += TQString("set %1(%2) = %3").arg(*it).arg(var).arg(expr); } config.writeEntry("FunctionList", saveList); } } void MainWindow::setupLayout() { TDEActionCollection *ac = actionCollection(); KStdAction::quit(kapp, TQ_SLOT(quit()), ac); KStdAction::showMenubar(this, TQ_SLOT(slotToggleMenuBar()), ac); TDEToggleAction *ta = new TDEToggleAction(i18n("&Degrees"), SHIFT + ALT + Key_D, this, TQ_SLOT(slotDegrees()), ac, "setDegreesMode"); ta->setExclusiveGroup("TrigMode"); ta->setChecked(trigMode() == Abakus::Degrees); ta = new TDEToggleAction(i18n("&Radians"), SHIFT + ALT + Key_R, this, TQ_SLOT(slotRadians()), ac, "setRadiansMode"); ta->setExclusiveGroup("TrigMode"); ta->setChecked(trigMode() == Abakus::Radians); ta = new TDEToggleAction(i18n("Show &History List"), SHIFT + ALT + Key_H, this, TQ_SLOT(slotToggleHistoryList()), ac, "toggleHistoryList"); ta->setChecked(true); ta = new TDEToggleAction(i18n("Show &Variables"), SHIFT + ALT + Key_V, this, TQ_SLOT(slotToggleVariableList()), ac, "toggleVariableList"); ta->setChecked(true); ta = new TDEToggleAction(i18n("Show &Functions"), SHIFT + ALT + Key_F, this, TQ_SLOT(slotToggleFunctionList()), ac, "toggleFunctionList"); ta->setChecked(true); ta = new TDEToggleAction(i18n("Activate &Compact Mode"), SHIFT + ALT + Key_C, this, TQ_SLOT(slotToggleCompactMode()), ac, "toggleCompactMode"); ta->setChecked(false); ta = new TDEToggleAction(i18n("Use R&PN Mode"), SHIFT + ALT + Key_P, this, TQ_SLOT(slotToggleExpressionMode()), ac, "toggleExpressionMode"); ta->setChecked(false); // Precision actions. ta = new TDEToggleAction(i18n("&Automatic Precision"), 0, this, TQ_SLOT(slotPrecisionAuto()), ac, "precisionAuto"); ta->setExclusiveGroup("Precision"); ta->setChecked(true); ta = new TDEToggleAction(i18n("&3 Decimal Digits"), 0, this, TQ_SLOT(slotPrecision3()), ac, "precision3"); ta->setExclusiveGroup("Precision"); ta->setChecked(false); ta = new TDEToggleAction(i18n("&8 Decimal Digits"), 0, this, TQ_SLOT(slotPrecision8()), ac, "precision8"); ta->setExclusiveGroup("Precision"); ta->setChecked(false); ta = new TDEToggleAction(i18n("&15 Decimal Digits"), 0, this, TQ_SLOT(slotPrecision15()), ac, "precision15"); ta->setExclusiveGroup("Precision"); ta->setChecked(false); ta = new TDEToggleAction(i18n("&50 Decimal Digits"), 0, this, TQ_SLOT(slotPrecision50()), ac, "precision50"); ta->setExclusiveGroup("Precision"); ta->setChecked(false); ta = new TDEToggleAction(i18n("C&ustom Precision..."), 0, this, TQ_SLOT(slotPrecisionCustom()), ac, "precisionCustom"); ta->setExclusiveGroup("Precision"); ta->setChecked(false); new TDEAction(i18n("Clear &History"), "edit-clear", SHIFT + ALT + Key_L, m_result, TQ_SLOT(clear()), ac, "clearHistory"); new TDEAction(i18n("Select Editor"), "goto", Key_F6, m_edit, TQ_SLOT(setFocus()), ac, "select_edit"); } void MainWindow::populateListViews() { TQStringList values = ValueManager::instance()->valueNames(); Abakus::number_t value = ValueManager::instance()->value("pi"); new ValueListViewItem(m_varList, "pi", value); value = ValueManager::instance()->value("e"); new ValueListViewItem(m_varList, "e", value); } TDEAction *MainWindow::action(const char *key) const { return actionCollection()->action(key); } void MainWindow::slotEntrySelected(const TQString &text) { m_edit->setText(text); m_edit->moveCursor(TQTextEdit::MoveEnd, false); } void MainWindow::slotResultSelected(const TQString &text) { m_edit->insert(text); } void MainWindow::slotToggleMenuBar() { menuBar()->setShown(!menuBar()->isShown()); } void MainWindow::slotToggleFunctionList() { bool show = action("toggleFunctionList")->isChecked(); m_fnList->setShown(show); if(!m_history->isShown()) { m_history->setShown(true); action("toggleHistoryList")->setChecked(true); slotToggleHistoryList(); } action("toggleCompactMode")->setChecked(false); } void MainWindow::slotToggleVariableList() { bool show = action("toggleVariableList")->isChecked(); m_varList->setShown(show); if(!m_history->isShown()) { m_history->setShown(true); action("toggleHistoryList")->setChecked(true); slotToggleHistoryList(); } action("toggleCompactMode")->setChecked(false); } void MainWindow::slotToggleHistoryList() { bool show = action("toggleHistoryList")->isChecked(); m_history->setShown(show); action("toggleCompactMode")->setChecked(false); } void MainWindow::slotNewFunction(const TQString &name) { UserFunction *userFn = FunctionManager::instance()->function(name)->userFn; UnaryFunction *fn = dynamic_cast(userFn->fn); TQString fnName = TQString("%1(%2)").arg(name, userFn->varName); TQString expr = fn->operand()->infixString(); new TDEListViewItem(m_fnList, fnName, expr); } void MainWindow::slotRemoveFunction(const TQString &name) { UserFunction *userFn = FunctionManager::instance()->function(name)->userFn; TQString fnName = TQString("%1(%2)").arg(name, userFn->varName); TQListViewItem *item = 0; while((item = m_fnList->findItem(fnName, 0)) != 0) delete item; } void MainWindow::slotNewValue(const TQString &name, Abakus::number_t value) { new ValueListViewItem(m_varList, name, value); } void MainWindow::slotChangeValue(const TQString &name, Abakus::number_t value) { ValueListViewItem *item = static_cast(m_varList->findItem(name, 0)); if(item) item->valueChanged(value); } void MainWindow::slotRemoveValue(const TQString &name) { delete m_varList->findItem(name, 0); } void MainWindow::slotToggleCompactMode() { if(action("toggleCompactMode")->isChecked()) { m_wasFnShown = m_fnList->isShown(); m_wasVarShown = m_varList->isShown(); m_wasHistoryShown = m_history->isShown(); m_fnList->setShown(false); m_varList->setShown(false); m_history->setShown(false); action("toggleFunctionList")->setChecked(false); action("toggleVariableList")->setChecked(false); action("toggleHistoryList")->setChecked(false); m_oldSize = size(); m_newSize = TQSize(0, 0); TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateSize())); } else { m_fnList->setShown(m_wasFnShown); m_varList->setShown(m_wasVarShown); m_history->setShown(m_wasHistoryShown); action("toggleFunctionList")->setChecked(m_wasFnShown); action("toggleVariableList")->setChecked(m_wasVarShown); action("toggleHistoryList")->setChecked(m_wasHistoryShown); m_newSize = m_oldSize; TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateSize())); } } void MainWindow::slotToggleExpressionMode() { } TQString MainWindow::interpolateExpression(const TQString &text, ResultListViewText *after) { TQString str(text); TQRegExp stackRE("\\$\\d+"); int pos; while((pos = stackRE.search(str)) != -1) { TQString stackStr = stackRE.cap(); Abakus::number_t value; unsigned numPos = stackStr.mid(1).toUInt(); if(!m_result->getStackValue(numPos, value)) { new ResultListViewText(m_result, text, i18n("Marker %1 isn't set").arg(stackStr), after, true); return TQString(); } str.replace(pos, stackStr.length(), value.toString()); } return str; } void MainWindow::slotPrecisionAuto() { Abakus::m_prec = -1; redrawResults(); } void MainWindow::slotPrecision3() { Abakus::m_prec = 3; redrawResults(); } void MainWindow::slotPrecision8() { Abakus::m_prec = 8; redrawResults(); } void MainWindow::slotPrecision15() { Abakus::m_prec = 15; redrawResults(); } void MainWindow::slotPrecision50() { Abakus::m_prec = 50; redrawResults(); } void MainWindow::slotPrecisionCustom() { bool ok = false; int precision = KInputDialog::getInteger(i18n("Select number of decimal digits to display"), i18n("Decimal precision:"), Abakus::m_prec, 0, 75, 1, &ok, this); if(ok) { Abakus::m_prec = precision; redrawResults(); } selectCorrectPrecisionAction(); } void MainWindow::redrawResults() { TQListViewItemIterator it(m_result); while(it.current()) { static_cast(it.current())->precisionChanged(); ++it; } it = TQListViewItemIterator (m_varList); while(it.current()) { static_cast(it.current())->valueChanged(); ++it; } // Because of the way we implemented the menu, it is possible to deselect // every possibility, so make sure we have at least one selected. selectCorrectPrecisionAction(); } // This function selects the correct precision action based on the value of // Abakus::m_prec. Useful for loading settings, or for custom precision // selection. void MainWindow::selectCorrectPrecisionAction() { switch(Abakus::m_prec) { case 3: action("precision3")->setChecked(true); break; case 8: action("precision8")->setChecked(true); break; case 15: action("precision15")->setChecked(true); break; case 50: action("precision50")->setChecked(true); break; case -1: action("precisionAuto")->setChecked(true); break; default: action("precisionCustom")->setChecked(true); } } #include "mainwindow.moc"