1 /* This file is part of the KDE project
2    Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org>
3    Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; only
8    version 2 of the License.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19 */
20 
21 
22 // built-in text functions
23 #include "TextModule.h"
24 
25 #include <math.h>
26 
27 #include <QRegExp>
28 
29 #include <klocale.h>
30 
31 #include "SheetsDebug.h"
32 #include "CalculationSettings.h"
33 #include "Function.h"
34 #include "FunctionModuleRegistry.h"
35 #include "ValueCalc.h"
36 #include "ValueConverter.h"
37 #include "ValueFormatter.h"
38 
39 using namespace Calligra::Sheets;
40 
41 // Functions DOLLAR and FIXED convert data to double, hence they will not
42 // support arbitrary precision, when it will be introduced.
43 
44 // prototypes
45 Value func_asc(valVector args, ValueCalc *calc, FuncExtra *);
46 Value func_char(valVector args, ValueCalc *calc, FuncExtra *);
47 Value func_clean(valVector args, ValueCalc *calc, FuncExtra *);
48 Value func_code(valVector args, ValueCalc *calc, FuncExtra *);
49 Value func_compare(valVector args, ValueCalc *calc, FuncExtra *);
50 Value func_concatenate(valVector args, ValueCalc *calc, FuncExtra *);
51 Value func_dollar(valVector args, ValueCalc *calc, FuncExtra *);
52 Value func_exact(valVector args, ValueCalc *calc, FuncExtra *);
53 Value func_find(valVector args, ValueCalc *calc, FuncExtra *);
54 Value func_fixed(valVector args, ValueCalc *calc, FuncExtra *);
55 Value func_jis(valVector args, ValueCalc *calc, FuncExtra *);
56 Value func_left(valVector args, ValueCalc *calc, FuncExtra *);
57 Value func_len(valVector args, ValueCalc *calc, FuncExtra *);
58 Value func_lower(valVector args, ValueCalc *calc, FuncExtra *);
59 Value func_mid(valVector args, ValueCalc *calc, FuncExtra *);
60 Value func_numbervalue(valVector args, ValueCalc *calc, FuncExtra *);
61 Value func_proper(valVector args, ValueCalc *calc, FuncExtra *);
62 Value func_regexp(valVector args, ValueCalc *calc, FuncExtra *);
63 Value func_regexpre(valVector args, ValueCalc *calc, FuncExtra *);
64 Value func_replace(valVector args, ValueCalc *calc, FuncExtra *);
65 Value func_rept(valVector args, ValueCalc *calc, FuncExtra *);
66 Value func_rot13(valVector args, ValueCalc *calc, FuncExtra *);
67 Value func_right(valVector args, ValueCalc *calc, FuncExtra *);
68 Value func_search(valVector args, ValueCalc *calc, FuncExtra *);
69 Value func_sleek(valVector args, ValueCalc *calc, FuncExtra *);
70 Value func_substitute(valVector args, ValueCalc *calc, FuncExtra *);
71 Value func_t (valVector args, ValueCalc *calc, FuncExtra *);
72 Value func_text(valVector args, ValueCalc *calc, FuncExtra *);
73 Value func_toggle(valVector args, ValueCalc *calc, FuncExtra *);
74 Value func_trim(valVector args, ValueCalc *calc, FuncExtra *);
75 Value func_unichar(valVector args, ValueCalc *calc, FuncExtra *);
76 Value func_unicode(valVector args, ValueCalc *calc, FuncExtra *);
77 Value func_upper(valVector args, ValueCalc *calc, FuncExtra *);
78 Value func_value(valVector args, ValueCalc *calc, FuncExtra *);
79 Value func_bahttext(valVector args, ValueCalc *calc, FuncExtra *);
80 
81 
82 CALLIGRA_SHEETS_EXPORT_FUNCTION_MODULE("kspreadtextmodule.json", TextModule)
83 
84 
TextModule(QObject * parent,const QVariantList &)85 TextModule::TextModule(QObject* parent, const QVariantList&)
86         : FunctionModule(parent)
87 {
88     Function *f;
89 
90     // one-parameter functions
91     f = new Function("ASC", func_asc);
92     add(f);
93     f = new Function("CHAR", func_char);
94     add(f);
95     f = new Function("CLEAN", func_clean);
96     add(f);
97     f = new Function("CODE", func_code);
98     add(f);
99     f = new Function("JIS", func_jis);
100     add(f);
101     f = new Function("LEN", func_len);
102     f->setAlternateName("LENB");
103     add(f);
104     f = new Function("LOWER", func_lower);
105     add(f);
106     f = new Function("PROPER", func_proper);
107     add(f);
108     f = new Function("ROT13", func_rot13);
109     f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13");
110     add(f);
111     f = new Function("SLEEK", func_sleek);
112     add(f);
113     f = new Function("T", func_t);
114     add(f);
115     f = new Function("TOGGLE", func_toggle);
116     add(f);
117     f = new Function("TRIM", func_trim);
118     add(f);
119     f = new Function("UNICHAR", func_unichar);
120     add(f);
121     f = new Function("UNICODE", func_unicode);
122     add(f);
123     f = new Function("UPPER", func_upper);
124     add(f);
125     f = new Function("VALUE", func_value);
126     add(f);
127 
128     // other functions
129     f = new Function("COMPARE", func_compare);
130     f->setParamCount(3);
131     add(f);
132     f = new Function("CONCATENATE", func_concatenate);
133     f->setParamCount(1, -1);
134     f->setAcceptArray();
135     add(f);
136     f = new Function("DOLLAR", func_dollar);
137     f->setParamCount(1, 2);
138     add(f);
139     f = new Function("EXACT", func_exact);
140     f->setParamCount(2);
141     add(f);
142     f = new Function("FIND", func_find);
143     f->setParamCount(2, 3);
144     f->setAlternateName("FINDB");
145     add(f);
146     f = new Function("FIXED", func_fixed);
147     f->setParamCount(1, 3);
148     add(f);
149     f = new Function("LEFT", func_left);
150     f->setParamCount(1, 2);
151     f->setAlternateName("LEFTB");
152     add(f);
153     f = new Function("MID", func_mid);
154     f->setParamCount(2, 3);
155     f->setAlternateName("MIDB");
156     add(f);
157     f = new Function("NUMBERVALUE", func_numbervalue);
158     f->setParamCount(2, 3);
159     add(f);
160     f = new Function("REGEXP", func_regexp);
161     f->setParamCount(2, 4);
162     add(f);
163     f = new Function("REGEXPRE", func_regexpre);
164     f->setParamCount(3);
165     add(f);
166     f = new Function("REPLACE", func_replace);
167     f->setParamCount(4);
168     f->setAlternateName("REPLACEB");
169     add(f);
170     f = new Function("REPT", func_rept);
171     f->setParamCount(2);
172     add(f);
173     f = new Function("RIGHT", func_right);
174     f->setParamCount(1, 2);
175     f->setAlternateName("RIGHTB");
176     add(f);
177     f = new Function("SEARCH", func_search);
178     f->setParamCount(2, 3);
179     f->setAlternateName("SEARCHB");
180     add(f);
181     f = new Function("SUBSTITUTE", func_substitute);
182     f->setParamCount(3, 4);
183     add(f);
184     f = new Function("TEXT", func_text);
185     f->setParamCount(1, 2);
186     add(f);
187     f = new Function("BAHTTEXT", func_bahttext);
188     f->setAlternateName("COM.MICROSOFT.BAHTTEXT");
189     f->setParamCount(1);
190     add(f);
191 }
192 
descriptionFileName() const193 QString TextModule::descriptionFileName() const
194 {
195     return QString("text.xml");
196 }
197 
198 
199 // Function: ASC
func_asc(valVector args,ValueCalc * calc,FuncExtra *)200 Value func_asc(valVector args, ValueCalc *calc, FuncExtra *)
201 {
202     QString s = calc->conv()->asString(args[0]).asString();
203     return Value(QString(s));
204 }
205 
206 // Function: CHAR
func_char(valVector args,ValueCalc * calc,FuncExtra *)207 Value func_char(valVector args, ValueCalc *calc, FuncExtra *)
208 {
209     int val = calc->conv()->asInteger(args[0]).asInteger();
210     if (val >= 0)
211         return Value(QString(QChar(val)));
212     else
213         return Value::errorNUM();
214 }
215 
216 // Function: CLEAN
func_clean(valVector args,ValueCalc * calc,FuncExtra *)217 Value func_clean(valVector args, ValueCalc *calc, FuncExtra *)
218 {
219     QString str(calc->conv()->asString(args[0]).asString());
220     QString result;
221     QChar   c;
222     int     i;
223     int     l = str.length();
224 
225     for (i = 0; i < l; ++i) {
226         c = str[i];
227         if (c.isPrint())
228             result += c;
229     }
230 
231     return Value(result);
232 }
233 
234 // Function: CODE
func_code(valVector args,ValueCalc * calc,FuncExtra *)235 Value func_code(valVector args, ValueCalc *calc, FuncExtra *)
236 {
237     QString str(calc->conv()->asString(args[0]).asString());
238     if (str.length() <= 0)
239         return Value::errorVALUE();
240 
241     return Value(str[0].unicode());
242 }
243 
244 // Function: COMPARE
func_compare(valVector args,ValueCalc * calc,FuncExtra *)245 Value func_compare(valVector args, ValueCalc *calc, FuncExtra *)
246 {
247     int  result = 0;
248     bool exact = calc->conv()->asBoolean(args[2]).asBoolean();
249 
250     QString s1 = calc->conv()->asString(args[0]).asString();
251     QString s2 = calc->conv()->asString(args[1]).asString();
252 
253     if (!exact)
254         result = s1.toLower().localeAwareCompare(s2.toLower());
255     else
256         result = s1.localeAwareCompare(s2);
257 
258     if (result < 0)
259         result = -1;
260     else if (result > 0)
261         result = 1;
262 
263     return Value(result);
264 }
265 
func_concatenate_helper(Value val,ValueCalc * calc,QString & tmp)266 void func_concatenate_helper(Value val, ValueCalc *calc,
267                              QString& tmp)
268 {
269     if (val.isArray()) {
270         for (unsigned int row = 0; row < val.rows(); ++row)
271             for (unsigned int col = 0; col < val.columns(); ++col)
272                 func_concatenate_helper(val.element(col, row), calc, tmp);
273     } else
274         tmp += calc->conv()->asString(val).asString();
275 }
276 
277 // Function: CONCATENATE
func_concatenate(valVector args,ValueCalc * calc,FuncExtra *)278 Value func_concatenate(valVector args, ValueCalc *calc, FuncExtra *)
279 {
280     QString tmp;
281     for (int i = 0; i < args.count(); ++i)
282         func_concatenate_helper(args[i], calc, tmp);
283 
284     return Value(tmp);
285 }
286 
287 // Function: DOLLAR
func_dollar(valVector args,ValueCalc * calc,FuncExtra *)288 Value func_dollar(valVector args, ValueCalc *calc, FuncExtra *)
289 {
290     // ValueConverter doesn't support money directly, hence we need to
291     // use the locale. This code has the same effect as the output
292     // of ValueFormatter for money format.
293 
294     // This function converts data to double/int, hence it won't support
295     // larger precision.
296 
297     double value = numToDouble(calc->conv()->toFloat(args[0]));
298     int precision = 2;
299     if (args.count() == 2)
300         precision = calc->conv()->asInteger(args[1]).asInteger();
301 
302     // do round, because formatMoney doesn't
303     value = floor(value * pow(10.0, precision) + 0.5) / pow(10.0, precision);
304 
305     const KLocale *locale = calc->settings()->locale();
306     QString s = locale->formatMoney(value, locale->currencySymbol(), precision);
307 
308     return Value(s);
309 }
310 
311 // Function: EXACT
func_exact(valVector args,ValueCalc * calc,FuncExtra *)312 Value func_exact(valVector args, ValueCalc *calc, FuncExtra *)
313 {
314     QString s1 = calc->conv()->asString(args[0]).asString();
315     QString s2 = calc->conv()->asString(args[1]).asString();
316     bool exact = (s1 == s2);
317     return Value(exact);
318 }
319 
320 // Function: FIND
func_find(valVector args,ValueCalc * calc,FuncExtra *)321 Value func_find(valVector args, ValueCalc *calc, FuncExtra *)
322 {
323     QString find_text, within_text;
324     int start_num = 1;
325 
326     find_text = calc->conv()->asString(args[0]).asString();
327     within_text = calc->conv()->asString(args[1]).asString();
328     if (args.count() == 3)
329         start_num = calc->conv()->asInteger(args[2]).asInteger();
330 
331     // conforms to Excel behaviour
332     if (start_num <= 0) return Value::errorVALUE();
333     if (start_num > (int)within_text.length()) return Value::errorVALUE();
334 
335     int pos = within_text.indexOf(find_text, start_num - 1);
336     if (pos < 0) return Value::errorVALUE();
337 
338     return Value(pos + 1);
339 }
340 
341 // Function: FIXED
func_fixed(valVector args,ValueCalc * calc,FuncExtra *)342 Value func_fixed(valVector args, ValueCalc *calc, FuncExtra *)
343 {
344     // uses double, hence won't support big precision
345 
346     int decimals = 2;
347     bool decimalsIsNegative = false;
348     bool no_commas = false;
349 
350     double number = numToDouble(calc->conv()->toFloat(args[0]));
351     if (args.count() > 1) {
352         if (args[1].less(Value(0))) {
353             decimalsIsNegative = true;
354             decimals = -1 * ((calc->roundUp(args[1])).asInteger());
355         } else {
356             decimals = calc->conv()->asInteger(args[1]).asInteger();
357         }
358     }
359     if (args.count() == 3)
360         no_commas = calc->conv()->asBoolean(args[2]).asBoolean();
361 
362     QString result;
363     const KLocale *locale = calc->settings()->locale();
364 
365     // unfortunately, we can't just use KLocale::formatNumber because
366     // * if decimals < 0, number is rounded
367     // * if no_commas is true, thousand separators shouldn't show up
368 
369     if (decimalsIsNegative) {
370         number = floor(number / pow(10.0, decimals) + 0.5) * pow(10.0, decimals);
371         decimals = 0;
372     }
373 
374     bool neg = number < 0;
375     result = QString::number(neg ? -number : number, 'f', decimals);
376 
377     int pos = result.indexOf('.');
378     if (pos == -1) pos = result.length();
379     else result.replace(pos, 1, locale->decimalSymbol());
380     if (!no_commas)
381         while (0 < (pos -= 3))
382             result.insert(pos, locale->thousandsSeparator());
383 
384     result.prepend(neg ? locale->negativeSign() :
385                    locale->positiveSign());
386 
387     return Value(result);
388 }
389 
390 // Function: JIS
func_jis(valVector args,ValueCalc * calc,FuncExtra *)391 Value func_jis(valVector args, ValueCalc *calc, FuncExtra *)
392 {
393     Q_UNUSED(args);
394     Q_UNUSED(calc);
395     return Value(QString("FIXME JIS()"));
396 }
397 
398 // Function: LEFT
func_left(valVector args,ValueCalc * calc,FuncExtra *)399 Value func_left(valVector args, ValueCalc *calc, FuncExtra *)
400 {
401     QString str = calc->conv()->asString(args[0]).asString();
402     int nb = 1;
403     if (args.count() == 2)
404         nb = calc->conv()->asInteger(args[1]).asInteger();
405     if (nb < 0)
406         return Value::errorVALUE();
407 
408     return Value(str.left(nb));
409 }
410 
411 // Function: LEN
func_len(valVector args,ValueCalc * calc,FuncExtra *)412 Value func_len(valVector args, ValueCalc *calc, FuncExtra *)
413 {
414     int nb = calc->conv()->asString(args[0]).asString().length();
415     return Value(nb);
416 }
417 
418 // Function: LOWER
func_lower(valVector args,ValueCalc * calc,FuncExtra *)419 Value func_lower(valVector args, ValueCalc *calc, FuncExtra *)
420 {
421     return Value(calc->conv()->asString(args[0]).asString().toLower());
422 }
423 
424 // Function: MID
func_mid(valVector args,ValueCalc * calc,FuncExtra *)425 Value func_mid(valVector args, ValueCalc *calc, FuncExtra *)
426 {
427     QString str = calc->conv()->asString(args[0]).asString();
428 
429     int pos = calc->conv()->asInteger(args[1]).asInteger();
430     if (pos < 0) {
431         return Value::errorVALUE();
432     }
433 
434     int len = 0x7fffffff;
435     if (args.count() == 3) {
436         len = (uint) calc->conv()->asInteger(args[2]).asInteger();
437         // the length cannot be less than zero
438         if (len < 0)
439             return Value::errorVALUE();
440     }
441 
442     // Excel compatible
443     pos--;
444 
445     // workaround for Qt bug
446     if (len > 0x7fffffff - pos) len = 0x7fffffff - pos;
447 
448     return Value(str.mid(pos, len));
449 }
450 
451 // Function: NUMBERVALUE
func_numbervalue(valVector args,ValueCalc * calc,FuncExtra *)452 Value func_numbervalue(valVector args, ValueCalc *calc, FuncExtra *)
453 {
454     QString text = calc->conv()->asString(args[0]).asString();
455 
456     QString decimalPoint = calc->conv()->asString(args[1]).asString();
457 
458     QString thousandsSeparator;
459     if (args.count() >= 3)
460         thousandsSeparator = calc->conv()->asString(args[2]).asString();
461     else if (decimalPoint == ".")
462         thousandsSeparator = ',';
463     else if (decimalPoint == ",")
464         thousandsSeparator = '.';
465 
466     KLocale l(*KLocale::global());
467     l.setDecimalSymbol(decimalPoint);
468     l.setThousandsSeparator(thousandsSeparator);
469     l.setPositiveSign("+");
470     l.setNegativeSign("-");
471 
472     bool ok;
473     double v = l.readNumber(text, &ok);
474     return ok ? Value(v) : Value::errorVALUE();
475 }
476 
477 // Function: PROPER
func_proper(valVector args,ValueCalc * calc,FuncExtra *)478 Value func_proper(valVector args, ValueCalc *calc, FuncExtra *)
479 {
480     QString str = calc->conv()->asString(args[0]).asString().toLower();
481 
482     QChar f;
483     bool  first = true;
484 
485     for (int i = 0; i < str.length(); ++i) {
486         if (first) {
487             f = str[i];
488             if (f.isNumber())
489                 continue;
490 
491             f = f.toUpper();
492 
493             str[i] = f;
494             first = false;
495 
496             continue;
497         }
498 
499         if (str[i].isSpace() || str[i].isPunct())
500             first = true;
501     }
502 
503     return Value(str);
504 }
505 
506 // Function: REGEXP
func_regexp(valVector args,ValueCalc * calc,FuncExtra *)507 Value func_regexp(valVector args, ValueCalc *calc, FuncExtra *)
508 {
509     // ensure that we got a valid regular expression
510     QRegExp exp(calc->conv()->asString(args[1]).asString());
511     if (!exp.isValid())
512         return Value::errorVALUE();
513 
514     QString s = calc->conv()->asString(args[0]).asString();
515     QString defText;
516     if (args.count() > 2)
517         defText = calc->conv()->asString(args[2]).asString();
518     int bkref = 0;
519     if (args.count() == 4)
520         bkref = calc->conv()->asInteger(args[3]).asInteger();
521     if (bkref < 0)   // strange back-reference
522         return Value::errorVALUE();
523 
524     QString returnValue;
525 
526     int pos = exp.indexIn(s);
527     if (pos == -1)
528         returnValue = defText;
529     else
530         returnValue = exp.cap(bkref);
531 
532     return Value(returnValue);
533 }
534 
535 // Function: REGEXPRE
func_regexpre(valVector args,ValueCalc * calc,FuncExtra *)536 Value func_regexpre(valVector args, ValueCalc *calc, FuncExtra *)
537 {
538     // ensure that we got a valid regular expression
539     QRegExp exp(calc->conv()->asString(args[1]).asString());
540     if (!exp.isValid())
541         return Value::errorVALUE();
542 
543     QString s = calc->conv()->asString(args[0]).asString();
544     QString str = calc->conv()->asString(args[2]).asString();
545 
546     int pos = 0;
547     while ((pos = exp.indexIn(s, pos)) != -1) {
548         int i = exp.matchedLength();
549         s = s.replace(pos, i, str);
550         pos += str.length();
551     }
552 
553     return Value(s);
554 }
555 
556 // Function: REPLACE
func_replace(valVector args,ValueCalc * calc,FuncExtra *)557 Value func_replace(valVector args, ValueCalc *calc, FuncExtra *)
558 {
559     QString text = calc->conv()->asString(args[0]).asString();
560     int pos = calc->conv()->asInteger(args[1]).asInteger();
561     int len = calc->conv()->asInteger(args[2]).asInteger();
562     QString new_text = calc->conv()->asString(args[3]).asString();
563 
564     if (pos < 0) pos = 0;
565 
566     QString result = text.replace(pos - 1, len, new_text);
567     return Value(result);
568 }
569 
570 // Function: REPT
func_rept(valVector args,ValueCalc * calc,FuncExtra *)571 Value func_rept(valVector args, ValueCalc *calc, FuncExtra *)
572 {
573     QString s = calc->conv()->asString(args[0]).asString();
574     int nb = calc->conv()->asInteger(args[1]).asInteger();
575 
576     if (nb < 0)
577         return Value::errorVALUE();
578 
579     QString result;
580     for (int i = 0; i < nb; i++) result += s;
581     return Value(result);
582 }
583 
584 // Function: RIGHT
func_right(valVector args,ValueCalc * calc,FuncExtra *)585 Value func_right(valVector args, ValueCalc *calc, FuncExtra *)
586 {
587     QString str = calc->conv()->asString(args[0]).asString();
588     int nb = 1;
589     if (args.count() == 2)
590         nb = calc->conv()->asInteger(args[1]).asInteger();
591 
592     if (nb < 0)
593         return Value::errorVALUE();
594 
595     return Value(str.right(nb));
596 }
597 
598 // Function: ROT13
func_rot13(valVector args,ValueCalc * calc,FuncExtra *)599 Value func_rot13(valVector args, ValueCalc *calc, FuncExtra *)
600 {
601     QString text = calc->conv()->asString(args[0]).asString();
602 
603     for (int i = 0; i < text.length(); i++) {
604         unsigned c = text[i].toUpper().unicode();
605         if ((c >= 'A') && (c <= 'M'))
606             text[i] = QChar(text[i].unicode() + 13);
607         if ((c >= 'N') && (c <= 'Z'))
608             text[i] = QChar(text[i].unicode() - 13);
609     }
610 
611     return Value(text);
612 }
613 
614 // Function: SEARCH
func_search(valVector args,ValueCalc * calc,FuncExtra *)615 Value func_search(valVector args, ValueCalc *calc, FuncExtra *)
616 {
617     QString find_text = calc->conv()->asString(args[0]).asString();
618     QString within_text = calc->conv()->asString(args[1]).asString();
619     int start_num = 1;
620     if (args.count() == 3)
621         start_num = calc->conv()->asInteger(args[2]).asInteger();
622 
623     // conforms to Excel behaviour
624     if (start_num <= 0) return Value::errorVALUE();
625     if (start_num > (int)within_text.length()) return Value::errorVALUE();
626 
627     // use globbing feature of QRegExp
628     QRegExp regex(find_text, Qt::CaseInsensitive, QRegExp::Wildcard);
629     int pos = within_text.indexOf(regex, start_num - 1);
630     if (pos < 0) return Value::errorNA();
631 
632     return Value(pos + 1);
633 }
634 
635 // Function: SLEEK
func_sleek(valVector args,ValueCalc * calc,FuncExtra *)636 Value func_sleek(valVector args, ValueCalc *calc, FuncExtra *)
637 {
638     QString str = calc->conv()->asString(args[0]).asString();
639     QString result;
640     QChar   c;
641     int     i;
642     int     l = str.length();
643 
644     for (i = 0; i < l; ++i) {
645         c = str[i];
646         if (!c.isSpace())
647             result += c;
648     }
649 
650     return Value(result);
651 }
652 
653 // Function: SUBSTITUTE
func_substitute(valVector args,ValueCalc * calc,FuncExtra *)654 Value func_substitute(valVector args, ValueCalc *calc, FuncExtra *)
655 {
656     int occurrence = 1;
657     bool all = true;
658 
659     if (args.count() == 4) {
660         occurrence = calc->conv()->asInteger(args[3]).asInteger();
661         all = false;
662     }
663 
664     QString text = calc->conv()->asString(args[0]).asString();
665     QString old_text = calc->conv()->asString(args[1]).asString();
666     QString new_text = calc->conv()->asString(args[2]).asString();
667 
668     if (occurrence <= 0) return Value::errorVALUE();
669     if (old_text.length() == 0) return Value(text);
670 
671     QString result = text;
672 
673     if (all) {
674         result.replace(old_text, new_text);   // case-sensitive
675     } else {
676         // We are only looking to modify a single value, by position.
677         int position = -1;
678         for (int i = 0; i < occurrence; ++i) {
679             position = result.indexOf(old_text, position + 1);
680         }
681         result.replace(position, old_text.size(), new_text);
682     }
683 
684     return Value(result);
685 }
686 
687 // Function: T
func_t(valVector args,ValueCalc * calc,FuncExtra *)688 Value func_t (valVector args, ValueCalc *calc, FuncExtra *)
689 {
690     if (args[0].isString())
691         return calc->conv()->asString(args[0]);
692     else
693         return Value("");
694 }
695 
696 // Function: TEXT
func_text(valVector args,ValueCalc * calc,FuncExtra *)697 Value func_text(valVector args, ValueCalc *calc, FuncExtra *)
698 {
699     ValueFormatter fmt(calc->conv());
700 
701     return Value(fmt.formatText(args[0], Format::Generic, -1, Style::OnlyNegSigned,
702                             QString(), QString(), QString(),
703                             calc->conv()->asString(args[1]).asString()));
704 }
705 
706 // Function: TOGGLE
func_toggle(valVector args,ValueCalc * calc,FuncExtra *)707 Value func_toggle(valVector args, ValueCalc *calc, FuncExtra *)
708 {
709     QString str = calc->conv()->asString(args[0]).asString();
710     int i;
711     int l = str.length();
712 
713     for (i = 0; i < l; ++i) {
714         QChar c = str[i];
715         QChar lc = c.toLower();
716         QChar uc = c.toUpper();
717 
718         if (c == lc) // it is in lowercase
719             str[i] = c.toUpper();
720         else if (c == uc) // it is in uppercase
721             str[i] = c.toLower();
722     }
723 
724     return Value(str);
725 }
726 
727 // Function: TRIM
func_trim(valVector args,ValueCalc * calc,FuncExtra *)728 Value func_trim(valVector args, ValueCalc *calc, FuncExtra *)
729 {
730     return Value(
731                calc->conv()->asString(args[0]).asString().simplified());
732 }
733 
734 // Function: UNICHAR
func_unichar(valVector args,ValueCalc * calc,FuncExtra *)735 Value func_unichar(valVector args, ValueCalc *calc, FuncExtra *)
736 {
737     ushort val = calc->conv()->asInteger(args[0]).asInteger();
738     if (val > 0) {
739         QString str;
740         str.setUtf16(&val, 1);
741         return Value(str);
742     } else
743         return Value::errorNUM();
744 }
745 
746 // Function: UNICODE
func_unicode(valVector args,ValueCalc * calc,FuncExtra *)747 Value func_unicode(valVector args, ValueCalc *calc, FuncExtra *)
748 {
749     QString str(calc->conv()->asString(args[0]).asString());
750     if (str.length() <= 0)
751         return Value::errorVALUE();
752 
753     return Value((int)str.toUcs4().at(0));
754 }
755 
756 // Function: UPPER
func_upper(valVector args,ValueCalc * calc,FuncExtra *)757 Value func_upper(valVector args, ValueCalc *calc, FuncExtra *)
758 {
759     return Value(calc->conv()->asString(args[0]).asString().toUpper());
760 }
761 
762 // Function: VALUE
func_value(valVector args,ValueCalc * calc,FuncExtra *)763 Value func_value(valVector args, ValueCalc *calc, FuncExtra *)
764 {
765     // same as the N function
766     return calc->conv()->asFloat(args[0]);
767 }
768 
769 #define UTF8_TH_0       "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
770 #define UTF8_TH_1       "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
771 #define UTF8_TH_2       "\340\270\252\340\270\255\340\270\207"
772 #define UTF8_TH_3       "\340\270\252\340\270\262\340\270\241"
773 #define UTF8_TH_4       "\340\270\252\340\270\265\340\271\210"
774 #define UTF8_TH_5       "\340\270\253\340\271\211\340\270\262"
775 #define UTF8_TH_6       "\340\270\253\340\270\201"
776 #define UTF8_TH_7       "\340\271\200\340\270\210\340\271\207\340\270\224"
777 #define UTF8_TH_8       "\340\271\201\340\270\233\340\270\224"
778 #define UTF8_TH_9       "\340\271\200\340\270\201\340\271\211\340\270\262"
779 #define UTF8_TH_10      "\340\270\252\340\270\264\340\270\232"
780 #define UTF8_TH_11      "\340\271\200\340\270\255\340\271\207\340\270\224"
781 #define UTF8_TH_20      "\340\270\242\340\270\265\340\271\210"
782 #define UTF8_TH_1E2     "\340\270\243\340\271\211\340\270\255\340\270\242"
783 #define UTF8_TH_1E3     "\340\270\236\340\270\261\340\270\231"
784 #define UTF8_TH_1E4     "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
785 #define UTF8_TH_1E5     "\340\271\201\340\270\252\340\270\231"
786 #define UTF8_TH_1E6     "\340\270\245\340\271\211\340\270\262\340\270\231"
787 #define UTF8_TH_DOT0    "\340\270\226\340\271\211\340\270\247\340\270\231"
788 #define UTF8_TH_BAHT    "\340\270\232\340\270\262\340\270\227"
789 #define UTF8_TH_SATANG  "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
790 #define UTF8_TH_MINUS   "\340\270\245\340\270\232"
791 
lclSplitBlock(double & rfInt,qint32 & rnBlock,double fValue,double fSize)792 inline void lclSplitBlock(double& rfInt, qint32& rnBlock, double fValue, double fSize)
793 {
794     rnBlock = static_cast< qint32 >(modf((fValue + 0.1) / fSize, &rfInt) * fSize + 0.1);
795 }
796 
797 /** Appends a digit (0 to 9) to the passed string. */
lclAppendDigit(QString & rText,qint32 nDigit)798 void lclAppendDigit(QString& rText, qint32 nDigit)
799 {
800     switch (nDigit) {
801     case 0: rText += QString::fromUtf8(UTF8_TH_0); break;
802     case 1: rText += QString::fromUtf8(UTF8_TH_1); break;
803     case 2: rText += QString::fromUtf8(UTF8_TH_2); break;
804     case 3: rText += QString::fromUtf8(UTF8_TH_3); break;
805     case 4: rText += QString::fromUtf8(UTF8_TH_4); break;
806     case 5: rText += QString::fromUtf8(UTF8_TH_5); break;
807     case 6: rText += QString::fromUtf8(UTF8_TH_6); break;
808     case 7: rText += QString::fromUtf8(UTF8_TH_7); break;
809     case 8: rText += QString::fromUtf8(UTF8_TH_8); break;
810     case 9: rText += QString::fromUtf8(UTF8_TH_9); break;
811     default: debugSheets << "lclAppendDigit - illegal digit"; break;
812     }
813 }
814 
815 /** Appends a value raised to a power of 10: nDigit*10^nPow10.
816     @param rText   The result text.
817     @param nDigit  A digit in the range from 1 to 9.
818     @param nPow10  A value in the range from 2 to 5.
819  */
lclAppendPow10(QString & rText,qint32 nDigit,qint32 nPow10)820 void lclAppendPow10(QString& rText, qint32 nDigit, qint32 nPow10)
821 {
822     Q_ASSERT((1 <= nDigit) && (nDigit <= 9));   // illegal digit?
823     lclAppendDigit(rText, nDigit);
824     switch (nPow10) {
825     case 2: rText += QString::fromUtf8(UTF8_TH_1E2); break;
826     case 3: rText += QString::fromUtf8(UTF8_TH_1E3); break;
827     case 4: rText += QString::fromUtf8(UTF8_TH_1E4); break;
828     case 5: rText += QString::fromUtf8(UTF8_TH_1E5); break;
829     default: debugSheets << "lclAppendPow10 - illegal power"; break;
830     }
831 }
832 
833 /** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
lclAppendBlock(QString & rText,qint32 nValue)834 void lclAppendBlock(QString& rText, qint32 nValue)
835 {
836     Q_ASSERT((1 <= nValue) && (nValue <= 999999));   // illegal value?
837     if (nValue >= 100000) {
838         lclAppendPow10(rText, nValue / 100000, 5);
839         nValue %= 100000;
840     }
841     if (nValue >= 10000) {
842         lclAppendPow10(rText, nValue / 10000, 4);
843         nValue %= 10000;
844     }
845     if (nValue >= 1000) {
846         lclAppendPow10(rText, nValue / 1000, 3);
847         nValue %= 1000;
848     }
849     if (nValue >= 100) {
850         lclAppendPow10(rText, nValue / 100, 2);
851         nValue %= 100;
852     }
853     if (nValue > 0) {
854         qint32 nTen = nValue / 10;
855         qint32 nOne = nValue % 10;
856         if (nTen >= 1) {
857             if (nTen >= 3)
858                 lclAppendDigit(rText, nTen);
859             else if (nTen == 2)
860                 rText += QString::fromUtf8(UTF8_TH_20);
861             rText += QString::fromUtf8(UTF8_TH_10);
862         }
863         if ((nTen > 0) && (nOne == 1))
864             rText += QString::fromUtf8(UTF8_TH_11);
865         else if (nOne > 0)
866             lclAppendDigit(rText, nOne);
867     }
868 }
869 
870 // Function: BAHTTEXT
func_bahttext(valVector args,ValueCalc * calc,FuncExtra *)871 Value func_bahttext(valVector args, ValueCalc *calc, FuncExtra *)
872 {
873     double value = numToDouble(calc->conv()->toFloat(args[0]));
874 
875     // sign
876     bool bMinus = value < 0.0;
877     value = fabs(value);
878 
879     // round to 2 digits after decimal point, value contains Satang as integer
880     value = floor(value * 100.0 + 0.5);
881 
882     // split Baht and Satang
883     double fBaht = 0.0;
884     qint32 nSatang = 0;
885     lclSplitBlock(fBaht, nSatang, value, 100.0);
886 
887     QString aText;
888 
889     // generate text for Baht value
890     if (fBaht == 0.0) {
891         if (nSatang == 0)
892             aText += QString::fromUtf8(UTF8_TH_0);
893     } else while (fBaht > 0.0) {
894             QString aBlock;
895             qint32 nBlock = 0;
896             lclSplitBlock(fBaht, nBlock, fBaht, 1.0e6);
897             if (nBlock > 0)
898                 lclAppendBlock(aBlock, nBlock);
899             // add leading "million", if there will come more blocks
900             if (fBaht > 0.0)
901                 aBlock = QString::fromUtf8(UTF8_TH_1E6) + aBlock;
902             aText.insert(0, aBlock);
903         }
904     if (aText.length() > 0)
905         aText += QString::fromUtf8(UTF8_TH_BAHT);
906 
907     // generate text for Satang value
908     if (nSatang == 0) {
909         aText += QString::fromUtf8(UTF8_TH_DOT0);
910     } else {
911         lclAppendBlock(aText, nSatang);
912         aText += QString::fromUtf8(UTF8_TH_SATANG);
913     }
914 
915     // add the minus sign
916     if (bMinus)
917         aText = QString::fromUtf8(UTF8_TH_MINUS) + aText;
918 
919     return Value(aText);
920 }
921 
922 #include "text.moc"
923