File indexing completed on 2024-05-12 16:35:35

0001 /* This file is part of the KDE project
0002    Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org>
0003    Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; only
0008    version 2 of the License.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 
0022 // built-in text functions
0023 #include "TextModule.h"
0024 
0025 #include <math.h>
0026 
0027 #include <QRegExp>
0028 
0029 #include <klocale.h>
0030 
0031 #include "SheetsDebug.h"
0032 #include "CalculationSettings.h"
0033 #include "Function.h"
0034 #include "FunctionModuleRegistry.h"
0035 #include "ValueCalc.h"
0036 #include "ValueConverter.h"
0037 #include "ValueFormatter.h"
0038 
0039 using namespace Calligra::Sheets;
0040 
0041 // Functions DOLLAR and FIXED convert data to double, hence they will not
0042 // support arbitrary precision, when it will be introduced.
0043 
0044 // prototypes
0045 Value func_asc(valVector args, ValueCalc *calc, FuncExtra *);
0046 Value func_char(valVector args, ValueCalc *calc, FuncExtra *);
0047 Value func_clean(valVector args, ValueCalc *calc, FuncExtra *);
0048 Value func_code(valVector args, ValueCalc *calc, FuncExtra *);
0049 Value func_compare(valVector args, ValueCalc *calc, FuncExtra *);
0050 Value func_concatenate(valVector args, ValueCalc *calc, FuncExtra *);
0051 Value func_dollar(valVector args, ValueCalc *calc, FuncExtra *);
0052 Value func_exact(valVector args, ValueCalc *calc, FuncExtra *);
0053 Value func_find(valVector args, ValueCalc *calc, FuncExtra *);
0054 Value func_fixed(valVector args, ValueCalc *calc, FuncExtra *);
0055 Value func_jis(valVector args, ValueCalc *calc, FuncExtra *);
0056 Value func_left(valVector args, ValueCalc *calc, FuncExtra *);
0057 Value func_len(valVector args, ValueCalc *calc, FuncExtra *);
0058 Value func_lower(valVector args, ValueCalc *calc, FuncExtra *);
0059 Value func_mid(valVector args, ValueCalc *calc, FuncExtra *);
0060 Value func_numbervalue(valVector args, ValueCalc *calc, FuncExtra *);
0061 Value func_proper(valVector args, ValueCalc *calc, FuncExtra *);
0062 Value func_regexp(valVector args, ValueCalc *calc, FuncExtra *);
0063 Value func_regexpre(valVector args, ValueCalc *calc, FuncExtra *);
0064 Value func_replace(valVector args, ValueCalc *calc, FuncExtra *);
0065 Value func_rept(valVector args, ValueCalc *calc, FuncExtra *);
0066 Value func_rot13(valVector args, ValueCalc *calc, FuncExtra *);
0067 Value func_right(valVector args, ValueCalc *calc, FuncExtra *);
0068 Value func_search(valVector args, ValueCalc *calc, FuncExtra *);
0069 Value func_sleek(valVector args, ValueCalc *calc, FuncExtra *);
0070 Value func_substitute(valVector args, ValueCalc *calc, FuncExtra *);
0071 Value func_t (valVector args, ValueCalc *calc, FuncExtra *);
0072 Value func_text(valVector args, ValueCalc *calc, FuncExtra *);
0073 Value func_toggle(valVector args, ValueCalc *calc, FuncExtra *);
0074 Value func_trim(valVector args, ValueCalc *calc, FuncExtra *);
0075 Value func_unichar(valVector args, ValueCalc *calc, FuncExtra *);
0076 Value func_unicode(valVector args, ValueCalc *calc, FuncExtra *);
0077 Value func_upper(valVector args, ValueCalc *calc, FuncExtra *);
0078 Value func_value(valVector args, ValueCalc *calc, FuncExtra *);
0079 Value func_bahttext(valVector args, ValueCalc *calc, FuncExtra *);
0080 
0081 
0082 CALLIGRA_SHEETS_EXPORT_FUNCTION_MODULE("kspreadtextmodule.json", TextModule)
0083 
0084 
0085 TextModule::TextModule(QObject* parent, const QVariantList&)
0086         : FunctionModule(parent)
0087 {
0088     Function *f;
0089 
0090     // one-parameter functions
0091     f = new Function("ASC", func_asc);
0092     add(f);
0093     f = new Function("CHAR", func_char);
0094     add(f);
0095     f = new Function("CLEAN", func_clean);
0096     add(f);
0097     f = new Function("CODE", func_code);
0098     add(f);
0099     f = new Function("JIS", func_jis);
0100     add(f);
0101     f = new Function("LEN", func_len);
0102     f->setAlternateName("LENB");
0103     add(f);
0104     f = new Function("LOWER", func_lower);
0105     add(f);
0106     f = new Function("PROPER", func_proper);
0107     add(f);
0108     f = new Function("ROT13", func_rot13);
0109     f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13");
0110     add(f);
0111     f = new Function("SLEEK", func_sleek);
0112     add(f);
0113     f = new Function("T", func_t);
0114     add(f);
0115     f = new Function("TOGGLE", func_toggle);
0116     add(f);
0117     f = new Function("TRIM", func_trim);
0118     add(f);
0119     f = new Function("UNICHAR", func_unichar);
0120     add(f);
0121     f = new Function("UNICODE", func_unicode);
0122     add(f);
0123     f = new Function("UPPER", func_upper);
0124     add(f);
0125     f = new Function("VALUE", func_value);
0126     add(f);
0127 
0128     // other functions
0129     f = new Function("COMPARE", func_compare);
0130     f->setParamCount(3);
0131     add(f);
0132     f = new Function("CONCATENATE", func_concatenate);
0133     f->setParamCount(1, -1);
0134     f->setAcceptArray();
0135     add(f);
0136     f = new Function("DOLLAR", func_dollar);
0137     f->setParamCount(1, 2);
0138     add(f);
0139     f = new Function("EXACT", func_exact);
0140     f->setParamCount(2);
0141     add(f);
0142     f = new Function("FIND", func_find);
0143     f->setParamCount(2, 3);
0144     f->setAlternateName("FINDB");
0145     add(f);
0146     f = new Function("FIXED", func_fixed);
0147     f->setParamCount(1, 3);
0148     add(f);
0149     f = new Function("LEFT", func_left);
0150     f->setParamCount(1, 2);
0151     f->setAlternateName("LEFTB");
0152     add(f);
0153     f = new Function("MID", func_mid);
0154     f->setParamCount(2, 3);
0155     f->setAlternateName("MIDB");
0156     add(f);
0157     f = new Function("NUMBERVALUE", func_numbervalue);
0158     f->setParamCount(2, 3);
0159     add(f);
0160     f = new Function("REGEXP", func_regexp);
0161     f->setParamCount(2, 4);
0162     add(f);
0163     f = new Function("REGEXPRE", func_regexpre);
0164     f->setParamCount(3);
0165     add(f);
0166     f = new Function("REPLACE", func_replace);
0167     f->setParamCount(4);
0168     f->setAlternateName("REPLACEB");
0169     add(f);
0170     f = new Function("REPT", func_rept);
0171     f->setParamCount(2);
0172     add(f);
0173     f = new Function("RIGHT", func_right);
0174     f->setParamCount(1, 2);
0175     f->setAlternateName("RIGHTB");
0176     add(f);
0177     f = new Function("SEARCH", func_search);
0178     f->setParamCount(2, 3);
0179     f->setAlternateName("SEARCHB");
0180     add(f);
0181     f = new Function("SUBSTITUTE", func_substitute);
0182     f->setParamCount(3, 4);
0183     add(f);
0184     f = new Function("TEXT", func_text);
0185     f->setParamCount(1, 2);
0186     add(f);
0187     f = new Function("BAHTTEXT", func_bahttext);
0188     f->setAlternateName("COM.MICROSOFT.BAHTTEXT");
0189     f->setParamCount(1);
0190     add(f);
0191 }
0192 
0193 QString TextModule::descriptionFileName() const
0194 {
0195     return QString("text.xml");
0196 }
0197 
0198 
0199 // Function: ASC
0200 Value func_asc(valVector args, ValueCalc *calc, FuncExtra *)
0201 {
0202     QString s = calc->conv()->asString(args[0]).asString();
0203     return Value(QString(s));
0204 }
0205 
0206 // Function: CHAR
0207 Value func_char(valVector args, ValueCalc *calc, FuncExtra *)
0208 {
0209     int val = calc->conv()->asInteger(args[0]).asInteger();
0210     if (val >= 0)
0211         return Value(QString(QChar(val)));
0212     else
0213         return Value::errorNUM();
0214 }
0215 
0216 // Function: CLEAN
0217 Value func_clean(valVector args, ValueCalc *calc, FuncExtra *)
0218 {
0219     QString str(calc->conv()->asString(args[0]).asString());
0220     QString result;
0221     QChar   c;
0222     int     i;
0223     int     l = str.length();
0224 
0225     for (i = 0; i < l; ++i) {
0226         c = str[i];
0227         if (c.isPrint())
0228             result += c;
0229     }
0230 
0231     return Value(result);
0232 }
0233 
0234 // Function: CODE
0235 Value func_code(valVector args, ValueCalc *calc, FuncExtra *)
0236 {
0237     QString str(calc->conv()->asString(args[0]).asString());
0238     if (str.length() <= 0)
0239         return Value::errorVALUE();
0240 
0241     return Value(str[0].unicode());
0242 }
0243 
0244 // Function: COMPARE
0245 Value func_compare(valVector args, ValueCalc *calc, FuncExtra *)
0246 {
0247     int  result = 0;
0248     bool exact = calc->conv()->asBoolean(args[2]).asBoolean();
0249 
0250     QString s1 = calc->conv()->asString(args[0]).asString();
0251     QString s2 = calc->conv()->asString(args[1]).asString();
0252 
0253     if (!exact)
0254         result = s1.toLower().localeAwareCompare(s2.toLower());
0255     else
0256         result = s1.localeAwareCompare(s2);
0257 
0258     if (result < 0)
0259         result = -1;
0260     else if (result > 0)
0261         result = 1;
0262 
0263     return Value(result);
0264 }
0265 
0266 void func_concatenate_helper(Value val, ValueCalc *calc,
0267                              QString& tmp)
0268 {
0269     if (val.isArray()) {
0270         for (unsigned int row = 0; row < val.rows(); ++row)
0271             for (unsigned int col = 0; col < val.columns(); ++col)
0272                 func_concatenate_helper(val.element(col, row), calc, tmp);
0273     } else
0274         tmp += calc->conv()->asString(val).asString();
0275 }
0276 
0277 // Function: CONCATENATE
0278 Value func_concatenate(valVector args, ValueCalc *calc, FuncExtra *)
0279 {
0280     QString tmp;
0281     for (int i = 0; i < args.count(); ++i)
0282         func_concatenate_helper(args[i], calc, tmp);
0283 
0284     return Value(tmp);
0285 }
0286 
0287 // Function: DOLLAR
0288 Value func_dollar(valVector args, ValueCalc *calc, FuncExtra *)
0289 {
0290     // ValueConverter doesn't support money directly, hence we need to
0291     // use the locale. This code has the same effect as the output
0292     // of ValueFormatter for money format.
0293 
0294     // This function converts data to double/int, hence it won't support
0295     // larger precision.
0296 
0297     double value = numToDouble(calc->conv()->toFloat(args[0]));
0298     int precision = 2;
0299     if (args.count() == 2)
0300         precision = calc->conv()->asInteger(args[1]).asInteger();
0301 
0302     // do round, because formatMoney doesn't
0303     value = floor(value * pow(10.0, precision) + 0.5) / pow(10.0, precision);
0304 
0305     const KLocale *locale = calc->settings()->locale();
0306     QString s = locale->formatMoney(value, locale->currencySymbol(), precision);
0307 
0308     return Value(s);
0309 }
0310 
0311 // Function: EXACT
0312 Value func_exact(valVector args, ValueCalc *calc, FuncExtra *)
0313 {
0314     QString s1 = calc->conv()->asString(args[0]).asString();
0315     QString s2 = calc->conv()->asString(args[1]).asString();
0316     bool exact = (s1 == s2);
0317     return Value(exact);
0318 }
0319 
0320 // Function: FIND
0321 Value func_find(valVector args, ValueCalc *calc, FuncExtra *)
0322 {
0323     QString find_text, within_text;
0324     int start_num = 1;
0325 
0326     find_text = calc->conv()->asString(args[0]).asString();
0327     within_text = calc->conv()->asString(args[1]).asString();
0328     if (args.count() == 3)
0329         start_num = calc->conv()->asInteger(args[2]).asInteger();
0330 
0331     // conforms to Excel behaviour
0332     if (start_num <= 0) return Value::errorVALUE();
0333     if (start_num > (int)within_text.length()) return Value::errorVALUE();
0334 
0335     int pos = within_text.indexOf(find_text, start_num - 1);
0336     if (pos < 0) return Value::errorVALUE();
0337 
0338     return Value(pos + 1);
0339 }
0340 
0341 // Function: FIXED
0342 Value func_fixed(valVector args, ValueCalc *calc, FuncExtra *)
0343 {
0344     // uses double, hence won't support big precision
0345 
0346     int decimals = 2;
0347     bool decimalsIsNegative = false;
0348     bool no_commas = false;
0349 
0350     double number = numToDouble(calc->conv()->toFloat(args[0]));
0351     if (args.count() > 1) {
0352         if (args[1].less(Value(0))) {
0353             decimalsIsNegative = true;
0354             decimals = -1 * ((calc->roundUp(args[1])).asInteger());
0355         } else {
0356             decimals = calc->conv()->asInteger(args[1]).asInteger();
0357         }
0358     }
0359     if (args.count() == 3)
0360         no_commas = calc->conv()->asBoolean(args[2]).asBoolean();
0361 
0362     QString result;
0363     const KLocale *locale = calc->settings()->locale();
0364 
0365     // unfortunately, we can't just use KLocale::formatNumber because
0366     // * if decimals < 0, number is rounded
0367     // * if no_commas is true, thousand separators shouldn't show up
0368 
0369     if (decimalsIsNegative) {
0370         number = floor(number / pow(10.0, decimals) + 0.5) * pow(10.0, decimals);
0371         decimals = 0;
0372     }
0373 
0374     bool neg = number < 0;
0375     result = QString::number(neg ? -number : number, 'f', decimals);
0376 
0377     int pos = result.indexOf('.');
0378     if (pos == -1) pos = result.length();
0379     else result.replace(pos, 1, locale->decimalSymbol());
0380     if (!no_commas)
0381         while (0 < (pos -= 3))
0382             result.insert(pos, locale->thousandsSeparator());
0383 
0384     result.prepend(neg ? locale->negativeSign() :
0385                    locale->positiveSign());
0386 
0387     return Value(result);
0388 }
0389 
0390 // Function: JIS
0391 Value func_jis(valVector args, ValueCalc *calc, FuncExtra *)
0392 {
0393     Q_UNUSED(args);
0394     Q_UNUSED(calc);
0395     return Value(QString("FIXME JIS()"));
0396 }
0397 
0398 // Function: LEFT
0399 Value func_left(valVector args, ValueCalc *calc, FuncExtra *)
0400 {
0401     QString str = calc->conv()->asString(args[0]).asString();
0402     int nb = 1;
0403     if (args.count() == 2)
0404         nb = calc->conv()->asInteger(args[1]).asInteger();
0405     if (nb < 0)
0406         return Value::errorVALUE();
0407 
0408     return Value(str.left(nb));
0409 }
0410 
0411 // Function: LEN
0412 Value func_len(valVector args, ValueCalc *calc, FuncExtra *)
0413 {
0414     int nb = calc->conv()->asString(args[0]).asString().length();
0415     return Value(nb);
0416 }
0417 
0418 // Function: LOWER
0419 Value func_lower(valVector args, ValueCalc *calc, FuncExtra *)
0420 {
0421     return Value(calc->conv()->asString(args[0]).asString().toLower());
0422 }
0423 
0424 // Function: MID
0425 Value func_mid(valVector args, ValueCalc *calc, FuncExtra *)
0426 {
0427     QString str = calc->conv()->asString(args[0]).asString();
0428 
0429     int pos = calc->conv()->asInteger(args[1]).asInteger();
0430     if (pos < 0) {
0431         return Value::errorVALUE();
0432     }
0433 
0434     int len = 0x7fffffff;
0435     if (args.count() == 3) {
0436         len = (uint) calc->conv()->asInteger(args[2]).asInteger();
0437         // the length cannot be less than zero
0438         if (len < 0)
0439             return Value::errorVALUE();
0440     }
0441 
0442     // Excel compatible
0443     pos--;
0444 
0445     // workaround for Qt bug
0446     if (len > 0x7fffffff - pos) len = 0x7fffffff - pos;
0447 
0448     return Value(str.mid(pos, len));
0449 }
0450 
0451 // Function: NUMBERVALUE
0452 Value func_numbervalue(valVector args, ValueCalc *calc, FuncExtra *)
0453 {
0454     QString text = calc->conv()->asString(args[0]).asString();
0455 
0456     QString decimalPoint = calc->conv()->asString(args[1]).asString();
0457 
0458     QString thousandsSeparator;
0459     if (args.count() >= 3)
0460         thousandsSeparator = calc->conv()->asString(args[2]).asString();
0461     else if (decimalPoint == ".")
0462         thousandsSeparator = ',';
0463     else if (decimalPoint == ",")
0464         thousandsSeparator = '.';
0465 
0466     KLocale l(*KLocale::global());
0467     l.setDecimalSymbol(decimalPoint);
0468     l.setThousandsSeparator(thousandsSeparator);
0469     l.setPositiveSign("+");
0470     l.setNegativeSign("-");
0471 
0472     bool ok;
0473     double v = l.readNumber(text, &ok);
0474     return ok ? Value(v) : Value::errorVALUE();
0475 }
0476 
0477 // Function: PROPER
0478 Value func_proper(valVector args, ValueCalc *calc, FuncExtra *)
0479 {
0480     QString str = calc->conv()->asString(args[0]).asString().toLower();
0481 
0482     QChar f;
0483     bool  first = true;
0484 
0485     for (int i = 0; i < str.length(); ++i) {
0486         if (first) {
0487             f = str[i];
0488             if (f.isNumber())
0489                 continue;
0490 
0491             f = f.toUpper();
0492 
0493             str[i] = f;
0494             first = false;
0495 
0496             continue;
0497         }
0498 
0499         if (str[i].isSpace() || str[i].isPunct())
0500             first = true;
0501     }
0502 
0503     return Value(str);
0504 }
0505 
0506 // Function: REGEXP
0507 Value func_regexp(valVector args, ValueCalc *calc, FuncExtra *)
0508 {
0509     // ensure that we got a valid regular expression
0510     QRegExp exp(calc->conv()->asString(args[1]).asString());
0511     if (!exp.isValid())
0512         return Value::errorVALUE();
0513 
0514     QString s = calc->conv()->asString(args[0]).asString();
0515     QString defText;
0516     if (args.count() > 2)
0517         defText = calc->conv()->asString(args[2]).asString();
0518     int bkref = 0;
0519     if (args.count() == 4)
0520         bkref = calc->conv()->asInteger(args[3]).asInteger();
0521     if (bkref < 0)   // strange back-reference
0522         return Value::errorVALUE();
0523 
0524     QString returnValue;
0525 
0526     int pos = exp.indexIn(s);
0527     if (pos == -1)
0528         returnValue = defText;
0529     else
0530         returnValue = exp.cap(bkref);
0531 
0532     return Value(returnValue);
0533 }
0534 
0535 // Function: REGEXPRE
0536 Value func_regexpre(valVector args, ValueCalc *calc, FuncExtra *)
0537 {
0538     // ensure that we got a valid regular expression
0539     QRegExp exp(calc->conv()->asString(args[1]).asString());
0540     if (!exp.isValid())
0541         return Value::errorVALUE();
0542 
0543     QString s = calc->conv()->asString(args[0]).asString();
0544     QString str = calc->conv()->asString(args[2]).asString();
0545 
0546     int pos = 0;
0547     while ((pos = exp.indexIn(s, pos)) != -1) {
0548         int i = exp.matchedLength();
0549         s = s.replace(pos, i, str);
0550         pos += str.length();
0551     }
0552 
0553     return Value(s);
0554 }
0555 
0556 // Function: REPLACE
0557 Value func_replace(valVector args, ValueCalc *calc, FuncExtra *)
0558 {
0559     QString text = calc->conv()->asString(args[0]).asString();
0560     int pos = calc->conv()->asInteger(args[1]).asInteger();
0561     int len = calc->conv()->asInteger(args[2]).asInteger();
0562     QString new_text = calc->conv()->asString(args[3]).asString();
0563 
0564     if (pos < 0) pos = 0;
0565 
0566     QString result = text.replace(pos - 1, len, new_text);
0567     return Value(result);
0568 }
0569 
0570 // Function: REPT
0571 Value func_rept(valVector args, ValueCalc *calc, FuncExtra *)
0572 {
0573     QString s = calc->conv()->asString(args[0]).asString();
0574     int nb = calc->conv()->asInteger(args[1]).asInteger();
0575 
0576     if (nb < 0)
0577         return Value::errorVALUE();
0578 
0579     QString result;
0580     for (int i = 0; i < nb; i++) result += s;
0581     return Value(result);
0582 }
0583 
0584 // Function: RIGHT
0585 Value func_right(valVector args, ValueCalc *calc, FuncExtra *)
0586 {
0587     QString str = calc->conv()->asString(args[0]).asString();
0588     int nb = 1;
0589     if (args.count() == 2)
0590         nb = calc->conv()->asInteger(args[1]).asInteger();
0591 
0592     if (nb < 0)
0593         return Value::errorVALUE();
0594 
0595     return Value(str.right(nb));
0596 }
0597 
0598 // Function: ROT13
0599 Value func_rot13(valVector args, ValueCalc *calc, FuncExtra *)
0600 {
0601     QString text = calc->conv()->asString(args[0]).asString();
0602 
0603     for (int i = 0; i < text.length(); i++) {
0604         unsigned c = text[i].toUpper().unicode();
0605         if ((c >= 'A') && (c <= 'M'))
0606             text[i] = QChar(text[i].unicode() + 13);
0607         if ((c >= 'N') && (c <= 'Z'))
0608             text[i] = QChar(text[i].unicode() - 13);
0609     }
0610 
0611     return Value(text);
0612 }
0613 
0614 // Function: SEARCH
0615 Value func_search(valVector args, ValueCalc *calc, FuncExtra *)
0616 {
0617     QString find_text = calc->conv()->asString(args[0]).asString();
0618     QString within_text = calc->conv()->asString(args[1]).asString();
0619     int start_num = 1;
0620     if (args.count() == 3)
0621         start_num = calc->conv()->asInteger(args[2]).asInteger();
0622 
0623     // conforms to Excel behaviour
0624     if (start_num <= 0) return Value::errorVALUE();
0625     if (start_num > (int)within_text.length()) return Value::errorVALUE();
0626 
0627     // use globbing feature of QRegExp
0628     QRegExp regex(find_text, Qt::CaseInsensitive, QRegExp::Wildcard);
0629     int pos = within_text.indexOf(regex, start_num - 1);
0630     if (pos < 0) return Value::errorNA();
0631 
0632     return Value(pos + 1);
0633 }
0634 
0635 // Function: SLEEK
0636 Value func_sleek(valVector args, ValueCalc *calc, FuncExtra *)
0637 {
0638     QString str = calc->conv()->asString(args[0]).asString();
0639     QString result;
0640     QChar   c;
0641     int     i;
0642     int     l = str.length();
0643 
0644     for (i = 0; i < l; ++i) {
0645         c = str[i];
0646         if (!c.isSpace())
0647             result += c;
0648     }
0649 
0650     return Value(result);
0651 }
0652 
0653 // Function: SUBSTITUTE
0654 Value func_substitute(valVector args, ValueCalc *calc, FuncExtra *)
0655 {
0656     int occurrence = 1;
0657     bool all = true;
0658 
0659     if (args.count() == 4) {
0660         occurrence = calc->conv()->asInteger(args[3]).asInteger();
0661         all = false;
0662     }
0663 
0664     QString text = calc->conv()->asString(args[0]).asString();
0665     QString old_text = calc->conv()->asString(args[1]).asString();
0666     QString new_text = calc->conv()->asString(args[2]).asString();
0667 
0668     if (occurrence <= 0) return Value::errorVALUE();
0669     if (old_text.length() == 0) return Value(text);
0670 
0671     QString result = text;
0672 
0673     if (all) {
0674         result.replace(old_text, new_text);   // case-sensitive
0675     } else {
0676         // We are only looking to modify a single value, by position.
0677         int position = -1;
0678         for (int i = 0; i < occurrence; ++i) {
0679             position = result.indexOf(old_text, position + 1);
0680         }
0681         result.replace(position, old_text.size(), new_text);
0682     }
0683 
0684     return Value(result);
0685 }
0686 
0687 // Function: T
0688 Value func_t (valVector args, ValueCalc *calc, FuncExtra *)
0689 {
0690     if (args[0].isString())
0691         return calc->conv()->asString(args[0]);
0692     else
0693         return Value("");
0694 }
0695 
0696 // Function: TEXT
0697 Value func_text(valVector args, ValueCalc *calc, FuncExtra *)
0698 {
0699     ValueFormatter fmt(calc->conv());
0700 
0701     return Value(fmt.formatText(args[0], Format::Generic, -1, Style::OnlyNegSigned,
0702                             QString(), QString(), QString(),
0703                             calc->conv()->asString(args[1]).asString()));
0704 }
0705 
0706 // Function: TOGGLE
0707 Value func_toggle(valVector args, ValueCalc *calc, FuncExtra *)
0708 {
0709     QString str = calc->conv()->asString(args[0]).asString();
0710     int i;
0711     int l = str.length();
0712 
0713     for (i = 0; i < l; ++i) {
0714         QChar c = str[i];
0715         QChar lc = c.toLower();
0716         QChar uc = c.toUpper();
0717 
0718         if (c == lc) // it is in lowercase
0719             str[i] = c.toUpper();
0720         else if (c == uc) // it is in uppercase
0721             str[i] = c.toLower();
0722     }
0723 
0724     return Value(str);
0725 }
0726 
0727 // Function: TRIM
0728 Value func_trim(valVector args, ValueCalc *calc, FuncExtra *)
0729 {
0730     return Value(
0731                calc->conv()->asString(args[0]).asString().simplified());
0732 }
0733 
0734 // Function: UNICHAR
0735 Value func_unichar(valVector args, ValueCalc *calc, FuncExtra *)
0736 {
0737     ushort val = calc->conv()->asInteger(args[0]).asInteger();
0738     if (val > 0) {
0739         QString str;
0740         str.setUtf16(&val, 1);
0741         return Value(str);
0742     } else
0743         return Value::errorNUM();
0744 }
0745 
0746 // Function: UNICODE
0747 Value func_unicode(valVector args, ValueCalc *calc, FuncExtra *)
0748 {
0749     QString str(calc->conv()->asString(args[0]).asString());
0750     if (str.length() <= 0)
0751         return Value::errorVALUE();
0752 
0753     return Value((int)str.toUcs4().at(0));
0754 }
0755 
0756 // Function: UPPER
0757 Value func_upper(valVector args, ValueCalc *calc, FuncExtra *)
0758 {
0759     return Value(calc->conv()->asString(args[0]).asString().toUpper());
0760 }
0761 
0762 // Function: VALUE
0763 Value func_value(valVector args, ValueCalc *calc, FuncExtra *)
0764 {
0765     // same as the N function
0766     return calc->conv()->asFloat(args[0]);
0767 }
0768 
0769 #define UTF8_TH_0       "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
0770 #define UTF8_TH_1       "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
0771 #define UTF8_TH_2       "\340\270\252\340\270\255\340\270\207"
0772 #define UTF8_TH_3       "\340\270\252\340\270\262\340\270\241"
0773 #define UTF8_TH_4       "\340\270\252\340\270\265\340\271\210"
0774 #define UTF8_TH_5       "\340\270\253\340\271\211\340\270\262"
0775 #define UTF8_TH_6       "\340\270\253\340\270\201"
0776 #define UTF8_TH_7       "\340\271\200\340\270\210\340\271\207\340\270\224"
0777 #define UTF8_TH_8       "\340\271\201\340\270\233\340\270\224"
0778 #define UTF8_TH_9       "\340\271\200\340\270\201\340\271\211\340\270\262"
0779 #define UTF8_TH_10      "\340\270\252\340\270\264\340\270\232"
0780 #define UTF8_TH_11      "\340\271\200\340\270\255\340\271\207\340\270\224"
0781 #define UTF8_TH_20      "\340\270\242\340\270\265\340\271\210"
0782 #define UTF8_TH_1E2     "\340\270\243\340\271\211\340\270\255\340\270\242"
0783 #define UTF8_TH_1E3     "\340\270\236\340\270\261\340\270\231"
0784 #define UTF8_TH_1E4     "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
0785 #define UTF8_TH_1E5     "\340\271\201\340\270\252\340\270\231"
0786 #define UTF8_TH_1E6     "\340\270\245\340\271\211\340\270\262\340\270\231"
0787 #define UTF8_TH_DOT0    "\340\270\226\340\271\211\340\270\247\340\270\231"
0788 #define UTF8_TH_BAHT    "\340\270\232\340\270\262\340\270\227"
0789 #define UTF8_TH_SATANG  "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
0790 #define UTF8_TH_MINUS   "\340\270\245\340\270\232"
0791 
0792 inline void lclSplitBlock(double& rfInt, qint32& rnBlock, double fValue, double fSize)
0793 {
0794     rnBlock = static_cast< qint32 >(modf((fValue + 0.1) / fSize, &rfInt) * fSize + 0.1);
0795 }
0796 
0797 /** Appends a digit (0 to 9) to the passed string. */
0798 void lclAppendDigit(QString& rText, qint32 nDigit)
0799 {
0800     switch (nDigit) {
0801     case 0: rText += QString::fromUtf8(UTF8_TH_0); break;
0802     case 1: rText += QString::fromUtf8(UTF8_TH_1); break;
0803     case 2: rText += QString::fromUtf8(UTF8_TH_2); break;
0804     case 3: rText += QString::fromUtf8(UTF8_TH_3); break;
0805     case 4: rText += QString::fromUtf8(UTF8_TH_4); break;
0806     case 5: rText += QString::fromUtf8(UTF8_TH_5); break;
0807     case 6: rText += QString::fromUtf8(UTF8_TH_6); break;
0808     case 7: rText += QString::fromUtf8(UTF8_TH_7); break;
0809     case 8: rText += QString::fromUtf8(UTF8_TH_8); break;
0810     case 9: rText += QString::fromUtf8(UTF8_TH_9); break;
0811     default: debugSheets << "lclAppendDigit - illegal digit"; break;
0812     }
0813 }
0814 
0815 /** Appends a value raised to a power of 10: nDigit*10^nPow10.
0816     @param rText   The result text.
0817     @param nDigit  A digit in the range from 1 to 9.
0818     @param nPow10  A value in the range from 2 to 5.
0819  */
0820 void lclAppendPow10(QString& rText, qint32 nDigit, qint32 nPow10)
0821 {
0822     Q_ASSERT((1 <= nDigit) && (nDigit <= 9));   // illegal digit?
0823     lclAppendDigit(rText, nDigit);
0824     switch (nPow10) {
0825     case 2: rText += QString::fromUtf8(UTF8_TH_1E2); break;
0826     case 3: rText += QString::fromUtf8(UTF8_TH_1E3); break;
0827     case 4: rText += QString::fromUtf8(UTF8_TH_1E4); break;
0828     case 5: rText += QString::fromUtf8(UTF8_TH_1E5); break;
0829     default: debugSheets << "lclAppendPow10 - illegal power"; break;
0830     }
0831 }
0832 
0833 /** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
0834 void lclAppendBlock(QString& rText, qint32 nValue)
0835 {
0836     Q_ASSERT((1 <= nValue) && (nValue <= 999999));   // illegal value?
0837     if (nValue >= 100000) {
0838         lclAppendPow10(rText, nValue / 100000, 5);
0839         nValue %= 100000;
0840     }
0841     if (nValue >= 10000) {
0842         lclAppendPow10(rText, nValue / 10000, 4);
0843         nValue %= 10000;
0844     }
0845     if (nValue >= 1000) {
0846         lclAppendPow10(rText, nValue / 1000, 3);
0847         nValue %= 1000;
0848     }
0849     if (nValue >= 100) {
0850         lclAppendPow10(rText, nValue / 100, 2);
0851         nValue %= 100;
0852     }
0853     if (nValue > 0) {
0854         qint32 nTen = nValue / 10;
0855         qint32 nOne = nValue % 10;
0856         if (nTen >= 1) {
0857             if (nTen >= 3)
0858                 lclAppendDigit(rText, nTen);
0859             else if (nTen == 2)
0860                 rText += QString::fromUtf8(UTF8_TH_20);
0861             rText += QString::fromUtf8(UTF8_TH_10);
0862         }
0863         if ((nTen > 0) && (nOne == 1))
0864             rText += QString::fromUtf8(UTF8_TH_11);
0865         else if (nOne > 0)
0866             lclAppendDigit(rText, nOne);
0867     }
0868 }
0869 
0870 // Function: BAHTTEXT
0871 Value func_bahttext(valVector args, ValueCalc *calc, FuncExtra *)
0872 {
0873     double value = numToDouble(calc->conv()->toFloat(args[0]));
0874 
0875     // sign
0876     bool bMinus = value < 0.0;
0877     value = fabs(value);
0878 
0879     // round to 2 digits after decimal point, value contains Satang as integer
0880     value = floor(value * 100.0 + 0.5);
0881 
0882     // split Baht and Satang
0883     double fBaht = 0.0;
0884     qint32 nSatang = 0;
0885     lclSplitBlock(fBaht, nSatang, value, 100.0);
0886 
0887     QString aText;
0888 
0889     // generate text for Baht value
0890     if (fBaht == 0.0) {
0891         if (nSatang == 0)
0892             aText += QString::fromUtf8(UTF8_TH_0);
0893     } else while (fBaht > 0.0) {
0894             QString aBlock;
0895             qint32 nBlock = 0;
0896             lclSplitBlock(fBaht, nBlock, fBaht, 1.0e6);
0897             if (nBlock > 0)
0898                 lclAppendBlock(aBlock, nBlock);
0899             // add leading "million", if there will come more blocks
0900             if (fBaht > 0.0)
0901                 aBlock = QString::fromUtf8(UTF8_TH_1E6) + aBlock;
0902             aText.insert(0, aBlock);
0903         }
0904     if (aText.length() > 0)
0905         aText += QString::fromUtf8(UTF8_TH_BAHT);
0906 
0907     // generate text for Satang value
0908     if (nSatang == 0) {
0909         aText += QString::fromUtf8(UTF8_TH_DOT0);
0910     } else {
0911         lclAppendBlock(aText, nSatang);
0912         aText += QString::fromUtf8(UTF8_TH_SATANG);
0913     }
0914 
0915     // add the minus sign
0916     if (bMinus)
0917         aText = QString::fromUtf8(UTF8_TH_MINUS) + aText;
0918 
0919     return Value(aText);
0920 }
0921 
0922 #include "text.moc"