/*************************************************************************** * Copyright (C) 2005 Tobi Vollebregt * * tobivollebregt@gmail.com * * * * Copyright (C) 2005 by Joe Ferris * * jferris@optimistictech.com * * * * 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 #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "calculatorcatalog.h" #include "expression.h" #include "actionregistry.h" #include "actionevalexpr.h" #include "status.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_E #define M_E 2.7182818284590452354 #endif static double frac(double x) { double nowhere; return modf(x, &nowhere); } static double deg_sin(double x) { return sin(x * (M_PI / 180.0)); } static double deg_cos(double x) { return cos(x * (M_PI / 180.0)); } static double deg_tan(double x) { return tan(x * (M_PI / 180.0)); } static double deg_asin(double x) { return asin(x) * (180.0 / M_PI); } static double deg_acos(double x) { return acos(x) * (180.0 / M_PI); } static double deg_atan(double x) { return atan(x) * (180.0 / M_PI); } static double deg_sinh(double x) { return sinh(x * (M_PI / 180.0)); } static double deg_cosh(double x) { return cosh(x * (M_PI / 180.0)); } static double deg_tanh(double x) { return tanh(x * (M_PI / 180.0)); } static double deg_asinh(double x) { return asinh(x) * (180.0 / M_PI); } static double deg_acosh(double x) { return acosh(x) * (180.0 / M_PI); } static double deg_atanh(double x) { return atanh(x) * (180.0 / M_PI); } const CalculatorCatalog::Function CalculatorCatalog::radiansFunctionTable[] = { { "sin", 3, sin }, { "cos", 3, cos }, { "tan", 3, tan }, { "asin", 4, asin }, { "acos", 4, acos }, { "atan", 4, atan }, { "sinh", 4, sinh }, { "cosh", 4, cosh }, { "tanh", 4, tanh }, { "asinh", 5, asinh }, { "acosh", 5, acosh }, { "atanh", 5, atanh }, { "sqrt", 4, sqrt }, { "log", 3, log10 }, { "ln", 2, log }, { "exp", 3, exp }, { "abs", 3, fabs }, { "frac", 4, frac }, { "round", 5, round }, { "int", 3, trunc }, { 0, 0, 0 } }; const CalculatorCatalog::Function CalculatorCatalog::degreesFunctionTable[] = { { "sin", 3, deg_sin }, { "cos", 3, deg_cos }, { "tan", 3, deg_tan }, { "asin", 4, deg_asin }, { "acos", 4, deg_acos }, { "atan", 4, deg_atan }, { "sinh", 4, deg_sinh }, { "cosh", 4, deg_cosh }, { "tanh", 4, deg_tanh }, { "asinh", 5, deg_asinh }, { "acosh", 5, deg_acosh }, { "atanh", 5, deg_atanh }, { "sqrt", 4, sqrt }, { "log", 3, log10 }, { "ln", 2, log }, { "exp", 3, exp }, { "abs", 3, fabs }, { "frac", 4, frac }, { "round", 5, round }, { "int", 3, trunc }, { 0, 0, 0 } }; K_EXPORT_COMPONENT_FACTORY( katapult_calculatorcatalog, KGenericFactory( "katapult_calculatorcatalog" ) ) CalculatorCatalog::CalculatorCatalog(TQObject*, const char*, const TQStringList&): _result(this, TQString()) { ActionRegistry::self()->registerAction(new ActionEvaluateExpression()); setVar(getVarID("pi"), M_PI); setVar(getVarID("e"), M_E); } CalculatorCatalog::~CalculatorCatalog() { } void CalculatorCatalog::queryChanged() { int newStatus = 0; TQString cmd = query(); if (cmd.isEmpty()) { reset(); setBestMatch(Match()); } else { if (accepts(cmd)) { int i, origLength = cmd.length(), length = origLength; //autocomplete functions cmd = cmd.lower(); for (i = length - 1; i >= 0 && cmd[i].isLetter(); --i) { } if (i != length - 1) { TQString start = cmd.mid(i + 1); int lengthOfShortest = 9999, shortest = -1; for (int j = 0; radiansFunctionTable[j].name; ++j) { if (TQString(radiansFunctionTable[j].name).startsWith(start)) { if (radiansFunctionTable[j].length < lengthOfShortest) { lengthOfShortest = radiansFunctionTable[j].length; shortest = j; } } } if (shortest != -1) { cmd = cmd.left(i + 1).append(radiansFunctionTable[shortest].name).append("("); length = cmd.length(); } } //fix parse errors at end of expression, //ie. close open parentheses, convert operators into NOPs for (i = length - 1; i >= 0 && (cmd[i] == '(' || cmd[i] == ' '); --i) { } if (i < 0 || cmd[i] == '+' || cmd[i] == '-') { cmd.append("0"); ++length; } else if (cmd[i] == '*' || cmd[i] == '/' || cmd[i] == '^') { cmd.append("1"); ++length; } else if (cmd[i].isLetter() && (i < length - 1 && cmd[i + 1] == '(')) { //just add a 0 if it's a function: we don't bother to backpropagate //through the parse tree (if it existed at all) to figure out a NOP value //for this particular (chain of) function(s). cmd.append("0"); ++length; } int openParen = 0; //use cmd.length() here, it may be > than length. for (i = 0; i < length; ++i) { if (cmd[i] == '(') ++openParen; if (cmd[i] == ')') --openParen; } if (openParen > 0) { char* closeParen = new char[openParen + 1]; memset(closeParen, ')', openParen); closeParen[openParen] = 0; cmd.append(closeParen); delete[] closeParen; } _result.setText(cmd); setBestMatch(Match(&_result, _result.parseError() ? 10 : 100, origLength)); //set status. //add S_Multiple to make sure katapult doesn't auto-exec and close the window //add S_Active to make sure katapult doesn't start the hideTimer or clearTimer newStatus = S_HasResults | S_Multiple | S_Active; } else { newStatus = 0; } } setStatus(newStatus); } void CalculatorCatalog::reset() { _result.setText(TQString()); } bool CalculatorCatalog::accepts(const TQString& str) const { //Heuristic to determine whether the string is an expression or not. //Accept anything containing [()+\\-/*^=.,0-9]. return TQRegExp("[()+\\-/*^=.,0-9]").search(str) >= 0; } int CalculatorCatalog::getVarID(const char* name) { VarNameToIdMap::iterator it = varNameToId.find(TQString(name)); if (it == varNameToId.end()) { _pendingVarName = TQString(name); return -1; } return *it; } double CalculatorCatalog::getVar(int id) const { return varIdToValue[id]; } double CalculatorCatalog::setVar(int id, double value) { if (id == -1) { id = varIdToValue.count(); varNameToId.insert(_pendingVarName, id); varIdToValue.push_back(value); } else { varIdToValue[id] = value; } return value; } /* void CalculatorCatalog::initialize() { } */ void CalculatorCatalog::readSettings(TDEConfigBase* config) { _fracDigits = config->readUnsignedNumEntry("FracDigits", 2); _bScientific = config->readBoolEntry("Scientific", false); _bDegrees = config->readBoolEntry("Degrees", false); _bClipboard = config->readBoolEntry("Clipboard", true); _formatString = config->readEntry("FormatString", "%1 = %2"); } void CalculatorCatalog::writeSettings(TDEConfigBase* config) { config->writeEntry("FracDigits", fracDigits()); config->writeEntry("Scientific", scientific()); config->writeEntry("Degrees", degrees()); config->writeEntry("Clipboard", clipboard()); config->writeEntry("FormatString", formatString()); } TQWidget * CalculatorCatalog::configure() { CalculatorCatalogSettings* settings = new CalculatorCatalogSettings(); settings->fracDigits->setValue(_fracDigits); connect(settings->fracDigits, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(fracDigitsChanged(int))); settings->normal->setChecked(!scientific()); settings->scientific->setChecked(scientific()); connect(settings->scientific, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(scientificChanged(bool))); settings->radians->setChecked(!degrees()); settings->degrees->setChecked(degrees()); connect(settings->degrees, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(degreesChanged(bool))); settings->clipboard->setChecked(clipboard()); connect(settings->clipboard, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(clipboardChanged(bool))); settings->formatString->setText(formatString()); connect(settings->formatString, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(formatStringChanged(const TQString&))); return settings; } void CalculatorCatalog::fracDigitsChanged(int n) { _fracDigits = n; } int CalculatorCatalog::fracDigits() const { return _fracDigits; } void CalculatorCatalog::scientificChanged(bool en) { _bScientific = en; } bool CalculatorCatalog::scientific() const { return _bScientific; } void CalculatorCatalog::degreesChanged(bool en) { _bDegrees = en; } bool CalculatorCatalog::degrees() const { return _bDegrees; } void CalculatorCatalog::formatStringChanged(const TQString& fmt) { _formatString = fmt; } TQString CalculatorCatalog::formatString() const { return _formatString; } void CalculatorCatalog::clipboardChanged(bool en) { _bClipboard = en; } bool CalculatorCatalog::clipboard() const { return _bClipboard; } const CalculatorCatalog::Function* CalculatorCatalog::functionTable() const { if (degrees()) { return degreesFunctionTable; } else { return radiansFunctionTable; } } #include "calculatorcatalog.moc"