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.
361 lines
10 KiB
361 lines
10 KiB
/***************************************************************************
|
|
* 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 <kservicegroup.h>
|
|
#include <tdesycocaentry.h>
|
|
#include <tdesycocatype.h>
|
|
#include <kapplication.h>
|
|
#include <knuminput.h>
|
|
#include <kcombobox.h>
|
|
|
|
#include <tqcheckbox.h>
|
|
#include <tqbuttongroup.h>
|
|
#include <tqradiobutton.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <math.h>
|
|
|
|
#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<CalculatorCatalog>( "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"
|