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.
katapult/katapult/plugins/catalogs/calculatorcatalog/calculatorcatalog.cpp

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"