kspread

kspread_numformat.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003 Norbert Andres, nandres@web.de
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20/*
21The only function visible from the outside is formatNumber, whose
22implementation is at the very bottom of this file. Its prototype
23is declared in kspread_util.h. However, it is not used anywhere.
24TODO: Find out whether it is supposed to be used instead of
25something else (locale()->formatNumber() maybe?) and either use it
26or get rid of it.
27Tomas
28*/
29#include <ctype.h>
30#include <math.h>
31
32#include <tqdatetime.h>
33#include <tqmap.h>
34#include <tqstring.h>
35
36#include <kcalendarsystem.h>
37#include <tdelocale.h>
38
39#include "kspread_util.h"
40#include "kspread_value.h"
41
42namespace NumFormat_Local
43{
44 enum { Unknown, TimeDate, Number, Scientific, Fraction } Type;
45
46 TQString g_Monday;
47 TQString g_Tuesday;
48 TQString g_Wednesday;
49 TQString g_Thursday;
50 TQString g_Friday;
51 TQString g_Saturday;
52 TQString g_Sunday;
53 TQString g_Mon;
54 TQString g_Tue;
55 TQString g_Wed;
56 TQString g_Thu;
57 TQString g_Fri;
58 TQString g_Sat;
59 TQString g_Sun;
60 TQString g_January;
61 TQString g_February;
62 TQString g_March;
63 TQString g_April;
64 TQString g_MayL;
65 TQString g_June;
66 TQString g_July;
67 TQString g_August;
68 TQString g_September;
69 TQString g_October;
70 TQString g_November;
71 TQString g_December;
72 TQString g_Jan;
73 TQString g_Feb;
74 TQString g_Mar;
75 TQString g_Apr;
76 TQString g_May;
77 TQString g_Jun;
78 TQString g_Jul;
79 TQString g_Aug;
80 TQString g_Sep;
81 TQString g_Oct;
82 TQString g_Nov;
83 TQString g_Dec;
84
85 struct DateTime
86 {
87 int year;
88 int month;
89 int day;
90 int hour;
91 int minute;
92 int second;
93 };
94
95 struct ConvertionInfo
96 {
97 DateTime * dt;
98
99 int rightOpt;
100 int rightReq;
101 int leftReq;
102 int rightSpace;
103 int leftSpace;
104 int upReq;
105
106 int reqCounter;
107 int reqFirst;
108 int optFirst;
109
110 bool ampm;
111
112 bool thSet;
113 bool showMinus;
114 bool negRed;
115 bool negBr;
116 TQString postfix;
117 TQString prefix;
118 };
119
120 class BaseFormat
121 {
122 public:
123 int type;
124
125 TQString postfix;
126 TQString prefix;
127 };
128
129 class NumberFormat : public BaseFormat
130 {
131 public:
132 bool thSet;
133 bool showMinus;
134 bool negRed;
135 bool negBr;
136 int rightOpt;
137 int rightReq;
138 int leftReq;
139 int rightSpace;
140 int leftSpace;
141 };
142
143 class FractionFormat : public BaseFormat
144 {
145 public:
146 bool thSet;
147 bool showMinus;
148 bool negRed;
149 bool negBr;
150 int optFirst;
151 int reqFirst;
152 int reqCounter;
153 int fraction;
154 int fractionDigists;
155 };
156
157 class DateTimeFormat : public BaseFormat
158 {
159 public:
160 bool ampm;
161 TQString format;
162 };
163
164 class ScientificFormat : public BaseFormat
165 {
166 public:
167 bool thSet;
168 int leftReq;
169 int rightReq;
170 int rightOpt;
171 int upReq;
172 bool showMinus;
173 bool negRed;
174 bool negBr;
175 int rightSpace;
176 int leftSpace;
177 };
178
179 class FormatStore
180 {
181 public:
182
183 int getType( TQString const & format, BaseFormat * f ) const
184 {
185 FormatMap::const_iterator iter = m_formats.find( format );
186 if ( iter == m_formats.end() )
187 {
188 f = 0;
189 return -1;
190 }
191
192 f = iter.data();
193 return f->type;
194 }
195
196 void addFraction( TQString const & format, FractionFormat * f )
197 {
198 m_formats.insert( format, f );
199 }
200
201 void addNumber( TQString const & format, NumberFormat * n )
202 {
203 m_formats.insert( format, n );
204 }
205
206 void addDateTime( TQString const & format, DateTimeFormat * d )
207 {
208 m_formats.insert( format, d );
209 }
210
211 void addScientific( TQString const & format, ScientificFormat * d )
212 {
213 m_formats.insert( format, d );
214 }
215
216 private:
217 class FormatMap : public TQMap<TQString, BaseFormat *> {};
218 FormatMap m_formats;
219 };
220
221 TQChar g_dcSymbol;
222 TQChar g_thSymbol;
223 TQChar g_posSymbol;
224 TQChar g_negSymbol;
225 DateTime g_dateTime;
226 ConvertionInfo g_convertionInfo;
227 bool g_init = false;
228
229 FormatStore g_formatStore;
230}
231
232using namespace NumFormat_Local;
233using namespace KSpread;
234
235void resetGlobals()
236{
237 g_convertionInfo.dt = 0;
238 g_convertionInfo.thSet = false;
239 g_convertionInfo.showMinus = true;
240 g_convertionInfo.negRed = false;
241 g_convertionInfo.negBr = false;
242 g_convertionInfo.reqCounter = 0;
243 g_convertionInfo.reqFirst = 0;
244 g_convertionInfo.prefix = "";
245 g_convertionInfo.postfix = "";
246 g_convertionInfo.rightOpt = 0;
247 g_convertionInfo.rightReq = 0;
248 g_convertionInfo.leftReq = 0;
249 g_convertionInfo.rightSpace = 0;
250 g_convertionInfo.leftSpace = 0;
251 g_convertionInfo.optFirst = 0;
252 g_convertionInfo.upReq = 0;
253 g_convertionInfo.ampm = false;
254}
255
256void initGlobals( TDELocale const * const locale )
257{
258 g_Monday = locale->calendar()->weekDayName( 1, false );
259 g_Tuesday = locale->calendar()->weekDayName( 2, false );
260 g_Wednesday = locale->calendar()->weekDayName( 3, false );
261 g_Thursday = locale->calendar()->weekDayName( 4, false );
262 g_Friday = locale->calendar()->weekDayName( 5, false );
263 g_Saturday = locale->calendar()->weekDayName( 6, false );
264 g_Sunday = locale->calendar()->weekDayName( 7, false );
265 g_Mon = locale->calendar()->weekDayName( 1, true );
266 g_Tue = locale->calendar()->weekDayName( 2, true );
267 g_Wed = locale->calendar()->weekDayName( 3, true );
268 g_Thu = locale->calendar()->weekDayName( 4, true );
269 g_Fri = locale->calendar()->weekDayName( 5, true );
270 g_Sat = locale->calendar()->weekDayName( 6, true );
271 g_Sun = locale->calendar()->weekDayName( 7, true );
272 g_January = locale->calendar()->monthName( 1, 2005, false );
273 g_February = locale->calendar()->monthName( 2, 2005, false );
274 g_March = locale->calendar()->monthName( 3, 2005, false );
275 g_April = locale->calendar()->monthName( 4, 2005, false );
276 g_MayL = locale->calendar()->monthName( 5, 2005, false );
277 g_June = locale->calendar()->monthName( 6, 2005, false );
278 g_July = locale->calendar()->monthName( 7, 2005, false );
279 g_August = locale->calendar()->monthName( 8, 2005, false );
280 g_September = locale->calendar()->monthName( 9, 2005, false );
281 g_October = locale->calendar()->monthName( 10, 2005, false );
282 g_November = locale->calendar()->monthName( 11, 2005, false );
283 g_December = locale->calendar()->monthName( 12, 2005, false );
284 g_Jan = locale->calendar()->monthName( 1, 2005, true );
285 g_Feb = locale->calendar()->monthName( 2, 2005, true );
286 g_Mar = locale->calendar()->monthName( 3, 2005, true );
287 g_Apr = locale->calendar()->monthName( 4, 2005, true );
288 g_May = locale->calendar()->monthName( 5, 2005, true );
289 g_Jun = locale->calendar()->monthName( 6, 2005, true );
290 g_Jul = locale->calendar()->monthName( 7, 2005, true );
291 g_Aug = locale->calendar()->monthName( 8, 2005, true );
292 g_Sep = locale->calendar()->monthName( 9, 2005, true );
293 g_Oct = locale->calendar()->monthName( 10, 2005, true );
294 g_Nov = locale->calendar()->monthName( 11, 2005, true );
295 g_Dec = locale->calendar()->monthName( 12, 2005, true );
296
297 g_dcSymbol = locale->decimalSymbol()[0];
298 g_thSymbol = locale->thousandsSeparator()[0];
299 g_posSymbol = locale->positiveSign()[0];
300 g_negSymbol = locale->negativeSign()[0];
301
302 g_init = true;
303}
304
305void convertDateTime( Value const & value )
306{
307 TQDateTime dt( value.asDateTime() );
308 TQDate d( dt.date() );
309 TQTime t( dt.time() );
310
311 g_dateTime.year = d.year();
312 g_dateTime.month = d.month();
313 g_dateTime.day = d.day();
314 g_dateTime.hour = t.hour();
315 g_dateTime.minute = t.minute();
316 g_dateTime.second = t.second();
317
318 g_convertionInfo.dt = &g_dateTime;
319}
320
321void parseNegativePart( TQString & format, int i,
322 int l, bool acceptDigits )
323{
324 g_convertionInfo.showMinus = false;
325 g_convertionInfo.negRed = false;
326 g_convertionInfo.negRed = false;
327 bool end = false;
328
329 while ( i < l && !end)
330 {
331 TQChar c( format[i] );
332 switch( c )
333 {
334 case '-':
335 g_convertionInfo.showMinus = true;
336 break;
337 case '(':
338 g_convertionInfo.negBr = true;
339 break;
340 case '[':
341 if ( format.find( "[red]", i, false ) == i )
342 {
343 g_convertionInfo.negRed = true;
344 i += 5;
345 }
346 break;
347 default:
348 end = true;
349 }
350 ++i;
351 }
352
353 // find postfix
354 bool quote = false;
355 for ( int j = l - 1; j > i; --j )
356 {
357 if ( format[j] == '"' )
358 {
359 quote = !quote;
360 continue;
361 }
362
363 if ( !quote && ( format[j] == '0' || format[j] != '?'
364 || format[j] != '#'
365 || ( format[j].isDigit() && acceptDigits ) ) )
366 {
367 g_convertionInfo.postfix = format.mid( j + 1 );
368 format.remove( (unsigned int) (j + 1), (unsigned int) (l - j) );
369 break;
370 }
371 }
372
373 int p = g_convertionInfo.postfix.find( '"' );
374 while ( p != -1 )
375 {
376 g_convertionInfo.postfix.remove( p, 1 );
377
378 p = g_convertionInfo.postfix.find( '"', p );
379 }
380}
381
382void createNumberStruct( BaseFormat * data, TQString const & format, bool insert )
383{
384 NumberFormat * d = new NumberFormat();
385 d->type = Number;
386 d->prefix = g_convertionInfo.prefix;
387 d->postfix = g_convertionInfo.postfix;
388 d->thSet = g_convertionInfo.thSet;
389 d->showMinus = g_convertionInfo.showMinus;
390 d->negRed = g_convertionInfo.negRed;
391 d->negBr = g_convertionInfo.negBr;
392 d->rightOpt = g_convertionInfo.rightOpt;
393 d->rightReq = g_convertionInfo.rightReq;
394 d->leftReq = g_convertionInfo.leftReq;
395 d->rightSpace = g_convertionInfo.rightSpace;
396 d->leftSpace = g_convertionInfo.leftSpace;
397
398 if ( insert )
399 g_formatStore.addNumber( format, d );
400 data = d;
401}
402
403void createDateTimeStruct( BaseFormat * data, TQString const & format,
404 TQString const & optFormat, bool insert )
405{
406 DateTimeFormat * d = new DateTimeFormat();
407 d->type = TimeDate;
408 d->prefix = g_convertionInfo.prefix;
409 d->postfix = g_convertionInfo.postfix;
410 d->ampm = g_convertionInfo.ampm;
411 d->format = optFormat;
412
413 if ( insert )
414 g_formatStore.addDateTime( format, d );
415 data = d;
416}
417
418void createScientificStruct( BaseFormat * data, TQString const & format, bool insert )
419{
420 ScientificFormat * d = new ScientificFormat();
421 d->type = Scientific;
422 d->prefix = g_convertionInfo.prefix;
423 d->postfix = g_convertionInfo.postfix;
424 d->thSet = g_convertionInfo.thSet;
425 d->showMinus = g_convertionInfo.showMinus;
426 d->negRed = g_convertionInfo.negRed;
427 d->negBr = g_convertionInfo.negBr;
428 d->rightOpt = g_convertionInfo.rightOpt;
429 d->rightReq = g_convertionInfo.rightReq;
430 d->leftReq = g_convertionInfo.leftReq;
431 d->rightSpace = g_convertionInfo.rightSpace;
432 d->leftSpace = g_convertionInfo.leftSpace;
433 d->upReq = g_convertionInfo.upReq;
434
435 if ( insert )
436 g_formatStore.addScientific( format, d );
437 data = d;
438}
439
440
441int doPreScan( TQString & format, TQString const & formatBack, TDELocale const * const /* locale */,
442 bool insert, BaseFormat * data )
443{
444 int type = g_formatStore.getType( format, data );
445 if ( data != 0 )
446 return type;
447
448 resetGlobals();
449
450 int l = format.length();
451 int i = 0;
452 int thFound = false;
453 int leftReq = 0;
454 int leftOpt = 0;
455 int rightOpt = 0;
456 int spaceInNum = -1;
457 bool dcSeen = false;
458 bool endFixed = false;
459
460 FractionFormat * df = 0;
461 int f = 0;
462 int d = 0;
463 int len = 0;
464 int n = 0;
465 bool ok = false;
466 TQString frac;
467
468 while ( i < l )
469 {
470 TQString s;
471 if ( endFixed )
472 {
473 g_convertionInfo.postfix += format.mid( i );
474 format.remove( i, l - i );
475 break;
476 }
477 TQChar ch( format[i] );
478 switch( ch )
479 {
480 case '[':
481 if ( type == Number )
482 endFixed = true;
483
484 if ( format[ i + 1] == '$' )
485 {
486 i += 2;
487 bool found = false;
488 while ( i < l && format[i] != ']' )
489 {
490 if ( format[i] == '-' )
491 found = true;
492 if ( !found )
493 {
494 if ( !endFixed )
495 g_convertionInfo.prefix += format[i];
496 else
497 g_convertionInfo.postfix += format[i];
498 format.remove( i, 1 );
499 --i; --l;
500 }
501 ++i;
502 }
503 }
504 else
505 {
506 if ( i + 1 >= l )
507 {
508 g_convertionInfo.postfix += '[';
509 format.remove( i, 1 );
510 --l; --i;
511 }
512 else
513 if ( ( format[ i + 1].lower() != 's' )
514 && ( format[ i + 1].lower() != 'm' )
515 && ( format[ i + 1].lower() != 'h' ) )
516 {
517 // strange!
518
519 if ( endFixed )
520 g_convertionInfo.postfix += format[i];
521 else
522 g_convertionInfo.prefix += format[i];
523 format.remove( i, 1 );
524 --l; --i;
525 }
526 else
527 {
528 type = TimeDate;
529 ++i;
530 TQChar c( format[i] );
531 ++i;
532 while ( i < l && format[i] != ']' )
533 {
534 if ( format[i] != c )
535 {
536 format.remove( i, 1 );
537 --l; --i;
538 break;
539 }
540 ++i;
541 }
542 }
543 }
544 break;
545
546 case '¤':
547 case '$':
548 case '¥':
549 case '£':
550 case '%':
551 if ( type == Number )
552 endFixed = true;
553
554 if ( endFixed )
555 g_convertionInfo.postfix += format[i];
556 else
557 g_convertionInfo.prefix += format[i];
558 format.remove( i, 1 );
559 --i; --l;
560 break;
561
562 case '#':
563 type = Number;
564 if ( !dcSeen && leftReq > 0 )
565 { // 00##.00 <= remove the '#'
566 format.remove( i, 1 );
567 --l; --i;
568 }
569 if ( !dcSeen )
570 ++leftOpt;
571 else
572 ++g_convertionInfo.rightOpt;
573 break;
574
575 case '0':
576 if ( spaceInNum > 0 )
577 { // for fractions
578 ++g_convertionInfo.reqCounter;
579 break;
580 }
581 type = Number;
582 if ( !dcSeen && rightOpt > 0 )
583 { // 00##.##00 <= remove the '0'
584 format.remove( i, 1 );
585 --l; --i;
586 }
587 if ( !dcSeen )
588 ++g_convertionInfo.leftReq;
589 else
590 ++g_convertionInfo.rightReq;
591 break;
592
593 case '/':
594 if ( ( i + 1 < l ) && ( format[i + 1] == ' ' ) )
595 ++i;
596 while ( i < l )
597 {
598 if ( format[i] != '?' && !format[i].isDigit() && format[i] != '#' )
599 {
600 g_convertionInfo.postfix = format.mid(i);
601 format.remove( i, l - i );
602 break;
603 }
604 else
605 {
606 ++d;
607 frac += format[i];
608 }
609 ++i;
610 }
611 if ( i < l )
612 {
613 if ( format[i] == ';' )
614 {
615 ++i;
616 parseNegativePart( format, i, l, true );
617 }
618 else
619 if ( i + 3 < l )
620 {
621 if ( ( format[i + 1] == ')' ) && ( format[i + 2] == ';' ) )
622 {
623 i += 3;
624 parseNegativePart( format, i, l, true );
625 }
626 }
627 }
628
629 ok = false;
630 f = frac.toInt( &ok );
631
632 df = new FractionFormat();
633 if ( ok )
634 df->fraction = f;
635 else
636 df->fraction = -1;
637 df->type = Fraction;
638 df->thSet = g_convertionInfo.thSet;
639 df->showMinus = g_convertionInfo.showMinus;
640 df->negRed = g_convertionInfo.negRed;
641 df->negBr = g_convertionInfo.negBr;
642 df->fractionDigists = d;
643 df->reqCounter = g_convertionInfo.reqCounter;
644 df->reqFirst = g_convertionInfo.reqFirst;
645 df->prefix = g_convertionInfo.prefix;
646 df->postfix = g_convertionInfo.postfix;
647
648 if ( insert )
649 g_formatStore.addFraction( formatBack, df );
650 data = df;
651
652 return Fraction;
653 break;
654
655 case ',':
656 if ( type == Unknown )
657 {
658 g_convertionInfo.prefix += ',';
659 }
660 else if ( type == Number )
661 {
662 if ( dcSeen )
663 {
664 g_convertionInfo.postfix += ',';
665 format.remove( i, 1 );
666 --i; --l;
667 }
668 else
669 {
670 if ( thFound )
671 {
672 format.remove( i, 1 );
673 --l; --i;
674 }
675 else
676 thFound = true;
677 }
678 }
679
680 case '.': // decimal point
681 if ( type == Unknown )
682 {
683 int j = i + 1;
684 if ( ( j < l )
685 && ( format[j] == '0' || format[j] == '#' ) )
686 {
687 type = Number;
688 dcSeen = true;
689 }
690 else
691 {
692 if ( j == l )
693 g_convertionInfo.postfix += '.';
694 else
695 g_convertionInfo.prefix += '.';
696 format.remove( i, 1 );
697 --i; --l;
698 }
699 }
700 else if ( type == Number )
701 {
702 dcSeen = true;
703 }
704 break;
705
706 case '*':
707 break;
708
709 case '"':
710 n = i;
711 ++i;
712 while ( i < l && format[i] != '"' )
713 {
714 s += format[i];
715 ++i;
716 }
717 if ( type == Unknown )
718 g_convertionInfo.prefix += s;
719 else
720 {
721 g_convertionInfo.postfix += s;
722 }
723 len = s.length();
724 format.remove( i, len );
725 i -= len; l -= len;
726 break;
727
728 case '_':
729 if ( type == Number )
730 {
731 bool pr = false;
732 if ( i + 3 < l )
733 {
734 if ( ( format[i + 1] != ')' ) || ( format[i + 2] != ';' ) )
735 pr = true;
736 else
737 {
738 i += 3;
739 parseNegativePart( format, i, l, false );
740
741 createNumberStruct( data, formatBack, insert );
742
743 return Number;
744 }
745 }
746
747 if ( pr )
748 {
749 g_convertionInfo.postfix += format.mid( i );
750 format.remove( i, l - i );
751
752 createNumberStruct( data, formatBack, insert );
753
754 return Number;
755 }
756 }
757 break;
758
759 case ';':
760 if ( type == Unknown )
761 {
762 g_convertionInfo.postfix += ';';
763 format.remove( i, 1 );
764 --i; --l;
765 }
766 else
767 {
768 if ( type == Number )
769 {
770 ++i;
771 parseNegativePart( format, i, l, false );
772
773 createNumberStruct( data, formatBack, insert );
774
775 return Number;
776 }
777 else
778 if ( type == Scientific )
779 {
780 ++i;
781 parseNegativePart( format, i, l, false );
782
783 createScientificStruct( data, formatBack, insert );
784
785 return Scientific;
786 }
787 }
788
789 case ' ':
790 if ( type == Number )
791 {
792 g_convertionInfo.optFirst = (leftOpt > 0 ? leftOpt : 0);
793 g_convertionInfo.reqFirst = (leftReq > 0 ? leftReq : 0);
794
795 spaceInNum = i;
796 g_convertionInfo.postfix += ' ';
797 }
798 else if ( type == Unknown )
799 {
800 g_convertionInfo.prefix += ' ';
801 format.remove( i, 1 );
802 --i; --l;
803 }
804 break;
805
806 case 'A':
807 case 'a':
808 if ( type == TimeDate || type == Unknown )
809 {
810 if ( ( i + 1 < l ) && ( format[i + 1].lower() == 'm' ) )
811 {
812 g_convertionInfo.ampm = true;
813 ++i;
814 if ( ( i + 3 < l ) && ( format[i + 1] == '/' )
815 && ( format[i + 2].lower() == 'p' )
816 && ( format[i + 3].lower() == 'm' ) )
817 {
818 i += 3;
819 }
820 }
821 else if ( type == Unknown )
822 {
823 g_convertionInfo.prefix += format[i];
824 format.remove( i, 1 );
825 --i; --l;
826 }
827 }
828 else
829 {
830 if ( !endFixed )
831 endFixed = true;
832 g_convertionInfo.postfix += format[i];
833 format.remove( i, 1 );
834 --i; --l;
835 }
836 break;
837
838 case 'P':
839 case 'p':
840 if ( type == TimeDate || type == Unknown )
841 {
842 if ( ( i + 1 < l ) && ( format[i + 1].lower() == 'm' ) )
843 {
844 g_convertionInfo.ampm = true;
845 i += 1;
846 }
847 else if ( type == Unknown )
848 {
849 g_convertionInfo.prefix += format[i];
850 format.remove( i, 1 );
851 --i; --l;
852 }
853 }
854 else
855 {
856 if ( !endFixed )
857 endFixed = true;
858 g_convertionInfo.postfix += format[i];
859 format.remove( i, 1 );
860 --i; --l;
861 }
862 break;
863
864 case 'M':
865 case 'm':
866 if ( type == Unknown )
867 type = TimeDate;
868 else if ( type != TimeDate )
869 endFixed = true;
870 break;
871
872 case 'S':
873 case 's':
874 case 'H':
875 case 'h':
876 if ( type != Unknown && type != TimeDate )
877 endFixed = true;
878 else
879 type = TimeDate;
880 break;
881
882 case 'D':
883 case 'd':
884 case 'Y':
885 case 'y':
886 if ( type != Unknown && type != TimeDate )
887 endFixed = true;
888 else
889 type = TimeDate;
890 break;
891
892 default:
893 if ( type == Unknown )
894 {
895 g_convertionInfo.prefix += format[i];
896 format.remove( i, 1 );
897 --i; --l;
898 }
899 else if ( type == Number || type == Scientific
900 || type == Fraction )
901 {
902 endFixed = true;
903 g_convertionInfo.postfix += format[i];
904 format.remove( i, 1 );
905 --l; --i;
906 }
907 }
908
909 ++i;
910 }
911
912 if ( type == Number )
913 createNumberStruct( data, formatBack, insert );
914 else if ( type == TimeDate )
915 createDateTimeStruct( data, formatBack, format, insert );
916 else if ( type == Scientific )
917 createScientificStruct( data, formatBack, insert );
918
919 return type;
920}
921
922void createNumber( TQString & result, Value const & value,
923 TQString const & /*format*/, bool & setRed,
924 NumberFormat const * const data )
925{
926 int prec = data->rightReq + data->rightOpt;
927 double num = value.asFloat();
928 double m[] = { 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 };
929 double mm = ( prec > 10 ) ? pow( 10.0, prec ) : m[prec];
930
931 num = floor( fabs( num ) * mm + 0.5 ) / mm;
932
933 bool negative = ( num < 0 ? true : false );
934 double nnum = ( negative ? -num : num );
935
936 result = TQString::number( nnum, 'f', prec );
937
938 int pos = result.find( '.' );
939 if ( pos >= 0 )
940 {
941 result = result.replace( pos, 1, g_dcSymbol );
942
943 // remove '0' from the end if not required
944 if ( data->rightOpt > 0 )
945 {
946 int i = result.length() - 1; // index
947 int n = result.length() - data->rightOpt;
948
949 for ( ; i > n; --i )
950 {
951 if ( result[i] != '0' )
952 break;
953 }
954 result = result.left( i + 1 ); //length
955
956 if ( i == pos ) // just decimal point
957 result = result.remove( i, 1 );
958 }
959
960 // prepend '0' if wanted
961 while ( data->leftReq > pos )
962 {
963 result.prepend( '0' );
964 ++pos;
965 }
966
967 // put in thousand symbol if wanted
968 if ( data->thSet && pos > 3 )
969 {
970 int l = pos - 3;
971 while ( 0 < l )
972 {
973 result.insert( l, g_thSymbol );
974 l -= 3;
975 }
976 }
977 }
978
979 if ( data->leftSpace > 0 )
980 {
981 for ( int i = 0; i < data->leftSpace; ++i )
982 result.prepend( ' ' );
983 }
984 if ( data->rightSpace > 0 )
985 {
986 for ( int i = 0; i < data->rightSpace; ++i )
987 result.append( ' ' );
988 }
989
990 if ( negative )
991 {
992 if ( data->showMinus )
993 result.prepend( g_negSymbol );
994
995 if ( data->negBr )
996 {
997 result.prepend( '(' );
998 result.append( ')' );
999 }
1000
1001 if ( data->negRed )
1002 setRed = true;
1003 }
1004
1005 result.prepend( data->prefix );
1006 result.append( data->postfix );
1007}
1008
1009void createFraction( TQString & result, Value const & value,
1010 TQString const & /*format*/, bool & setRed,
1011 FractionFormat const * const data )
1012{
1013 double num = value.asFloat();
1014
1015 bool negative = ( num < 0 ? true : false );
1016
1017 double fnum = floor( negative ? -num : num );
1018
1019 double dec = num - fnum;
1020 double fraction;
1021 int index = 0;
1022
1023 if ( data->fraction <= 0 )
1024 {
1025 // #,### ?/???
1026 double nnum = ( negative ? -num : num );
1027 double precision, denominator, numerator;
1028 int index = 2 + data->fractionDigists;
1029 int limit = 9;
1030 if ( data->fractionDigists == 2 )
1031 limit += 90;
1032 if ( data->fractionDigists >= 3 )
1033 limit += 990;
1034
1035 do
1036 {
1037 double val1 = nnum;
1038 double val2 = rint( nnum );
1039 double inter2 = 1;
1040 double inter4, p, q;
1041 inter4 = p = q = 0.0;
1042
1043 precision = pow( 10.0, - index );
1044 numerator = val2;
1045 denominator = 1;
1046
1047 while ( fabs( numerator / denominator - nnum ) > precision )
1048 {
1049 val1 = (1 / ( val1 - val2 ) );
1050 val2 = rint( val1 );
1051 p = val2 * numerator + inter2;
1052 q = val2 * denominator + inter4;
1053 inter2 = numerator;
1054 inter4 = denominator;
1055
1056 numerator = p;
1057 denominator = q;
1058 }
1059 --index;
1060 } while ( fabs( denominator ) > limit );
1061
1062 index = (int) fabs( numerator );
1063 fraction = (int) fabs( denominator );
1064 }
1065 else
1066 {
1067 // # #/4
1068 fraction = data->fraction;
1069
1070 double calc = 0.0;
1071 double diff = dec;
1072 double d;
1073 for ( int i = 1; i <= fraction; ++i )
1074 {
1075 calc = i * 1.0 / index;
1076 d = fabs( dec - calc );
1077 if ( d < diff )
1078 {
1079 index = i;
1080 diff = d;
1081 }
1082 }
1083 }
1084
1085 // ? index/fraction
1086
1087 // 2.25: #/4 => 9/4
1088 if ( data->optFirst == 0 && data->reqFirst == 0 && fnum > 0 )
1089 index += (int) (fnum * fraction);
1090
1091 TQString frac;
1092 TQString left;
1093 if ( index > 0 )
1094 {
1095 TQString numerator;
1096 TQString denominator;
1097
1098 numerator = TQString::number( index );
1099 int n = numerator.length() - data->reqCounter;
1100 for ( int i = 0; i < n; ++i )
1101 {
1102 numerator.prepend( '0' );
1103 }
1104
1105 denominator = TQString::number( fraction );
1106 frac = numerator + '/' + denominator;
1107 }
1108
1109 if ( data->optFirst > 0 || data->reqFirst > 0 )
1110 {
1111 if ( fnum == 0 && data->reqFirst > 0 )
1112 {
1113 for ( int i = 0; i < data->reqFirst; ++i )
1114 left += '0';
1115 }
1116 else if ( fnum > 0 )
1117 {
1118 left = TQString::number( fnum );
1119 int n = data->reqFirst - left.length();
1120 if ( n > 0 )
1121 {
1122 for ( int i = 0; i < n; ++i )
1123 {
1124 left.prepend( '0' );
1125 }
1126 }
1127 }
1128 }
1129
1130 if ( data->thSet )
1131 {
1132 int l = left.length() - 3;
1133 while ( 0 < l )
1134 {
1135 left.insert( l, g_thSymbol );
1136 l -= 3;
1137 }
1138 }
1139
1140 left = left + ' ' + frac;
1141
1142 if ( negative )
1143 {
1144 if ( data->showMinus )
1145 left.prepend( g_negSymbol );
1146
1147 if ( data->negBr )
1148 {
1149 left.prepend( '(' );
1150 left.append( ')' );
1151 }
1152
1153 if ( data->negRed )
1154 setRed = true;
1155 }
1156
1157 result = left;
1158}
1159
1160void createScientific( TQString & result, Value const & value,
1161 TQString const & /*format*/, bool & setRed,
1162 ScientificFormat const * const data )
1163{
1164 double num = value.asFloat();
1165
1166 bool negative = ( num < 0 ? true : false );
1167 double nnum = ( negative ? -num : num );
1168
1169 result = TQString::number( nnum, 'E', data->rightReq + data->rightOpt );
1170
1171 int pos = result.find( '.' );
1172 if ( pos >= 0 )
1173 {
1174 result = result.replace( pos, 1, g_dcSymbol );
1175 if ( data->rightOpt > 0 )
1176 {
1177 int i = result.find( 'E', pos, false ) - 1;
1178 int n = result.length() - data->rightOpt;
1179
1180 if ( i > 0 )
1181 {
1182 int rem = 0;
1183 for ( ; i > n; --i )
1184 {
1185 if ( result[i] != '0' )
1186 break;
1187 else
1188 ++rem;
1189 }
1190 result = result.remove( i + 1, rem );
1191 }
1192 }
1193
1194 while ( data->leftReq > pos )
1195 {
1196 result.prepend( '0' );
1197 ++pos;
1198 }
1199
1200 if ( data->thSet && pos > 3 )
1201 {
1202 int l = pos - 3;
1203 while ( 0 < l )
1204 {
1205 result.insert( l, g_thSymbol );
1206 l -= 3;
1207 }
1208 }
1209 }
1210
1211 if ( negative )
1212 {
1213 if ( data->showMinus )
1214 result.prepend( g_negSymbol );
1215
1216 if ( data->negBr )
1217 {
1218 result.prepend( '(' );
1219 result.append( ')' );
1220 }
1221
1222 if ( data->negRed )
1223 setRed = true;
1224 }
1225
1226 result.prepend( data->prefix );
1227 result.append( data->postfix );
1228}
1229
1230void appendAMPM( TQString & result, Value const & value )
1231{
1232 if ( !g_convertionInfo.dt )
1233 convertDateTime( value );
1234
1235 int hour = g_convertionInfo.dt->hour;
1236 if ( hour > 12 )
1237 result.append( i18n("PM") );
1238 else
1239 result.append( i18n("AM") );
1240}
1241
1242void appendHour( TQString & result, Value const & value,
1243 int digits, bool elapsed, bool ampm )
1244{
1245 if ( !g_convertionInfo.dt )
1246 convertDateTime( value );
1247
1248 int hour = g_convertionInfo.dt->hour;
1249 if ( elapsed )
1250 {
1251 TQDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
1252 TQDate d2( 1900, 1, 1 );
1253 hour += ( d2.daysTo( d1 ) * 24 );
1254 }
1255 if ( hour < 10 && digits == 2 )
1256 result += '0';
1257 else
1258 if ( hour > 12 && ampm )
1259 {
1260 hour -= 12;
1261 if ( digits == 2 && hour < 10 )
1262 result += '0';
1263 }
1264
1265 result += TQString::number( hour );
1266}
1267
1268void appendMinutes( TQString & result, Value const & value,
1269 int digits, bool elapsed )
1270{
1271 if ( !g_convertionInfo.dt )
1272 convertDateTime( value );
1273
1274 int minute = g_convertionInfo.dt->minute;
1275 if ( elapsed )
1276 {
1277 TQDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
1278 TQDate d2( 1900, 1, 1 );
1279 minute += ( d2.daysTo( d1 ) * 24 * 60 );
1280 }
1281 if ( minute < 10 && digits == 2 )
1282 result += '0';
1283
1284 result += TQString::number( minute );
1285}
1286
1287void appendSecond( TQString & result, Value const & value,
1288 int digits, bool elapsed )
1289{
1290 if ( !g_convertionInfo.dt )
1291 convertDateTime( value );
1292
1293 int second = g_convertionInfo.dt->second;
1294 if ( elapsed )
1295 {
1296 TQDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
1297 TQDate d2( 1900, 1, 1 );
1298 second += ( d2.daysTo( d1 ) * 24 * 60 * 60 );
1299 }
1300 if ( second < 10 && digits == 2 )
1301 result += '0';
1302
1303 result += TQString::number( second );
1304}
1305
1306void appendYear( TQString & result, Value const & value,
1307 int digits )
1308{
1309 if ( !g_convertionInfo.dt )
1310 convertDateTime( value );
1311
1312 int year = g_convertionInfo.dt->year;
1313 if ( digits <= 2 )
1314 result += TQString::number( year ).right( 2 );
1315 else
1316 result += TQString::number( year );
1317}
1318
1319void appendMonth( TQString & result, Value const & value,
1320 int digits )
1321{
1322 if ( !g_convertionInfo.dt )
1323 convertDateTime( value );
1324
1325 int month = g_convertionInfo.dt->month;
1326 if ( digits == 1 )
1327 result += TQString::number( month );
1328 else
1329 if ( digits == 2 )
1330 {
1331 if ( month < 10 )
1332 result += '0';
1333
1334 result += TQString::number( month );
1335 }
1336 else
1337 {
1338 switch ( month )
1339 {
1340 case 1:
1341 result += ( digits != 3 ? g_January : g_Jan );
1342 break;
1343
1344 case 2:
1345 result += ( digits != 3 ? g_February : g_Feb );
1346 break;
1347
1348 case 3:
1349 result += ( digits != 3 ? g_March : g_Mar );
1350 break;
1351
1352 case 4:
1353 result += ( digits != 3 ? g_April : g_Apr );
1354 break;
1355
1356 case 5:
1357 result += ( digits != 3 ? g_MayL : g_May );
1358 break;
1359
1360 case 6:
1361 result += ( digits != 3 ? g_June : g_Jun );
1362 break;
1363
1364 case 7:
1365 result += ( digits != 3 ? g_July : g_Jul );
1366 break;
1367
1368 case 8:
1369 result += ( digits != 3 ? g_August : g_Aug );
1370 break;
1371
1372 case 9:
1373 result += ( digits != 3 ? g_September : g_Sep );
1374 break;
1375
1376 case 10:
1377 result += ( digits != 3 ? g_October : g_Oct );
1378 break;
1379
1380 case 11:
1381 result += ( digits != 3 ? g_November : g_Nov );
1382 break;
1383
1384 case 12:
1385 result += ( digits != 3 ? g_December : g_Dec );
1386 break;
1387 }
1388 }
1389}
1390
1391void appendDays( TQString & result, Value const & value,
1392 int digits )
1393{
1394 if ( !g_convertionInfo.dt )
1395 convertDateTime( value );
1396
1397 int day = g_convertionInfo.dt->day;
1398 if ( digits == 1 )
1399 result += TQString::number( day );
1400 else
1401 if ( digits == 2 )
1402 {
1403 if ( day < 10 )
1404 result += '0';
1405
1406 result += TQString::number( day );
1407 }
1408 else
1409 {
1410 TQDate date( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
1411 int weekDay = date.dayOfWeek();
1412 switch ( weekDay )
1413 {
1414 case 1:
1415 result += ( digits != 3 ? g_Monday : g_Mon );
1416 break;
1417
1418 case 2:
1419 result += ( digits != 3 ? g_Tuesday : g_Tue );
1420 break;
1421
1422 case 3:
1423 result += ( digits != 3 ? g_Wednesday : g_Wed );
1424 break;
1425
1426 case 4:
1427 result += ( digits != 3 ? g_Thursday : g_Thu );
1428 break;
1429
1430 case 5:
1431 result += ( digits != 3 ? g_Friday : g_Fri );
1432 break;
1433
1434 case 6:
1435 result += ( digits != 3 ? g_Saturday : g_Sat );
1436 break;
1437
1438 case 7:
1439 result += ( digits != 3 ? g_Sunday : g_Sun );
1440 break;
1441 }
1442 }
1443}
1444
1445void createDateTime( TQString & result, Value const & value,
1446 TQString const & /*format*/,
1447 DateTimeFormat const * const data )
1448{
1449 result = data->prefix;
1450 bool elapsed = false;
1451 bool elapsedFound = false;
1452 bool minute = false; // how to interpret 'm'
1453 int digits = 1;
1454 int i = 0;
1455 int l = (int) data->format.length();
1456 while ( i < l )
1457 {
1458 switch( data->format[i].lower() )
1459 {
1460 case '"':
1461 ++i;
1462 while ( i < l )
1463 {
1464 if ( data->format[i] == '"' )
1465 break;
1466 else
1467 result += data->format[i];
1468 }
1469 break;
1470
1471 case '[':
1472 if ( elapsedFound )
1473 result += '[';
1474 else
1475 {
1476 elapsed = true;
1477 elapsedFound = true;
1478 }
1479 break;
1480
1481 case ']':
1482 if ( elapsed )
1483 elapsed = false;
1484 else
1485 result += ']';
1486 break;
1487
1488 case 'h':
1489 minute = true;
1490 if ( data->format[i + 1] == 'h' )
1491 {
1492 appendHour( result, value, 2, elapsed, data->ampm );
1493 ++i;
1494 }
1495 else
1496 appendHour( result, value, 1, elapsed, data->ampm );
1497 break;
1498
1499 case 'm':
1500 digits = 1;
1501
1502 while ( data->format[i + 1] == 'm' )
1503 {
1504 ++i;
1505 ++digits;
1506 }
1507
1508 if ( minute )
1509 appendMinutes( result, value, digits, elapsed );
1510 else
1511 appendMonth( result, value, digits );
1512
1513 break;
1514
1515 case 's':
1516 minute = true;
1517 if ( data->format[i + 1] == 's' )
1518 {
1519 appendSecond( result, value, 2, elapsed );
1520 ++i;
1521 }
1522 else
1523 appendSecond( result, value, 1, elapsed );
1524 break;
1525
1526 case 'd':
1527 minute = false;
1528 digits = 1;
1529
1530 while ( data->format[i + 1] == 'd' )
1531 {
1532 ++i;
1533 ++digits;
1534 }
1535 appendDays( result, value, digits );
1536 break;
1537
1538 case 'y':
1539 minute = false;
1540 digits = 1;
1541
1542 while ( data->format[i + 1] == 'y' )
1543 {
1544 ++i;
1545 ++digits;
1546 }
1547 appendYear( result, value, digits );
1548 break;
1549
1550 case 'a':
1551 case 'p':
1552 if ( data->format[i + 1] == 'm' )
1553 {
1554 ++i;
1555 if ( data->format[i + 1] == '/'
1556 && data->format[i + 2].lower() == 'p'
1557 && data->format[i + 3].lower() == 'm' )
1558 i += 3;
1559
1560 appendAMPM( result, value );
1561 }
1562
1563 default:
1564 result += data->format[i];
1565 }
1566
1567 ++i;
1568 }
1569
1570 result += data->postfix;
1571}
1572
1573TQString formatNumber( Value const & value, TQString format, bool & setRed,
1574 TDELocale const * const locale, bool insert )
1575{
1576 // need delocalized strings: dcSymbol: '.', thSymbol = ','
1577 if ( !g_init )
1578 initGlobals( locale );
1579
1580 TQString backup( format );
1581 TQString result;
1582 BaseFormat * data = 0;
1583 setRed = false;
1584
1585 int t = doPreScan( format, backup, locale, insert, data );
1586
1587 if ( t == Number )
1588 {
1589 createNumber( result, value, format, setRed, (NumberFormat *) data );
1590
1591 if ( !insert )
1592 delete (NumberFormat *) data;
1593
1594 return result;
1595 }
1596 else if ( t == Fraction )
1597 {
1598 createFraction( result, value, format, setRed, (FractionFormat *) data );
1599
1600 if ( !insert )
1601 delete (FractionFormat *) data;
1602
1603 return result;
1604 }
1605 else if ( t == Scientific )
1606 {
1607 createScientific( result, value, format, setRed, (ScientificFormat *) data );
1608
1609 if ( !insert )
1610 delete (ScientificFormat *) data;
1611
1612 return result;
1613 }
1614 else if ( t == TimeDate )
1615 {
1616 createDateTime( result, value, format, (DateTimeFormat *) data );
1617
1618 if ( !insert )
1619 delete (DateTimeFormat *) data;
1620
1621 return result;
1622 }
1623 else if ( data != 0 )
1624 {
1625 result = data->prefix + data->postfix;
1626
1627 if ( !insert )
1628 delete data;
1629
1630 return result;
1631 }
1632
1633 return result;
1634}
Provides a wrapper for cell value.
double asFloat() const
Returns the floating-point value of this value.
TQDateTime asDateTime() const
Returns the date/time representation of this value.
The KSpread namespace.
Definition commands.h:39