1 /* This file is part of the KDE project
2    Copyright (C) 2004-2006 David Faure <faure@kde.org>
3    Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
4    Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
5 
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License version 2 as published by the Free Software Foundation.
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 #include "KoOdfNumberStyles.h"
22 
23 #include "KoGenStyles.h"
24 #include "KoXmlNS.h"
25 
26 #include <QBuffer>
27 #include <QDateTime>
28 #include <QTime>
29 
30 #include <klocalizedstring.h>
31 #include <OdfDebug.h>
32 
33 #include <KoXmlReader.h>
34 #include <KoXmlWriter.h>
35 #include <writeodf/writeodfnumber.h>
36 
37 #include <math.h>
38 
39 using namespace writeodf;
40 
41 namespace KoOdfNumberStyles
42 {
43 
44     static bool saveOdfTimeFormat(KoXmlWriter &elementWriter, QString &format, QString &text, bool &antislash);
45     static void parseOdfDatelocale(KoXmlWriter &elementWriter, QString &format, QString &text);
46     static bool saveOdflocaleTimeFormat(KoXmlWriter &elementWriter, QString &format, QString &text);
47     static void parseOdfTimelocale(KoXmlWriter &elementWriter, QString &format, QString &text);
48     static void addCalligraNumericStyleExtension(KoXmlWriter &elementWriter, const QString &_suffix, const QString &_prefix);
49 
50 
format(const QString & value,const NumericStyleFormat & format)51 QString format(const QString &value, const NumericStyleFormat &format)
52 {
53     switch (format.type) {
54         case Number: {
55             bool ok;
56             qreal v = value.toDouble(&ok);
57             return ok ? formatNumber(v, format.formatStr, format.precision) : value;
58         } break;
59         case Boolean: {
60             return formatBoolean(value, format.formatStr);
61         } break;
62         case Date: {
63             bool ok;
64             int v = value.toInt(&ok);
65             return ok ? formatDate(v, format.formatStr) : value;
66         } break;
67         case Time: {
68             bool ok;
69             qreal v = value.toDouble(&ok);
70             return ok ? formatTime(v, format.formatStr) : value;
71         } break;
72         case Percentage: {
73             return formatPercent(value, format.formatStr, format.precision);
74         } break;
75         case Currency: {
76             bool ok;
77             qreal v = value.toDouble(&ok);
78             return ok ? formatCurrency(v, format.formatStr, format.currencySymbol, format.precision) : value;
79         } break;
80         case Scientific: {
81             bool ok;
82             qreal v = value.toDouble(&ok);
83             return ok ? formatScientific(v, format.formatStr, format.precision) : value;
84         } break;
85         case Fraction: {
86             bool ok;
87             qreal v = value.toDouble(&ok);
88             return ok ? formatFraction(v, format.formatStr) : value;
89         } break;
90         case Text: {
91             return value;
92         } break;
93     }
94     return value;
95 }
96 
formatNumber(qreal value,const QString & format,int precision)97 QString formatNumber(qreal value, const QString &format, int precision)
98 {
99     QString result;
100     int start = 0;
101     bool showNegative = format.startsWith('-');
102     if (showNegative)
103         start = 1;
104     for (int i = start; i < format.length(); ++i) {
105         QChar c = format[ i ];
106         switch (c.unicode()) {
107             case '.':
108             case ',':
109             case '#':
110             case '0':
111             case '?': {
112 //                bool grouping = false;
113                 bool gotDot = false;
114                 bool gotE = false;
115                 bool gotFraction = false;
116                 int decimalPlaces = 0;
117                 int integerDigits = 0;
118                 int optionalDecimalPlaces = 0;
119                 int optionalIntegerDigits = 0;
120                 int exponentDigits = 0;
121                 int numeratorDigits = 0;
122                 int denominatorDigits = 0;
123                 char ch = format[ i ].toLatin1();
124                 do {
125                     if (ch == '.') {
126                         gotDot = true;
127                     } else if (ch == ',') {
128                 //        grouping = true;
129                     } else if (ch == 'E' || ch == 'e') {
130                         //SET_TYPE_OR_RETURN(KoGenStyle::NumericScientificStyle);
131 
132                         if (i >= format.length() - 1) break;
133                         const char chN = format[ i + 1 ].toLatin1();
134                         if (chN == '-' || chN == '+') {
135                             gotE = true;
136                             ++i;
137                         }
138                     } else if (ch == '0' && gotE) {
139                         ++exponentDigits;
140                     } else if (ch == '0' && !gotDot && !gotFraction) {
141                         ++integerDigits;
142                     } else if (ch == '#' && !gotDot && !gotFraction) {
143                         ++optionalIntegerDigits;
144                     } else if (ch == '0' && gotDot && !gotFraction) {
145                         ++decimalPlaces;
146                     } else if (ch == '#' && gotDot && !gotFraction) {
147                         ++optionalDecimalPlaces;
148                     } else if (ch == '?' && !gotFraction) {
149                         ++numeratorDigits;
150                     } else if (ch == '?' && gotFraction) {
151                         ++denominatorDigits;
152                     } else if (ch == '/') {
153                         //SET_TYPE_OR_RETURN(KoGenStyle::NumericFractionStyle);
154                         if (gotDot) return QString(); // invalid
155                         gotFraction = true;
156                     }
157 
158                     if (i >= format.length() - 1) break;
159                     ch = format[ ++i ].toLatin1();
160 
161                     if (ch == ' ') {
162                         // spaces are not allowed - but there's an exception: if this is a fraction. Let's check for '?' or '/'
163                         const char c = format[ i + 1 ].toLatin1();
164                         if (c == '?' || c == '/')
165                             ch = format[ ++i ].toLatin1();
166                     }
167                 } while (i < format.length() && (ch == '.' || ch == ',' || ch == '#' || ch == '0' || ch == 'E' || ch == 'e' || ch == '?' || ch == '/'));
168                 if (!(ch == '.' || ch == ',' || ch == '#' || ch == '0' || ch == 'E' || ch == 'e' || ch == '?' || ch == '/')) {
169                     --i;
170                 }
171 
172                 QString v(QString::number(qAbs(value), 'f', precision >= 0 ? precision : (optionalDecimalPlaces + decimalPlaces)));
173                 int p = v.indexOf('.');
174                 QString integerValue = p >= 0 ? v.left(p) : v;
175                 if (integerValue.length() < integerDigits)
176                     integerValue.prepend(QString().fill('0', integerDigits - integerValue.length()));
177                 QString decimalValue =  p >= 0 ? v.mid(p + 1) : QString();
178                 if (decimalValue.length() < decimalPlaces)
179                     decimalValue.append(QString().fill('0', decimalPlaces - decimalValue.length()));
180 
181                 if (showNegative && value < 0)
182                     result.append('-');
183                 result.append(integerValue);
184                 if (!decimalValue.isEmpty())
185                     result.append('.' + decimalValue);
186             } break;
187             case '\\': { // backslash escapes the next char
188                 if (i < format.length() - 1) {
189                     result.append(format[ ++i ]);
190                 }
191             } break;
192             default:
193                 result.append(c);
194                 break;
195         }
196     }
197 
198     return result;
199 }
200 
formatBoolean(const QString & value,const QString & format)201 QString formatBoolean(const QString &value, const QString &format)
202 {
203     Q_UNUSED(format);
204     bool ok = false;
205     int v = value.toInt(&ok);
206     return ok && v != 0 ? "TRUE" : "FALSE";
207 }
208 
formatDate(int value,const QString & format)209 QString formatDate(int value, const QString &format)
210 {
211     QDateTime dt(QDate(1899, 12, 30)); // reference date
212     dt = dt.addDays(value);
213     return dt.toString(format);
214 }
215 
formatTime(qreal value,const QString & format)216 QString formatTime(qreal value, const QString &format)
217 {
218     QTime t(0,0,0);
219     t = t.addSecs(qRound(value * 86400.0)); // 24 hours
220     return t.toString(format);
221 }
222 
formatCurrency(qreal value,const QString & format,const QString & currencySymbol,int precision)223 QString formatCurrency(qreal value, const QString &format, const QString& currencySymbol, int precision)
224 {
225     if (currencySymbol == "CCC") // undocumented hack, see doc attached to comment 6 at bug 282972
226         return QLocale().toCurrencyString(value, "USD");
227     if (format.isEmpty()) // no format means use locale format
228         return QLocale().toCurrencyString(value, currencySymbol.isEmpty() ? QLocale().currencySymbol(QLocale::CurrencySymbol)
229                                                                           : currencySymbol);
230     return formatNumber(value, format, precision);
231 }
232 
formatScientific(qreal value,const QString & format,int precision)233 QString formatScientific(qreal value, const QString &format, int precision)
234 {
235     Q_UNUSED(format);
236     QString v(QString::number(value, 'E', precision));
237     int pos = v.indexOf('.');
238     if (pos != -1) {
239         v.replace(pos, 1, QLocale().decimalPoint());
240     }
241     return v;
242 }
243 
formatFraction(qreal value,const QString & format)244 QString formatFraction(qreal value, const QString &format)
245 {
246     QString prefix = value < 0 ? "-" : "";
247     value = fabs(value);
248     qreal result = value - floor(value);
249 
250     if (result == 0) // return w/o fraction part if not necessary
251         return prefix + QString::number(value);
252 
253     int index = 0;
254     int limit = 0;
255     if (format.endsWith(QLatin1String("/2"))) {
256         index = 2;
257     } else if (format.endsWith(QLatin1String("/4"))) {
258         index = 4;
259     } else if (format.endsWith(QLatin1String("/8"))) {
260         index = 8;
261     } else if (format.endsWith(QLatin1String("/16"))) {
262         index = 16;
263     } else if (format.endsWith(QLatin1String("/10"))) {
264         index = 10;
265     } else if (format.endsWith(QLatin1String("/100"))) {
266         index = 100;
267     } else if (format.endsWith(QLatin1String("/?"))) {
268         index = 3;
269         limit = 9;
270     } else if (format.endsWith(QLatin1String("/??"))) {
271         index = 4;
272         limit = 99;
273     } else if (format.endsWith(QLatin1String("/???"))) {
274         index = 5;
275         limit = 999;
276     } else { // fallback
277         return prefix + QString::number(value);
278     }
279 
280     // handle halves, quarters, tenths, ...
281     if (!format.endsWith(QLatin1String("/?")) &&
282         !format.endsWith(QLatin1String("/??")) &&
283         !format.endsWith(QLatin1String("/???"))) {
284         qreal calc = 0;
285         int index1 = 0;
286         qreal diff = result;
287         for (int i = 1; i <= index; i++) {
288             calc = i * 1.0 / index;
289             if (fabs(result - calc) < diff) {
290                 index1 = i;
291                 diff = fabs(result - calc);
292             }
293         }
294         if (index1 == 0)
295             return prefix + QString("%1").arg(floor(value));
296         if (index1 == index)
297             return prefix + QString("%1").arg(floor(value) + 1);
298         if (floor(value) == 0)
299             return prefix + QString("%1/%2").arg(index1).arg(index);
300         return prefix + QString("%1 %2/%3").arg(floor(value)).arg(index1).arg(index);
301     }
302 
303     // handle Format::fraction_one_digit, Format::fraction_two_digit and Format::fraction_three_digit style
304     qreal target = result;
305     qreal numerator = 1;
306     qreal denominator = 1;
307     qreal bestNumerator = 0;
308     qreal bestDenominator = 1;
309     qreal bestDist = target;
310 
311     // as soon as either numerator or denominator gets above the limit, we're done
312     while (numerator <= limit && denominator <= limit) {
313         qreal dist = fabs((numerator / denominator) - target);
314         if (dist < bestDist) {
315             bestDist = dist;
316             bestNumerator = numerator;
317             bestDenominator = denominator;
318         }
319         if (numerator / denominator > target) {
320             ++denominator;
321         } else {
322             ++numerator;
323         }
324     }
325 
326     if (bestNumerator == 0)
327         return prefix + QString().setNum(floor(value));
328     if (bestDenominator == bestNumerator)
329         return prefix + QString().setNum(floor(value + 1));
330     if (floor(value) == 0)
331         return prefix + QString("%1/%2").arg(bestNumerator).arg(bestDenominator);
332     return prefix + QString("%1 %2/%3").arg(floor(value)).arg(bestNumerator).arg(bestDenominator);
333 
334 }
335 
formatPercent(const QString & value,const QString &,int precision)336 QString formatPercent(const QString &value, const QString &/*format*/, int precision)
337 {
338     if (value.contains('.')) {
339         bool ok;
340         qreal v = value.toDouble(&ok);
341         if (ok)
342             return QString::number(v * 100., 'f', precision) + QLatin1String("%");
343     }
344     return value;
345 }
346 
347 // OO spec 2.5.4. p68. Conversion to Qt format: see qdate.html
348 // OpenCalcImport::loadFormat has similar code, but slower, intermixed with other stuff,
349 // lacking long-textual forms.
loadOdfNumberStyle(const KoXmlElement & parent)350 QPair<QString, NumericStyleFormat> loadOdfNumberStyle(const KoXmlElement &parent)
351 {
352     NumericStyleFormat dataStyle;
353 
354     const QString localName = parent.localName();
355     if (localName == "number-style")
356         dataStyle.type = Number;
357     else if (localName == "currency-style")
358         dataStyle.type = Currency;
359     else if (localName == "percentage-style")
360         dataStyle.type = Percentage;
361     else if (localName == "boolean-style")
362         dataStyle.type = Boolean;
363     else if (localName == "text-style")
364         dataStyle.type = Text;
365     else if (localName == "date-style")
366         dataStyle.type = Date;
367     else if (localName == "time-style")
368         dataStyle.type = Time;
369 
370     QString format;
371     int precision = -1;
372     int leadingZ  = 1;
373 
374     bool thousandsSep = false;
375     //todo negred
376     //bool negRed = false;
377     bool ok = false;
378     int i = 0;
379     KoXmlElement e;
380     QString prefix;
381     QString suffix;
382     forEachElement(e, parent) {
383         if (e.namespaceURI() != KoXmlNS::number)
384             continue;
385         QString localName = e.localName();
386         const QString numberStyle = e.attributeNS(KoXmlNS::number, "style", QString());
387         const bool shortForm = numberStyle == "short" || numberStyle.isEmpty();
388         if (localName == "day") {
389             format += shortForm ? "d" : "dd";
390         } else if (localName == "day-of-week") {
391             format += shortForm ? "ddd" : "dddd";
392         } else if (localName == "month") {
393             if (e.attributeNS(KoXmlNS::number, "possessive-form", QString()) == "true") {
394                 format += shortForm ? "PPP" : "PPPP";
395             }
396             // TODO the spec has a strange mention of number:format-source
397             else if (e.attributeNS(KoXmlNS::number, "textual", QString()) == "true") {
398                 bool isExtraShort = false;      // find out if we have to use the extra-short month name (just 1st letter)
399                 if (e.attributeNS(KoXmlNS::calligra, "number-length", QString()) == "extra-short") {
400                     isExtraShort = true;
401                 }
402 
403                 if (!isExtraShort) {            // for normal month format (first 3 letters or complete name)
404                     format += shortForm ? "MMM" : "MMMM";
405                 } else {                        // for the extra-short month name use 'X' as a special mark
406                     format += "X";
407                 }
408             } else { // month number
409                 format += shortForm ? "M" : "MM";
410             }
411         } else if (localName == "year") {
412             format += shortForm ? "yy" : "yyyy";
413         } else if (localName == "era") {
414             //TODO I don't know what is it... (define into oo spec)
415         } else if (localName == "week-of-year" || localName == "quarter") {
416             // ### not supported in Qt
417         } else if (localName == "hours") {
418             format += shortForm ? "h" : "hh";
419         } else if (localName == "minutes") {
420             format += shortForm ? "m" : "mm";
421         } else if (localName == "seconds") {
422             format += shortForm ? "s" : "ss";
423         } else if (localName == "am-pm") {
424             format += "ap";
425         } else if (localName == "text") {   // literal
426             format += e.text();
427         } else if (localName == "suffix") {
428             suffix = e.text();
429             debugOdf << " suffix :" << suffix;
430         } else if (localName == "prefix") {
431             prefix = e.text();
432             debugOdf << " prefix :" << prefix;
433         } else if (localName == "currency-symbol") {
434             dataStyle.currencySymbol = e.text();
435             debugOdf << " currency-symbol:" << dataStyle.currencySymbol;
436             format += e.text();
437             //TODO
438             // number:language="de" number:country="DE">€</number:currency-symbol>
439             // Stefan: localization of the symbol?
440         } else if (localName == "number") {
441             if (e.hasAttributeNS(KoXmlNS::number, "grouping")) {
442                 thousandsSep = e.attributeNS(KoXmlNS::number, "grouping", QString()).toLower() == "true";
443             }
444             if (e.hasAttributeNS(KoXmlNS::number, "decimal-places")) {
445                 int d = e.attributeNS(KoXmlNS::number, "decimal-places", QString()).toInt(&ok);
446                 if (ok)
447                     precision = d;
448             }
449             if (e.hasAttributeNS(KoXmlNS::number, "min-integer-digits")) {
450                 int d = e.attributeNS(KoXmlNS::number, "min-integer-digits", QString()).toInt(&ok);
451                 if (ok)
452                     leadingZ = d;
453             }
454             if (thousandsSep && leadingZ <= 3) {
455                 format += "#,";
456                 for (i = leadingZ; i <= 3; ++i)
457                     format += '#';
458             }
459             for (i = 1; i <= leadingZ; ++i) {
460                 format +=  '0';
461                 if ((i % 3 == 0) && thousandsSep)
462                     format = + ',' ;
463             }
464             if (precision > -1) {
465                 format += '.';
466                 for (i = 0; i < precision; ++i)
467                     format += '0';
468             }
469         } else if (localName == "scientific-number") {
470             if (dataStyle.type == Number)
471                 dataStyle.type = Scientific;
472             int exp = 2;
473 
474             if (e.hasAttributeNS(KoXmlNS::number, "decimal-places")) {
475                 int d = e.attributeNS(KoXmlNS::number, "decimal-places", QString()).toInt(&ok);
476                 if (ok)
477                     precision = d;
478             }
479 
480             if (e.hasAttributeNS(KoXmlNS::number, "min-integer-digits")) {
481                 int d = e.attributeNS(KoXmlNS::number, "min-integer-digits", QString()).toInt(&ok);
482                 if (ok)
483                     leadingZ = d;
484             }
485 
486             if (e.hasAttributeNS(KoXmlNS::number, "min-exponent-digits")) {
487                 int d = e.attributeNS(KoXmlNS::number, "min-exponent-digits", QString()).toInt(&ok);
488                 if (ok)
489                     exp = d;
490                 if (exp <= 0)
491                     exp = 1;
492             }
493 
494             if (e.hasAttributeNS(KoXmlNS::number, "grouping")) {
495                 thousandsSep = e.attributeNS(KoXmlNS::number, "grouping", QString()).toLower() == "true";
496             }
497 
498             if (thousandsSep && leadingZ <= 3) {
499                 format += "#,";
500                 for (i = leadingZ; i <= 3; ++i)
501                     format += '#';
502             }
503 
504             for (i = 1; i <= leadingZ; ++i) {
505                 format += '0';
506                 if ((i % 3 == 0) && thousandsSep)
507                     format += ',';
508             }
509 
510             if (precision > -1) {
511                 format += '.';
512                 for (i = 0; i < precision; ++i)
513                     format += '0';
514             }
515 
516             format += "E+";
517             for (i = 0; i < exp; ++i)
518                 format += '0';
519         } else if (localName == "fraction") {
520             if (dataStyle.type == Number)
521                 dataStyle.type = Fraction;
522             int integer = 0;
523             int numerator = 1;
524             int denominator = 1;
525             int denominatorValue = 0;
526             if (e.hasAttributeNS(KoXmlNS::number, "min-integer-digits")) {
527                 int d = e.attributeNS(KoXmlNS::number, "min-integer-digits", QString()).toInt(&ok);
528                 if (ok)
529                     integer = d;
530             }
531             if (e.hasAttributeNS(KoXmlNS::number, "min-numerator-digits")) {
532                 int d = e.attributeNS(KoXmlNS::number, "min-numerator-digits", QString()).toInt(&ok);
533                 if (ok)
534                     numerator = d;
535             }
536             if (e.hasAttributeNS(KoXmlNS::number, "min-denominator-digits")) {
537                 int d = e.attributeNS(KoXmlNS::number, "min-denominator-digits", QString()).toInt(&ok);
538                 if (ok)
539                     denominator = d;
540             }
541             if (e.hasAttributeNS(KoXmlNS::number, "denominator-value")) {
542                 int d = e.attributeNS(KoXmlNS::number, "denominator-value", QString()).toInt(&ok);
543                 if (ok)
544                     denominatorValue = d;
545             }
546             if (e.hasAttributeNS(KoXmlNS::number, "grouping")) {
547                 thousandsSep = e.attributeNS(KoXmlNS::number, "grouping", QString()).toLower() == "true";
548             }
549 
550             for (i = 0; i < integer; ++i)
551                 format += '#';
552 
553             format += ' ';
554 
555             for (i = 0; i < numerator; ++i)
556                 format += '?';
557 
558             format += '/';
559 
560             if (denominatorValue != 0)
561                 format += QString::number(denominatorValue);
562             else {
563                 for (i = 0; i < denominator; ++i)
564                     format += '?';
565             }
566         }
567 
568         // stylesmap's are embedded into a style and are pointing to another style that
569         // should be used instead of this style if the defined condition is true. E.g.;
570         // <number:number-style style:name="N139P0" style:volatile="true"/>
571         // <number:number-style style:name="N139P1" style:volatile="true"/>
572         // <number:number-style style:name="N139P2" style:volatile="true"/>
573         // <number:text-style style:name="N139">
574         //   <style:map style:condition="value()&gt;0" style:apply-style-name="N139P0"/>
575         //   <style:map style:condition="value()&lt;0" style:apply-style-name="N139P1"/>
576         //   <style:map style:condition="value()=0" style:apply-style-name="N139P2"/>
577         // </number:text-style>
578         for (KoXmlNode node(e); !node.isNull(); node = node.nextSibling()) {
579             KoXmlElement elem = node.toElement();
580             if (elem.namespaceURI() == KoXmlNS::style && elem.localName() == "map") {
581                 QString condition, applyStyleName;
582                 if (elem.hasAttributeNS(KoXmlNS::style, "condition"))
583                     condition = elem.attributeNS(KoXmlNS::style, "condition");
584                 if (elem.hasAttributeNS(KoXmlNS::style, "apply-style-name"))
585                     applyStyleName = elem.attributeNS(KoXmlNS::style, "apply-style-name");
586                 dataStyle.styleMaps.append( QPair<QString,QString>(condition,applyStyleName) );
587             }
588         }
589     }
590 
591     const QString styleName = parent.attributeNS(KoXmlNS::style, "name", QString());
592 
593 debugOdf<<"99 *****************************************************************************";
594 //Q_ASSERT(false);
595     debugOdf << "data style:" << styleName << " qt format=" << format;
596     if (!prefix.isEmpty()) {
597         debugOdf << " format.left( prefix.length() ) :" << format.left(prefix.length()) << " prefix :" << prefix;
598         if (format.left(prefix.length()) == prefix) {
599             format = format.right(format.length() - prefix.length());
600         } else
601             prefix.clear();
602     }
603     if (!suffix.isEmpty()) {
604         debugOdf << "format.right( suffix.length() ) :" << format.right(suffix.length()) << " suffix :" << suffix;
605         if (format.right(suffix.length()) == suffix) {
606             format = format.left(format.length() - suffix.length());
607         } else
608             suffix.clear();
609     }
610 
611     dataStyle.formatStr = format;
612     dataStyle.prefix = prefix;
613     dataStyle.suffix = suffix;
614     dataStyle.precision = precision;
615     dataStyle.thousandsSep = thousandsSep;
616     debugOdf << " finish insert format :" << format << " prefix :" << prefix << " suffix :" << suffix;
617     return QPair<QString, NumericStyleFormat>(styleName, dataStyle);
618 }
619 
saveOdfNumberStyle(KoGenStyles & mainStyles,const NumericStyleFormat & format)620 QString saveOdfNumberStyle(KoGenStyles &mainStyles, const NumericStyleFormat &format)
621 {
622     QString styleName;
623     switch (format.type) {
624         case KoOdfNumberStyles::Number: {
625             styleName = KoOdfNumberStyles::saveOdfNumberStyle(mainStyles, format.formatStr, format.prefix, format.suffix, format.thousandsSep);
626         } break;
627         case KoOdfNumberStyles::Boolean: {
628             styleName = KoOdfNumberStyles::saveOdfBooleanStyle(mainStyles, format.formatStr, format.prefix, format.suffix);
629         } break;
630         case KoOdfNumberStyles::Date: {
631             bool localeFormat = format.formatStr.isEmpty();
632             styleName = KoOdfNumberStyles::saveOdfDateStyle(mainStyles, format.formatStr, localeFormat, format.prefix, format.suffix);
633         } break;
634         case KoOdfNumberStyles::Time: {
635             bool localeFormat = format.formatStr.isEmpty();
636             styleName = KoOdfNumberStyles::saveOdfTimeStyle(mainStyles, format.formatStr, localeFormat, format.prefix, format.suffix);
637         } break;
638         case KoOdfNumberStyles::Percentage: {
639             styleName = KoOdfNumberStyles::saveOdfPercentageStyle(mainStyles, format.formatStr, format.prefix, format.suffix);
640         } break;
641         case KoOdfNumberStyles::Currency: {
642             styleName = KoOdfNumberStyles::saveOdfCurrencyStyle(mainStyles, format.formatStr, format.currencySymbol, format.prefix, format.suffix);
643         } break;
644         case KoOdfNumberStyles::Scientific: {
645             styleName = KoOdfNumberStyles::saveOdfScientificStyle(mainStyles, format.formatStr, format.prefix, format.suffix);
646         } break;
647         case KoOdfNumberStyles::Fraction: {
648             styleName = KoOdfNumberStyles::saveOdfFractionStyle(mainStyles, format.formatStr, format.prefix, format.suffix);
649         } break;
650         case KoOdfNumberStyles::Text: {
651             styleName = KoOdfNumberStyles::saveOdfTextStyle(mainStyles, format.formatStr, format.prefix, format.suffix);
652         } break;
653     }
654     return styleName;
655 }
656 
addTextNumber(QString & text,KoXmlWriter & elementWriter)657 void addTextNumber(QString& text, KoXmlWriter &elementWriter)
658 {
659     if (!text.isEmpty()) {
660         number_text(&elementWriter).addTextNode(text);
661         text.clear();
662     }
663 }
664 
parseOdfTimelocale(KoXmlWriter & elementWriter,QString & format,QString & text)665 void parseOdfTimelocale(KoXmlWriter &elementWriter, QString &format, QString &text)
666 {
667     debugOdf << "parseOdfTimelocale(KoXmlWriter &elementWriter, QString & format, QString & text ) :" << format;
668     do {
669         if (!saveOdflocaleTimeFormat(elementWriter, format, text)) {
670             text += format[0];
671             format.remove(0, 1);
672         }
673     } while (format.length() > 0);
674     addTextNumber(text, elementWriter);
675 }
676 
saveOdflocaleTimeFormat(KoXmlWriter & elementWriter,QString & format,QString & text)677 bool saveOdflocaleTimeFormat(KoXmlWriter &elementWriter, QString &format, QString &text)
678 {
679     bool changed = false;
680     if (format.startsWith("%H")) {   //hh
681         //hour in 24h
682         addTextNumber(text, elementWriter);
683 
684         number_hours(&elementWriter).set_number_style("long");
685         format.remove(0, 2);
686         changed = true;
687     } else if (format.startsWith("%k")) { //h
688         addTextNumber(text, elementWriter);
689 
690         number_hours(&elementWriter).set_number_style("short");
691         format.remove(0, 2);
692         changed = true;
693     } else if (format.startsWith("%I")) { // ?????
694         //TODO hour in 12h
695         changed = true;
696     } else if (format.startsWith("%l")) {
697         //TODO hour in 12h with 1 digit
698         changed = true;
699     } else if (format.startsWith("%M")) { // mm
700         addTextNumber(text, elementWriter);
701 
702         number_minutes(&elementWriter).set_number_style("long");
703         format.remove(0, 2);
704         changed = true;
705 
706     } else if (format.startsWith("%S")) {  //ss
707         addTextNumber(text, elementWriter);
708 
709         number_seconds(&elementWriter).set_number_style("long");
710         format.remove(0, 2);
711         changed = true;
712     } else if (format.startsWith("%p")) {
713         //TODO am or pm
714         addTextNumber(text, elementWriter);
715 
716         number_am_pm(&elementWriter).end();
717         format.remove(0, 2);
718         changed = true;
719     }
720     return changed;
721 }
722 
723 
saveOdfTimeFormat(KoXmlWriter & elementWriter,QString & format,QString & text,bool & antislash)724 bool saveOdfTimeFormat(KoXmlWriter &elementWriter, QString &format, QString &text, bool &antislash)
725 {
726     bool changed = false;
727     //we can also add time to date.
728     if (antislash) {
729         text += format[0];
730         format.remove(0, 1);
731         antislash = false;
732         changed = true;
733     } else if (format.startsWith("hh")) {
734         addTextNumber(text, elementWriter);
735 
736         number_hours(&elementWriter).set_number_style("long");
737         format.remove(0, 2);
738         changed = true;
739     } else if (format.startsWith('h')) {
740         addTextNumber(text, elementWriter);
741 
742         number_hours(&elementWriter).set_number_style("short");
743         format.remove(0, 1);
744         changed = true;
745     } else if (format.startsWith("mm")) {
746         addTextNumber(text, elementWriter);
747 
748         number_minutes(&elementWriter).set_number_style("long");
749         format.remove(0, 2);
750         changed = true;
751     } else if (format.startsWith('m')) {
752         addTextNumber(text, elementWriter);
753 
754         number_minutes(&elementWriter).set_number_style("short");
755         format.remove(0, 1);
756         changed = true;
757     } else if (format.startsWith("ss")) {
758         addTextNumber(text, elementWriter);
759 
760         number_seconds(&elementWriter).set_number_style("long");
761         format.remove(0, 2);
762         changed = true;
763     } else if (format.startsWith('s')) {
764         addTextNumber(text, elementWriter);
765 
766         number_seconds(&elementWriter).set_number_style("short");
767         format.remove(0, 1);
768         changed = true;
769     } else if (format.startsWith("ap")) {
770         addTextNumber(text, elementWriter);
771 
772         number_am_pm(&elementWriter).end();
773         format.remove(0, 2);
774         changed = true;
775     }
776     return changed;
777 }
778 
saveOdfTimeStyle(KoGenStyles & mainStyles,const QString & _format,bool localeFormat,const QString & _prefix,const QString & _suffix)779 QString saveOdfTimeStyle(KoGenStyles &mainStyles, const QString &_format, bool localeFormat,
780         const QString &_prefix, const QString &_suffix)
781 {
782     Q_UNUSED(_prefix);
783     Q_UNUSED(_suffix);
784     //debugOdf << "QString KoOdfNumberStyles::saveOdfTimeStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
785     QString format(_format);
786     KoGenStyle currentStyle(KoGenStyle::NumericTimeStyle);
787     QBuffer buffer;
788     buffer.open(QIODevice::WriteOnly);
789     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
790     QString text;
791     if (localeFormat) {
792         parseOdfTimelocale(elementWriter, format, text);
793     } else {
794         bool antislash = false;
795         do {
796             if (!saveOdfTimeFormat(elementWriter, format, text, antislash)) {
797                 QString elem(format[0]);
798                 format.remove(0, 1);
799                 if (elem == "\\") {
800                     antislash = true;
801                 } else {
802                     text += elem;
803                     antislash = false;
804                 }
805             }
806         } while (format.length() > 0);
807         addTextNumber(text, elementWriter);
808     }
809     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
810     currentStyle.addChildElement("number", elementContents);
811     return mainStyles.insert(currentStyle, "N");
812 }
813 
814 //convert locale string to good format
parseOdfDatelocale(KoXmlWriter & elementWriter,QString & format,QString & text)815 void parseOdfDatelocale(KoXmlWriter &elementWriter, QString &format, QString &text)
816 {
817     debugOdf << format;
818     do {
819         if (format.startsWith("%Y")) {
820             addTextNumber(text, elementWriter);
821 
822             number_year(&elementWriter).set_number_style("long");
823             format.remove(0, 2);
824         } else if (format.startsWith("%y")) {
825 
826             addTextNumber(text, elementWriter);
827 
828             number_year(&elementWriter).set_number_style("short");
829             format.remove(0, 2);
830         } else if (format.startsWith("%n")) {
831             addTextNumber(text, elementWriter);
832 
833             number_month month(&elementWriter);
834             month.set_number_style("short");
835             month.set_number_textual("false");
836             format.remove(0, 2);
837         } else if (format.startsWith("%m")) {
838             addTextNumber(text, elementWriter);
839 
840             number_month month(&elementWriter);
841             month.set_number_style("long");
842             month.set_number_textual("false");  //not necessary remove it
843             format.remove(0, 2);
844         } else if (format.startsWith("%e")) {
845             addTextNumber(text, elementWriter);
846 
847             number_day(&elementWriter).set_number_style("short");
848             format.remove(0, 2);
849         } else if (format.startsWith("%d")) {
850             addTextNumber(text, elementWriter);
851 
852             number_day(&elementWriter).set_number_style("long");
853             format.remove(0, 2);
854         } else if (format.startsWith("%b")) {
855             addTextNumber(text, elementWriter);
856 
857             number_month month(&elementWriter);
858             month.set_number_style("short");
859             month.set_number_textual("true");
860             format.remove(0, 2);
861         } else if (format.startsWith("%B")) {
862             addTextNumber(text, elementWriter);
863 
864             number_month month(&elementWriter);
865             month.set_number_style("long");
866             month.set_number_textual("true");
867             elementWriter.endElement();
868             format.remove(0, 2);
869         } else if (format.startsWith("%a")) {
870             addTextNumber(text, elementWriter);
871 
872             number_day_of_week(&elementWriter).set_number_style("short");
873 
874             format.remove(0, 2);
875         } else if (format.startsWith("%A")) {
876             addTextNumber(text, elementWriter);
877 
878             number_day_of_week(&elementWriter).set_number_style("long");
879             format.remove(0, 2);
880         } else {
881             if (!saveOdflocaleTimeFormat(elementWriter, format, text)) {
882                 text += format[0];
883                 format.remove(0, 1);
884             }
885         }
886     } while (format.length() > 0);
887     addTextNumber(text, elementWriter);
888 }
889 
saveOdfDateStyle(KoGenStyles & mainStyles,const QString & _format,bool localeFormat,const QString & _prefix,const QString & _suffix)890 QString saveOdfDateStyle(KoGenStyles &mainStyles, const QString &_format, bool localeFormat,
891         const QString &_prefix, const QString &_suffix)
892 {
893     Q_UNUSED(_prefix);
894     Q_UNUSED(_suffix);
895     //debugOdf << _format;
896     QString format(_format);
897 
898     // Not supported into Qt: "era" "week-of-year" "quarter"
899 
900     KoGenStyle currentStyle(KoGenStyle::NumericDateStyle);
901     QBuffer buffer;
902     buffer.open(QIODevice::WriteOnly);
903     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
904     QString text;
905     if (localeFormat) {
906         parseOdfDatelocale(elementWriter, format, text);
907     } else {
908         bool antislash = false;
909         do {
910             if (antislash) {
911                 text += format[0];
912                 format.remove(0, 1);
913             }
914             //TODO implement loading ! What is it ?
915             else if (format.startsWith("MMMMM")) {        // MMMMM is extra-short month name (only 1st character)
916                 addTextNumber(text, elementWriter);
917 
918                 number_month month(&elementWriter);
919                 month.set_number_textual("true");
920                 month.set_calligra_number_length("extra-short");
921                 format.remove(0, 5);
922             } else if (format.startsWith("MMMM")) {
923                 addTextNumber(text, elementWriter);
924 
925                 number_month month(&elementWriter);
926                 month.set_number_style("long");
927                 month.set_number_textual("true");
928                 format.remove(0, 4);
929             } else if (format.startsWith("MMM")) {
930                 addTextNumber(text, elementWriter);
931 
932                 number_month month(&elementWriter);
933                 month.set_number_style("short");
934                 month.set_number_textual("true");
935                 format.remove(0, 3);
936             } else if (format.startsWith("MM")) {
937                 addTextNumber(text, elementWriter);
938 
939                 number_month month(&elementWriter);
940                 month.set_number_style("long");
941                 month.set_number_textual("false"); //not necessary remove it
942                 format.remove(0, 2);
943             } else if (format.startsWith('M')) {
944                 addTextNumber(text, elementWriter);
945 
946                 number_month month(&elementWriter);
947                 month.set_number_style("short");
948                 month.set_number_textual("false");
949                 format.remove(0, 1);
950             } else if (format.startsWith("PPPP")) {
951                 addTextNumber(text, elementWriter);
952                 //<number:month number:possessive-form="true" number:textual="true" number:style="long"/>
953 
954                 number_month month(&elementWriter);
955                 month.set_number_style("short");
956                 month.set_number_textual("false");
957                 month.set_number_possessive_form("true");
958                 format.remove(0, 4);
959             } else if (format.startsWith("PPP")) {
960                 addTextNumber(text, elementWriter);
961                 //<number:month number:possessive-form="true" number:textual="true" number:style="short"/>
962                 number_month month(&elementWriter);
963                 month.set_number_possessive_form("true");
964                 month.set_number_style("short");
965                 month.set_number_textual("false");
966                 format.remove(0, 3);
967             } else if (format.startsWith("dddd")) {
968                 addTextNumber(text, elementWriter);
969 
970                 number_day_of_week(&elementWriter).set_number_style("long");
971                 format.remove(0, 4);
972             } else if (format.startsWith("ddd")) {
973                 addTextNumber(text, elementWriter);
974 
975                 number_day_of_week(&elementWriter).set_number_style("short");
976                 format.remove(0, 3);
977             } else if (format.startsWith("dd")) {
978                 addTextNumber(text, elementWriter);
979 
980                 number_day(&elementWriter).set_number_style("long");
981                 format.remove(0, 2);
982             } else if (format.startsWith('d')) {
983                 addTextNumber(text, elementWriter);
984 
985                 number_day(&elementWriter).set_number_style("short");
986                 format.remove(0, 1);
987             } else if (format.startsWith("yyyy")) {
988                 addTextNumber(text, elementWriter);
989 
990                 number_year(&elementWriter).set_number_style("long");
991                 format.remove(0, 4);
992             } else if (format.startsWith("yy")) {
993                 addTextNumber(text, elementWriter);
994 
995                 number_year(&elementWriter).set_number_style("short");
996                 format.remove(0, 2);
997             } else {
998                 if (!saveOdfTimeFormat(elementWriter, format, text, antislash)) {
999                     QString elem(format[0]);
1000                     format.remove(0, 1);
1001                     if (elem == "\\") {
1002                         antislash = true;
1003                     } else {
1004                         text += elem;
1005                         antislash = false;
1006                     }
1007                 }
1008             }
1009         } while (format.length() > 0);
1010         addTextNumber(text, elementWriter);
1011     }
1012 
1013     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1014     currentStyle.addChildElement("number", elementContents);
1015     return mainStyles.insert(currentStyle, "N");
1016 }
1017 
1018 
saveOdfFractionStyle(KoGenStyles & mainStyles,const QString & _format,const QString & _prefix,const QString & _suffix)1019 QString saveOdfFractionStyle(KoGenStyles &mainStyles, const QString &_format,
1020         const QString &_prefix, const QString &_suffix)
1021 {
1022     //debugOdf << "QString saveOdfFractionStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1023     QString format(_format);
1024 
1025     KoGenStyle currentStyle(KoGenStyle::NumericFractionStyle);
1026     QBuffer buffer;
1027     buffer.open(QIODevice::WriteOnly);
1028     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1029     QString text;
1030     int integer = 0;
1031     int numerator = 0;
1032     int denominator = 0;
1033     int denominatorValue = 0;
1034     bool beforeSlash = true;
1035     do {
1036         if (format[0] == '#')
1037             integer++;
1038         else if (format[0] == '/')
1039             beforeSlash = false;
1040         else if (format[0] == '?') {
1041             if (beforeSlash)
1042                 numerator++;
1043             else
1044                 denominator++;
1045         } else {
1046             bool ok;
1047             int value = format.toInt(&ok);
1048             if (ok) {
1049                 denominatorValue = value;
1050                 break;
1051             }
1052         }
1053         format.remove(0, 1);
1054     } while (format.length() > 0);
1055 
1056     text = _prefix;
1057     addTextNumber(text, elementWriter);
1058 
1059     number_fraction fraction(&elementWriter);
1060     fraction.set_number_min_integer_digits(integer);
1061     fraction.set_number_min_numerator_digits(numerator);
1062     fraction.set_number_min_denominator_digits(denominator);
1063     if (denominatorValue != 0) {
1064         fraction.set_number_denominator_value(denominatorValue);
1065     }
1066     fraction.end();
1067 
1068     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1069 
1070     text = _suffix;
1071     addTextNumber(text, elementWriter);
1072 
1073     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1074     currentStyle.addChildElement("number", elementContents);
1075     return mainStyles.insert(currentStyle, "N");
1076 }
1077 
1078 
saveOdfNumberStyle(KoGenStyles & mainStyles,const QString & _format,const QString & _prefix,const QString & _suffix,bool thousandsSep)1079 QString saveOdfNumberStyle(KoGenStyles &mainStyles, const QString &_format,
1080         const QString &_prefix, const QString &_suffix, bool thousandsSep)
1081 {
1082     //debugOdf << "QString saveOdfNumberStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1083     QString format(_format);
1084 
1085     KoGenStyle currentStyle(KoGenStyle::NumericNumberStyle);
1086     QBuffer buffer;
1087     buffer.open(QIODevice::WriteOnly);
1088     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1089     QString text;
1090     int decimalplaces = 0;
1091     int integerdigits = 0;
1092     bool beforeSeparator = true;
1093     do {
1094         if (format[0] == '.' || format[0] == ',')
1095             beforeSeparator = false;
1096         else if (format[0] == '0' && beforeSeparator)
1097             integerdigits++;
1098         else if (format[0] == '0' && !beforeSeparator)
1099             decimalplaces++;
1100         else
1101             debugOdf << " error format 0";
1102         format.remove(0, 1);
1103     } while (format.length() > 0);
1104     text = _prefix ;
1105     addTextNumber(text, elementWriter);
1106     number_number number(&elementWriter);
1107     //debugOdf << " decimalplaces :" << decimalplaces << " integerdigits :" << integerdigits;
1108     if (!beforeSeparator) {
1109         number.set_number_decimal_places(decimalplaces);
1110     }
1111     number.set_number_min_integer_digits(integerdigits);
1112     if (thousandsSep) {
1113         number.set_number_grouping(true);
1114     }
1115     number.end();
1116 
1117     text = _suffix ;
1118     addTextNumber(text, elementWriter);
1119     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1120 
1121     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1122     currentStyle.addChildElement("number", elementContents);
1123     return mainStyles.insert(currentStyle, "N");
1124 }
1125 
saveOdfBooleanStyle(KoGenStyles & mainStyles,const QString & format,const QString & prefix,const QString & suffix)1126 QString saveOdfBooleanStyle(KoGenStyles &mainStyles, const QString &format, const QString &prefix, const QString &suffix)
1127 {
1128     Q_UNUSED(format);
1129 
1130     KoGenStyle currentStyle(KoGenStyle::NumericBooleanStyle);
1131 
1132     QBuffer buffer;
1133     buffer.open(QIODevice::WriteOnly);
1134     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1135     QString text = prefix;
1136     addTextNumber(text, elementWriter);
1137     number_boolean(&elementWriter).end();
1138     text = suffix;
1139     addTextNumber(text, elementWriter);
1140 
1141     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1142     currentStyle.addChildElement("number", elementContents);
1143     return mainStyles.insert(currentStyle, "N");
1144 }
1145 
saveOdfPercentageStyle(KoGenStyles & mainStyles,const QString & _format,const QString & _prefix,const QString & _suffix)1146 QString saveOdfPercentageStyle(KoGenStyles &mainStyles, const QString &_format,
1147         const QString &_prefix, const QString &_suffix)
1148 {
1149     //<number:percentage-style style:name="N11">
1150     //<number:number number:decimal-places="2" number:min-integer-digits="1"/>
1151     //<number:text>%</number:text>
1152     //</number:percentage-style>
1153 
1154     //debugOdf << "QString saveOdfPercentageStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1155     QString format(_format);
1156 
1157     KoGenStyle currentStyle(KoGenStyle::NumericPercentageStyle);
1158     QBuffer buffer;
1159     buffer.open(QIODevice::WriteOnly);
1160     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1161     QString text;
1162     int decimalplaces = 0;
1163     int integerdigits = 0;
1164     bool beforeSeparator = true;
1165     do {
1166         if (format[0] == '.' || format[0] == ',')
1167             beforeSeparator = false;
1168         else if (format[0] == '0' && beforeSeparator)
1169             integerdigits++;
1170         else if (format[0] == '0' && !beforeSeparator)
1171             decimalplaces++;
1172         else
1173             debugOdf << " error format 0";
1174         format.remove(0, 1);
1175     } while (format.length() > 0);
1176     text = _prefix ;
1177     addTextNumber(text, elementWriter);
1178     number_number number(&elementWriter);
1179     if (!beforeSeparator) {
1180         number.set_number_decimal_places(decimalplaces);
1181     }
1182     number.set_number_min_integer_digits(integerdigits);
1183     number.end();
1184 
1185     QString percent(QChar('%'));
1186     addTextNumber(percent, elementWriter);
1187 
1188     text = _suffix ;
1189     addTextNumber(text, elementWriter);
1190     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1191 
1192     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1193     currentStyle.addChildElement("number", elementContents);
1194     return mainStyles.insert(currentStyle, "N");
1195 
1196 }
1197 
saveOdfScientificStyle(KoGenStyles & mainStyles,const QString & _format,const QString & _prefix,const QString & _suffix,bool thousandsSep)1198 QString saveOdfScientificStyle(KoGenStyles &mainStyles, const QString &_format,
1199         const QString &_prefix, const QString &_suffix, bool thousandsSep)
1200 {
1201     //<number:number-style style:name="N60">
1202     //<number:scientific-number number:decimal-places="2" number:min-integer-digits="1" number:min-exponent-digits="3"/>
1203     //</number:number-style>
1204 
1205     //example 000,000e+0000
1206     //debugOdf << "QString saveOdfScientificStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1207     QString format(_format);
1208 
1209     KoGenStyle currentStyle(KoGenStyle::NumericScientificStyle);
1210     QBuffer buffer;
1211     buffer.open(QIODevice::WriteOnly);
1212     int decimalplace = 0;
1213     int integerdigits = 0;
1214     int exponentdigits = 0;
1215     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1216     QString text;
1217     bool beforeSeparator = true;
1218     bool exponential = false;
1219     bool positive = true;
1220     do {
1221         if (!exponential) {
1222             if (format[0] == '0' && beforeSeparator)
1223                 integerdigits++;
1224             else if (format[0] == ',' || format[0] == '.')
1225                 beforeSeparator = false;
1226             else if (format[0] == '0' && !beforeSeparator)
1227                 decimalplace++;
1228             else if (format[0].toLower() == 'e') {
1229                 format.remove(0, 1);
1230                 if (format[0] == '+')
1231                     positive = true;
1232                 else if (format[0] == '-')
1233                     positive = false;
1234                 else
1235                     debugOdf << "Error into scientific number";
1236                 exponential = true;
1237             }
1238         } else {
1239             if (format[0] == '0' && positive)
1240                 exponentdigits++;
1241             else if (format[0] == '0' && !positive)
1242                 exponentdigits--;
1243             else
1244                 debugOdf << " error into scientific number exponential value";
1245         }
1246         format.remove(0, 1);
1247     } while (format.length() > 0);
1248     text =  _prefix ;
1249     addTextNumber(text, elementWriter);
1250 
1251     number_scientific_number number(&elementWriter);
1252     //debugOdf << " decimalplace :" << decimalplace << " integerdigits :" << integerdigits << " exponentdigits :" << exponentdigits;
1253     if (!beforeSeparator) {
1254         number.set_number_decimal_places(decimalplace);
1255     }
1256     number.set_number_min_integer_digits(integerdigits);
1257     number.set_number_min_exponent_digits(exponentdigits);
1258     if (thousandsSep) {
1259         number.set_number_grouping(true);
1260     }
1261     number.end();
1262 
1263     text = _suffix;
1264     addTextNumber(text, elementWriter);
1265     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1266 
1267     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1268     currentStyle.addChildElement("number", elementContents);
1269     return mainStyles.insert(currentStyle, "N");
1270 }
1271 
saveOdfCurrencyStyle(KoGenStyles & mainStyles,const QString & _format,const QString & symbol,const QString & _prefix,const QString & _suffix)1272 QString saveOdfCurrencyStyle(KoGenStyles &mainStyles,
1273         const QString &_format, const QString &symbol,
1274         const QString &_prefix, const QString &_suffix)
1275 {
1276 
1277     //<number:currency-style style:name="N107P0" style:volatile="true">
1278     //<number:number number:decimal-places="2" number:min-integer-digits="1" number:grouping="true"/>
1279     //<number:text> </number:text>
1280     //<number:currency-symbol>VEB</number:currency-symbol>
1281     //</number:currency-style>
1282 
1283     //debugOdf << "QString saveOdfCurrencyStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1284     QString format(_format);
1285 
1286     KoGenStyle currentStyle(KoGenStyle::NumericCurrencyStyle);
1287     QBuffer buffer;
1288     buffer.open(QIODevice::WriteOnly);
1289     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1290     QString text;
1291     int decimalplaces = 0;
1292     int integerdigits = 0;
1293     bool beforeSeparator = true;
1294     do {
1295         if (format[0] == '.' || format[0] == ',')
1296             beforeSeparator = false;
1297         else if (format[0] == '0' && beforeSeparator)
1298             integerdigits++;
1299         else if (format[0] == '0' && !beforeSeparator)
1300             decimalplaces++;
1301         else
1302             debugOdf << " error format 0";
1303         format.remove(0, 1);
1304     } while (format.length() > 0);
1305 
1306     text =  _prefix ;
1307     addTextNumber(text, elementWriter);
1308 
1309     number_number number(&elementWriter);
1310     //debugOdf << " decimalplaces :" << decimalplaces << " integerdigits :" << integerdigits;
1311     if (!beforeSeparator) {
1312         number.set_number_decimal_places(decimalplaces);
1313     }
1314     number.set_number_min_integer_digits(integerdigits);
1315     number.end();
1316 
1317     text =  _suffix ;
1318     addTextNumber(text, elementWriter);
1319     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1320 
1321     number_currency_symbol sym(&elementWriter);
1322     //debugOdf << " currency-symbol:" << symbol;
1323     sym.addTextNode(symbol.toUtf8());
1324     sym.end();
1325 
1326     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1327     currentStyle.addChildElement("number", elementContents);
1328     return mainStyles.insert(currentStyle, "N");
1329 }
1330 
saveOdfTextStyle(KoGenStyles & mainStyles,const QString & _format,const QString & _prefix,const QString & _suffix)1331 QString saveOdfTextStyle(KoGenStyles &mainStyles, const QString &_format, const QString &_prefix, const QString &_suffix)
1332 {
1333     Q_UNUSED(_format);
1334 
1335     //<number:text-style style:name="N100">
1336     //<number:text-content/>
1337     ///</number:text-style>
1338 
1339     //debugOdf << "QString saveOdfTextStyle( KoGenStyles &mainStyles, const QString & _format ) :" << _format;
1340 
1341     KoGenStyle currentStyle(KoGenStyle::NumericTextStyle);
1342     QBuffer buffer;
1343     buffer.open(QIODevice::WriteOnly);
1344     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
1345     QString text =  _prefix ;
1346     addTextNumber(text, elementWriter);
1347 
1348     number_text_content(&elementWriter).end();
1349 
1350     text =  _suffix ;
1351     addTextNumber(text, elementWriter);
1352     addCalligraNumericStyleExtension(elementWriter, _suffix, _prefix);
1353 
1354     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1355     currentStyle.addChildElement("number", elementContents);
1356     return mainStyles.insert(currentStyle, "N");
1357 }
1358 
1359 //This is an extension of numeric style. For the moment we used namespace of
1360 //oasis format for specific calligra extension. Change it for the future.
addCalligraNumericStyleExtension(KoXmlWriter & elementWriter,const QString & _suffix,const QString & _prefix)1361 void addCalligraNumericStyleExtension(KoXmlWriter &elementWriter, const QString &_suffix, const QString &_prefix)
1362 {
1363     if (!_suffix.isEmpty()) {
1364         elementWriter.startElement("number:suffix");
1365         elementWriter.addTextNode(_suffix);
1366         elementWriter.endElement();
1367     }
1368     if (!_prefix.isEmpty()) {
1369         elementWriter.startElement("number:prefix");
1370         elementWriter.addTextNode(_prefix);
1371         elementWriter.endElement();
1372     }
1373 }
1374 }
1375