1/* This file is part of the KDE project
2 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3 Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4 Copyright 2004 Tomas Mecir <mecirt@gmail.com>
5 Copyright 1998-2004 KSpread Team <calligra-devel@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22
23#include "ValueFormatter.h"
24
25#include "CalculationSettings.h"
26#include "Cell.h"
27#include "Localization.h"
28#include "ValueConverter.h"
29
30#include <kcalendarsystem.h>
31#include <kdebug.h>
32#include <klocale.h>
33
34#include <float.h>
35#include <math.h>
36
37using namespace Calligra::Sheets;
38
39ValueFormatter::ValueFormatter(const ValueConverter* converter)
40 : m_converter(converter)
41{
42}
43
44const CalculationSettings* ValueFormatter::settings() const
45{
46 return m_converter->settings();
47}
48
49Value ValueFormatter::formatText(const Value &value, Format::Type fmtType, int precision,
50 Style::FloatFormat floatFormat, const QString &prefix,
51 const QString &postfix, const QString &currencySymbol,
52 const QString &formatString, bool thousandsSep)
53{
54 if (value.isError())
55 return Value(value.errorMessage());
56
57 //if we have an array, use its first element
58 if (value.isArray())
59 return formatText(value.element(0, 0), fmtType, precision,
60 floatFormat, prefix, postfix, currencySymbol, formatString);
61
62 Value result;
63
64 //step 1: determine formatting that will be used
65 fmtType = determineFormatting(value, fmtType);
66
67 //step 2: format the value !
68 bool ok = false;
69
70 //text
71 if (fmtType == Format::Text) {
72 QString str = m_converter->asString(value).asString();
73 if (!str.isEmpty() && str[0] == '\'')
74 str = str.mid(1);
75 result = Value(str);
76 if (value.isBoolean()) {
77 result.setFormat(Value::fmt_Boolean);
78 }
79 ok = true;
80 }
81
82 //datetime
83 else if (fmtType == Format::DateTime || (Format::isDate(fmtType) && !formatString.isEmpty()) ) {
84 Value dateValue = m_converter->asDateTime(value, &ok);
85 if (ok) {
86 result = Value(dateTimeFormat(dateValue.asDateTime(settings()), fmtType, formatString));
87 result.setFormat(Value::fmt_DateTime);
88 }
89 }
90
91 //
92 else if (Format::isDate(fmtType)) {
93 Value dateValue = m_converter->asDate(value, &ok);
94 if (ok) {
95 result = Value(dateFormat(dateValue.asDate(settings()), fmtType, formatString));
96 result.setFormat(Value::fmt_Date);
97 }
98 }
99
100 //time
101 else if (Format::isTime(fmtType)) {
102 Value timeValue = m_converter->asDateTime(value, &ok);
103 if (ok) {
104 result = Value(timeFormat(timeValue.asDateTime(settings()), fmtType, formatString));
105 result.setFormat(Value::fmt_Time);
106 }
107 }
108
109 //fraction
110 else if (Format::isFraction(fmtType)) {
111 Value fractionValue = m_converter->asFloat(value, &ok);
112 if (ok) {
113 result = Value(fractionFormat(fractionValue.asFloat(), fmtType));
114 result.setFormat(Value::fmt_Number);
115 }
116 }
117
118 //another
119 else {
120 // complex
121 if (value.isComplex()) {
122 Value complexValue = m_converter->asComplex(value, &ok);
123 if (ok) {
124 result = Value(complexFormat(complexValue, precision, fmtType, floatFormat, currencySymbol, thousandsSep));
125 result.setFormat(Value::fmt_Number);
126 }
127 }
128
129 // real number
130 else {
131 Number number = m_converter->asFloat(value, &ok).asFloat();
132 if (ok) {
133 result = Value(createNumberFormat(number, precision, fmtType, floatFormat, currencySymbol, formatString, thousandsSep));
134 result.setFormat(Value::fmt_Number);
135 }
136 }
137 }
138
139 // Only string values can fail. If so, keep the string.
140 if (!ok) {
141 QString str = m_converter->asString(value).asString();
142 if (!str.isEmpty() && str[0] == '\'')
143 str = str.mid(1);
144 result = Value(str);
145 }
146
147 if (!prefix.isEmpty())
148 result = Value(prefix + ' ' + result.asString());
149
150 if (!postfix.isEmpty())
151 result = Value(result.asString() + ' ' + postfix);
152
153 //kDebug() <<"ValueFormatter says:" << str;
154 return result;
155}
156
157Format::Type ValueFormatter::determineFormatting(const Value &value,
158 Format::Type fmtType)
159{
160 //now, everything depends on whether the formatting is Generic or not
161 if (fmtType == Format::Generic) {
162 //here we decide based on value's format...
163 Value::Format fmt = value.format();
164 switch (fmt) {
165 case Value::fmt_None:
166 fmtType = Format::Text;
167 break;
168 case Value::fmt_Boolean:
169 fmtType = Format::Text;
170 break;
171 case Value::fmt_Number: {
172 Number val = fabs(value.asFloat());
173 if (((val > 10000e+10) || (val < 10000e-10)) && (val != 0.0))
174 fmtType = Format::Scientific;
175 else
176 fmtType = Format::Number;
177 }
178 break;
179 case Value::fmt_Percent:
180 fmtType = Format::Percentage;
181 break;
182 case Value::fmt_Money:
183 fmtType = Format::Money;
184 break;
185 case Value::fmt_DateTime:
186 fmtType = Format::DateTime;
187 break;
188 case Value::fmt_Date:
189 fmtType = Format::ShortDate;
190 break;
191 case Value::fmt_Time:
192 fmtType = Format::Time8; // [h]:mm
193 break;
194 case Value::fmt_String:
195 //this should never happen
196 fmtType = Format::Text;
197 break;
198 };
199 return fmtType;
200 } else {
201 //we'll mostly want to use the given formatting, the only exception
202 //being Boolean values
203
204 //TODO: is this correct? We may also want to convert bools to 1s and 0s
205 //if we want to display a number...
206
207 //TODO: what to do about Custom formatting? We don't support it as of now,
208 // but we'll have it ... one day, that is ...
209 if (value.isBoolean())
210 return Format::Text;
211 else
212 return fmtType;
213 }
214}
215
216
217QString ValueFormatter::removeTrailingZeros(const QString& str, const QString& decimalSymbol)
218{
219 if (!str.contains(decimalSymbol))
220 //no decimal symbol -> nothing to do
221 return str;
222
223 int start = 0;
224 int cslen = m_converter->settings()->locale()->currencySymbol().length();
225 if (str.indexOf('%') != -1)
226 start = 2;
227 else if (str.indexOf(m_converter->settings()->locale()->currencySymbol()) ==
228 ((int)(str.length() - cslen)))
229 start = cslen + 1;
230 else if ((start = str.indexOf('E')) != -1)
231 start = str.length() - start;
232 else
233 start = 0;
234
235 QString result = str;
236 int i = str.length() - start;
237 bool bFinished = false;
238 while (!bFinished && i > 0) {
239 QChar ch = result[i - 1];
240 if (ch == '0')
241 result.remove(--i, 1);
242 else {
243 bFinished = true;
244 if (result.mid(i - decimalSymbol.length(), decimalSymbol.length()) == decimalSymbol)
245 result.remove(i - decimalSymbol.length(), decimalSymbol.length());
246 }
247 }
248 return result;
249}
250
251QString ValueFormatter::createNumberFormat(Number value, int precision,
252 Format::Type fmt, Style::FloatFormat floatFormat, const QString& currencySymbol,
253 const QString& _formatString, bool thousandsSep)
254{
255 QString prefix, postfix;
256 QString formatString(_formatString);
257
258 // try to split formatstring into prefix, formatstring and postfix.
259 if (!formatString.isEmpty() ) {
260 QRegExp re( QLatin1String( "^([^0#.,E+]*)([0#.,E+]*)(.*)$" ) );
261 if( re.exactMatch( formatString ) ) {
262 prefix = re.cap( 1 );
263 formatString = re.cap( 2 );
264 postfix = re.cap( 3 );
265 }
266 if (formatString.isEmpty()) {
267 return prefix + postfix;
268 } else if (formatString.contains(QLatin1Char('.'))) {
269 precision = formatString.length() - formatString.indexOf(QLatin1Char('.')) - 1;
270 } else if (precision != -1) {
271 precision = 0;
272 }
273 }
274
275 int p = precision;
276 if (p == -1) {
277 // If precision (obtained from the cell style) is -1 (arbitrary), use the document default decimal precision
278 // and if that value is -1 too then use either automatic decimal place adjustment or a hardcoded default.
279 p = settings()->defaultDecimalPrecision();
280 if (p == -1) {
281 if (fmt == Format::Number) {
282 QString s = QString::number(double(numToDouble(value)));
283 int _p = s.indexOf('.');
284 p = _p >= 0 ? qMax(0, 10 - _p) : 0;
285 } else {
286 p = 2; // hardcoded default
287 }
288 }
289 }
290
291 QString localizedNumber;
292 int pos = 0;
293
294 // Always unsigned ?
295 if ((floatFormat == Style::AlwaysUnsigned) && (value < 0.0))
296 value *= -1.0;
297
298 //multiply value by 100 for percentage format
299 if (fmt == Format::Percentage)
300 value *= 100;
301
302 // round the number, based on desired precision if not scientific is chosen
303 //(scientific has relative precision)
304 if (fmt != Format::Scientific) {
305 // this will avoid displaying negative zero, i.e "-0.0000"
306 // TODO: is this really a good solution?
307 if (fabs(value) < DBL_EPSILON) value = 0.0;
308
309 double m[] = { 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 };
310 double mm = (p > 10) ? ::pow(10.0, p) : m[p];
311 bool neg = value < 0;
312 value = floor(numToDouble(fabs(value)) * mm + 0.5) / mm;
313 if (neg) value = -value;
314 }
315
316 double val = numToDouble(value);
317 switch (fmt) {
318 case Format::Number:
319 localizedNumber = m_converter->settings()->locale()->formatNumber(val, p);
320 break;
321 case Format::Percentage:
322 localizedNumber = m_converter->settings()->locale()->formatNumber(val, p);
323 if(!postfix.endsWith('%')) // percent formattings needs to end with a "%"-sign
324 postfix += '%';
325 break;
326 case Format::Money:
327 localizedNumber = m_converter->settings()->locale()->formatMoney(val, currencySymbol.isEmpty() ? m_converter->settings()->locale()->currencySymbol() : currencySymbol, p);
328 break;
329 case Format::Scientific: {
330 const QString decimalSymbol = m_converter->settings()->locale()->decimalSymbol();
331 localizedNumber = QString::number(val, 'E', p);
332 if ((pos = localizedNumber.indexOf('.')) != -1)
333 localizedNumber.replace(pos, 1, decimalSymbol);
334 break;
335 }
336 default :
337 //other formatting?
338 // This happens with Format::Custom...
339 kDebug(36001) << "Wrong usage of ValueFormatter::createNumberFormat fmt=" << fmt << "";
340 break;
341 }
342
343 //prepend positive sign if needed
344 if ((floatFormat == Style::AlwaysSigned) && value >= 0)
345 if (m_converter->settings()->locale()->positiveSign().isEmpty())
346 localizedNumber = '+' + localizedNumber;
347
348 // Remove trailing zeros and the decimal point if necessary
349 // unless the number has no decimal point
350 if (precision == -1) {
351 QString decimalSymbol = m_converter->settings()->locale()->decimalSymbol();
352 if (decimalSymbol.isNull())
353 decimalSymbol = '.';
354
355 localizedNumber = removeTrailingZeros(localizedNumber, decimalSymbol);
356 }
357
358 // Remove thousands separators if necessary
359 if (!thousandsSep) {
360 const QString separator = m_converter->settings()->locale()->thousandsSeparator();
361 if (!separator.isNull()) {
362 localizedNumber.remove(separator);
363 }
364 }
365
366 // remove negative sign if prefix already ends with '-'
367 if (!prefix.isEmpty() && prefix[prefix.length()-1] == '-' && !localizedNumber.isEmpty() && localizedNumber[0] == '-') {
368 localizedNumber = localizedNumber.mid(1);
369 }
370
371 return prefix + localizedNumber + postfix;
372}
373
374QString ValueFormatter::fractionFormat(Number value, Format::Type fmtType)
375{
376 bool isNegative = value < 0;
377 QString prefix = isNegative ? "-" : "";
378 value = abs(value);
379 Number result = value - floor(numToDouble(value));
380 int index;
381 int limit = 0;
382
383 /* return w/o fraction part if not necessary */
384 if (result == 0)
385 return prefix + QString::number((double) numToDouble(value));
386
387 switch (fmtType) {
388 case Format::fraction_half:
389 index = 2;
390 break;
391 case Format::fraction_quarter:
392 index = 4;
393 break;
394 case Format::fraction_eighth:
395 index = 8;
396 break;
397 case Format::fraction_sixteenth:
398 index = 16;
399 break;
400 case Format::fraction_tenth:
401 index = 10;
402 break;
403 case Format::fraction_hundredth:
404 index = 100;
405 break;
406 case Format::fraction_one_digit:
407 index = 3;
408 limit = 9;
409 break;
410 case Format::fraction_two_digits:
411 index = 4;
412 limit = 99;
413 break;
414 case Format::fraction_three_digits:
415 index = 5;
416 limit = 999;
417 break;
418 default:
419 kDebug(36001) << "Error in Fraction format";
420 return prefix + QString::number((double) numToDouble(value));
421 break;
422 } /* switch */
423
424
425 /* handle halves, quarters, tenths, ... */
426 if (fmtType != Format::fraction_three_digits
427 && fmtType != Format::fraction_two_digits
428 && fmtType != Format::fraction_one_digit) {
429 Number calc = 0;
430 int index1 = 0;
431 Number diff = result;
432 for (int i = 1; i <= index; i++) {
433 calc = i * 1.0 / index;
434 if (fabs(result - calc) < diff) {
435 index1 = i;
436 diff = fabs(result - calc);
437 }
438 }
439 if (index1 == 0) return prefix + QString("%1").arg((double) floor(numToDouble(value)));
440 if (index1 == index) return prefix + QString("%1").arg((double) floor(numToDouble(value)) + 1);
441 if (floor(numToDouble(value)) == 0)
442 return prefix + QString("%1/%2").arg(index1).arg(index);
443
444 return prefix + QString("%1 %2/%3")
445 .arg((double) floor(numToDouble(value)))
446 .arg(index1)
447 .arg(index);
448 }
449
450
451 /* handle Format::fraction_one_digit, Format::fraction_two_digit and Format::fraction_three_digit style */
452 double target = numToDouble(result);
453 double numerator = 1;
454 double denominator = 1;
455 double bestNumerator = 0;
456 double bestDenominator = 1;
457 double bestDist = target;
458
459 // as soon as either numerator or denominator gets above the limit, we're done
460 while (numerator <= limit && denominator <= limit) {
461 double dist = abs((numerator / denominator) - target);
462 if (dist < bestDist) {
463 bestDist = dist;
464 bestNumerator = numerator;
465 bestDenominator = denominator;
466 }
467 if (numerator / denominator > target) {
468 denominator++;
469 } else {
470 numerator++;
471 }
472 }
473
474 if (bestNumerator == 0)
475 return prefix + QString().setNum((double) floor(numToDouble(value)));
476 else if (bestDenominator == bestNumerator)
477 return prefix + QString().setNum((double) floor(numToDouble(value + 1)));
478 else {
479 if (floor(numToDouble(value)) == 0)
480 return prefix + QString("%1/%2").arg(bestNumerator).arg(bestDenominator);
481 else
482 return prefix + QString("%1 %2/%3")
483 .arg((double)floor(numToDouble(value)))
484 .arg(bestNumerator)
485 .arg(bestDenominator);
486 }
487}
488
489QString ValueFormatter::timeFormat(const QDateTime &_dt, Format::Type fmtType, const QString& formatString)
490{
491 if (!formatString.isEmpty()) {
492 return _dt.toString( formatString );
493 }
494
495 const QDateTime dt(_dt.toUTC());
496 QString result;
497 if (fmtType == Format::Time)
498 result = m_converter->settings()->locale()->formatTime(dt.time(), false);
499 else if (fmtType == Format::SecondeTime)
500 result = m_converter->settings()->locale()->formatTime(dt.time(), true);
501 else {
502 const int d = settings()->referenceDate().daysTo(dt.date());
503 int h, m, s;
504 if (fmtType != Format::Time6 && fmtType != Format::Time7 && fmtType != Format::Time8) { // time
505 h = dt.time().hour();
506 m = dt.time().minute();
507 s = dt.time().second();
508 } else if (d >= 0) { // positive duration
509 h = dt.time().hour() + 24 * d;
510 m = dt.time().minute();
511 s = dt.time().second();
512 } else { // negative duration
513 s = (60 - dt.time().second()) % 60;
514 m = (60 - dt.time().minute() - ((s == 0) ? 0 : 1)) % 60;
515 h = -(dt.time().hour() + 24 * d) - ((m == 0 && s == 0) ? 0 : 1);
516 }
517 const bool pm = (h > 12);
518 const QString sign = d < 0 ? QString('-') : QString("");
519
520 if (fmtType == Format::Time1) { // 9:01 AM
521 result = QString("%1:%2 %3")
522 .arg(QString::number(pm ? h - 12 : h), 1)
523 .arg(QString::number(m), 2, '0')
524 .arg(pm ? i18n("PM") : i18n("AM"));
525 } else if (fmtType == Format::Time2) { // 9:01:05 AM
526 result = QString("%1:%2:%3 %4")
527 .arg(QString::number(pm ? h - 12 : h), 1)
528 .arg(QString::number(m), 2, '0')
529 .arg(QString::number(s), 2, '0')
530 .arg(pm ? i18n("PM") : i18n("AM"));
531 } else if (fmtType == Format::Time3) { // 9 h 01 min 28 s
532 result = QString("%1 %2 %3 %4 %5 %6")
533 .arg(QString::number(h), 2, '0')
534 .arg(i18n("h"))
535 .arg(QString::number(m), 2, '0')
536 .arg(i18n("min"))
537 .arg(QString::number(s), 2, '0')
538 .arg(i18n("s"));
539 } else if (fmtType == Format::Time4) { // 9:01
540 result = QString("%1:%2")
541 .arg(QString::number(h), 1)
542 .arg(QString::number(m), 2, '0');
543 } else if (fmtType == Format::Time5) { // 9:01:12
544 result = QString("%1:%2:%3")
545 .arg(QString::number(h), 1)
546 .arg(QString::number(m), 2, '0')
547 .arg(QString::number(s), 2, '0');
548 } else if (fmtType == Format::Time6) { // [mm]:ss
549 result = sign + QString("%1:%2")
550 .arg(QString::number(m + h * 60), 2, '0')
551 .arg(QString::number(s), 2, '0');
552 } else if (fmtType == Format::Time7) { // [h]:mm:ss
553 result = sign + QString("%1:%2:%3")
554 .arg(QString::number(h), 1)
555 .arg(QString::number(m), 2, '0')
556 .arg(QString::number(s), 2, '0');
557 } else if (fmtType == Format::Time8) { // [h]:mm
558 result = sign + QString("%1:%2")
559 .arg(QString::number(h), 1)
560 .arg(QString::number(m), 2, '0');
561 }
562 }
563 return result;
564}
565
566QString ValueFormatter::dateTimeFormat(const QDateTime &_dt, Format::Type fmtType, const QString& formatString )
567{
568 if( !formatString.isEmpty() ) {
569 if (formatString.contains('X')) { // if we have the special extra-short month in the format string
570 int monthPos = formatString.indexOf('X');
571 QString before = formatString.left(monthPos); // get string before and after the extra-short month sign
572 QString after = formatString.right(formatString.size() - monthPos - 1);
573 QString monthShort = _dt.toString("MMM").left(1); // format the month as extra-short (only 1st letter)
574 return _dt.toString( before ) + monthShort + _dt.toString( after ); // and construct the final date
575 }
576
577 return _dt.toString( formatString );
578 }
579
580 Q_UNUSED(fmtType);
581 QString result;
582 // pretty lame, just asssuming something for the format
583 // TODO: locale-aware formatting
584 result += dateFormat(_dt.date(), Format::ShortDate) + ' ' + timeFormat(_dt, Format::Time1);
585 return result;
586}
587
588QString ValueFormatter::dateFormat(const QDate &date, Format::Type fmtType, const QString& formatString )
589{
590 if( !formatString.isEmpty() ) {
591 return date.toString( formatString );
592 }
593
594 QString tmp;
595 if (fmtType == Format::ShortDate) {
596 tmp = m_converter->settings()->locale()->formatDate(date, KLocale::ShortDate);
597 } else if (fmtType == Format::TextDate) {
598 tmp = m_converter->settings()->locale()->formatDate(date, KLocale::LongDate);
599 } else if (fmtType == Format::Date1) { /*18-Feb-99 */
600 tmp = QString().sprintf("%02d", date.day()) +
601 '-' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
602 '-' + QString::number(date.year()).right(2);
603 } else if (fmtType == Format::Date2) { /*18-Feb-1999 */
604 tmp = QString().sprintf("%02d", date.day()) +
605 '-' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
606 '-' + QString::number(date.year());
607 } else if (fmtType == Format::Date3) { /*18-Feb */
608 tmp = QString().sprintf("%02d", date.day()) +
609 '-' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber);
610 } else if (fmtType == Format::Date4) { /*18-05 */
611 tmp = QString().sprintf("%02d", date.day()) +
612 '-' + QString().sprintf("%02d", date.month());
613 } else if (fmtType == Format::Date5) { /*18/05/00 */
614 tmp = QString().sprintf("%02d", date.day()) +
615 '/' + QString().sprintf("%02d", date.month()) +
616 '/' + QString::number(date.year()).right(2);
617 } else if (fmtType == Format::Date6) { /*18/05/1999 */
618 tmp = QString().sprintf("%02d", date.day()) +
619 '/' + QString().sprintf("%02d", date.month()) +
620 '/' + QString::number(date.year());
621 } else if (fmtType == Format::Date7) { /*Feb-99 */
622 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
623 '-' + QString::number(date.year()).right(2);
624 } else if (fmtType == Format::Date8) { /*February-99 */
625 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber) +
626 '-' + QString::number(date.year()).right(2);
627 } else if (fmtType == Format::Date9) { /*February-1999 */
628 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber) +
629 '-' + QString::number(date.year());
630 } else if (fmtType == Format::Date10) { /*F-99 */
631 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber).at(0) +
632 '-' + QString::number(date.year()).right(2);
633 } else if (fmtType == Format::Date11) { /*18/Feb */
634 tmp = QString().sprintf("%02d", date.day())
635 + '/' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber);
636 } else if (fmtType == Format::Date12) { /*18/02 */
637 tmp = QString().sprintf("%02d", date.day()) +
638 '/' + QString().sprintf("%02d", date.month());
639 } else if (fmtType == Format::Date13) { /*18/Feb/1999 */
640 tmp = QString().sprintf("%02d", date.day()) +
641 '/' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
642 '/' + QString::number(date.year());
643 } else if (fmtType == Format::Date14) { /*2000/Feb/18 */
644 tmp = QString::number(date.year()) +
645 '/' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
646 '/' + QString().sprintf("%02d", date.day());
647 } else if (fmtType == Format::Date15) { /*2000-Feb-18 */
648 tmp = QString::number(date.year()) +
649 '-' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
650 '-' + QString().sprintf("%02d", date.day());
651 } else if (fmtType == Format::Date16) { /*2000-02-18 */
652 tmp = QString::number(date.year()) +
653 '-' + QString().sprintf("%02d", date.month()) +
654 '-' + QString().sprintf("%02d", date.day());
655 } else if (fmtType == Format::Date17) { /*2 february 2000 */
656 tmp = QString().sprintf("%d", date.day()) +
657 ' ' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber) +
658 ' ' + QString::number(date.year());
659 } else if (fmtType == Format::Date18) { /*02/18/1999 */
660 tmp = QString().sprintf("%02d", date.month()) +
661 '/' + QString().sprintf("%02d", date.day()) +
662 '/' + QString::number(date.year());
663 } else if (fmtType == Format::Date19) { /*02/18/99 */
664 tmp = QString().sprintf("%02d", date.month()) +
665 '/' + QString().sprintf("%02d", date.day()) +
666 '/' + QString::number(date.year()).right(2);
667 } else if (fmtType == Format::Date20) { /*Feb/18/99 */
668 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
669 '/' + QString().sprintf("%02d", date.day()) +
670 '/' + QString::number(date.year()).right(2);
671 } else if (fmtType == Format::Date21) { /*Feb/18/1999 */
672 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
673 '/' + QString().sprintf("%02d", date.day()) +
674 '/' + QString::number(date.year());
675 } else if (fmtType == Format::Date22) { /*Feb-1999 */
676 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
677 '-' + QString::number(date.year());
678 } else if (fmtType == Format::Date23) { /*1999 */
679 tmp = QString::number(date.year());
680 } else if (fmtType == Format::Date24) { /*99 */
681 tmp = QString::number(date.year()).right(2);
682 } else if (fmtType == Format::Date25) { /*2000/02/18 */
683 tmp = QString::number(date.year()) +
684 '/' + QString().sprintf("%02d", date.month()) +
685 '/' + QString().sprintf("%02d", date.day());
686 } else if (fmtType == Format::Date26) { /*2000/Feb/18 */
687 tmp = QString::number(date.year()) +
688 '/' + m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
689 '/' + QString().sprintf("%02d", date.day());
690 } else if (fmtType == Format::Date27) { /*Feb/99 */
691 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
692 '/' + QString::number(date.year()).right(2);
693 } else if (fmtType == Format::Date28) { /*Feb/1999 */
694 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::ShortNumber) +
695 '/' + QString::number(date.year());
696 } else if (fmtType == Format::Date29) { /*February/99 */
697 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber) +
698 '/' + QString::number(date.year()).right(2);
699 } else if (fmtType == Format::Date30) { /*February/1999 */
700 tmp = m_converter->settings()->locale()->calendar()->formatDate(date, KLocale::Month, KLocale::LongNumber) +
701 '/' + QString::number(date.year());
702 } else if (fmtType == Format::Date31) { /*18-02 */
703 tmp = QString().sprintf("%02d", date.day()) +
704 '-' + QString().sprintf("%02d", date.month());
705 } else if (fmtType == Format::Date32) { /*02/99 */
706 tmp = QString().sprintf("%02d", date.month()) + '/' +
707 QString::number(date.year()).right(2);
708 } else if (fmtType == Format::Date33) { /*02-99 */
709 tmp = QString().sprintf("%02d", date.month()) +
710 '-' + QString::number(date.year()).right(2);
711 } else if (fmtType == Format::Date34 || fmtType == Format::Date35) { /*Mon, 2 Feb 2000 and Mon, 2 February 2000 */
712 QLocale l(QLocale::English);
713 tmp = l.toString(date, fmtType == Format::Date34 ? "ddd d MMM yy" : "dddd d MMM yyyy");
714 } else { /*fallback... */
715 tmp = m_converter->settings()->locale()->formatDate(date, KLocale::ShortDate);
716 }
717
718 // Missing compared with gnumeric:
719 // "m/d/yy h:mm", /* 20 */
720 // "m/d/yyyy h:mm", /* 21 */
721 // "mmm/ddd/yy", /* 12 */
722 // "mmm/ddd/yyyy", /* 13 */
723 // "mm/ddd/yy", /* 14 */
724 // "mm/ddd/yyyy", /* 15 */
725
726 return tmp;
727}
728
729QString ValueFormatter::complexFormat(const Value& value, int precision,
730 Format::Type formatType,
731 Style::FloatFormat floatFormat,
732 const QString& currencySymbol,
733 bool thousandsSep)
734{
735 // FIXME Stefan: percentage, currency and scientific formats!
736 QString str;
737 const Number real = value.asComplex().real();
738 const Number imag = value.asComplex().imag();
739 str = createNumberFormat(real, precision, formatType, floatFormat, QString(), QString(), thousandsSep);
740 str += createNumberFormat(imag, precision, formatType, Style::AlwaysSigned, currencySymbol, QString(), thousandsSep);
741 str += 'i';
742 return str;
743}
744