1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
25 #include <comphelper/string.hxx>
26 #include <tools/debug.hxx>
27 #include <osl/diagnose.h>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <rtl/math.hxx>
30 #include <rtl/instance.hxx>
31 #include <unotools/charclass.hxx>
32 #include <unotools/calendarwrapper.hxx>
33 #include <unotools/nativenumberwrapper.hxx>
34 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
35 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
36 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
37 #include <com/sun/star/i18n/AmPmValue.hpp>
39 #include <svl/zformat.hxx>
40 #include <zforscan.hxx>
42 #include "zforfind.hxx"
43 #include <svl/zforlist.hxx>
44 #include "numhead.hxx"
45 #include <unotools/digitgroupingiterator.hxx>
46 #include <svl/nfsymbol.hxx>
53 struct Gregorian
: public rtl::StaticWithInit
<const OUString
, Gregorian
>
55 const OUString
operator () ()
57 return OUString("gregorian");
61 const sal_uInt16 UPPER_PRECISION
= 300; // entirely arbitrary...
62 const double EXP_LOWER_BOUND
= 1.0E-4; // prefer scientific notation below this value.
66 const double _D_MAX_U_LONG_
= (double) 0xffffffff; // 4294967295.0
67 const double _D_MAX_LONG_
= (double) 0x7fffffff; // 2147483647.0
68 const sal_uInt16 _MAX_FRACTION_PREC
= 3;
69 const double D_EPS
= 1.0E-2;
71 const double _D_MAX_D_BY_100
= 1.7E306
;
72 const double _D_MIN_M_BY_1000
= 2.3E-305;
74 static sal_uInt8 cCharWidths
[ 128-32 ] = {
75 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
76 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
77 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
78 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
79 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
80 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
84 sal_Int32
SvNumberformat::InsertBlanks( OUStringBuffer
& r
, sal_Int32 nPos
, sal_Unicode c
)
88 int n
= 2; // Default fuer Zeichen > 128 (HACK!)
91 n
= (int)cCharWidths
[ c
- 32 ];
95 r
.insert( nPos
++, (sal_Unicode
)' ');
101 static long GetPrecExp( double fAbsVal
)
103 DBG_ASSERT( fAbsVal
> 0.0, "GetPrecExp: fAbsVal <= 0.0" );
104 if ( fAbsVal
< 1e-7 || fAbsVal
> 1e7
)
106 // die Schere, ob's schneller ist oder nicht, liegt zwischen 1e6 und 1e7
107 return (long) floor( log10( fAbsVal
) ) + 1;
117 while( fAbsVal
>= 10 )
126 const sal_uInt16 nNewCurrencyVersionId
= 0x434E; // "NC"
127 const sal_Unicode cNewCurrencyMagic
= 0x01; // Magic for format code in comment
128 const sal_uInt16 nNewStandardFlagVersionId
= 0x4653; // "SF"
130 /***********************Funktion SvNumberformatInfo******************************/
132 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo
& rNumFor
, sal_uInt16 nAnz
)
134 for (sal_uInt16 i
= 0; i
< nAnz
; ++i
)
136 sStrArray
[i
] = rNumFor
.sStrArray
[i
];
137 nTypeArray
[i
] = rNumFor
.nTypeArray
[i
];
139 eScannedType
= rNumFor
.eScannedType
;
140 bThousand
= rNumFor
.bThousand
;
141 nThousand
= rNumFor
.nThousand
;
142 nCntPre
= rNumFor
.nCntPre
;
143 nCntPost
= rNumFor
.nCntPost
;
144 nCntExp
= rNumFor
.nCntExp
;
147 void ImpSvNumberformatInfo::Save(SvStream
& rStream
, sal_uInt16 nAnz
) const
149 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
151 rStream
.WriteUniOrByteString( sStrArray
[i
], rStream
.GetStreamCharSet() );
152 short nType
= nTypeArray
[i
];
155 // der Krampf fuer Versionen vor SV_NUMBERFORMATTER_VERSION_NEW_CURR
156 case NF_SYMBOLTYPE_CURRENCY
:
157 rStream
<< short( NF_SYMBOLTYPE_STRING
);
159 case NF_SYMBOLTYPE_CURRDEL
:
160 case NF_SYMBOLTYPE_CURREXT
:
161 rStream
<< short(0); // werden ignoriert (hoffentlich..)
164 if ( nType
> NF_KEY_LASTKEYWORD_SO5
)
166 rStream
<< short( NF_SYMBOLTYPE_STRING
); // all new keywords are string
175 rStream
<< eScannedType
<< sal_Bool(bThousand
) << nThousand
176 << nCntPre
<< nCntPost
<< nCntExp
;
179 void ImpSvNumberformatInfo::Load(SvStream
& rStream
, sal_uInt16 nAnz
)
181 for (sal_uInt16 i
= 0; i
< nAnz
; ++i
)
183 sStrArray
[i
] = SvNumberformat::LoadString( rStream
);
184 rStream
>> nTypeArray
[i
];
186 sal_Bool bStreamThousand
;
187 rStream
>> eScannedType
>> bStreamThousand
>> nThousand
188 >> nCntPre
>> nCntPost
>> nCntExp
;
189 bThousand
= bStreamThousand
;
192 //============================================================================
195 sal_uInt8
SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum
, LanguageType eLang
, bool bDate
)
197 sal_uInt8 nNatNum
= 0;
198 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
199 eLang
&= 0x03FF; // 10 bit primary language
202 if ( nDBNum
== 4 && eLang
== LANGUAGE_KOREAN
)
206 else if ( nDBNum
<= 3 )
208 nNatNum
= nDBNum
; // known to be good for: zh,ja,ko / 1,2,3
218 case (LANGUAGE_CHINESE
& 0x03FF):
221 case (LANGUAGE_JAPANESE
& 0x03FF):
224 case (LANGUAGE_KOREAN
& 0x03FF):
232 case (LANGUAGE_CHINESE
& 0x03FF):
235 case (LANGUAGE_JAPANESE
& 0x03FF):
238 case (LANGUAGE_KOREAN
& 0x03FF):
246 case (LANGUAGE_CHINESE
& 0x03FF):
249 case (LANGUAGE_JAPANESE
& 0x03FF):
252 case (LANGUAGE_KOREAN
& 0x03FF):
260 case (LANGUAGE_JAPANESE
& 0x03FF):
263 case (LANGUAGE_KOREAN
& 0x03FF):
274 /* XXX NOTE: even though the MapNatNumToDBNum method is currently unused please
275 * don't remove it in case we'd have to use it for some obscure exports to
279 sal_uInt8
SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum
, LanguageType eLang
, bool bDate
)
281 sal_uInt8 nDBNum
= 0;
282 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
283 eLang
&= 0x03FF; // 10 bit primary language
286 if ( nNatNum
== 9 && eLang
== LANGUAGE_KOREAN
)
290 else if ( nNatNum
<= 3 )
292 nDBNum
= nNatNum
; // known to be good for: zh,ja,ko / 1,2,3
302 case (LANGUAGE_JAPANESE
& 0x03FF):
305 case (LANGUAGE_KOREAN
& 0x03FF):
313 case (LANGUAGE_KOREAN
& 0x03FF):
321 case (LANGUAGE_KOREAN
& 0x03FF):
329 case (LANGUAGE_CHINESE
& 0x03FF):
332 case (LANGUAGE_JAPANESE
& 0x03FF):
340 case (LANGUAGE_CHINESE
& 0x03FF):
343 case (LANGUAGE_JAPANESE
& 0x03FF):
351 case (LANGUAGE_CHINESE
& 0x03FF):
359 case (LANGUAGE_JAPANESE
& 0x03FF):
369 case (LANGUAGE_KOREAN
& 0x03FF):
384 /***********************Funktionen SvNumFor******************************/
386 ImpSvNumFor::ImpSvNumFor()
389 aI
.nTypeArray
= NULL
;
391 aI
.eScannedType
= NUMBERFORMAT_UNDEFINED
;
392 aI
.bThousand
= false;
400 ImpSvNumFor::~ImpSvNumFor()
402 delete [] aI
.sStrArray
;
403 delete [] aI
.nTypeArray
;
406 void ImpSvNumFor::Enlarge(sal_uInt16 nAnz
)
408 if ( nAnzStrings
!= nAnz
)
410 delete [] aI
.nTypeArray
;
411 delete [] aI
.sStrArray
;
415 aI
.nTypeArray
= new short[nAnz
];
416 aI
.sStrArray
= new OUString
[nAnz
];
420 aI
.nTypeArray
= NULL
;
426 void ImpSvNumFor::Copy( const ImpSvNumFor
& rNumFor
, ImpSvNumberformatScan
* pSc
)
428 Enlarge( rNumFor
.nAnzStrings
);
429 aI
.Copy( rNumFor
.aI
, nAnzStrings
);
430 sColorName
= rNumFor
.sColorName
;
433 pColor
= pSc
->GetColor( sColorName
); // #121103# don't copy pointer between documents
437 pColor
= rNumFor
.pColor
;
439 aNatNum
= rNumFor
.aNatNum
;
442 void ImpSvNumFor::Save(SvStream
& rStream
) const
444 rStream
<< nAnzStrings
;
445 aI
.Save(rStream
, nAnzStrings
);
446 rStream
.WriteUniOrByteString( sColorName
, rStream
.GetStreamCharSet() );
449 void ImpSvNumFor::Load(SvStream
& rStream
, ImpSvNumberformatScan
& rSc
,
450 OUString
& rLoadedColorName
)
453 rStream
>> nAnz
; //! noch nicht direkt nAnzStrings wg. Enlarge
455 aI
.Load( rStream
, nAnz
);
456 sColorName
= rStream
.ReadUniOrByteString( rStream
.GetStreamCharSet() );
457 rLoadedColorName
= sColorName
;
458 pColor
= rSc
.GetColor(sColorName
);
461 bool ImpSvNumFor::HasNewCurrency() const
463 for ( sal_uInt16 j
=0; j
<nAnzStrings
; j
++ )
465 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
473 bool ImpSvNumFor::GetNewCurrencySymbol( OUString
& rSymbol
,
474 OUString
& rExtension
) const
476 for ( sal_uInt16 j
=0; j
<nAnzStrings
; j
++ )
478 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
480 rSymbol
= aI
.sStrArray
[j
];
481 if ( j
< nAnzStrings
-1 && aI
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_CURREXT
)
483 rExtension
= aI
.sStrArray
[j
+1];
492 //! kein Erase an rSymbol, rExtension
496 void ImpSvNumFor::SaveNewCurrencyMap( SvStream
& rStream
) const
500 for ( j
=0; j
<nAnzStrings
; j
++ )
502 switch ( aI
.nTypeArray
[j
] )
504 case NF_SYMBOLTYPE_CURRENCY
:
505 case NF_SYMBOLTYPE_CURRDEL
:
506 case NF_SYMBOLTYPE_CURREXT
:
512 for ( j
=0; j
<nAnzStrings
; j
++ )
514 switch ( aI
.nTypeArray
[j
] )
516 case NF_SYMBOLTYPE_CURRENCY
:
517 case NF_SYMBOLTYPE_CURRDEL
:
518 case NF_SYMBOLTYPE_CURREXT
:
519 rStream
<< j
<< aI
.nTypeArray
[j
];
525 void ImpSvNumFor::LoadNewCurrencyMap( SvStream
& rStream
)
529 for ( sal_uInt16 j
=0; j
<nCnt
; j
++ )
533 rStream
>> nPos
>> nType
;
534 if ( nPos
< nAnzStrings
)
536 aI
.nTypeArray
[nPos
] = nType
;
541 /***********************Funktionen SvNumberformat************************/
543 enum BracketFormatSymbolType
545 BRACKET_SYMBOLTYPE_FORMAT
= -1, // subformat string
546 BRACKET_SYMBOLTYPE_COLOR
= -2, // color
547 BRACKET_SYMBOLTYPE_ERROR
= -3, // error
548 BRACKET_SYMBOLTYPE_DBNUM1
= -4, // DoubleByteNumber, represent numbers
549 BRACKET_SYMBOLTYPE_DBNUM2
= -5, // using CJK characters, Excel compatible.
550 BRACKET_SYMBOLTYPE_DBNUM3
= -6,
551 BRACKET_SYMBOLTYPE_DBNUM4
= -7,
552 BRACKET_SYMBOLTYPE_DBNUM5
= -8,
553 BRACKET_SYMBOLTYPE_DBNUM6
= -9,
554 BRACKET_SYMBOLTYPE_DBNUM7
= -10,
555 BRACKET_SYMBOLTYPE_DBNUM8
= -11,
556 BRACKET_SYMBOLTYPE_DBNUM9
= -12,
557 BRACKET_SYMBOLTYPE_LOCALE
= -13,
558 BRACKET_SYMBOLTYPE_NATNUM0
= -14, // Our NativeNumber support, ASCII
559 BRACKET_SYMBOLTYPE_NATNUM1
= -15, // Our NativeNumber support, represent
560 BRACKET_SYMBOLTYPE_NATNUM2
= -16, // numbers using CJK, CTL, ...
561 BRACKET_SYMBOLTYPE_NATNUM3
= -17,
562 BRACKET_SYMBOLTYPE_NATNUM4
= -18,
563 BRACKET_SYMBOLTYPE_NATNUM5
= -19,
564 BRACKET_SYMBOLTYPE_NATNUM6
= -20,
565 BRACKET_SYMBOLTYPE_NATNUM7
= -21,
566 BRACKET_SYMBOLTYPE_NATNUM8
= -22,
567 BRACKET_SYMBOLTYPE_NATNUM9
= -23,
568 BRACKET_SYMBOLTYPE_NATNUM10
= -24,
569 BRACKET_SYMBOLTYPE_NATNUM11
= -25,
570 BRACKET_SYMBOLTYPE_NATNUM12
= -26,
571 BRACKET_SYMBOLTYPE_NATNUM13
= -27,
572 BRACKET_SYMBOLTYPE_NATNUM14
= -28,
573 BRACKET_SYMBOLTYPE_NATNUM15
= -29,
574 BRACKET_SYMBOLTYPE_NATNUM16
= -30,
575 BRACKET_SYMBOLTYPE_NATNUM17
= -31,
576 BRACKET_SYMBOLTYPE_NATNUM18
= -32,
577 BRACKET_SYMBOLTYPE_NATNUM19
= -33
580 SvNumberformat::SvNumberformat( ImpSvNumberformatScan
& rSc
, LanguageType eLge
)
582 , nNewStandardDefined(0)
585 maLocale
.meLanguage
= eLge
;
588 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat
& rFormat
)
590 sFormatstring
= rFormat
.sFormatstring
;
591 eType
= rFormat
.eType
;
592 maLocale
= rFormat
.maLocale
;
593 fLimit1
= rFormat
.fLimit1
;
594 fLimit2
= rFormat
.fLimit2
;
597 bStandard
= rFormat
.bStandard
;
598 bIsUsed
= rFormat
.bIsUsed
;
599 sComment
= rFormat
.sComment
;
600 nNewStandardDefined
= rFormat
.nNewStandardDefined
;
602 // #121103# when copying between documents, get color pointers from own scanner
603 ImpSvNumberformatScan
* pColorSc
= ( &rScan
!= &rFormat
.rScan
) ? &rScan
: NULL
;
605 for (sal_uInt16 i
= 0; i
< 4; i
++)
607 NumFor
[i
].Copy(rFormat
.NumFor
[i
], pColorSc
);
611 SvNumberformat::SvNumberformat( SvNumberformat
& rFormat
)
612 : rScan(rFormat
.rScan
), bStarFlag( rFormat
.bStarFlag
)
614 ImpCopyNumberformat( rFormat
);
617 SvNumberformat::SvNumberformat( SvNumberformat
& rFormat
, ImpSvNumberformatScan
& rSc
)
619 , bStarFlag( rFormat
.bStarFlag
)
621 ImpCopyNumberformat( rFormat
);
624 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType
)
626 if ( nSymbolType
> 0 )
628 return true; // conditions
630 switch ( nSymbolType
)
632 case BRACKET_SYMBOLTYPE_COLOR
:
633 case BRACKET_SYMBOLTYPE_DBNUM1
:
634 case BRACKET_SYMBOLTYPE_DBNUM2
:
635 case BRACKET_SYMBOLTYPE_DBNUM3
:
636 case BRACKET_SYMBOLTYPE_DBNUM4
:
637 case BRACKET_SYMBOLTYPE_DBNUM5
:
638 case BRACKET_SYMBOLTYPE_DBNUM6
:
639 case BRACKET_SYMBOLTYPE_DBNUM7
:
640 case BRACKET_SYMBOLTYPE_DBNUM8
:
641 case BRACKET_SYMBOLTYPE_DBNUM9
:
642 case BRACKET_SYMBOLTYPE_LOCALE
:
643 case BRACKET_SYMBOLTYPE_NATNUM0
:
644 case BRACKET_SYMBOLTYPE_NATNUM1
:
645 case BRACKET_SYMBOLTYPE_NATNUM2
:
646 case BRACKET_SYMBOLTYPE_NATNUM3
:
647 case BRACKET_SYMBOLTYPE_NATNUM4
:
648 case BRACKET_SYMBOLTYPE_NATNUM5
:
649 case BRACKET_SYMBOLTYPE_NATNUM6
:
650 case BRACKET_SYMBOLTYPE_NATNUM7
:
651 case BRACKET_SYMBOLTYPE_NATNUM8
:
652 case BRACKET_SYMBOLTYPE_NATNUM9
:
653 case BRACKET_SYMBOLTYPE_NATNUM10
:
654 case BRACKET_SYMBOLTYPE_NATNUM11
:
655 case BRACKET_SYMBOLTYPE_NATNUM12
:
656 case BRACKET_SYMBOLTYPE_NATNUM13
:
657 case BRACKET_SYMBOLTYPE_NATNUM14
:
658 case BRACKET_SYMBOLTYPE_NATNUM15
:
659 case BRACKET_SYMBOLTYPE_NATNUM16
:
660 case BRACKET_SYMBOLTYPE_NATNUM17
:
661 case BRACKET_SYMBOLTYPE_NATNUM18
:
662 case BRACKET_SYMBOLTYPE_NATNUM19
:
669 OUString
SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer
& rString
, sal_Int32
& nPos
,
670 LanguageType
& nLang
, const LocaleType
& aTmpLocale
)
673 /* TODO: this could be enhanced to allow other possible locale dependent
674 * calendars and numerals. BUT only if our locale data allows it! For LCID
675 * numerals and calendars see
676 * http://office.microsoft.com/en-us/excel/HA010346351033.aspx */
677 if (MsLangId::getRealLanguage( aTmpLocale
.meLanguage
) == LANGUAGE_THAI
)
679 // Numeral shape code "D" = Thai digits.
680 if (aTmpLocale
.mnNumeralShape
== 0xD)
682 rString
.insert( nPos
, "[NatNum1]");
684 // Calendar type code "07" = Thai Buddhist calendar, insert this after
685 // all prefixes have been consumed as it is actually a format modifier
687 if (aTmpLocale
.mnCalendarType
== 0x07)
689 // Currently calendars are tied to the locale of the entire number
690 // format, e.g. [~buddhist] in en_US doesn't work.
691 // => Having different locales in sub formats does not work!
692 /* TODO: calendars could be tied to a sub format's NatNum info
693 * instead, or even better be available for any locale. Needs a
694 * different implementation of GetCal() and locale data calendars.
696 // If this is not Thai yet, make it so.
697 if (MsLangId::getRealLanguage( maLocale
.meLanguage
) != LANGUAGE_THAI
)
699 maLocale
= aTmpLocale
;
700 nLang
= maLocale
.meLanguage
= LANGUAGE_THAI
;
702 sCalendar
="[~buddhist]";
709 SvNumberformat::SvNumberformat(OUString
& rString
,
710 ImpSvNumberformatScan
* pSc
,
711 ImpSvNumberInputScan
* pISc
,
712 sal_Int32
& nCheckPos
,
716 , nNewStandardDefined(0)
719 OUStringBuffer
sBuff(rString
);
721 // If the group (AKA thousand) separator is a Non-Breaking Space (French)
722 // replace all occurrences by a simple space.
723 // The tokens will be changed to the LocaleData separator again later on.
724 const sal_Unicode cNBSp
= 0xA0;
725 const OUString
& rThSep
= GetFormatter().GetNumThousandSep();
726 if ( rThSep
.getLength() == 1 && rThSep
[0] == cNBSp
)
728 sBuff
.replace( cNBSp
, ' ');
731 if (rScan
.GetConvertMode())
733 maLocale
.meLanguage
= rScan
.GetNewLnge();
734 eLan
= maLocale
.meLanguage
; // Wechsel auch zurueckgeben
738 maLocale
.meLanguage
= eLan
;
744 eOp1
= NUMBERFORMAT_OP_NO
;
745 eOp2
= NUMBERFORMAT_OP_NO
;
746 eType
= NUMBERFORMAT_DEFINED
;
748 bool bCancel
= false;
749 bool bCondition
= false;
755 // Split into 4 sub formats
757 for ( nIndex
= 0; nIndex
< 4 && !bCancel
; nIndex
++ )
759 // Original language/country may have to be reestablished
760 if (rScan
.GetConvertMode())
762 (rScan
.GetNumberformatter())->ChangeIntl(rScan
.GetTmpLnge());
764 OUString sInsertCalendar
; // a calendar resulting from parsing LCID
766 nPosOld
= nPos
; // Start position of substring
767 // first get bracketed prefixes; e.g. conditions, color
770 eSymbolType
= ImpNextSymbol(sBuff
, nPos
, sStr
);
771 if (eSymbolType
> 0) // condition
773 if ( nIndex
== 0 && !bCondition
)
776 eOp1
= (SvNumberformatLimitOps
) eSymbolType
;
778 else if ( nIndex
== 1 && bCondition
)
780 eOp2
= (SvNumberformatLimitOps
) eSymbolType
;
784 bCancel
= true; // break for
790 sal_Int32 nAnzChars
= ImpGetNumber(sBuff
, nPos
, sStr
);
793 short F_Type
= NUMBERFORMAT_UNDEFINED
;
794 if (!pISc
->IsNumberFormat(sStr
,F_Type
,fNumber
) ||
795 ( F_Type
!= NUMBERFORMAT_NUMBER
&&
796 F_Type
!= NUMBERFORMAT_SCIENTIFIC
) )
799 nPos
= nPos
- nAnzChars
;
800 sBuff
.remove(nPos
, nAnzChars
);
801 sBuff
.insert(nPos
, (sal_Unicode
)'0');
808 sBuff
.insert(nPos
++,(sal_Unicode
)'0');
818 if ( sBuff
[nPos
] == ']' )
824 bCancel
= true; // break for
828 nPosOld
= nPos
; // position before string
830 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
832 OUString
sSymbol( sStr
);
833 switch ( eSymbolType
)
835 case BRACKET_SYMBOLTYPE_COLOR
:
836 if ( NumFor
[nIndex
].GetColor() != NULL
)
837 { // error, more than one color
838 bCancel
= true; // break for
843 Color
* pColor
= pSc
->GetColor( sStr
);
844 NumFor
[nIndex
].SetColor( pColor
, sStr
);
847 bCancel
= true; // break for
852 case BRACKET_SYMBOLTYPE_NATNUM0
:
853 case BRACKET_SYMBOLTYPE_NATNUM1
:
854 case BRACKET_SYMBOLTYPE_NATNUM2
:
855 case BRACKET_SYMBOLTYPE_NATNUM3
:
856 case BRACKET_SYMBOLTYPE_NATNUM4
:
857 case BRACKET_SYMBOLTYPE_NATNUM5
:
858 case BRACKET_SYMBOLTYPE_NATNUM6
:
859 case BRACKET_SYMBOLTYPE_NATNUM7
:
860 case BRACKET_SYMBOLTYPE_NATNUM8
:
861 case BRACKET_SYMBOLTYPE_NATNUM9
:
862 case BRACKET_SYMBOLTYPE_NATNUM10
:
863 case BRACKET_SYMBOLTYPE_NATNUM11
:
864 case BRACKET_SYMBOLTYPE_NATNUM12
:
865 case BRACKET_SYMBOLTYPE_NATNUM13
:
866 case BRACKET_SYMBOLTYPE_NATNUM14
:
867 case BRACKET_SYMBOLTYPE_NATNUM15
:
868 case BRACKET_SYMBOLTYPE_NATNUM16
:
869 case BRACKET_SYMBOLTYPE_NATNUM17
:
870 case BRACKET_SYMBOLTYPE_NATNUM18
:
871 case BRACKET_SYMBOLTYPE_NATNUM19
:
872 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
874 bCancel
= true; // break for
880 //! eSymbolType is negative
881 sal_uInt8 nNum
= (sal_uInt8
)(0 - (eSymbolType
- BRACKET_SYMBOLTYPE_NATNUM0
));
882 sStr
+= OUString::valueOf( (sal_Int32
)nNum
);
883 NumFor
[nIndex
].SetNatNumNum( nNum
, false );
886 case BRACKET_SYMBOLTYPE_DBNUM1
:
887 case BRACKET_SYMBOLTYPE_DBNUM2
:
888 case BRACKET_SYMBOLTYPE_DBNUM3
:
889 case BRACKET_SYMBOLTYPE_DBNUM4
:
890 case BRACKET_SYMBOLTYPE_DBNUM5
:
891 case BRACKET_SYMBOLTYPE_DBNUM6
:
892 case BRACKET_SYMBOLTYPE_DBNUM7
:
893 case BRACKET_SYMBOLTYPE_DBNUM8
:
894 case BRACKET_SYMBOLTYPE_DBNUM9
:
895 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
897 bCancel
= true; // break for
903 //! eSymbolType is negative
904 sal_uInt8 nNum
= (sal_uInt8
)(1 - (eSymbolType
- BRACKET_SYMBOLTYPE_DBNUM1
));
905 sStr
+= OUString((sal_Unicode
)('0' + nNum
));
906 NumFor
[nIndex
].SetNatNumNum( nNum
, true );
909 case BRACKET_SYMBOLTYPE_LOCALE
:
910 if ( NumFor
[nIndex
].GetNatNum().GetLang() != LANGUAGE_DONTKNOW
||
911 sBuff
[nPos
-1] != ']' )
912 // Check also for ']' to avoid pulling in
913 // locale data for the preview string for not
914 // yet completed LCIDs in the dialog.
916 bCancel
= true; // break for
922 LocaleType
aTmpLocale( ImpGetLocaleType( sStr
, nTmp
));
923 if (aTmpLocale
.meLanguage
== LANGUAGE_DONTKNOW
)
925 bCancel
= true; // break for
930 // Only the first sub format's locale will be
931 // used as the format's overall locale.
932 // Sorts this also under the corresponding
933 // locale for the dialog.
934 // If we don't support the locale this would
935 // result in an unknown (empty) language
936 // listbox entry and the user would never see
938 if (nIndex
== 0 && (aTmpLocale
.meLanguage
== 0 ||
939 SvNumberFormatter::IsLocaleInstalled( aTmpLocale
.meLanguage
)))
941 maLocale
= aTmpLocale
;
942 eLan
= aTmpLocale
.meLanguage
; // return to caller
943 /* TODO: fiddle with scanner to make this
944 * known? A change in the locale may affect
945 * separators and keywords. On the other
946 * hand they may have been entered as used
947 * in the originating locale, there's no
948 * way to predict other than analyzing the
949 * format code, we assume here the current
950 * context is used, which is most likely
954 sStr
= "$-" + aTmpLocale
.generateCode();
955 NumFor
[nIndex
].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale
.meLanguage
));
957 // "$-NNCCLLLL" Numerals and Calendar
958 if (sSymbol
.getLength() > 6)
960 sInsertCalendar
= ImpObtainCalendarAndNumerals( sBuff
, nPos
, eLan
, aTmpLocale
);
962 /* NOTE: there can be only one calendar
963 * inserted so the last one wins, though
964 * our own calendar modifiers support
965 * multiple calendars within one sub format
966 * code if at different positions. */
979 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
982 sBuff
.insert(nPosOld
, sStr
);
983 nPos
= nPosOld
+ sStr
.getLength();
984 sBuff
.insert(nPos
, "]");
985 sBuff
.insert(nPosOld
, "[");
987 nPosOld
= nPos
; // position before string
991 nPos
= nPosOld
; // prefix removed for whatever reason
997 while ( !bCancel
&& lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) );
999 // The remaining format code string
1002 if (eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
)
1004 if (nIndex
== 1 && eOp1
== NUMBERFORMAT_OP_NO
)
1006 eOp1
= NUMBERFORMAT_OP_GT
; // undefined condition, default: > 0
1008 else if (nIndex
== 2 && eOp2
== NUMBERFORMAT_OP_NO
)
1010 eOp2
= NUMBERFORMAT_OP_LT
; // undefined condition, default: < 0
1018 if (!sInsertCalendar
.isEmpty())
1020 sStr
= sInsertCalendar
+ sStr
;
1022 sal_Int32 nStrPos
= pSc
->ScanFormat( sStr
);
1023 sal_uInt16 nAnz
= pSc
->GetAnzResStrings();
1024 if (nAnz
== 0) // error
1028 if (nStrPos
== 0) // ok
1030 // e.g. Thai T speciality
1031 if (pSc
->GetNatNumModifier() && !NumFor
[nIndex
].GetNatNum().IsSet())
1033 sStr
= "[NatNum" + OUString::valueOf( sal_Int32(pSc
->GetNatNumModifier())) + "]" + sStr
;
1034 NumFor
[nIndex
].SetNatNumNum( pSc
->GetNatNumModifier(), false );
1036 // #i53826# #i42727# For the Thai T speciality we need
1037 // to freeze the locale and immunize it against
1038 // conversions during exports, just in case we want to
1039 // save to Xcl. This disables the feature of being able
1040 // to convert a NatNum to another locale. You can't
1042 // FIXME: implement a specialized export conversion
1043 // that works on tokens (have to tokenize all first)
1044 // and doesn't use the format string and
1045 // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1046 // sc/source/filter/excel/xestyle.cxx
1047 // XclExpNumFmtBuffer::WriteFormatRecord().
1048 LanguageType eLanguage
;
1049 if (NumFor
[nIndex
].GetNatNum().GetNatNum() == 1 &&
1050 ((eLanguage
= MsLangId::getRealLanguage( eLan
)) == LANGUAGE_THAI
) &&
1051 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1053 sStr
= "[$-" + OUString::valueOf( sal_Int32(eLanguage
), 16 ).toAsciiUpperCase() + "]" + sStr
;
1054 NumFor
[nIndex
].SetNatNumLang( eLanguage
);
1056 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
1057 sBuff
.insert(nPosOld
, sStr
);
1058 nPos
= nPosOld
+ sStr
.getLength();
1059 if (nPos
< sBuff
.getLength())
1061 sBuff
.insert(nPos
, ";");
1064 NumFor
[nIndex
].Enlarge(nAnz
);
1065 pSc
->CopyInfo(&(NumFor
[nIndex
].Info()), nAnz
);
1069 eType
= (short) NumFor
[nIndex
].Info().eScannedType
;
1071 else if (nIndex
== 3)
1072 { // #77026# Everything recognized IS text
1073 NumFor
[nIndex
].Info().eScannedType
= NUMBERFORMAT_TEXT
;
1075 else if ( (short) NumFor
[nIndex
].Info().eScannedType
!= eType
)
1077 eType
= NUMBERFORMAT_DEFINED
;
1082 nCheckPos
= nPosOld
+ nStrPos
; // error in string
1083 bCancel
= true; // break for
1087 else if (eSymbolType
== BRACKET_SYMBOLTYPE_ERROR
) // error
1089 nCheckPos
= nPosOld
;
1092 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
1094 nCheckPos
= nPosOld
+ 1; // error, prefix in string
1095 bCancel
= true; // break for
1098 if ( bCancel
&& !nCheckPos
)
1100 nCheckPos
= 1; // nCheckPos is used as an error condition
1104 if ( NumFor
[nIndex
].GetNatNum().IsSet() &&
1105 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1107 NumFor
[nIndex
].SetNatNumLang( eLan
);
1110 if (sBuff
.getLength() == nPos
)
1112 if ( nIndex
== 2 && eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
&&
1113 sBuff
[nPos
- 1] == ';' )
1115 // #83510# A 4th subformat explicitly specified to be empty
1116 // hides any text. Need the type here for HasTextFormat()
1117 NumFor
[3].Info().eScannedType
= NUMBERFORMAT_TEXT
;
1121 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
1123 NumFor
[nIndex
].SetNatNumDate( (NumFor
[nIndex
].Info().eScannedType
& NUMBERFORMAT_DATE
) != 0 );
1127 if ( bCondition
&& !nCheckPos
)
1129 if ( nIndex
== 1 && NumFor
[0].GetCount() == 0 &&
1130 sBuff
[sBuff
.getLength() - 1] != ';' )
1132 // No format code => GENERAL but not if specified empty
1133 OUString
aAdd( pSc
->GetStandardName() );
1134 if ( !pSc
->ScanFormat( aAdd
) )
1136 sal_uInt16 nAnz
= pSc
->GetAnzResStrings();
1139 NumFor
[0].Enlarge(nAnz
);
1140 pSc
->CopyInfo( &(NumFor
[0].Info()), nAnz
);
1145 else if ( nIndex
== 1 && NumFor
[nIndex
].GetCount() == 0 &&
1146 sBuff
[sBuff
.getLength() - 1] != ';' &&
1147 (NumFor
[0].GetCount() > 1 ||
1148 (NumFor
[0].GetCount() == 1 &&
1149 NumFor
[0].Info().nTypeArray
[0] != NF_KEY_GENERAL
)) )
1151 // No trailing second subformat => GENERAL but not if specified empty
1152 // and not if first subformat is GENERAL
1153 OUString
aAdd( pSc
->GetStandardName() );
1154 if ( !pSc
->ScanFormat( aAdd
) )
1156 sal_uInt16 nAnz
= pSc
->GetAnzResStrings();
1159 NumFor
[nIndex
].Enlarge(nAnz
);
1160 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nAnz
);
1166 else if ( nIndex
== 2 && NumFor
[nIndex
].GetCount() == 0 &&
1167 sBuff
[sBuff
.getLength() - 1] != ';' &&
1168 eOp2
!= NUMBERFORMAT_OP_NO
)
1170 // No trailing third subformat => GENERAL but not if specified empty
1171 OUString
aAdd( pSc
->GetStandardName() );
1172 if ( !pSc
->ScanFormat( aAdd
) )
1174 sal_uInt16 nAnz
= pSc
->GetAnzResStrings();
1177 NumFor
[nIndex
].Enlarge(nAnz
);
1178 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nAnz
);
1185 rString
= sBuff
.makeStringAndClear();
1186 sFormatstring
= rString
;
1188 if (NumFor
[2].GetCount() == 0 && // kein 3. Teilstring
1189 eOp1
== NUMBERFORMAT_OP_GT
&& eOp2
== NUMBERFORMAT_OP_NO
&&
1190 fLimit1
== 0.0 && fLimit2
== 0.0)
1192 eOp1
= NUMBERFORMAT_OP_GE
; // 0 zum ersten Format dazu
1197 SvNumberformat::~SvNumberformat()
1201 //---------------------------------------------------------------------------
1203 //---------------------------------------------------------------------------
1204 // Zerlegt die Eingabe in Symbole fuer die weitere
1205 // Verarbeitung (Turing-Maschine).
1206 //---------------------------------------------------------------------------
1207 // Ausgangs Zustand = SsStart
1208 //---------------+-------------------+-----------------------+---------------
1209 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1210 //---------------+-------------------+-----------------------+---------------
1211 // SsStart | ; | Pos-- | SsGetString
1212 // | [ | Symbol += Zeichen | SsGetBracketed
1213 // | ] | Fehler | SsStop
1215 // | Sonst | Symbol += Zeichen | SsGetString
1216 //---------------+-------------------+-----------------------+---------------
1217 // SsGetString | ; | | SsStop
1218 // | Sonst | Symbol+=Zeichen |
1219 //---------------+-------------------+-----------------------+---------------
1220 // SsGetBracketed| <, > = | del [ |
1221 // | | Symbol += Zeichen | SsGetCon
1223 // | h, H, m, M, s, S | Symbol += Zeichen | SsGetTime
1224 // | sonst | del [ |
1225 // | | Symbol += Zeichen | SsGetPrefix
1226 //---------------+-------------------+-----------------------+---------------
1227 // SsGetTime | ] | Symbol += Zeichen | SsGetString
1228 // | h, H, m, M, s, S | Symbol += Zeichen, * | SsGetString
1229 // | sonst | del [; Symbol+=Zeichen| SsGetPrefix
1230 //---------------+-------------------+-----------------------+---------------
1231 // SsGetPrefix | ] | | SsStop
1232 // | sonst | Symbol += Zeichen |
1233 //---------------+-------------------+-----------------------+---------------
1234 // SsGetCon | >, = | Symbol+=Zeichen |
1236 // | sonst | Fehler | SsStop
1237 //---------------+-------------------+-----------------------+---------------
1238 // * : Sonderbedingung
1244 SsGetCon
, // condition
1245 SsGetString
, // format string
1246 SsGetPrefix
, // color or NatNumN
1247 SsGetTime
, // [HH] for time
1248 SsGetBracketed
// any [...] not decided yet
1251 // read a string until ']' and delete spaces in input
1253 sal_Int32
SvNumberformat::ImpGetNumber(OUStringBuffer
& rString
,
1257 sal_Int32 nStartPos
= nPos
;
1259 sal_Int32 nLen
= rString
.getLength();
1260 OUStringBuffer sBuffSymbol
;
1261 while ( nPos
< nLen
&& ((cToken
= rString
[nPos
]) != ']') )
1265 rString
.remove(nPos
,1);
1271 sBuffSymbol
.append(cToken
);
1274 sSymbol
= sBuffSymbol
.makeStringAndClear();
1275 return nPos
- nStartPos
;
1280 sal_Unicode
toUniChar(sal_uInt8 n
)
1291 return sal_Unicode(c
);
1294 bool IsCombiningSymbol( OUStringBuffer
& rStringBuffer
, sal_Int32 nPos
)
1299 switch (rStringBuffer
[nPos
])
1316 OUString
SvNumberformat::LocaleType::generateCode() const
1318 OUStringBuffer aBuf
;
1320 // TODO: We may re-enable this later. Don't remove it! --Kohei
1323 sal_uInt8 nVal
= mnNumeralShape
;
1324 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1326 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1327 if (n
|| aBuf
.getLength())
1329 aBuf
.append(toUniChar(n
));
1335 if (mnNumeralShape
|| mnCalendarType
)
1337 sal_uInt8 nVal
= mnCalendarType
;
1338 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1340 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1341 if (n
|| aBuf
.getLength())
1343 aBuf
.append(toUniChar(n
));
1350 sal_uInt16 n16
= static_cast<sal_uInt16
>(meLanguage
);
1351 for (sal_uInt8 i
= 0; i
< 4; ++i
)
1353 sal_uInt8 n
= static_cast<sal_uInt8
>((n16
& 0xF000) >> 12);
1354 // Omit leading zeros for consistency.
1355 if (n
|| aBuf
.getLength() || i
== 3)
1357 aBuf
.append(toUniChar(n
));
1362 return aBuf
.makeStringAndClear();
1365 SvNumberformat::LocaleType::LocaleType()
1368 , meLanguage(LANGUAGE_DONTKNOW
)
1372 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum
)
1375 , meLanguage(LANGUAGE_DONTKNOW
)
1377 meLanguage
= static_cast<LanguageType
>(nRawNum
& 0x0000FFFF);
1378 nRawNum
= (nRawNum
>> 16);
1379 mnCalendarType
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1380 nRawNum
= (nRawNum
>> 8);
1381 mnNumeralShape
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1385 SvNumberformat::LocaleType
SvNumberformat::ImpGetLocaleType(const OUString
& rString
, sal_Int32
& nPos
)
1387 sal_uInt32 nNum
= 0;
1388 sal_Unicode cToken
= 0;
1389 sal_Int32 nStart
= nPos
;
1390 sal_Int32 nLen
= rString
.getLength();
1391 while ( nPos
< nLen
&& (nPos
- nStart
< 8) && ((cToken
= rString
[nPos
]) != ']') )
1393 if ( '0' <= cToken
&& cToken
<= '9' )
1396 nNum
+= cToken
- '0';
1398 else if ( 'a' <= cToken
&& cToken
<= 'f' )
1401 nNum
+= cToken
- 'a' + 10;
1403 else if ( 'A' <= cToken
&& cToken
<= 'F' )
1406 nNum
+= cToken
- 'A' + 10;
1410 return LANGUAGE_DONTKNOW
;
1415 return (cToken
== ']' || nPos
== nLen
) ? LocaleType(nNum
) : LocaleType();
1418 static bool lcl_matchKeywordAndGetNumber( const OUString
& rString
, const sal_Int32 nPos
,
1419 const OUString
& rKeyword
, sal_Int32
& nNumber
)
1421 if (0 <= nPos
&& nPos
+ rKeyword
.getLength() < rString
.getLength() && rString
.matchIgnoreAsciiCase( rKeyword
, nPos
))
1423 nNumber
= rString
.copy( nPos
+ rKeyword
.getLength()).toInt32();
1433 short SvNumberformat::ImpNextSymbol(OUStringBuffer
& rString
,
1437 short eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1439 sal_Unicode cLetter
= ' '; // Zwischenergebnis
1440 sal_Int32 nLen
= rString
.getLength();
1441 ScanState eState
= SsStart
;
1442 OUStringBuffer sBuffSymbol
;
1444 const NfKeywordTable
& rKeywords
= rScan
.GetKeywords();
1445 while (nPos
< nLen
&& eState
!= SsStop
)
1447 cToken
= rString
[nPos
];
1454 eState
= SsGetBracketed
;
1455 sBuffSymbol
.append(cToken
);
1457 else if (cToken
== ';')
1459 eState
= SsGetString
;
1461 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1463 else if (cToken
== ']')
1466 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1468 else if (cToken
== ' ') // Skip Blanks
1471 rString
.remove(nPos
, 1);
1476 sBuffSymbol
.append(cToken
);
1477 eState
= SsGetString
;
1478 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1481 case SsGetBracketed
:
1487 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1488 sBuffSymbol
.append(cToken
);
1494 eSymbolType
= NUMBERFORMAT_OP_LT
;
1497 eSymbolType
= NUMBERFORMAT_OP_GT
;
1500 eSymbolType
= NUMBERFORMAT_OP_EQ
;
1507 rString
.remove(nPos
, 1);
1511 if ( rString
[nPos
] == '-' )
1514 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1515 eSymbolType
= BRACKET_SYMBOLTYPE_LOCALE
;
1516 eState
= SsGetPrefix
;
1519 { // currency as of SV_NUMBERFORMATTER_VERSION_NEW_CURR
1520 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1521 eState
= SsGetString
;
1523 sBuffSymbol
.append(cToken
);
1526 // calendarID as of SV_NUMBERFORMATTER_VERSION_CALENDAR
1527 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1528 sBuffSymbol
.append(cToken
);
1529 eState
= SsGetString
;
1533 const OUString
aNatNum("NATNUM");
1534 const OUString
aDBNum("DBNUM");
1535 const OUString
aBufStr( rString
.toString());
1536 sal_Int32 nNatNumNum
;
1538 if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aNatNum
, nNatNumNum
) &&
1539 0 <= nNatNumNum
&& nNatNumNum
<= 19 )
1541 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1542 sBuffSymbol
.append( aBufStr
.copy( --nPos
, aNatNum
.getLength()+1 ));
1543 nPos
+= aNatNum
.getLength()+1;
1544 //! SymbolType is negative
1545 eSymbolType
= (short) (BRACKET_SYMBOLTYPE_NATNUM0
- nNatNumNum
);
1546 eState
= SsGetPrefix
;
1548 else if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aDBNum
, nDBNum
) &&
1549 '1' <= nDBNum
&& nDBNum
<= '9' )
1551 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1552 sBuffSymbol
.append( aBufStr
.copy( --nPos
, aDBNum
.getLength()+1 ));
1553 nPos
+= aDBNum
.getLength()+1;
1554 //! SymbolType is negative
1555 eSymbolType
= sal::static_int_cast
< short >( BRACKET_SYMBOLTYPE_DBNUM1
- (nDBNum
- '1'));
1556 eState
= SsGetPrefix
;
1560 sal_Unicode cUpper
= rChrCls().uppercase( aBufStr
, nPos
-1, 1)[0];
1561 if ( cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1562 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1563 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1565 sBuffSymbol
.append(cToken
);
1571 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1572 sBuffSymbol
.append(cToken
);
1573 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1574 eState
= SsGetPrefix
;
1581 if (cToken
== ';' && (nPos
< 2 || !IsCombiningSymbol( rString
, nPos
-2)))
1587 sBuffSymbol
.append(cToken
);
1593 sBuffSymbol
.append(cToken
);
1594 eState
= SsGetString
;
1595 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1599 sal_Unicode cUpper
= rChrCls().uppercase(rString
.toString(), nPos
-1, 1)[0];
1600 if (cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1601 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1602 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1604 if (cLetter
== cToken
)
1606 sBuffSymbol
.append(cToken
);
1611 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1612 sBuffSymbol
.append(cToken
);
1613 eState
= SsGetPrefix
;
1618 sBuffSymbol
.stripStart((sal_Unicode
)'[');
1619 sBuffSymbol
.append(cToken
);
1620 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1621 eState
= SsGetPrefix
;
1630 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1635 sBuffSymbol
.append(cToken
);
1638 eSymbolType
= NUMBERFORMAT_OP_NE
;
1643 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1649 sBuffSymbol
.append(cToken
);
1651 eSymbolType
= NUMBERFORMAT_OP_LE
;
1653 else if (cLetter
== '>')
1655 sBuffSymbol
.append(cToken
);
1657 eSymbolType
= NUMBERFORMAT_OP_GE
;
1662 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1667 rString
.remove(nPos
,1);
1683 sBuffSymbol
.append(cToken
);
1690 sSymbol
= sBuffSymbol
.makeStringAndClear();
1694 NfHackConversion
SvNumberformat::Load( SvStream
& rStream
,
1695 ImpSvNumMultipleReadHeader
& rHdr
,
1696 SvNumberFormatter
* pHackConverter
,
1697 ImpSvNumberInputScan
& rISc
)
1700 sal_uInt16 nOp1
, nOp2
;
1701 sFormatstring
= SvNumberformat::LoadString( rStream
);
1702 sal_Bool bStreamStandard
, bStreamUsed
;
1703 rStream
>> eType
>> fLimit1
>> fLimit2
1704 >> nOp1
>> nOp2
>> bStreamStandard
>> bStreamUsed
;
1705 bStandard
= bStreamStandard
;
1706 bIsUsed
= bStreamUsed
;
1707 NfHackConversion eHackConversion
= NF_CONVERT_NONE
;
1708 bool bOldConvert
= false;
1709 LanguageType eOldTmpLang
= 0;
1710 LanguageType eOldNewLang
= 0;
1711 if ( pHackConverter
)
1713 // werden nur hierbei gebraucht
1714 bOldConvert
= rScan
.GetConvertMode();
1715 eOldTmpLang
= rScan
.GetTmpLnge();
1716 eOldNewLang
= rScan
.GetNewLnge();
1718 OUString aLoadedColorName
;
1719 for (sal_uInt16 i
= 0; i
< 4; i
++)
1721 NumFor
[i
].Load( rStream
, rScan
, aLoadedColorName
);
1722 if ( pHackConverter
&& eHackConversion
== NF_CONVERT_NONE
)
1724 //! HACK! ER 29.07.97 13:52
1725 // leider wurde nicht gespeichert, was SYSTEM on Save wirklich war :-/
1726 // aber immerhin wird manchmal fuer einen Entry FARBE oder COLOR gespeichert..
1727 // System-German FARBE nach System-xxx COLOR umsetzen und vice versa,
1728 //! geht davon aus, dass onSave nur GERMAN und ENGLISH KeyWords in
1729 //! ImpSvNumberformatScan existierten
1730 if ( !aLoadedColorName
.isEmpty() &&
1731 !NumFor
[i
].GetColor() &&
1732 aLoadedColorName
!= rScan
.GetColorString() )
1734 if ( rScan
.GetColorString() == "FARBE" )
1735 { // English -> German
1736 eHackConversion
= NF_CONVERT_ENGLISH_GERMAN
;
1737 rScan
.GetNumberformatter()->ChangeIntl( LANGUAGE_ENGLISH_US
);
1738 rScan
.SetConvertMode( LANGUAGE_ENGLISH_US
, LANGUAGE_GERMAN
);
1741 { // German -> English
1742 eHackConversion
= NF_CONVERT_GERMAN_ENGLISH
;
1743 rScan
.GetNumberformatter()->ChangeIntl( LANGUAGE_GERMAN
);
1744 rScan
.SetConvertMode( LANGUAGE_GERMAN
, LANGUAGE_ENGLISH_US
);
1746 OUString aColorName
= NumFor
[i
].GetColorName();
1747 const Color
* pColor
= rScan
.GetColor( aColorName
);
1748 if ( !pColor
&& aLoadedColorName
== aColorName
)
1750 eHackConversion
= NF_CONVERT_NONE
;
1752 rScan
.GetNumberformatter()->ChangeIntl( LANGUAGE_SYSTEM
);
1753 rScan
.SetConvertMode( eOldTmpLang
, eOldNewLang
);
1754 rScan
.SetConvertMode( bOldConvert
);
1758 eOp1
= (SvNumberformatLimitOps
) nOp1
;
1759 eOp2
= (SvNumberformatLimitOps
) nOp2
;
1760 OUString aComment
; // wird nach dem NewCurrency-Geraffel richtig gesetzt
1761 if ( rHdr
.BytesLeft() )
1763 // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD
1764 aComment
= SvNumberformat::LoadString( rStream
);
1765 rStream
>> nNewStandardDefined
;
1768 sal_Int32 nNewCurrencyEnd
= -1;
1769 bool bNewCurrencyComment
= ( aComment
.getLength() > 1 && aComment
[0] == cNewCurrencyMagic
&&
1770 (nNewCurrencyEnd
= aComment
.indexOf( cNewCurrencyMagic
, 1 )) >= 0 );
1771 bool bNewCurrencyLoaded
= false;
1772 bool bNewCurrency
= false;
1775 while ( rHdr
.BytesLeft() && bGoOn
)
1777 // as of SV_NUMBERFORMATTER_VERSION_NEW_CURR
1779 sal_Bool bStreamCurr
;
1783 case nNewCurrencyVersionId
:
1784 bNewCurrencyLoaded
= true;
1785 rStream
>> bStreamCurr
;
1786 bNewCurrency
= bStreamCurr
;
1789 for ( sal_uInt16 j
=0; j
<4; j
++ )
1791 NumFor
[j
].LoadNewCurrencyMap( rStream
);
1795 case nNewStandardFlagVersionId
:
1796 rStream
>> bStreamStandard
; // the real standard flag
1797 bStandard
= bStreamStandard
;
1800 SAL_WARN( "svl.numbers", "SvNumberformat::Load: unknown header bytes left nId" );
1801 bGoOn
= false; // stop reading unknown stream left over of newer versions
1802 // Would be nice to have multiple read/write headers instead
1803 // but old versions wouldn't know it, TLOT.
1808 if ( bNewCurrencyLoaded
)
1810 if ( bNewCurrency
&& bNewCurrencyComment
)
1811 { // original Formatstring und Kommentar wiederherstellen
1812 sFormatstring
= aComment
.copy( 1, nNewCurrencyEnd
-1 );
1813 if(nNewCurrencyEnd
+ 1 < aComment
.getLength())
1815 aComment
= aComment
.copy(nNewCurrencyEnd
+ 1 );
1823 else if ( bNewCurrencyComment
)
1825 // neu, aber mit Version vor SV_NUMBERFORMATTER_VERSION_NEW_CURR gespeichert
1826 // original Formatstring und Kommentar wiederherstellen
1827 sFormatstring
= aComment
.copy( 1, nNewCurrencyEnd
- 1 );
1828 if(nNewCurrencyEnd
+ 1 < aComment
.getLength())
1830 aComment
= aComment
.copy(nNewCurrencyEnd
+ 1 );
1837 short nDefined
= ( eType
& NUMBERFORMAT_DEFINED
);
1838 sal_uInt16 nNewStandard
= nNewStandardDefined
;
1840 OUString
aStr( sFormatstring
);
1841 sal_Int32 nCheckPos
= 0;
1842 SvNumberformat
* pFormat
= new SvNumberformat( aStr
, &rScan
, &rISc
,
1843 nCheckPos
, maLocale
.meLanguage
, bStandard
);
1844 DBG_ASSERT( !nCheckPos
, "SvNumberformat::Load: NewCurrencyRescan nCheckPos" );
1845 ImpCopyNumberformat( *pFormat
);
1847 // Zustaende wiederherstellen
1851 SetNewStandardDefined( nNewStandard
);
1854 SetComment( aComment
);
1856 if ( eHackConversion
!= NF_CONVERT_NONE
)
1858 //! und weiter mit dem HACK!
1859 switch ( eHackConversion
)
1861 case NF_CONVERT_ENGLISH_GERMAN
:
1862 ConvertLanguage( *pHackConverter
,
1863 LANGUAGE_ENGLISH_US
, LANGUAGE_GERMAN
, true );
1865 case NF_CONVERT_GERMAN_ENGLISH
:
1866 ConvertLanguage( *pHackConverter
,
1867 LANGUAGE_GERMAN
, LANGUAGE_ENGLISH_US
, true );
1870 SAL_WARN( "svl.numbers", "SvNumberformat::Load: eHackConversion unknown" );
1873 return eHackConversion
;
1876 void SvNumberformat::ConvertLanguage( SvNumberFormatter
& rConverter
,
1877 LanguageType eConvertFrom
,
1878 LanguageType eConvertTo
, bool bSystem
)
1880 sal_Int32 nCheckPos
;
1882 short nType
= eType
;
1883 OUString
aFormatString( sFormatstring
);
1886 rConverter
.PutandConvertEntrySystem( aFormatString
, nCheckPos
, nType
,
1887 nKey
, eConvertFrom
, eConvertTo
);
1891 rConverter
.PutandConvertEntry( aFormatString
, nCheckPos
, nType
,
1892 nKey
, eConvertFrom
, eConvertTo
);
1894 const SvNumberformat
* pFormat
= rConverter
.GetEntry( nKey
);
1895 DBG_ASSERT( pFormat
, "SvNumberformat::ConvertLanguage: Conversion ohne Format" );
1898 ImpCopyNumberformat( *pFormat
);
1899 // aus Formatter/Scanner uebernommene Werte zuruecksetzen
1902 maLocale
.meLanguage
= LANGUAGE_SYSTEM
;
1904 // pColor zeigt noch auf Tabelle in temporaerem Formatter/Scanner
1905 for ( sal_uInt16 i
= 0; i
< 4; i
++ )
1907 OUString aColorName
= NumFor
[i
].GetColorName();
1908 Color
* pColor
= rScan
.GetColor( aColorName
);
1909 NumFor
[i
].SetColor( pColor
, aColorName
);
1915 OUString
SvNumberformat::LoadString( SvStream
& rStream
)
1917 CharSet eStream
= rStream
.GetStreamCharSet();
1918 OString aStr
= read_lenPrefixed_uInt8s_ToOString
<sal_uInt16
>(rStream
);
1919 sal_Char cStream
= NfCurrencyEntry::GetEuroSymbol( eStream
);
1920 if (aStr
.indexOf(cStream
) < 0)
1922 // simple conversion to unicode
1923 return OStringToOUString(aStr
, eStream
);
1925 sal_Unicode cSource
= OUString(&cStream
, 1, eStream
).toChar();
1926 sal_Unicode cTarget
= NfCurrencyEntry::GetEuroSymbol();
1927 OUStringBuffer
aBuf(OStringToOUString(aStr
, eStream
));
1928 aBuf
.replace(cSource
, cTarget
);
1930 return aBuf
.makeStringAndClear();
1933 void SvNumberformat::Save( SvStream
& rStream
, ImpSvNumMultipleWriteHeader
& rHdr
) const
1935 OUString
aFormatstring( sFormatstring
);
1936 OUStringBuffer
aComment( sComment
.getLength() + sFormatstring
.getLength() + 2 );
1938 bool bNewCurrency
= HasNewCurrency();
1941 // SV_NUMBERFORMATTER_VERSION_NEW_CURR im Kommentar speichern
1942 aComment
.insert( 0, cNewCurrencyMagic
);
1943 aComment
.insert( 0, cNewCurrencyMagic
);
1944 aComment
.insert( 1, aFormatstring
);
1945 Build50Formatstring( aFormatstring
); // alten Formatstring generieren
1948 // old SO5 versions do behave strange (no output) if standard flag is set
1949 // on formats not prepared for it (not having the following exact types)
1950 bool bOldStandard
= bStandard
;
1955 case NUMBERFORMAT_NUMBER
:
1956 case NUMBERFORMAT_DATE
:
1957 case NUMBERFORMAT_TIME
:
1958 case NUMBERFORMAT_DATETIME
:
1959 case NUMBERFORMAT_PERCENT
:
1960 case NUMBERFORMAT_SCIENTIFIC
:
1964 bOldStandard
= false;
1969 rStream
.WriteUniOrByteString( aFormatstring
, rStream
.GetStreamCharSet() );
1970 rStream
<< eType
<< fLimit1
<< fLimit2
<< (sal_uInt16
) eOp1
<< (sal_uInt16
) eOp2
1971 << sal_Bool(bOldStandard
) << sal_Bool(bIsUsed
);
1972 for (sal_uInt16 i
= 0; i
< 4; i
++)
1974 NumFor
[i
].Save(rStream
);
1976 // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD
1977 rStream
.WriteUniOrByteString( aComment
.makeStringAndClear(), rStream
.GetStreamCharSet() );
1978 rStream
<< nNewStandardDefined
;
1979 // ab SV_NUMBERFORMATTER_VERSION_NEW_CURR
1980 rStream
<< nNewCurrencyVersionId
;
1981 rStream
<< sal_Bool(bNewCurrency
);
1984 for ( sal_uInt16 j
=0; j
<4; j
++ )
1986 NumFor
[j
].SaveNewCurrencyMap( rStream
);
1990 // the real standard flag to load with versions >638 if different
1991 if ( bStandard
!= bOldStandard
)
1993 rStream
<< nNewStandardFlagVersionId
;
1994 rStream
<< (sal_Bool
)bStandard
;
2000 bool SvNumberformat::HasNewCurrency() const
2002 for ( sal_uInt16 j
=0; j
<4; j
++ )
2004 if ( NumFor
[j
].HasNewCurrency() )
2012 bool SvNumberformat::GetNewCurrencySymbol( OUString
& rSymbol
,
2013 OUString
& rExtension
) const
2015 for ( sal_uInt16 j
=0; j
<4; j
++ )
2017 if ( NumFor
[j
].GetNewCurrencySymbol( rSymbol
, rExtension
) )
2028 OUString
SvNumberformat::StripNewCurrencyDelimiters( const OUString
& rStr
,
2032 OUString
aSource(rStr
);
2033 sal_Int32 nStartPos
, nPos
, nLen
;
2034 nLen
= aSource
.getLength();
2036 while ( (nPos
= aSource
.indexOf( "[$", nStartPos
)) >= 0 )
2039 if ( (nEnd
= GetQuoteEnd( aSource
, nPos
)) >= 0 )
2041 aTmp
+= aSource
.copy( nStartPos
, ++nEnd
- nStartPos
);
2046 aTmp
+= aSource
.copy( nStartPos
, nPos
- nStartPos
);
2047 nStartPos
= nPos
+ 2;
2049 nEnd
= nStartPos
- 1;
2052 nDash
= aSource
.indexOf( '-', ++nEnd
);
2054 while ( (nEnd
= GetQuoteEnd( aSource
, nDash
)) >= 0 );
2056 nEnd
= nStartPos
- 1;
2059 nClose
= aSource
.indexOf( ']', ++nEnd
);
2061 while ( (nEnd
= GetQuoteEnd( aSource
, nClose
)) >= 0 );
2065 /* there should always be a closing ]
2066 * but the old String class would have hidden
2067 * that. so be conservative too
2073 if(nDash
>= 0 && nDash
< nClose
)
2077 if ( !bQuoteSymbol
|| aSource
[ nStartPos
] == '"' )
2079 aTmp
+= aSource
.copy( nStartPos
, nPos
- nStartPos
);
2084 aTmp
+= aSource
.copy( nStartPos
, nPos
- nStartPos
);
2087 nStartPos
= nClose
+ 1;
2090 if ( nLen
> nStartPos
)
2092 aTmp
+= aSource
.copy( nStartPos
, nLen
- nStartPos
);
2097 void SvNumberformat::Build50Formatstring( OUString
& rStr
) const
2099 rStr
= StripNewCurrencyDelimiters( sFormatstring
, true );
2102 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUStringBuffer
& OutString
)
2105 ImpGetOutputStandard(fNumber
, sTemp
);
2109 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUString
& OutString
)
2111 sal_uInt16 nStandardPrec
= rScan
.GetStandardPrec();
2113 if ( fabs(fNumber
) > 1.0E15
) // #58531# war E16
2115 nStandardPrec
= ::std::min(nStandardPrec
, static_cast<sal_uInt16
>(14)); // limits to 14 decimals
2116 OutString
= ::rtl::math::doubleToUString( fNumber
,
2117 rtl_math_StringFormat_E
, nStandardPrec
/*2*/,
2118 GetFormatter().GetNumDecimalSep()[0]);
2122 ImpGetOutputStdToPrecision(fNumber
, OutString
, nStandardPrec
);
2126 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber
, OUString
& rOutString
, sal_uInt16 nPrecision
) const
2128 // Make sure the precision doesn't go over the maximum allowable precision.
2129 nPrecision
= ::std::min(UPPER_PRECISION
, nPrecision
);
2133 // debugger test case for ANSI standard correctness
2135 // expect 0.00123 OK
2136 aTest
= ::rtl::math::doubleToUString( 0.001234567,
2137 rtl_math_StringFormat_G
, 3, '.', true );
2139 aTest
= ::rtl::math::doubleToUString( 123.4567,
2140 rtl_math_StringFormat_G
, 3, '.', true );
2142 aTest
= ::rtl::math::doubleToUString( 123.4567,
2143 rtl_math_StringFormat_G
, 4, '.', true );
2144 // expect 1e+03 (as 999.6 rounded to 3 significant digits results in
2145 // 1000 with an exponent equal to significant digits)
2146 // Currently (24-Jan-2003) we do fail in this case and output 1000
2147 // instead, negligible.
2148 aTest
= ::rtl::math::doubleToUString( 999.6,
2149 rtl_math_StringFormat_G
, 3, '.', true );
2150 // expect what? result is 1.2e+004
2151 aTest
= ::rtl::math::doubleToUString( 12345.6789,
2152 rtl_math_StringFormat_G
, -3, '.', true );
2156 // We decided to strip trailing zeros unconditionally, since binary
2157 // double-precision rounding error makes it impossible to determine e.g.
2158 // whether 844.10000000000002273737 is what the user has typed, or the
2159 // user has typed 844.1 but IEEE 754 represents it that way internally.
2161 rOutString
= ::rtl::math::doubleToUString( rNumber
,
2162 rtl_math_StringFormat_F
, nPrecision
/*2*/,
2163 GetFormatter().GetNumDecimalSep()[0], true );
2164 if (rOutString
[0] == (sal_Unicode
)'-' &&
2165 comphelper::string::getTokenCount(rOutString
, '0') == rOutString
.getLength())
2167 rOutString
= comphelper::string::stripStart(rOutString
, '-'); // nicht -0
2169 rOutString
= impTransliterate(rOutString
, NumFor
[0].GetNatNum());
2172 void SvNumberformat::ImpGetOutputInputLine(double fNumber
, OUString
& OutString
)
2174 bool bModified
= false;
2175 if ( (eType
& NUMBERFORMAT_PERCENT
) && (fabs(fNumber
) < _D_MAX_D_BY_100
))
2192 OutString
= ::rtl::math::doubleToUString( fNumber
,
2193 rtl_math_StringFormat_Automatic
,
2194 rtl_math_DecimalPlaces_Max
,
2195 GetFormatter().GetNumDecimalSep()[0], true );
2197 if ( eType
& NUMBERFORMAT_PERCENT
&& bModified
)
2204 short SvNumberformat::ImpCheckCondition(double& fNumber
,
2206 SvNumberformatLimitOps eOp
)
2210 case NUMBERFORMAT_OP_NO
:
2212 case NUMBERFORMAT_OP_EQ
:
2213 return (short) (fNumber
== fLimit
);
2214 case NUMBERFORMAT_OP_NE
:
2215 return (short) (fNumber
!= fLimit
);
2216 case NUMBERFORMAT_OP_LT
:
2217 return (short) (fNumber
< fLimit
);
2218 case NUMBERFORMAT_OP_LE
:
2219 return (short) (fNumber
<= fLimit
);
2220 case NUMBERFORMAT_OP_GT
:
2221 return (short) (fNumber
> fLimit
);
2222 case NUMBERFORMAT_OP_GE
:
2223 return (short) (fNumber
>= fLimit
);
2229 bool SvNumberformat::GetOutputString(const OUString
& sString
,
2230 OUString
& OutString
,
2233 OUStringBuffer sOutBuff
;
2235 if (eType
& NUMBERFORMAT_TEXT
)
2239 else if (NumFor
[3].GetCount() > 0)
2245 *ppColor
= NULL
; // no change of color
2248 *ppColor
= NumFor
[nIx
].GetColor();
2249 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2251 if (rInfo
.eScannedType
== NUMBERFORMAT_TEXT
)
2253 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
2254 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
2256 switch (rInfo
.nTypeArray
[i
])
2258 case NF_SYMBOLTYPE_STAR
:
2261 sOutBuff
.append((sal_Unicode
) 0x1B);
2262 sOutBuff
.append(rInfo
.sStrArray
[i
][1]);
2266 case NF_SYMBOLTYPE_BLANK
:
2267 InsertBlanks( sOutBuff
, sOutBuff
.getLength(),
2268 rInfo
.sStrArray
[i
][1] );
2270 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
2271 case NF_SYMBOLTYPE_DEL
:
2272 sOutBuff
.append(sString
);
2275 sOutBuff
.append(rInfo
.sStrArray
[i
]);
2279 OutString
= sOutBuff
.makeStringAndClear();
2283 sal_uLong
SvNumberformat::ImpGGT(sal_uLong x
, sal_uLong y
)
2302 sal_uLong
SvNumberformat::ImpGGTRound(sal_uLong x
, sal_uLong y
)
2311 while ((double)z
/(double)y
> D_EPS
)
2323 void lcl_GetOutputStringScientific(double fNumber
, sal_uInt16 nCharCount
,
2324 const SvNumberFormatter
& rFormatter
, OUString
& rOutString
)
2326 bool bSign
= ::rtl::math::isSignBitSet(fNumber
);
2328 // 1.000E+015 (one digit and the decimal point, and the five chars for the exponential part, totalling 7).
2329 sal_uInt16 nPrec
= nCharCount
> 7 ? nCharCount
- 7 : 0;
2332 // Make room for the negative sign.
2335 nPrec
= ::std::min(nPrec
, static_cast<sal_uInt16
>(14)); // limit to 14 decimals.
2337 rOutString
= ::rtl::math::doubleToUString(fNumber
, rtl_math_StringFormat_E
,
2338 nPrec
, rFormatter
.GetNumDecimalSep()[0]);
2341 sal_Int32
lcl_GetForcedDenominator(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nAnz
)
2345 for( i
= 0; i
< nAnz
; i
++ )
2347 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC_FDIV
)
2349 aDiv
+= rInfo
.sStrArray
[i
];
2352 return aDiv
.toInt32();
2355 // TODO: More optimizations?
2356 void lcl_ForcedDenominator(sal_uLong
&nFrac
, sal_uLong
&nDiv
, sal_uLong nForcedDiv
)
2358 double fFrac
= (double)nFrac
/ (double)nDiv
;
2359 double fMultiplier
= (double)nForcedDiv
/ (double)nDiv
;
2360 nFrac
= (sal_uLong
)( (double)nFrac
* fMultiplier
);
2362 double fFracNew
= (double)nFrac
/ (double)nForcedDiv
;
2363 double fFracNew1
= (double)(nFrac
+ 1) / (double)nForcedDiv
;
2364 double fDiff
= fFrac
- fFracNew
;
2365 if( fDiff
> ( fFracNew1
- fFrac
) )
2374 sal_Int32
SvNumberformat::GetForcedDenominatorForType( sal_uInt16 nNumFor
) const
2376 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2377 sal_uInt16 nAnz
= NumFor
[nNumFor
].GetCount();
2378 return lcl_GetForcedDenominator( rInfo
, nAnz
);
2381 bool SvNumberformat::GetOutputString(double fNumber
, sal_uInt16 nCharCount
, OUString
& rOutString
) const
2383 using namespace std
;
2385 if (eType
!= NUMBERFORMAT_NUMBER
)
2389 double fTestNum
= fNumber
;
2390 bool bSign
= ::rtl::math::isSignBitSet(fTestNum
);
2393 fTestNum
= -fTestNum
;
2395 if (fTestNum
< EXP_LOWER_BOUND
)
2397 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2401 double fExp
= log10(fTestNum
);
2402 // Values < 1.0 always have one digit before the decimal point.
2403 sal_uInt16 nDigitPre
= fExp
>= 0.0 ? static_cast<sal_uInt16
>(ceil(fExp
)) : 1;
2407 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2411 sal_uInt16 nPrec
= nCharCount
>= nDigitPre
? nCharCount
- nDigitPre
: 0;
2414 // Subtract the negative sign.
2419 // Subtract the decimal point.
2422 ImpGetOutputStdToPrecision(fNumber
, rOutString
, nPrec
);
2423 if (rOutString
.getLength() > nCharCount
)
2425 // String still wider than desired. Switch to scientific notation.
2426 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2431 bool SvNumberformat::GetOutputString(double fNumber
,
2432 OUString
& OutString
,
2436 OUStringBuffer sBuff
;
2438 *ppColor
= NULL
; // keine Farbaenderung
2439 if (eType
& NUMBERFORMAT_LOGICAL
)
2443 OutString
= rScan
.GetTrueString();
2447 OutString
= rScan
.GetFalseString();
2451 if (eType
& NUMBERFORMAT_TEXT
)
2453 ImpGetOutputStandard(fNumber
, sBuff
);
2454 OutString
= sBuff
.makeStringAndClear();
2457 bool bHadStandard
= false;
2458 if (bStandard
) // einzelne Standardformate
2460 if (rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
) // alle Zahlformate InputLine
2462 ImpGetOutputInputLine(fNumber
, OutString
);
2467 case NUMBERFORMAT_NUMBER
: // Standardzahlformat
2468 if (rScan
.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION
)
2470 bool bSign
= ::rtl::math::isSignBitSet(fNumber
);
2473 if (!(fNumber
< 0.0))
2481 ImpGetOutputStdToPrecision(fNumber
, sTemp
, 10); // Use 10 decimals for general 'unlimited' format.
2482 sBuff
.append(sTemp
);
2484 if (fNumber
< EXP_LOWER_BOUND
)
2486 sal_Int32 nLen
= sBuff
.getLength();
2491 // #i112250# With the 10-decimal limit, small numbers are formatted as "0".
2492 // Switch to scientific in that case, too:
2493 if (nLen
> 11 || ((nLen
== 1 && sBuff
[0] == (sal_Unicode
)'0') && fNumber
!= 0.0))
2495 sal_uInt16 nStandardPrec
= rScan
.GetStandardPrec();
2496 nStandardPrec
= ::std::min(nStandardPrec
, static_cast<sal_uInt16
>(14)); // limits to 14 decimals
2497 sBuff
= ::rtl::math::doubleToUString( fNumber
,
2498 rtl_math_StringFormat_E
, nStandardPrec
/*2*/,
2499 GetFormatter().GetNumDecimalSep()[0], true);
2504 sBuff
.insert(0, (sal_Unicode
)'-');
2506 OutString
= sBuff
.makeStringAndClear();
2509 ImpGetOutputStandard(fNumber
, sBuff
);
2510 bHadStandard
= true;
2512 case NUMBERFORMAT_DATE
:
2513 bRes
|= ImpGetDateOutput(fNumber
, 0, sBuff
);
2514 bHadStandard
= true;
2516 case NUMBERFORMAT_TIME
:
2517 bRes
|= ImpGetTimeOutput(fNumber
, 0, sBuff
);
2518 bHadStandard
= true;
2520 case NUMBERFORMAT_DATETIME
:
2521 bRes
|= ImpGetDateTimeOutput(fNumber
, 0, sBuff
);
2522 bHadStandard
= true;
2526 if ( !bHadStandard
)
2528 sal_uInt16 nIx
; // Index des Teilformats
2529 short nCheck
= ImpCheckCondition(fNumber
, fLimit1
, eOp1
);
2530 if (nCheck
== -1 || nCheck
== 1) // nur 1 String oder True
2536 nCheck
= ImpCheckCondition(fNumber
, fLimit2
, eOp2
);
2537 if (nCheck
== -1 || nCheck
== 1)
2546 if (fNumber
< 0.0 &&
2547 ((nIx
== 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2548 (nIx
== 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2550 fNumber
= -fNumber
; // eliminate sign
2552 *ppColor
= NumFor
[nIx
].GetColor();
2553 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2554 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
2555 if (nAnz
== 0 && rInfo
.eScannedType
== NUMBERFORMAT_UNDEFINED
)
2557 return false; // leer => nichts
2559 else if (nAnz
== 0) // sonst Standard-Format
2561 ImpGetOutputStandard(fNumber
, sBuff
);
2562 OutString
= sBuff
.makeStringAndClear();
2565 switch (rInfo
.eScannedType
)
2567 case NUMBERFORMAT_TEXT
:
2568 case NUMBERFORMAT_DEFINED
:
2569 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
2571 switch (rInfo
.nTypeArray
[i
])
2573 case NF_SYMBOLTYPE_STAR
:
2576 sBuff
.append((sal_Unicode
) 0x1B);
2577 sBuff
.append(rInfo
.sStrArray
[i
][1]);
2581 case NF_SYMBOLTYPE_BLANK
:
2582 InsertBlanks(sBuff
, sBuff
.getLength(),
2583 rInfo
.sStrArray
[i
][1] );
2585 case NF_SYMBOLTYPE_STRING
:
2586 case NF_SYMBOLTYPE_CURRENCY
:
2587 sBuff
.append(rInfo
.sStrArray
[i
]);
2589 case NF_SYMBOLTYPE_THSEP
:
2590 if (rInfo
.nThousand
== 0)
2592 sBuff
.append(rInfo
.sStrArray
[i
]);
2600 case NUMBERFORMAT_DATE
:
2601 bRes
|= ImpGetDateOutput(fNumber
, nIx
, sBuff
);
2603 case NUMBERFORMAT_TIME
:
2604 bRes
|= ImpGetTimeOutput(fNumber
, nIx
, sBuff
);
2606 case NUMBERFORMAT_DATETIME
:
2607 bRes
|= ImpGetDateTimeOutput(fNumber
, nIx
, sBuff
);
2609 case NUMBERFORMAT_NUMBER
:
2610 case NUMBERFORMAT_PERCENT
:
2611 case NUMBERFORMAT_CURRENCY
:
2612 bRes
|= ImpGetNumberOutput(fNumber
, nIx
, sBuff
);
2614 case NUMBERFORMAT_FRACTION
:
2615 bRes
|= ImpGetFractionOutput(fNumber
, nIx
, sBuff
);
2617 case NUMBERFORMAT_SCIENTIFIC
:
2618 bRes
|= ImpGetScientificOutput(fNumber
, nIx
, sBuff
);
2622 OutString
= sBuff
.makeStringAndClear();
2626 bool SvNumberformat::ImpGetScientificOutput(double fNumber
,
2628 OUStringBuffer
& sStr
)
2633 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2634 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
2638 if (nIx
== 0) // nicht in hinteren
2640 bSign
= true; // Formaten
2645 sStr
= ::rtl::math::doubleToUString( fNumber
,
2646 rtl_math_StringFormat_E
,
2647 rInfo
.nCntPre
+ rInfo
.nCntPost
- 1, '.' );
2648 OUStringBuffer ExpStr
;
2650 sal_Int32 nExPos
= sStr
.indexOf((sal_Unicode
)'E');
2654 // split into mantisse and exponent and get rid of "E+" or "E-"
2655 sal_Int32 nExpStart
= nExPos
+ 1;
2657 switch ( sStr
[ nExpStart
] )
2666 ExpStr
= sStr
.toString().copy( nExpStart
); // part following the "E+"
2667 sStr
.truncate( nExPos
);
2668 // cut any decimal delimiter
2669 sal_Int32 index
= 0;
2671 while((index
= sStr
.indexOf((sal_Unicode
)'.', index
)) >= 0)
2673 sStr
.remove(index
, 1);
2675 if ( rInfo
.nCntPre
!= 1 ) // rescale Exp
2677 sal_Int32 nExp
= ExpStr
.toString().toInt32() * nExpSign
;
2679 nExp
-= (sal_Int32
)rInfo
.nCntPre
- 1;
2689 ExpStr
= OUString::valueOf( nExp
);
2693 sal_uInt16 j
= nAnz
-1; // last symbol
2694 sal_Int32 k
; // position in ExpStr
2695 sal_Int32 nZeros
= 0; // erase leading zeros
2697 bRes
|= ImpNumberFill(ExpStr
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_EXP
);
2699 while (nZeros
< k
&& ExpStr
[nZeros
] == (sal_Unicode
)'0')
2705 ExpStr
.remove( 0, nZeros
);
2710 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_EXP
)
2712 const OUString
& rStr
= rInfo
.sStrArray
[j
];
2715 ExpStr
.insert(0, (sal_Unicode
)'-');
2717 else if (rStr
.getLength() > 1 && rStr
[1] == (sal_Unicode
)'+')
2719 ExpStr
.insert(0, (sal_Unicode
)'+');
2721 ExpStr
.insert(0, rStr
[0]);
2731 // weiter Hauptzahl:
2738 k
= sStr
.getLength(); // hinter letzter Ziffer
2739 bRes
|= ImpNumberFillWithThousands(sStr
, fNumber
, k
, j
, nIx
,
2740 rInfo
.nCntPre
+ rInfo
.nCntPost
);
2744 sStr
.insert(0, (sal_Unicode
)'-');
2746 sStr
.append(ExpStr
);
2751 bool SvNumberformat::ImpGetFractionOutput(double fNumber
,
2753 OUStringBuffer
& sBuff
)
2756 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2757 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
2758 OUStringBuffer sStr
, sFrac
, sDiv
; // Strings, Wert fuer
2759 sal_uLong nFrac
, nDiv
; // Vorkommaanteil
2760 bool bSign
= false; // Zaehler und Nenner
2764 if (nIx
== 0) // nicht in hinteren
2765 bSign
= true; // Formaten
2769 double fNum
= floor(fNumber
); // Vorkommateil
2771 fNumber
-= fNum
; // Nachkommateil
2772 if (fNum
> _D_MAX_U_LONG_
|| rInfo
.nCntExp
> 9)
2775 sBuff
= rScan
.GetErrorString();
2778 if (rInfo
.nCntExp
== 0)
2780 SAL_WARN( "svl.numbers", "SvNumberformat:: Bruch, nCntExp == 0");
2785 sal_uLong nBasis
= ((sal_uLong
)floor( pow(10.0,rInfo
.nCntExp
))) - 1; // 9, 99, 999 ,...
2786 sal_uLong x0
, y0
, x1
, y1
;
2788 if (rInfo
.nCntExp
<= _MAX_FRACTION_PREC
)
2795 fNumber
-= (fNumber
- 0.5) * 2.0;
2801 // Einstieg in Farey-Serie
2803 x0
= (sal_uLong
) floor(fNumber
*nBasis
); // z.B. 2/9 <= x < 3/9
2804 if (x0
== 0) // => x0 = 2
2810 else if (x0
== (nBasis
-1)/2) // (b-1)/2, 1/2
2811 { // geht (nBasis ungerade)
2818 y0
= nBasis
; // 1/n; 1/(n-1)
2824 y0
= nBasis
; // z.B. 2/9 2/8
2827 double fUg
= (double) x0
/ (double) y0
;
2828 double fOg
= (double) x1
/ (double) y1
;
2829 sal_uLong nGgt
= ImpGGT(y0
, x0
); // x0/y0 kuerzen
2831 y0
/= nGgt
; // Einschachteln:
2838 // #i21648# GCC over-optimizes something resulting
2839 // in wrong fTest values throughout the loops.
2842 double fTest
= (double)x1
/(double)y1
;
2848 fTest
= (double)x1
/(double)y1
;
2850 while (fTest
< fUg
&& y1
> 1)
2853 fTest
= (double)x1
/(double)y1
;
2865 nGgt
= ImpGGT(y1
, x1
); // x1/y1 kuerzen
2868 if (x2
*y0
- x0
*y2
== 1 || y1
<= 1) // Test, ob x2/y2
2869 bStop
= true; // naechste Farey-Zahl
2882 flow
= (double)x0
/(double)y0
;
2883 fup
= (double)x1
/(double)y1
;
2884 while (fNumber
> fup
)
2886 sal_uLong x2
= ((y0
+nBasis
)/y1
)*x1
- x0
; // naechste Farey-Zahl
2887 sal_uLong y2
= ((y0
+nBasis
)/y1
)*y1
- y0
;
2894 fup
= (double)x1
/(double)y1
;
2896 if (fNumber
- flow
< fup
- fNumber
)
2906 if (bUpperHalf
) // Original restaur.
2908 if (nFrac
== 0 && nDiv
== 1) // 1/1
2914 nFrac
= nDiv
- nFrac
;
2918 else // grosse Nenner
2919 { // 0,1234->123/1000
2923 nFrac
= ((sal_uLong
)floor(0.5 + fNumber
* 10000000.0));
2924 nGgt
= ImpGGT(nDiv
, nFrac
);
2932 nGgt
= ImpGGTRound(nDiv
, nFrac
);
2942 nFrac
= ((sal_uLong
)floor(0.5 + fNumber
*
2943 pow(10.0,rInfo
.nCntExp
)));
2944 nGgt
= ImpGGTRound(nDiv
, nFrac
);
2953 if( sal_Int32 nForcedDiv
= lcl_GetForcedDenominator(NumFor
[nIx
].Info(), nAnz
) )
2955 lcl_ForcedDenominator(nFrac
, nDiv
, nForcedDiv
);
2963 if (rInfo
.nCntPre
== 0) // unechter Bruch
2965 double fNum1
= fNum
* (double)nDiv
+ (double)nFrac
;
2967 if (fNum1
> _D_MAX_U_LONG_
)
2969 sBuff
= rScan
.GetErrorString();
2972 nFrac
= (sal_uLong
) floor(fNum1
);
2974 else if (fNum
== 0.0 && nFrac
!= 0)
2980 sprintf( aBuf
, "%.f", fNum
); // simple rounded integer (#100211# - checked)
2981 sStr
.appendAscii( aBuf
);
2982 impTransliterate(sStr
, NumFor
[nIx
].GetNatNum());
2984 if (rInfo
.nCntPre
> 0 && nFrac
== 0)
2990 sFrac
= ImpIntToString( nIx
, nFrac
);
2991 sDiv
= ImpIntToString( nIx
, nDiv
);
2994 sal_uInt16 j
= nAnz
-1; // letztes Symbol->rueckw.
2995 sal_Int32 k
; // Nenner:
2997 bRes
|= ImpNumberFill(sDiv
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRAC
);
3000 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRAC
)
3002 if (rInfo
.nCntPre
> 0 && nFrac
== 0)
3004 sDiv
.insert(0, (sal_Unicode
)' ');
3008 sDiv
.insert(0, rInfo
.sStrArray
[j
][0]);
3026 bRes
|= ImpNumberFill(sFrac
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRACBLANK
);
3027 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRACBLANK
)
3029 sFrac
.insert(0, rInfo
.sStrArray
[j
]);
3047 k
= sStr
.getLength(); // hinter letzter Ziffer
3048 bRes
|= ImpNumberFillWithThousands(sStr
, fNumber
, k
, j
, nIx
,
3051 if (bSign
&& !(nFrac
== 0 && fNum
== 0.0))
3053 sBuff
.insert(0, (sal_Unicode
)'-'); // nicht -0
3056 sBuff
.append(sFrac
);
3061 bool SvNumberformat::ImpGetTimeOutput(double fNumber
,
3063 OUStringBuffer
& sBuff
)
3065 using namespace ::com::sun::star::i18n
;
3066 bool bCalendarSet
= false;
3067 double fNumberOrig
= fNumber
;
3078 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3079 if (rInfo
.bThousand
) // []-Format
3081 if (fNumber
> 1.0E10
) // zu gross
3083 sBuff
= rScan
.GetErrorString();
3089 fNumber
-= floor(fNumber
); // sonst Datum abtrennen
3093 if ( rScan
.GetStandardPrec() == 300 &&
3094 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< 7 )
3095 { // round at 7 decimals (+5 of 86400 == 12 significant digits)
3102 nCntPost
= rInfo
.nCntPost
;
3104 if (bSign
&& !rInfo
.bThousand
) // kein []-Format
3106 fNumber
= 1.0 - fNumber
; // "Kehrwert"
3108 double fTime
= fNumber
* 86400.0;
3109 fTime
= ::rtl::math::round( fTime
, int(nCntPost
) );
3110 if (bSign
&& fTime
== 0.0)
3112 bSign
= false; // nicht -00:00:00
3114 if( floor( fTime
) > _D_MAX_U_LONG_
)
3116 sBuff
= rScan
.GetErrorString();
3119 sal_uLong nSeconds
= (sal_uLong
)floor( fTime
);
3121 OUStringBuffer
sSecStr( ::rtl::math::doubleToUString( fTime
-nSeconds
,
3122 rtl_math_StringFormat_F
, int(nCntPost
), '.'));
3123 sSecStr
.stripStart((sal_Unicode
)'0');
3124 sSecStr
.stripStart((sal_Unicode
)'.');
3127 sSecStr
.stripEnd((sal_Unicode
)'0');
3128 for(sal_Int32 index
= sSecStr
.getLength(); index
< rInfo
.nCntPost
; ++index
)
3130 sSecStr
.append((sal_Unicode
)'0');
3132 impTransliterate(sSecStr
, NumFor
[nIx
].GetNatNum());
3133 nCntPost
= sSecStr
.getLength();
3137 impTransliterate(sSecStr
, NumFor
[nIx
].GetNatNum());
3140 sal_Int32 nSecPos
= 0; // Zum Ziffernweisen
3142 sal_uLong nHour
, nMin
, nSec
;
3143 if (!rInfo
.bThousand
) // kein [] Format
3145 nHour
= (nSeconds
/3600) % 24;
3146 nMin
= (nSeconds
%3600) / 60;
3149 else if (rInfo
.nThousand
== 3) // [ss]
3155 else if (rInfo
.nThousand
== 2) // [mm]:ss
3158 nMin
= nSeconds
/ 60;
3159 nSec
= nSeconds
% 60;
3161 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
3163 nHour
= nSeconds
/ 3600;
3164 nMin
= (nSeconds
%3600) / 60;
3169 // TODO What should these be set to?
3175 sal_Unicode cAmPm
= ' '; // a oder p
3176 if (rInfo
.nCntExp
) // AM/PM
3183 else if (nHour
< 12)
3196 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
3197 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
3200 switch (rInfo
.nTypeArray
[i
])
3202 case NF_SYMBOLTYPE_STAR
:
3205 sBuff
.append((sal_Unicode
)0x1B);
3206 sBuff
.append(rInfo
.sStrArray
[i
][1]);
3210 case NF_SYMBOLTYPE_BLANK
:
3211 InsertBlanks(sBuff
, sBuff
.getLength(),
3212 rInfo
.sStrArray
[i
][1] );
3214 case NF_SYMBOLTYPE_STRING
:
3215 case NF_SYMBOLTYPE_CURRENCY
:
3216 case NF_SYMBOLTYPE_DATESEP
:
3217 case NF_SYMBOLTYPE_TIMESEP
:
3218 case NF_SYMBOLTYPE_TIME100SECSEP
:
3219 sBuff
.append(rInfo
.sStrArray
[i
]);
3221 case NF_SYMBOLTYPE_DIGIT
:
3222 nLen
= ( bInputLine
&& i
> 0 &&
3223 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
3224 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
3225 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
3226 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
; j
++)
3228 sBuff
.append(sSecStr
[nSecPos
]);
3232 case NF_KEY_AMPM
: // AM/PM
3233 if ( !bCalendarSet
)
3235 double fDiff
= DateTime(*(rScan
.GetNullDate())) - GetCal().getEpochStart();
3236 fDiff
+= fNumberOrig
;
3237 GetCal().setLocalDateTime( fDiff
);
3238 bCalendarSet
= true;
3242 sBuff
.append(GetCal().getDisplayName(
3243 CalendarDisplayIndex::AM_PM
, AmPmValue::AM
, 0 ));
3247 sBuff
.append(GetCal().getDisplayName(
3248 CalendarDisplayIndex::AM_PM
, AmPmValue::PM
, 0 ));
3251 case NF_KEY_AP
: // A/P
3254 sBuff
.append((sal_Unicode
)'a');
3258 sBuff
.append((sal_Unicode
)'p');
3261 case NF_KEY_MI
: // M
3262 sBuff
.append(ImpIntToString( nIx
, nMin
));
3264 case NF_KEY_MMI
: // MM
3265 sBuff
.append(ImpIntToString( nIx
, nMin
, 2 ));
3268 sBuff
.append(ImpIntToString( nIx
, nHour
));
3270 case NF_KEY_HH
: // HH
3271 sBuff
.append(ImpIntToString( nIx
, nHour
, 2 ));
3274 sBuff
.append(ImpIntToString( nIx
, nSec
));
3276 case NF_KEY_SS
: // SS
3277 sBuff
.append(ImpIntToString( nIx
, nSec
, 2 ));
3283 if (bSign
&& rInfo
.bThousand
)
3285 sBuff
.insert(0, (sal_Unicode
)'-');
3291 /** If a day of month occurs within the format, the month name is in possessive
3292 genitive case if the day follows the month, and partitive case if the day
3293 precedes the month. If there is no day of month the nominative case (noun)
3294 is returned. Also if the month is immediately preceded or followed by a
3295 literal string other than space the nominative name is used, this prevents
3296 duplicated casing for MMMM\t\a and such in documents imported from (e.g.
3297 Finnish) Excel or older LibO/OOo releases.
3300 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3301 // inspection of month name around that one, that would enable different month
3302 // cases in one format. Though probably the most rare use case ever..
3304 sal_Int32
SvNumberformat::ImpUseMonthCase( int & io_nState
, const ImpSvNumFor
& rNumFor
, NfKeywordIndex eCodeType
) const
3306 using namespace ::com::sun::star::i18n
;
3309 bool bMonthSeen
= false;
3310 bool bDaySeen
= false;
3311 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3312 const sal_uInt16 nCount
= rNumFor
.GetCount();
3313 for (sal_uInt16 i
= 0; i
< nCount
&& io_nState
== 0; ++i
)
3316 switch (rInfo
.nTypeArray
[i
])
3332 if ((i
< nCount
-1 &&
3333 rInfo
.nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
3334 rInfo
.sStrArray
[i
+1][0] != ' ') ||
3335 (i
> 0 && rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
&&
3336 ((nLen
= rInfo
.sStrArray
[i
-1].getLength()) > 0) &&
3337 rInfo
.sStrArray
[i
-1][nLen
-1] != ' '))
3354 io_nState
= 1; // no day of month
3360 // no day of month or forced nominative
3364 return CalendarDisplayCode::SHORT_MONTH_NAME
;
3366 return CalendarDisplayCode::LONG_MONTH_NAME
;
3368 return CalendarDisplayCode::NARROW_MONTH_NAME
;
3373 // day of month follows month (the month's 17th)
3377 return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME
;
3379 return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME
;
3381 return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME
;
3386 // day of month precedes month (17 of month)
3390 return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME
;
3392 return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME
;
3394 return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME
;
3399 SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3400 return CalendarDisplayCode::LONG_MONTH_NAME
;
3404 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor
& rNumFor
) const
3406 if ( GetCal().getUniqueID() != Gregorian::get() )
3410 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3411 const sal_uInt16 nAnz
= rNumFor
.GetCount();
3413 for ( i
= 0; i
< nAnz
; i
++ )
3415 switch ( rInfo
.nTypeArray
[i
] )
3417 case NF_SYMBOLTYPE_CALENDAR
:
3431 void SvNumberformat::SwitchToOtherCalendar( OUString
& rOrgCalendar
,
3432 double& fOrgDateTime
) const
3434 CalendarWrapper
& rCal
= GetCal();
3435 const OUString
&rGregorian
= Gregorian::get();
3436 if ( rCal
.getUniqueID() == rGregorian
)
3438 using namespace ::com::sun::star::i18n
;
3439 ::com::sun::star::uno::Sequence
< OUString
> xCals
= rCal
.getAllCalendars(
3440 rLoc().getLanguageTag().getLocale() );
3441 sal_Int32 nCnt
= xCals
.getLength();
3444 for ( sal_Int32 j
=0; j
< nCnt
; j
++ )
3446 if ( xCals
[j
] != rGregorian
)
3448 if ( !rOrgCalendar
.getLength() )
3450 rOrgCalendar
= rCal
.getUniqueID();
3451 fOrgDateTime
= rCal
.getDateTime();
3453 rCal
.loadCalendar( xCals
[j
], rLoc().getLanguageTag().getLocale() );
3454 rCal
.setDateTime( fOrgDateTime
);
3462 void SvNumberformat::SwitchToGregorianCalendar( const OUString
& rOrgCalendar
,
3463 double fOrgDateTime
) const
3465 CalendarWrapper
& rCal
= GetCal();
3466 const OUString
&rGregorian
= Gregorian::get();
3467 if ( rOrgCalendar
.getLength() && rCal
.getUniqueID() != rGregorian
)
3469 rCal
.loadCalendar( rGregorian
, rLoc().getLanguageTag().getLocale() );
3470 rCal
.setDateTime( fOrgDateTime
);
3474 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString
& rOrgCalendar
, double& fOrgDateTime
)
3476 using namespace ::com::sun::star::i18n
;
3477 CalendarWrapper
& rCal
= GetCal();
3478 const OUString
&rGregorian
= Gregorian::get();
3479 if ( rCal
.getUniqueID() != rGregorian
)
3481 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3482 if ( nVal
== 0 && rCal
.getLoadedCalendar().Eras
[0].ID
== "Dummy" )
3484 if ( !rOrgCalendar
.getLength() )
3486 rOrgCalendar
= rCal
.getUniqueID();
3487 fOrgDateTime
= rCal
.getDateTime();
3489 else if ( rOrgCalendar
== rGregorian
)
3493 rCal
.loadCalendar( rGregorian
, rLoc().getLanguageTag().getLocale() );
3494 rCal
.setDateTime( fOrgDateTime
);
3503 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3504 * unused please don't remove it, it would be needed by
3505 * SwitchToSpecifiedCalendar(), see comment in
3506 * ImpSvNumberInputScan::GetDateRef() */
3508 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString
& rOrgCalendar
,
3509 double& fOrgDateTime
,
3510 const ImpSvNumFor
& rNumFor
) const
3512 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3513 const sal_uInt16 nAnz
= rNumFor
.GetCount();
3514 for ( sal_uInt16 i
= 0; i
< nAnz
; i
++ )
3516 if ( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_CALENDAR
)
3518 CalendarWrapper
& rCal
= GetCal();
3519 if ( !rOrgCalendar
.getLength() )
3521 rOrgCalendar
= rCal
.getUniqueID();
3522 fOrgDateTime
= rCal
.getDateTime();
3524 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLocale() );
3525 rCal
.setDateTime( fOrgDateTime
);
3534 void SvNumberformat::ImpAppendEraG( OUStringBuffer
& OutString
,
3535 const CalendarWrapper
& rCal
,
3538 using namespace ::com::sun::star::i18n
;
3539 if ( rCal
.getUniqueID() == "gengou" )
3542 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3561 OutString
.append(cEra
);
3565 OutString
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
3569 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor
& rNumFor
)
3571 bool bIsIso
= false;
3572 if ((eType
& NUMBERFORMAT_DATE
) == NUMBERFORMAT_DATE
)
3583 State eState
= eNone
;
3584 short const * const pType
= rNumFor
.Info().nTypeArray
;
3585 sal_uInt16 nAnz
= rNumFor
.GetCount();
3586 for (sal_uInt16 i
=0; i
< nAnz
&& !bIsIso
&& eState
!= eNotIso
; ++i
)
3590 case NF_KEY_YY
: // two digits not strictly ISO 8601
3592 if (eState
!= eNone
)
3601 case NF_KEY_M
: // single digit not strictly ISO 8601
3603 if (eState
!= eAtSep1
)
3612 case NF_KEY_D
: // single digit not strictly ISO 8601
3614 if (eState
!= eAtSep2
)
3623 case NF_SYMBOLTYPE_STRING
:
3624 case NF_SYMBOLTYPE_DATESEP
:
3625 if (comphelper::string::equals(rNumFor
.Info().sStrArray
[i
], '-'))
3627 if (eState
== eAtYear
)
3631 else if (eState
== eAtMonth
)
3652 SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3657 bool SvNumberformat::ImpGetDateOutput(double fNumber
,
3659 OUStringBuffer
& sBuff
)
3661 using namespace ::com::sun::star::i18n
;
3664 CalendarWrapper
& rCal
= GetCal();
3665 double fDiff
= DateTime(*(rScan
.GetNullDate())) - rCal
.getEpochStart();
3667 rCal
.setLocalDateTime( fNumber
);
3668 int nUseMonthCase
= 0; // not decided yet
3669 OUString aOrgCalendar
; // empty => not changed yet
3671 double fOrgDateTime(0.0);
3672 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
] );
3673 if ( bOtherCalendar
)
3675 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3677 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
) )
3679 bOtherCalendar
= false;
3681 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3682 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
3683 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
3686 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
3688 switch (rInfo
.nTypeArray
[i
])
3690 case NF_SYMBOLTYPE_CALENDAR
:
3691 if ( !aOrgCalendar
.getLength() )
3693 aOrgCalendar
= rCal
.getUniqueID();
3694 fOrgDateTime
= rCal
.getDateTime();
3696 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
3697 rCal
.setDateTime( fOrgDateTime
);
3698 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3700 case NF_SYMBOLTYPE_STAR
:
3703 sBuff
.append((sal_Unicode
) 0x1B);
3704 sBuff
.append(rInfo
.sStrArray
[i
][1]);
3708 case NF_SYMBOLTYPE_BLANK
:
3709 InsertBlanks( sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
3711 case NF_SYMBOLTYPE_STRING
:
3712 case NF_SYMBOLTYPE_CURRENCY
:
3713 case NF_SYMBOLTYPE_DATESEP
:
3714 case NF_SYMBOLTYPE_TIMESEP
:
3715 case NF_SYMBOLTYPE_TIME100SECSEP
:
3716 sBuff
.append(rInfo
.sStrArray
[i
]);
3719 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_MONTH
, nNatNum
));
3721 case NF_KEY_MM
: // MM
3722 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_MONTH
, nNatNum
));
3724 case NF_KEY_MMM
: // MMM
3725 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
3726 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
3729 case NF_KEY_MMMM
: // MMMM
3730 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
3731 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
3734 case NF_KEY_MMMMM
: // MMMMM
3735 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
3736 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
3740 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
3742 case NF_KEY_QQ
: // QQ
3743 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
3746 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
));
3748 case NF_KEY_DD
: // DD
3749 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
3751 case NF_KEY_DDD
: // DDD
3752 if ( bOtherCalendar
)
3754 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3756 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
3757 if ( bOtherCalendar
)
3759 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3762 case NF_KEY_DDDD
: // DDDD
3763 if ( bOtherCalendar
)
3765 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3767 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
3768 if ( bOtherCalendar
)
3770 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3773 case NF_KEY_YY
: // YY
3774 if ( bOtherCalendar
)
3776 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3778 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3779 if ( bOtherCalendar
)
3781 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3784 case NF_KEY_YYYY
: // YYYY
3785 if ( bOtherCalendar
)
3787 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3789 aYear
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
3790 if (aYear
.getLength() < 4)
3792 using namespace comphelper::string
;
3793 // Ensure that year consists of at least 4 digits, so it
3794 // can be distinguished from 2 digits display and edited
3795 // without suddenly being hit by the 2-digit year magic.
3796 OUStringBuffer aBuf
;
3797 padToLength(aBuf
, 4 - aYear
.getLength(), sal_Unicode('0'));
3798 impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum());
3804 sBuff
.append(aYear
);
3806 if ( bOtherCalendar
)
3808 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3811 case NF_KEY_EC
: // E
3812 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3814 case NF_KEY_EEC
: // EE
3816 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
3818 case NF_KEY_NN
: // NN
3819 case NF_KEY_AAA
: // AAA
3820 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
3822 case NF_KEY_NNN
: // NNN
3823 case NF_KEY_AAAA
: // AAAA
3824 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
3826 case NF_KEY_NNNN
: // NNNN
3827 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
3828 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
3830 case NF_KEY_WW
: // WW
3831 sBuff
.append(ImpIntToString( nIx
,
3832 rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
3835 ImpAppendEraG(sBuff
, rCal
, nNatNum
);
3837 case NF_KEY_GG
: // GG
3838 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
3840 case NF_KEY_GGG
: // GGG
3841 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
3843 case NF_KEY_RR
: // RR => GGGEE
3844 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
3848 if ( aOrgCalendar
.getLength() )
3850 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
3855 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber
,
3857 OUStringBuffer
& sBuff
)
3859 using namespace ::com::sun::star::i18n
;
3862 CalendarWrapper
& rCal
= GetCal();
3863 double fDiff
= DateTime(*(rScan
.GetNullDate())) - rCal
.getEpochStart();
3866 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3869 if ( rScan
.GetStandardPrec() == 300 &&
3870 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< 7 )
3872 // round at 7 decimals (+5 of 86400 == 12 significant digits)
3879 nCntPost
= rInfo
.nCntPost
;
3881 double fTime
= (fNumber
- floor( fNumber
)) * 86400.0;
3882 fTime
= ::rtl::math::round( fTime
, int(nCntPost
) );
3883 if (fTime
>= 86400.0)
3885 // result of fNumber==x.999999999... rounded up, use correct date/time
3887 fNumber
= floor( fNumber
+ 0.5) + fTime
;
3889 rCal
.setLocalDateTime( fNumber
);
3891 int nUseMonthCase
= 0; // not decided yet
3892 OUString aOrgCalendar
; // empty => not changed yet
3893 double fOrgDateTime(0.0);
3894 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
] );
3895 if ( bOtherCalendar
)
3897 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3899 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
) )
3901 bOtherCalendar
= false;
3903 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
3905 sal_uLong nSeconds
= (sal_uLong
)floor( fTime
);
3906 OUStringBuffer
sSecStr( ::rtl::math::doubleToUString( fTime
-nSeconds
,
3907 rtl_math_StringFormat_F
, int(nCntPost
), '.'));
3908 sSecStr
.stripStart((sal_Unicode
)'0');
3909 sSecStr
.stripStart((sal_Unicode
)'.');
3912 sSecStr
.stripEnd((sal_Unicode
)'0');
3913 for(sal_Int32 index
= sSecStr
.getLength(); index
< rInfo
.nCntPost
; ++index
)
3915 sSecStr
.append((sal_Unicode
)'0');
3917 impTransliterate(sSecStr
, NumFor
[nIx
].GetNatNum());
3918 nCntPost
= sSecStr
.getLength();
3922 impTransliterate(sSecStr
, NumFor
[nIx
].GetNatNum());
3925 sal_Int32 nSecPos
= 0; // Zum Ziffernweisen
3927 sal_uLong nHour
, nMin
, nSec
;
3928 if (!rInfo
.bThousand
) // [] Format
3930 nHour
= (nSeconds
/3600) % 24;
3931 nMin
= (nSeconds
%3600) / 60;
3934 else if (rInfo
.nThousand
== 3) // [ss]
3940 else if (rInfo
.nThousand
== 2) // [mm]:ss
3943 nMin
= nSeconds
/ 60;
3944 nSec
= nSeconds
% 60;
3946 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
3948 nHour
= nSeconds
/ 3600;
3949 nMin
= (nSeconds
%3600) / 60;
3954 nHour
= 0; // TODO What should these values be?
3958 sal_Unicode cAmPm
= ' '; // a oder p
3959 if (rInfo
.nCntExp
) // AM/PM
3966 else if (nHour
< 12)
3979 const sal_uInt16 nAnz
= NumFor
[nIx
].GetCount();
3982 for (sal_uInt16 i
= 0; i
< nAnz
; i
++)
3984 switch (rInfo
.nTypeArray
[i
])
3986 case NF_SYMBOLTYPE_CALENDAR
:
3987 if ( !aOrgCalendar
.getLength() )
3989 aOrgCalendar
= rCal
.getUniqueID();
3990 fOrgDateTime
= rCal
.getDateTime();
3992 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
3993 rCal
.setDateTime( fOrgDateTime
);
3994 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3996 case NF_SYMBOLTYPE_STAR
:
3999 sBuff
.append((sal_Unicode
) 0x1B);
4000 sBuff
.append(rInfo
.sStrArray
[i
][1]);
4004 case NF_SYMBOLTYPE_BLANK
:
4005 InsertBlanks( sBuff
, sBuff
.getLength(),
4006 rInfo
.sStrArray
[i
][1] );
4008 case NF_SYMBOLTYPE_STRING
:
4009 case NF_SYMBOLTYPE_CURRENCY
:
4010 case NF_SYMBOLTYPE_DATESEP
:
4011 case NF_SYMBOLTYPE_TIMESEP
:
4012 case NF_SYMBOLTYPE_TIME100SECSEP
:
4013 sBuff
.append(rInfo
.sStrArray
[i
]);
4015 case NF_SYMBOLTYPE_DIGIT
:
4016 nLen
= ( bInputLine
&& i
> 0 &&
4017 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
4018 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
4019 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
4020 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
; j
++)
4022 sBuff
.append(sSecStr
[ nSecPos
]);
4026 case NF_KEY_AMPM
: // AM/PM
4029 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4030 AmPmValue::AM
, 0 ));
4034 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4035 AmPmValue::PM
, 0 ));
4038 case NF_KEY_AP
: // A/P
4041 sBuff
.append((sal_Unicode
)'a');
4045 sBuff
.append((sal_Unicode
)'p');
4048 case NF_KEY_MI
: // M
4049 sBuff
.append(ImpIntToString( nIx
, nMin
));
4051 case NF_KEY_MMI
: // MM
4052 sBuff
.append(ImpIntToString( nIx
, nMin
, 2 ));
4055 sBuff
.append(ImpIntToString( nIx
, nHour
));
4057 case NF_KEY_HH
: // HH
4058 sBuff
.append(ImpIntToString( nIx
, nHour
, 2 ));
4061 sBuff
.append(ImpIntToString( nIx
, nSec
));
4063 case NF_KEY_SS
: // SS
4064 sBuff
.append(ImpIntToString( nIx
, nSec
, 2 ));
4067 sBuff
.append(rCal
.getDisplayString(
4068 CalendarDisplayCode::SHORT_MONTH
, nNatNum
));
4070 case NF_KEY_MM
: // MM
4071 sBuff
.append(rCal
.getDisplayString(
4072 CalendarDisplayCode::LONG_MONTH
, nNatNum
));
4074 case NF_KEY_MMM
: // MMM
4075 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4076 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4079 case NF_KEY_MMMM
: // MMMM
4080 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4081 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4084 case NF_KEY_MMMMM
: // MMMMM
4085 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4086 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4090 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
4092 case NF_KEY_QQ
: // QQ
4093 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
4096 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
));
4098 case NF_KEY_DD
: // DD
4099 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
4101 case NF_KEY_DDD
: // DDD
4102 if ( bOtherCalendar
)
4104 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4106 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4107 if ( bOtherCalendar
)
4109 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4112 case NF_KEY_DDDD
: // DDDD
4113 if ( bOtherCalendar
)
4115 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4117 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4118 if ( bOtherCalendar
)
4120 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4123 case NF_KEY_YY
: // YY
4124 if ( bOtherCalendar
)
4126 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4128 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4129 if ( bOtherCalendar
)
4131 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4134 case NF_KEY_YYYY
: // YYYY
4135 if ( bOtherCalendar
)
4137 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4139 aYear
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
4140 if (aYear
.getLength() < 4)
4142 using namespace comphelper::string
;
4143 // Ensure that year consists of at least 4 digits, so it
4144 // can be distinguished from 2 digits display and edited
4145 // without suddenly being hit by the 2-digit year magic.
4146 OUStringBuffer aBuf
;
4147 padToLength(aBuf
, 4 - aYear
.getLength(), sal_Unicode('0'));
4148 impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum());
4154 sBuff
.append(aYear
);
4156 if ( bOtherCalendar
)
4158 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4161 case NF_KEY_EC
: // E
4162 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4164 case NF_KEY_EEC
: // EE
4166 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
4168 case NF_KEY_NN
: // NN
4169 case NF_KEY_AAA
: // AAA
4170 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4172 case NF_KEY_NNN
: // NNN
4173 case NF_KEY_AAAA
: // AAAA
4174 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4176 case NF_KEY_NNNN
: // NNNN
4177 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4178 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
4180 case NF_KEY_WW
: // WW
4181 sBuff
.append(ImpIntToString( nIx
, rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
4184 ImpAppendEraG( sBuff
, rCal
, nNatNum
);
4186 case NF_KEY_GG
: // GG
4187 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
4189 case NF_KEY_GGG
: // GGG
4190 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
4192 case NF_KEY_RR
: // RR => GGGEE
4193 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
4197 if ( aOrgCalendar
.getLength() )
4199 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
4204 bool SvNumberformat::ImpGetNumberOutput(double fNumber
,
4206 OUStringBuffer
& sStr
)
4212 if (nIx
== 0) // nicht in hinteren
4214 bSign
= true; // Formaten
4225 if ( ::rtl::math::isSignBitSet( fNumber
) )
4227 fNumber
= -fNumber
; // yes, -0.0 is possible, eliminate '-'
4230 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4231 if (rInfo
.eScannedType
== NUMBERFORMAT_PERCENT
)
4233 if (fNumber
< _D_MAX_D_BY_100
)
4239 sStr
= rScan
.GetErrorString();
4245 bool bInteger
= false;
4246 if ( rInfo
.nThousand
!= FLAG_STANDARD_IN_FORMAT
)
4248 // special formatting only if no GENERAL keyword in format code
4249 const sal_uInt16 nThousand
= rInfo
.nThousand
;
4251 for (i
= 0; i
< nThousand
; i
++)
4253 if (fNumber
> _D_MIN_M_BY_1000
)
4264 nPrecExp
= GetPrecExp( fNumber
);
4270 if (rInfo
.nCntPost
) // NachkommaStellen
4272 if ((rInfo
.nCntPost
+ nPrecExp
) > 15 && nPrecExp
< 15)
4274 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 15-nPrecExp
, '.');
4275 for (long l
= 15-nPrecExp
; l
< (long) rInfo
.nCntPost
; l
++)
4277 sStr
.append((sal_Unicode
)'0');
4282 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, rInfo
.nCntPost
, '.' );
4284 sStr
.stripStart((sal_Unicode
)'0'); // fuehrende Nullen weg
4286 else if (fNumber
== 0.0) // Null
4288 // nothing to be done here, keep empty string sStr,
4289 // ImpNumberFillWithThousands does the rest
4293 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 0, '.');
4294 sStr
.stripStart((sal_Unicode
)'0'); // fuehrende Nullen weg
4296 sal_Int32 nPoint
= sStr
.indexOf((sal_Unicode
)'.' );
4299 register const sal_Unicode
* p
= sStr
.getStr() + nPoint
;
4300 while ( *++p
== '0' )
4306 sStr
.remove( nPoint
, 1 ); // . herausnehmen
4308 if (bSign
&& (sStr
.getLength() == 0 ||
4309 comphelper::string::getTokenCount(sStr
.toString(), '0') == sStr
.getLength()+1)) // nur 00000
4311 bSign
= false; // nicht -0.00
4313 } // End of != FLAG_STANDARD_IN_FORMAT
4315 // von hinten nach vorn
4317 k
= sStr
.getLength(); // hinter letzter Ziffer
4318 j
= NumFor
[nIx
].GetCount()-1; // letztes Symbol
4319 // Nachkommastellen:
4320 if (rInfo
.nCntPost
> 0)
4322 bool bTrailing
= true; // ob Endnullen?
4323 bool bFilled
= false; // ob aufgefuellt wurde ?
4325 while (j
> 0 && // rueckwaerts
4326 (nType
= rInfo
.nTypeArray
[j
]) != NF_SYMBOLTYPE_DECSEP
)
4330 case NF_SYMBOLTYPE_STAR
:
4333 sStr
.insert(k
, rInfo
.sStrArray
[j
][1]);
4334 sStr
.insert(k
, (sal_Unicode
) 0x1B);
4338 case NF_SYMBOLTYPE_BLANK
:
4339 /*k = */ InsertBlanks(sStr
, k
, rInfo
.sStrArray
[j
][1] );
4341 case NF_SYMBOLTYPE_STRING
:
4342 case NF_SYMBOLTYPE_CURRENCY
:
4343 case NF_SYMBOLTYPE_PERCENT
:
4344 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4346 case NF_SYMBOLTYPE_THSEP
:
4347 if (rInfo
.nThousand
== 0)
4349 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4352 case NF_SYMBOLTYPE_DIGIT
:
4354 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4355 const sal_Unicode
* p1
= rStr
.getStr();
4356 register const sal_Unicode
* p
= p1
+ rStr
.getLength();
4359 const sal_Unicode c
= *p
;
4361 if ( sStr
[k
] != (sal_Unicode
)'0' )
4371 else if ( c
== '-' )
4375 sStr
[ k
] = (sal_Unicode
)'-';
4379 else if ( c
== '?' )
4381 sStr
[ k
] = (sal_Unicode
)' ';
4384 else if ( !bFilled
) // #
4392 case NF_KEY_CCC
: // CCC-Waehrung
4393 sStr
.insert(k
, rScan
.GetCurAbbrev());
4395 case NF_KEY_GENERAL
: // Standard im String
4397 OUStringBuffer sNum
;
4398 ImpGetOutputStandard(fNumber
, sNum
);
4399 sNum
.stripStart((sal_Unicode
)'-');
4400 sStr
.insert(k
, sNum
.makeStringAndClear());
4410 bRes
|= ImpNumberFillWithThousands(sStr
, fNumber
, k
, j
, nIx
, // ggfs Auffuellen mit .
4412 if ( rInfo
.nCntPost
> 0 )
4414 const OUString
& rDecSep
= GetFormatter().GetNumDecimalSep();
4415 sal_Int32 nLen
= rDecSep
.getLength();
4416 if ( sStr
.getLength() > nLen
&& ( sStr
.indexOf( rDecSep
, sStr
.getLength() - nLen
) == sStr
.getLength() - nLen
) )
4418 sStr
.truncate( sStr
.getLength() - nLen
); // no decimals => strip DecSep
4423 sStr
.insert(0, (sal_Unicode
)'-');
4425 impTransliterate(sStr
, NumFor
[nIx
].GetNatNum());
4429 bool SvNumberformat::ImpNumberFillWithThousands( OUStringBuffer
& sBuff
, // number string
4430 double& rNumber
, // number
4431 sal_Int32 k
, // position within string
4432 sal_uInt16 j
, // symbol index within format code
4433 sal_uInt16 nIx
, // subformat index
4434 sal_Int32 nDigCnt
) // count of integer digits in format
4437 sal_Int32 nLeadingStringChars
= 0; // inserted StringChars before number
4438 sal_Int32 nDigitCount
= 0; // count of integer digits from the right
4440 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4441 // no normal thousands separators if number divided by thousands
4442 bool bDoThousands
= (rInfo
.nThousand
== 0);
4443 utl::DigitGroupingIterator
aGrouping( GetFormatter().GetLocaleData()->getDigitGrouping());
4445 while (!bStop
) // backwards
4451 switch (rInfo
.nTypeArray
[j
])
4453 case NF_SYMBOLTYPE_DECSEP
:
4456 case NF_SYMBOLTYPE_STRING
:
4457 case NF_SYMBOLTYPE_CURRENCY
:
4458 case NF_SYMBOLTYPE_PERCENT
:
4459 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4462 nLeadingStringChars
= nLeadingStringChars
+ rInfo
.sStrArray
[j
].getLength();
4465 case NF_SYMBOLTYPE_STAR
:
4468 sBuff
.insert(k
, rInfo
.sStrArray
[j
][1]);
4469 sBuff
.insert(k
, (sal_Unicode
) 0x1B);
4473 case NF_SYMBOLTYPE_BLANK
:
4474 /*k = */ InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4476 case NF_SYMBOLTYPE_THSEP
:
4477 // #i7284# #102685# Insert separator also if number is divided
4478 // by thousands and the separator is specified somewhere in
4479 // between and not only at the end.
4480 // #i12596# But do not insert if it's a parenthesized negative
4482 // In fact, do not insert if divided and regex [0#,],[^0#] and
4483 // no other digit symbol follows (which was already detected
4484 // during scan of format code, otherwise there would be no
4485 // division), else do insert. Same in ImpNumberFill() below.
4486 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4488 bDoThousands
= ((j
== 0) ||
4489 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4490 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4491 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4497 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4499 else if (nDigitCount
< nDigCnt
)
4501 // Leading '#' displays nothing (e.g. no leading
4502 // separator for numbers <1000 with #,##0 format).
4503 // Leading '?' displays blank.
4504 // Everything else, including nothing, displays the
4506 sal_Unicode cLeader
= 0;
4507 if (j
> 0 && rInfo
.nTypeArray
[j
-1] == NF_SYMBOLTYPE_DIGIT
)
4509 const OUString
& rStr
= rInfo
.sStrArray
[j
-1];
4510 sal_Int32 nLen
= rStr
.getLength();
4513 cLeader
= rStr
[ nLen
- 1 ];
4522 // erAck: 2008-04-03T16:24+0200
4523 // Actually this currently isn't executed
4524 // because the format scanner in the context of
4525 // "?," doesn't generate a group separator but
4526 // a literal ',' character instead that is
4527 // inserted unconditionally. Should be changed
4528 // on some occasion.
4529 sBuff
.insert(k
, (sal_Unicode
)' ');
4532 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4535 aGrouping
.advance();
4538 case NF_SYMBOLTYPE_DIGIT
:
4540 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4541 const sal_Unicode
* p1
= rStr
.getStr();
4542 register const sal_Unicode
* p
= p1
+ rStr
.getLength();
4555 sBuff
.insert(0, (sal_Unicode
)'0');
4558 sBuff
.insert(0, (sal_Unicode
)' ');
4562 if (nDigitCount
== nDigCnt
&& k
> 0)
4564 // more digits than specified
4565 ImpDigitFill(sBuff
, 0, k
, nIx
, nDigitCount
, aGrouping
);
4570 case NF_KEY_CCC
: // CCC currency
4571 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4573 case NF_KEY_GENERAL
: // "General" in string
4575 OUStringBuffer sNum
;
4576 ImpGetOutputStandard(rNumber
, sNum
);
4577 sNum
.stripStart((sal_Unicode
)'-');
4578 sBuff
.insert(k
, sNum
.makeStringAndClear());
4584 j
--; // next format code string
4587 k
= k
+ nLeadingStringChars
; // MSC converts += to int and then warns, so ...
4588 if (k
> nLeadingStringChars
)
4590 ImpDigitFill(sBuff
, nLeadingStringChars
, k
, nIx
, nDigitCount
, aGrouping
);
4595 void SvNumberformat::ImpDigitFill(OUStringBuffer
& sStr
, // number string
4596 sal_Int32 nStart
, // start of digits
4597 sal_Int32
& k
, // position within string
4598 sal_uInt16 nIx
, // subformat index
4599 sal_Int32
& nDigitCount
, // count of integer digits from the right so far
4600 utl::DigitGroupingIterator
& rGrouping
) // current grouping
4602 if (NumFor
[nIx
].Info().bThousand
) // only if grouping
4603 { // fill in separators
4604 const OUString
& rThousandSep
= GetFormatter().GetNumThousandSep();
4607 if (nDigitCount
== rGrouping
.getPos())
4609 sStr
.insert( k
, rThousandSep
);
4610 rGrouping
.advance();
4622 bool SvNumberformat::ImpNumberFill( OUStringBuffer
& sBuff
, // number string
4623 double& rNumber
, // number for "General" format
4624 sal_Int32
& k
, // position within string
4625 sal_uInt16
& j
, // symbol index within format code
4626 sal_uInt16 nIx
, // subformat index
4627 short eSymbolType
) // type of stop condition
4630 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4631 // no normal thousands separators if number divided by thousands
4632 bool bDoThousands
= (rInfo
.nThousand
== 0);
4635 k
= sBuff
.getLength(); // behind last digit
4637 while (j
> 0 && (nType
= rInfo
.nTypeArray
[j
]) != eSymbolType
)
4641 case NF_SYMBOLTYPE_STAR
:
4644 sBuff
.insert(k
, rInfo
.sStrArray
[j
][1]);
4645 sBuff
.insert(k
, sal_Unicode(0x1B));
4649 case NF_SYMBOLTYPE_BLANK
:
4650 k
= InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4652 case NF_SYMBOLTYPE_THSEP
:
4653 // Same as in ImpNumberFillWithThousands() above, do not insert
4654 // if divided and regex [0#,],[^0#] and no other digit symbol
4655 // follows (which was already detected during scan of format
4656 // code, otherwise there would be no division), else do insert.
4657 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4659 bDoThousands
= ((j
== 0) ||
4660 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4661 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4662 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4664 if ( bDoThousands
&& k
> 0 )
4666 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4669 case NF_SYMBOLTYPE_DIGIT
:
4671 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4672 const sal_Unicode
* p1
= rStr
.getStr();
4673 register const sal_Unicode
* p
= p1
+ rStr
.getLength();
4685 sBuff
.insert(0, (sal_Unicode
)'0');
4688 sBuff
.insert(0, (sal_Unicode
)' ');
4695 case NF_KEY_CCC
: // CCC-Waehrung
4696 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4698 case NF_KEY_GENERAL
: // Standard im String
4700 OUStringBuffer sNum
;
4701 ImpGetOutputStandard(rNumber
, sNum
);
4702 sNum
.stripStart((sal_Unicode
)'-');
4703 sBuff
.insert(k
, sNum
.makeStringAndClear());
4706 case NF_SYMBOLTYPE_FRAC_FDIV
: // Do Nothing
4710 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4713 j
--; // naechster String
4718 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand
,
4720 sal_uInt16
& nPrecision
,
4721 sal_uInt16
& nAnzLeading
) const
4723 // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4726 GetNumForInfo( 0, nDummyType
, bThousand
, nPrecision
, nAnzLeading
);
4728 // "negative in red" is only useful for the whole format
4730 const Color
* pColor
= NumFor
[1].GetColor();
4731 if (fLimit1
== 0.0 && fLimit2
== 0.0 && pColor
4732 && (*pColor
== rScan
.GetRedColor()))
4742 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor
, short& rScannedType
,
4743 bool& bThousand
, sal_uInt16
& nPrecision
, sal_uInt16
& nAnzLeading
) const
4745 // take info from a specified sub-format (for XML export)
4752 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
4753 rScannedType
= rInfo
.eScannedType
;
4754 bThousand
= rInfo
.bThousand
;
4755 nPrecision
= rInfo
.nCntPost
;
4756 if (bStandard
&& rInfo
.eScannedType
== NUMBERFORMAT_NUMBER
)
4766 const sal_uInt16 nAnz
= NumFor
[nNumFor
].GetCount();
4767 while (!bStop
&& i
< nAnz
)
4769 short nType
= rInfo
.nTypeArray
[i
];
4770 if ( nType
== NF_SYMBOLTYPE_DIGIT
)
4772 const sal_Unicode
* p
= rInfo
.sStrArray
[i
].getStr();
4777 while ( *p
++ == '0' )
4782 else if (nType
== NF_SYMBOLTYPE_DECSEP
|| nType
== NF_SYMBOLTYPE_EXP
)
4791 const OUString
* SvNumberformat::GetNumForString( sal_uInt16 nNumFor
, sal_uInt16 nPos
,
4792 bool bString
/* = false */ ) const
4798 sal_uInt16 nAnz
= NumFor
[nNumFor
].GetCount();
4803 if ( nPos
== 0xFFFF )
4808 short* pType
= NumFor
[nNumFor
].Info().nTypeArray
+ nPos
;
4809 while ( nPos
> 0 && (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4810 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4815 if ( (*pType
!= NF_SYMBOLTYPE_STRING
) && (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4821 else if ( nPos
> nAnz
- 1 )
4828 short* pType
= NumFor
[nNumFor
].Info().nTypeArray
+ nPos
;
4829 while ( nPos
< nAnz
&& (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4830 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4835 if ( nPos
>= nAnz
|| ((*pType
!= NF_SYMBOLTYPE_STRING
) &&
4836 (*pType
!= NF_SYMBOLTYPE_CURRENCY
)) )
4841 return &NumFor
[nNumFor
].Info().sStrArray
[nPos
];
4844 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor
, sal_uInt16 nPos
,
4845 bool bString
/* = false */ ) const
4851 sal_uInt16 nAnz
= NumFor
[nNumFor
].GetCount();
4856 if ( nPos
== 0xFFFF )
4862 short* pType
= NumFor
[nNumFor
].Info().nTypeArray
+ nPos
;
4863 while ( nPos
> 0 && (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4864 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4869 if ( (*pType
!= NF_SYMBOLTYPE_STRING
) && (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4875 else if ( nPos
> nAnz
- 1 )
4882 short* pType
= NumFor
[nNumFor
].Info().nTypeArray
+ nPos
;
4883 while ( nPos
< nAnz
&& (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4884 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4889 if ( (*pType
!= NF_SYMBOLTYPE_STRING
) && (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4894 return NumFor
[nNumFor
].Info().nTypeArray
[nPos
];
4897 bool SvNumberformat::IsNegativeWithoutSign() const
4899 if ( IsSecondSubformatRealNegative() )
4901 const OUString
* pStr
= GetNumForString( 1, 0, true );
4904 return !HasStringNegativeSign( *pStr
);
4910 bool SvNumberformat::IsNegativeInBracket() const
4912 sal_uInt16 nAnz
= NumFor
[1].GetCount();
4917 OUString
*tmpStr
= NumFor
[1].Info().sStrArray
;
4918 using comphelper::string::equals
;
4919 return (equals(tmpStr
[0], '(') && equals(tmpStr
[nAnz
-1], ')'));
4922 bool SvNumberformat::HasPositiveBracketPlaceholder() const
4924 sal_uInt16 nAnz
= NumFor
[0].GetCount();
4925 OUString
*tmpStr
= NumFor
[0].Info().sStrArray
;
4926 return (tmpStr
[nAnz
-1].equalsAscii( "_)" ));
4929 DateFormat
SvNumberformat::GetDateOrder() const
4931 if ( (eType
& NUMBERFORMAT_DATE
) == NUMBERFORMAT_DATE
)
4933 short const * const pType
= NumFor
[0].Info().nTypeArray
;
4934 sal_uInt16 nAnz
= NumFor
[0].GetCount();
4935 for ( sal_uInt16 j
=0; j
<nAnz
; j
++ )
4960 SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
4962 return rLoc().getDateFormat();
4965 sal_uInt32
SvNumberformat::GetExactDateOrder() const
4967 sal_uInt32 nRet
= 0;
4968 if ( (eType
& NUMBERFORMAT_DATE
) != NUMBERFORMAT_DATE
)
4970 SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
4973 short const * const pType
= NumFor
[0].Info().nTypeArray
;
4974 sal_uInt16 nAnz
= NumFor
[0].GetCount();
4976 for ( sal_uInt16 j
=0; j
<nAnz
&& nShift
< 3; j
++ )
4982 nRet
= (nRet
<< 8) | 'D';
4990 nRet
= (nRet
<< 8) | 'M';
4999 nRet
= (nRet
<< 8) | 'Y';
5007 void SvNumberformat::GetConditions( SvNumberformatLimitOps
& rOper1
, double& rVal1
,
5008 SvNumberformatLimitOps
& rOper2
, double& rVal2
) const
5016 Color
* SvNumberformat::GetColor( sal_uInt16 nNumFor
) const
5022 return NumFor
[nNumFor
].GetColor();
5025 static void lcl_SvNumberformat_AddLimitStringImpl( OUString
& rStr
,
5026 SvNumberformatLimitOps eOp
,
5027 double fLimit
, const String
& rDecSep
)
5029 if ( eOp
!= NUMBERFORMAT_OP_NO
)
5033 case NUMBERFORMAT_OP_EQ
:
5036 case NUMBERFORMAT_OP_NE
:
5039 case NUMBERFORMAT_OP_LT
:
5042 case NUMBERFORMAT_OP_LE
:
5045 case NUMBERFORMAT_OP_GT
:
5048 case NUMBERFORMAT_OP_GE
:
5052 SAL_WARN( "svl.numbers", "unsupported number format" );
5055 rStr
+= ::rtl::math::doubleToUString( fLimit
,
5056 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
5057 rDecSep
.GetChar(0), true);
5062 OUString
SvNumberformat::GetMappedFormatstring( const NfKeywordTable
& rKeywords
,
5063 const LocaleDataWrapper
& rLocWrp
,
5064 bool bDontQuote
) const
5066 OUStringBuffer aStr
;
5068 // 1 subformat matches all if no condition specified,
5069 bDefault
[0] = ( NumFor
[1].GetCount() == 0 && eOp1
== NUMBERFORMAT_OP_NO
);
5070 // with 2 subformats [>=0];[<0] is implied if no condition specified
5071 bDefault
[1] = ( !bDefault
[0] && NumFor
[2].GetCount() == 0 &&
5072 eOp1
== NUMBERFORMAT_OP_GE
&& fLimit1
== 0.0 &&
5073 eOp2
== NUMBERFORMAT_OP_NO
&& fLimit2
== 0.0 );
5074 // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5075 // note that subformats may be empty (;;;) and NumFor[2].GetnAnz()>0 is not checked.
5076 bDefault
[2] = ( !bDefault
[0] && !bDefault
[1] &&
5077 eOp1
== NUMBERFORMAT_OP_GT
&& fLimit1
== 0.0 &&
5078 eOp2
== NUMBERFORMAT_OP_LT
&& fLimit2
== 0.0 );
5079 bool bDefaults
= bDefault
[0] || bDefault
[1] || bDefault
[2];
5080 // from now on bDefault[] values are used to append empty subformats at the end
5081 bDefault
[3] = false;
5084 // conditions specified
5085 if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
== NUMBERFORMAT_OP_NO
)
5087 bDefault
[0] = bDefault
[1] = true; // [];x
5089 else if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
!= NUMBERFORMAT_OP_NO
&&
5090 NumFor
[2].GetCount() == 0 )
5092 bDefault
[0] = bDefault
[1] = bDefault
[2] = bDefault
[3] = true; // [];[];;
5094 // nothing to do if conditions specified for every subformat
5096 else if ( bDefault
[0] )
5098 bDefault
[0] = false; // a single unconditional subformat is never delimited
5102 if ( bDefault
[2] && NumFor
[2].GetCount() == 0 && NumFor
[1].GetCount() > 0 )
5104 bDefault
[3] = true; // special cases x;x;; and ;x;;
5106 for ( int i
=0; i
<3 && !bDefault
[i
]; ++i
)
5111 int nSem
= 0; // needed ';' delimiters
5112 int nSub
= 0; // subformats delimited so far
5113 for ( int n
=0; n
<4; n
++ )
5120 bool LCIDInserted
= false;
5127 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp1
,
5128 fLimit1
, rLocWrp
.getNumDecimalSep() );
5131 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp2
,
5132 fLimit2
, rLocWrp
.getNumDecimalSep() );
5137 const OUString
& rColorName
= NumFor
[n
].GetColorName();
5138 if ( !rColorName
.isEmpty() )
5140 const NfKeywordTable
& rKey
= rScan
.GetKeywords();
5141 for ( int j
= NF_KEY_FIRSTCOLOR
; j
<= NF_KEY_LASTCOLOR
; j
++ )
5143 if ( rKey
[j
] == rColorName
)
5146 aPrefix
+= rKeywords
[j
];
5153 const SvNumberNatNum
& rNum
= NumFor
[n
].GetNatNum();
5155 sal_uInt16 nAnz
= NumFor
[n
].GetCount();
5156 if ( nSem
&& (nAnz
|| !aPrefix
.isEmpty()) )
5158 for ( ; nSem
; --nSem
)
5162 for ( ; nSub
<= n
; ++nSub
)
5164 bDefault
[nSub
] = false;
5168 if ( !aPrefix
.isEmpty() )
5170 aStr
.append( aPrefix
);
5174 const short* pType
= NumFor
[n
].Info().nTypeArray
;
5175 const OUString
* pStr
= NumFor
[n
].Info().sStrArray
;
5176 for ( sal_uInt16 j
=0; j
<nAnz
; j
++ )
5178 if ( 0 <= pType
[j
] && pType
[j
] < NF_KEYWORD_ENTRIES_COUNT
)
5180 aStr
.append( rKeywords
[pType
[j
]] );
5181 if( NF_KEY_NNNN
== pType
[j
] )
5183 aStr
.append( rLocWrp
.getLongDateDayOfWeekSep() );
5190 case NF_SYMBOLTYPE_DECSEP
:
5191 aStr
.append( rLocWrp
.getNumDecimalSep() );
5193 case NF_SYMBOLTYPE_THSEP
:
5194 aStr
.append( rLocWrp
.getNumThousandSep() );
5196 case NF_SYMBOLTYPE_DATESEP
:
5197 aStr
.append( rLocWrp
.getDateSep() );
5199 case NF_SYMBOLTYPE_TIMESEP
:
5200 aStr
.append( rLocWrp
.getTimeSep() );
5202 case NF_SYMBOLTYPE_TIME100SECSEP
:
5203 aStr
.append( rLocWrp
.getTime100SecSep() );
5205 case NF_SYMBOLTYPE_STRING
:
5208 aStr
.append( pStr
[j
] );
5210 else if ( pStr
[j
].getLength() == 1 )
5212 aStr
.append( '\\' );
5213 aStr
.append( pStr
[j
] );
5218 aStr
.append( pStr
[j
] );
5222 case NF_SYMBOLTYPE_CALDEL
:
5223 if ( pStr
[j
+1].equalsAscii("buddhist") )
5225 aStr
.insert( 0, "[$-" );
5226 if ( rNum
.IsSet() && rNum
.GetNatNum() == 1 &&
5227 MsLangId::getRealLanguage( rNum
.GetLang() ) ==
5230 aStr
.insert( 3, "D07041E]" ); // date in Thai digit, Buddhist era
5234 aStr
.insert( 3, "107041E]" ); // date in Arabic digit, Buddhist era
5238 LCIDInserted
= true;
5241 aStr
.append( pStr
[j
] );
5246 // The Thai T NatNum modifier during Xcl export.
5247 if (rNum
.IsSet() && rNum
.GetNatNum() == 1 &&
5248 rKeywords
[NF_KEY_THAI_T
].equalsAscii( "T") &&
5249 MsLangId::getRealLanguage( rNum
.GetLang()) ==
5250 LANGUAGE_THAI
&& !LCIDInserted
)
5253 aStr
.insert( 0, "[$-D00041E]" ); // number in Thai digit
5256 for ( ; nSub
<4 && bDefault
[nSub
]; ++nSub
)
5257 { // append empty subformats
5258 aStr
.append( (sal_Unicode
)';' );
5260 return aStr
.makeStringAndClear();
5263 OUString
SvNumberformat::ImpGetNatNumString( const SvNumberNatNum
& rNum
,
5264 sal_Int32 nVal
, sal_uInt16 nMinDigits
) const
5269 if ( nMinDigits
== 2 )
5271 // speed up the most common case
5272 if ( 0 <= nVal
&& nVal
< 10 )
5274 sal_Unicode aBuf
[2];
5276 aBuf
[1] = '0' + nVal
;
5277 aStr
= OUString(aBuf
, SAL_N_ELEMENTS(aBuf
));
5281 aStr
= OUString::valueOf( nVal
);
5286 OUString
aValStr( OUString::valueOf( nVal
) );
5287 if ( aValStr
.getLength() >= nMinDigits
)
5293 OUStringBuffer aBuf
;
5294 for(sal_Int32 index
= 0; index
< nMinDigits
- aValStr
.getLength(); ++index
)
5296 aBuf
.append((sal_Unicode
)'0');
5298 aBuf
.append(aValStr
);
5299 aStr
= aBuf
.makeStringAndClear();
5305 aStr
= OUString::valueOf( nVal
);
5307 return impTransliterate(aStr
, rNum
);
5310 OUString
SvNumberformat::impTransliterateImpl(const OUString
& rStr
,
5311 const SvNumberNatNum
& rNum
) const
5313 com::sun::star::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5314 return GetFormatter().GetNatNum()->getNativeNumberString( rStr
,
5315 aLocale
, rNum
.GetNatNum() );
5318 void SvNumberformat::impTransliterateImpl(OUStringBuffer
& rStr
,
5319 const SvNumberNatNum
& rNum
) const
5321 com::sun::star::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5323 OUString
sTemp(rStr
.makeStringAndClear());
5324 sTemp
= GetFormatter().GetNatNum()->getNativeNumberString( sTemp
, aLocale
, rNum
.GetNatNum() );
5328 void SvNumberformat::GetNatNumXml( com::sun::star::i18n::NativeNumberXmlAttributes
& rAttr
,
5329 sal_uInt16 nNumFor
) const
5333 const SvNumberNatNum
& rNum
= NumFor
[nNumFor
].GetNatNum();
5336 com::sun::star::lang::Locale
aLocale(
5337 LanguageTag( rNum
.GetLang() ).getLocale() );
5338 rAttr
= GetFormatter().GetNatNum()->convertToXmlAttributes(
5339 aLocale
, rNum
.GetNatNum() );
5343 rAttr
= com::sun::star::i18n::NativeNumberXmlAttributes();
5348 rAttr
= com::sun::star::i18n::NativeNumberXmlAttributes();
5353 bool SvNumberformat::HasStringNegativeSign( const OUString
& rStr
)
5355 // fuer Sign muss '-' am Anfang oder am Ende des TeilStrings sein (Blanks ignored)
5356 sal_Int32 nLen
= rStr
.getLength();
5361 const sal_Unicode
* const pBeg
= rStr
.getStr();
5362 const sal_Unicode
* const pEnd
= pBeg
+ nLen
;
5363 register const sal_Unicode
* p
= pBeg
;
5366 if ( *p
== (sal_Unicode
)'-' )
5371 while ( *p
== (sal_Unicode
)' ' && ++p
< pEnd
);
5377 if ( *p
== (sal_Unicode
)'-' )
5382 while ( *p
== (sal_Unicode
)' ' && pBeg
< --p
);
5387 bool SvNumberformat::IsInQuote( const OUString
& rStr
, sal_Int32 nPos
,
5388 sal_Unicode cQuote
, sal_Unicode cEscIn
, sal_Unicode cEscOut
)
5390 sal_Int32 nLen
= rStr
.getLength();
5395 register const sal_Unicode
* p0
= rStr
.getStr();
5396 register const sal_Unicode
* p
= p0
;
5397 register const sal_Unicode
* p1
= p0
+ nPos
;
5398 bool bQuoted
= false;
5409 if ( *(p
-1) != cEscIn
)
5416 if ( *(p
-1) != cEscOut
)
5428 sal_Int32
SvNumberformat::GetQuoteEnd( const OUString
& rStr
, sal_Int32 nPos
,
5429 sal_Unicode cQuote
, sal_Unicode cEscIn
,
5430 sal_Unicode cEscOut
)
5432 sal_Int32 nLen
= rStr
.getLength();
5437 if ( !IsInQuote( rStr
, nPos
, cQuote
, cEscIn
, cEscOut
) )
5439 if ( rStr
[ nPos
] == cQuote
)
5441 return nPos
; // schliessendes cQuote
5445 register const sal_Unicode
* p0
= rStr
.getStr();
5446 register const sal_Unicode
* p
= p0
+ nPos
;
5447 register const sal_Unicode
* p1
= p0
+ nLen
;
5450 if ( *p
== cQuote
&& p
> p0
&& *(p
-1) != cEscIn
)
5452 return sal::static_int_cast
< sal_Int32
>(p
- p0
);
5456 return nLen
; // String Ende
5459 sal_uInt16
SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor
) const
5461 sal_uInt16 nCnt
= 0;
5462 sal_uInt16 nAnz
= NumFor
[nNumFor
].GetCount();
5463 short const * const pType
= NumFor
[nNumFor
].Info().nTypeArray
;
5464 for ( sal_uInt16 j
=0; j
<nAnz
; ++j
)
5468 case NF_SYMBOLTYPE_STRING
:
5469 case NF_SYMBOLTYPE_CURRENCY
:
5470 case NF_SYMBOLTYPE_DATESEP
:
5471 case NF_SYMBOLTYPE_TIMESEP
:
5472 case NF_SYMBOLTYPE_TIME100SECSEP
:
5473 case NF_SYMBOLTYPE_PERCENT
:
5481 const CharClass
& SvNumberformat::rChrCls() const
5483 return rScan
.GetChrCls();
5486 const LocaleDataWrapper
& SvNumberformat::rLoc() const
5488 return rScan
.GetLoc();
5491 CalendarWrapper
& SvNumberformat::GetCal() const
5493 return rScan
.GetCal();
5496 const SvNumberFormatter
& SvNumberformat::GetFormatter() const
5498 return *rScan
.GetNumberformatter();
5501 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */