merge the formfield patch from ooo-build
[ooovba.git] / basic / source / sbx / sbxscan.cxx
blob07a3012b60e29841ae29638181b75d9d810f52db
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: sbxscan.cxx,v $
10 * $Revision: 1.14 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basic.hxx"
33 #include <tools/errcode.hxx>
34 #include <basic/sbx.hxx>
35 #include "sbxconv.hxx"
37 #include "svtools/syslocale.hxx"
39 #if defined ( UNX )
40 #include <stdlib.h>
41 #endif
43 #ifndef _APP_HXX //autogen
44 #include <vcl/svapp.hxx>
45 #endif
46 #include <math.h>
47 #include <string.h>
48 #include <ctype.h>
50 #include "sbxres.hxx"
51 #include <basic/sbxbase.hxx>
52 #include <basic/sbxform.hxx>
53 #include <svtools/svtools.hrc>
55 #include "basrid.hxx"
56 #include "runtime.hxx"
58 #include <svtools/zforlist.hxx>
59 #include <comphelper/processfactory.hxx>
62 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
64 SvtSysLocale aSysLocale;
65 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
66 rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0];
67 rcThousandSep = rData.getNumThousandSep().GetBuffer()[0];
70 // Scannen eines Strings nach BASIC-Konventionen
71 // Dies entspricht den ueblichen Konventionen, nur dass der Exponent
72 // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt.
73 // Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten.
74 // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp
75 // Fixed ist und das ganze nicht hineinpasst!
77 SbxError ImpScan( const XubString& rWSrc, double& nVal, SbxDataType& rType,
78 USHORT* pLen, BOOL bAllowIntntl, BOOL bOnlyIntntl )
80 ByteString aBStr( rWSrc, RTL_TEXTENCODING_ASCII_US );
82 // Bei International Komma besorgen
83 char cIntntlComma, cIntntl1000;
84 char cNonIntntlComma = '.';
86 sal_Unicode cDecimalSep, cThousandSep = 0;
87 if( bAllowIntntl || bOnlyIntntl )
89 ImpGetIntntlSep( cDecimalSep, cThousandSep );
90 cIntntlComma = (char)cDecimalSep;
91 cIntntl1000 = (char)cThousandSep;
93 // Sonst einfach auch auf . setzen
94 else
96 cIntntlComma = cNonIntntlComma;
97 cIntntl1000 = cNonIntntlComma; // Unschaedlich machen
99 // Nur International -> IntnlComma uebernehmen
100 if( bOnlyIntntl )
102 cNonIntntlComma = cIntntlComma;
103 cIntntl1000 = (char)cThousandSep;
106 const char* pStart = aBStr.GetBuffer();
107 const char* p = pStart;
108 char buf[ 80 ], *q = buf;
109 BOOL bRes = TRUE;
110 BOOL bMinus = FALSE;
111 nVal = 0;
112 SbxDataType eScanType = SbxSINGLE;
113 // Whitespace wech
114 while( *p &&( *p == ' ' || *p == '\t' ) ) p++;
115 // Zahl? Dann einlesen und konvertieren.
116 if( *p == '-' )
117 p++, bMinus = TRUE;
118 if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma ||
119 *p == cIntntl1000) && isdigit( *(p+1 ) ) ) )
121 short exp = 0; // >0: Exponentteil
122 short comma = 0; // >0: Nachkomma
123 short ndig = 0; // Anzahl Ziffern
124 short ncdig = 0; // Anzahl Ziffern nach Komma
125 ByteString aSearchStr( "0123456789DEde" );
126 // Kommas ergaenzen
127 aSearchStr += cNonIntntlComma;
128 if( cIntntlComma != cNonIntntlComma )
129 aSearchStr += cIntntlComma;
130 if( bOnlyIntntl )
131 aSearchStr += cIntntl1000;
132 const char* pSearchStr = aSearchStr.GetBuffer();
133 while( strchr( pSearchStr, *p ) && *p )
135 // 1000er-Trenner ueberlesen
136 if( bOnlyIntntl && *p == cIntntl1000 )
138 p++;
139 continue;
142 // Komma oder Exponent?
143 if( *p == cNonIntntlComma || *p == cIntntlComma )
145 // Immer '.' einfuegen, damit atof funktioniert
146 p++;
147 if( ++comma > 1 )
148 continue;
149 else
150 *q++ = '.';
152 else if( strchr( "DdEe", *p ) )
154 if( ++exp > 1 )
156 p++; continue;
158 if( toupper( *p ) == 'D' )
159 eScanType = SbxDOUBLE;
160 *q++ = 'E'; p++;
161 // Vorzeichen hinter Exponent?
162 if( *p == '+' )
163 p++;
164 else
165 if( *p == '-' )
166 *q++ = *p++;
168 else
170 *q++ = *p++;
171 if( comma && !exp ) ncdig++;
173 if( !exp ) ndig++;
175 *q = 0;
176 // Komma, Exponent mehrfach vorhanden?
177 if( comma > 1 || exp > 1 )
178 bRes = FALSE;
179 // Kann auf Integer gefaltet werden?
180 if( !comma && !exp )
182 if( nVal >= SbxMININT && nVal <= SbxMAXINT )
183 eScanType = SbxINTEGER;
184 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
185 eScanType = SbxLONG;
188 nVal = atof( buf );
189 ndig = ndig - comma;
190 // zu viele Zahlen fuer SINGLE?
191 if( ndig > 15 || ncdig > 6 )
192 eScanType = SbxDOUBLE;
194 // Typkennung?
195 if( strchr( "%!&#", *p ) && *p ) p++;
197 // Hex/Oktalzahl? Einlesen und konvertieren:
198 else if( *p == '&' )
200 p++;
201 eScanType = SbxLONG;
202 const char *cmp = "0123456789ABCDEF";
203 char base = 16;
204 char ndig = 8;
205 char xch = *p++;
206 switch( toupper( xch ) )
208 case 'O': cmp = "01234567"; base = 8; ndig = 11; break;
209 case 'H': break;
210 default : bRes = FALSE;
212 long l = 0;
213 int i;
214 while( isalnum( *p ) )
216 char ch = sal::static_int_cast< char >( toupper( *p ) );
217 p++;
218 if( strchr( cmp, ch ) ) *q++ = ch;
219 else bRes = FALSE;
221 *q = 0;
222 for( q = buf; *q; q++ )
224 i =( *q & 0xFF ) - '0';
225 if( i > 9 ) i -= 7;
226 l =( l * base ) + i;
227 if( !ndig-- )
228 bRes = FALSE;
230 if( *p == '&' ) p++;
231 nVal = (double) l;
232 if( l >= SbxMININT && l <= SbxMAXINT )
233 eScanType = SbxINTEGER;
235 else if ( SbiRuntime::isVBAEnabled() )
237 OSL_TRACE("Reporting error converting");
238 return SbxERR_CONVERSION;
240 if( pLen )
241 *pLen = (USHORT) ( p - pStart );
242 if( !bRes )
243 return SbxERR_CONVERSION;
244 if( bMinus )
245 nVal = -nVal;
246 rType = eScanType;
247 return SbxERR_OK;
250 // Schnittstelle fuer CDbl im Basic
251 SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, BOOL bSingle )
253 SbxDataType t;
254 USHORT nLen = 0;
255 SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
256 /*bAllowIntntl*/FALSE, /*bOnlyIntntl*/TRUE );
257 // Komplett gelesen?
258 if( nRetError == SbxERR_OK && nLen != rSrc.Len() )
259 nRetError = SbxERR_CONVERSION;
261 if( bSingle )
263 SbxValues aValues( nVal );
264 nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow
266 return nRetError;
269 ////////////////////////////////////////////////////////////////////////////
271 static double roundArray[] = {
272 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, 0.5e-7,
273 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
275 /***************************************************************************
277 |* void myftoa( double, char *, short, short, BOOL, BOOL )
279 |* Beschreibung: Konversion double --> ASCII
280 |* Parameter: double die Zahl.
281 |* char * der Zielpuffer
282 |* short Anzahl Nachkommastellen
283 |* short Weite des Exponenten( 0=kein E )
284 |* BOOL TRUE: mit 1000er Punkten
285 |* BOOL TRUE: formatfreie Ausgabe
287 ***************************************************************************/
289 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
290 BOOL bPt, BOOL bFix, sal_Unicode cForceThousandSep = 0 )
293 short nExp = 0; // Exponent
294 short nDig = nPrec + 1; // Anzahl Digits in Zahl
295 short nDec; // Anzahl Vorkommastellen
296 register int i, digit;
298 // Komma besorgen
299 sal_Unicode cDecimalSep, cThousandSep;
300 ImpGetIntntlSep( cDecimalSep, cThousandSep );
301 if( cForceThousandSep )
302 cThousandSep = cForceThousandSep;
304 // Exponentberechnung:
305 nExp = 0;
306 if( nNum > 0.0 )
308 while( nNum < 1.0 ) nNum *= 10.0, nExp--;
309 while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
311 if( !bFix && !nExpWidth )
312 nDig = nDig + nExp;
313 else if( bFix && !nPrec )
314 nDig = nExp + 1;
316 // Zahl runden:
317 if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
319 nNum = 1.0;
320 ++nExp;
321 if( !nExpWidth ) ++nDig;
324 // Bestimmung der Vorkommastellen:
325 if( !nExpWidth )
327 if( nExp < 0 )
329 // #41691: Auch bei bFix eine 0 spendieren
330 *pBuf++ = '0';
331 if( nPrec ) *pBuf++ = (char)cDecimalSep;
332 i = -nExp - 1;
333 if( nDig <= 0 ) i = nPrec;
334 while( i-- ) *pBuf++ = '0';
335 nDec = 0;
337 else
338 nDec = nExp+1;
340 else
341 nDec = 1;
343 // Zahl ausgeben:
344 if( nDig > 0 )
346 for( i = 0 ; ; ++i )
348 if( i < 16 )
350 digit = (int) nNum;
351 *pBuf++ = sal::static_int_cast< char >(digit + '0');
352 nNum =( nNum - digit ) * 10.0;
353 } else
354 *pBuf++ = '0';
355 if( --nDig == 0 ) break;
356 if( nDec )
358 nDec--;
359 if( !nDec )
360 *pBuf++ = (char)cDecimalSep;
361 else if( !(nDec % 3 ) && bPt )
362 *pBuf++ = (char)cThousandSep;
367 // Exponent ausgeben:
368 if( nExpWidth )
370 if( nExpWidth < 3 ) nExpWidth = 3;
371 nExpWidth -= 2;
372 *pBuf++ = 'E';
373 *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
374 while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
375 if( nExp >= 100 || nExpWidth == 3 )
377 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
378 nExp %= 100;
380 if( nExp/10 || nExpWidth >= 2 )
381 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
382 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
384 *pBuf = 0;
387 // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen
388 // aufbereitet. Evtl. wird ein Minus vorangestellt.
389 // Diese Routine ist public, weil sie auch von den Put-Funktionen
390 // der Klasse SbxImpSTRING verwendet wird.
392 #ifdef _MSC_VER
393 #pragma optimize( "", off )
394 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
395 #endif
397 void ImpCvtNum( double nNum, short nPrec, XubString& rRes, BOOL bCoreString )
399 char *q;
400 char cBuf[ 40 ], *p = cBuf;
402 sal_Unicode cDecimalSep, cThousandSep;
403 ImpGetIntntlSep( cDecimalSep, cThousandSep );
404 if( bCoreString )
405 cDecimalSep = '.';
407 if( nNum < 0.0 ) {
408 nNum = -nNum;
409 *p++ = '-';
411 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
412 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum > dMaxNumWithoutExp ) ) ? 4:0,
413 FALSE, TRUE, cDecimalSep );
414 // Trailing Zeroes weg:
415 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
416 q = p; p--;
417 while( nPrec && *p == '0' ) nPrec--, p--;
418 if( *p == cDecimalSep ) p--;
419 while( *q ) *++p = *q++;
420 *++p = 0;
421 rRes = String::CreateFromAscii( cBuf );
424 #ifdef _MSC_VER
425 #pragma optimize( "", on )
426 #endif
428 BOOL ImpConvStringExt( XubString& rSrc, SbxDataType eTargetType )
430 // Merken, ob ueberhaupt was geaendert wurde
431 BOOL bChanged = FALSE;
432 String aNewString;
434 // Nur Spezial-Fälle behandeln, als Default tun wir nichts
435 switch( eTargetType )
437 // Bei Fliesskomma International beruecksichtigen
438 case SbxSINGLE:
439 case SbxDOUBLE:
440 case SbxCURRENCY:
442 ByteString aBStr( rSrc, RTL_TEXTENCODING_ASCII_US );
444 // Komma besorgen
445 sal_Unicode cDecimalSep, cThousandSep;
446 ImpGetIntntlSep( cDecimalSep, cThousandSep );
447 aNewString = rSrc;
449 // Ersetzen, wenn DecimalSep kein '.' (nur den ersten)
450 if( cDecimalSep != (sal_Unicode)'.' )
452 USHORT nPos = aNewString.Search( cDecimalSep );
453 if( nPos != STRING_NOTFOUND )
455 aNewString.SetChar( nPos, '.' );
456 bChanged = TRUE;
459 break;
462 // Bei BOOL TRUE und FALSE als String pruefen
463 case SbxBOOL:
465 if( rSrc.EqualsIgnoreCaseAscii( "true" ) )
467 aNewString = String::CreateFromInt32(SbxTRUE);
468 bChanged = TRUE;
470 else
471 if( rSrc.EqualsIgnoreCaseAscii( "false" ) )
473 aNewString = String::CreateFromInt32(SbxFALSE);
474 bChanged = TRUE;
476 break;
478 default: break;
480 // String bei Aenderung uebernehmen
481 if( bChanged )
482 rSrc = aNewString;
483 return bChanged;
487 // Formatierte Zahlenausgabe
488 // Der Returnwert ist die Anzahl Zeichen, die aus dem
489 // Format verwendt wurden.
491 #ifdef _old_format_code_
492 // lasse diesen Code vorl"aufig drin, zum 'abgucken'
493 // der bisherigen Implementation
495 static USHORT printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt )
497 const String& rFmt = rWFmt;
498 char cFill = ' '; // Fuellzeichen
499 char cPre = 0; // Startzeichen( evtl. "$" )
500 short nExpDig= 0; // Anzahl Exponentstellen
501 short nPrec = 0; // Anzahl Nachkommastellen
502 short nWidth = 0; // Zahlenweite gesamnt
503 short nLen; // Laenge konvertierte Zahl
504 BOOL bPoint = FALSE; // TRUE: mit 1000er Kommas
505 BOOL bTrail = FALSE; // TRUE, wenn folgendes Minus
506 BOOL bSign = FALSE; // TRUE: immer mit Vorzeichen
507 BOOL bNeg = FALSE; // TRUE: Zahl ist negativ
508 char cBuf [1024]; // Zahlenpuffer
509 char * p;
510 const char* pFmt = rFmt;
511 rRes.Erase();
512 // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben.
513 if( *pFmt == '$' )
514 if( *++pFmt != '$' ) rRes += '$';
515 if( *pFmt == '*' )
516 if( *++pFmt != '*' ) rRes += '*';
518 switch( *pFmt++ )
520 case 0:
521 break;
522 case '+':
523 bSign = TRUE; nWidth++; break;
524 case '*':
525 nWidth++; cFill = '*';
526 if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$';
527 break;
528 case '$':
529 nWidth++; cPre = '$'; break;
530 case '#':
531 case '.':
532 case ',':
533 pFmt--; break;
535 // Vorkomma:
536 for( ;; )
538 while( *pFmt == '#' ) pFmt++, nWidth++;
539 // 1000er Kommas?
540 if( *pFmt == ',' )
542 nWidth++; pFmt++; bPoint = TRUE;
543 } else break;
545 // Nachkomma:
546 if( *pFmt == '.' )
548 while( *++pFmt == '#' ) nPrec++;
549 nWidth += nPrec + 1;
551 // Exponent:
552 while( *pFmt == '^' )
553 pFmt++, nExpDig++, nWidth++;
554 // Folgendes Minus:
555 if( !bSign && *pFmt == '-' )
556 pFmt++, bTrail = TRUE;
558 // Zahl konvertieren:
559 if( nPrec > 15 ) nPrec = 15;
560 if( nNum < 0.0 ) nNum = -nNum, bNeg = TRUE;
561 p = cBuf;
562 if( bSign ) *p++ = bNeg ? '-' : '+';
563 myftoa( nNum, p, nPrec, nExpDig, bPoint, FALSE );
564 nLen = strlen( cBuf );
566 // Ueberlauf?
567 if( cPre ) nLen++;
568 if( nLen > nWidth ) rRes += '%';
569 else {
570 nWidth -= nLen;
571 while( nWidth-- ) rRes += (xub_Unicode)cFill;
572 if( cPre ) rRes += (xub_Unicode)cPre;
574 rRes += (xub_Unicode*)&(cBuf[0]);
575 if( bTrail )
576 rRes += bNeg ? '-' : ' ';
578 return (USHORT) ( pFmt - (const char*) rFmt );
581 #endif //_old_format_code_
583 static USHORT printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt )
585 const xub_Unicode* pStr = rStr.GetBuffer();
586 const xub_Unicode* pFmtStart = rFmt.GetBuffer();
587 const xub_Unicode* pFmt = pFmtStart;
588 rRes.Erase();
589 switch( *pFmt )
591 case '!':
592 rRes += *pStr++; pFmt++; break;
593 case '\\':
596 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
597 pFmt++;
598 } while( *pFmt != '\\' );
599 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
600 pFmt++; break;
601 case '&':
602 rRes = rStr;
603 pFmt++; break;
604 default:
605 rRes = rStr;
606 break;
608 return (USHORT) ( pFmt - pFmtStart );
611 /////////////////////////////////////////////////////////////////////////
613 BOOL SbxValue::Scan( const XubString& rSrc, USHORT* pLen )
615 SbxError eRes = SbxERR_OK;
616 if( !CanWrite() )
617 eRes = SbxERR_PROP_READONLY;
618 else
620 double n;
621 SbxDataType t;
622 eRes = ImpScan( rSrc, n, t, pLen );
623 if( eRes == SbxERR_OK )
625 if( !IsFixed() )
626 SetType( t );
627 PutDouble( n );
630 if( eRes )
632 SetError( eRes ); return FALSE;
634 else
635 return TRUE;
639 ResMgr* implGetResMgr( void )
641 static ResMgr* pResMgr = NULL;
642 if( !pResMgr )
644 ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale();
645 pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale );
647 return pResMgr;
650 class SbxValueFormatResId : public ResId
652 public:
653 SbxValueFormatResId( USHORT nId )
654 : ResId( nId, *implGetResMgr() )
659 enum VbaFormatType
661 VBA_FORMAT_TYPE_OFFSET, // standard number format
662 VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
663 VBA_FORMAT_TYPE_NULL
666 struct VbaFormatInfo
668 VbaFormatType meType;
669 const char* mpVbaFormat; // Format string in vba
670 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
671 const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED
674 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
675 { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 }
677 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
678 { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 }
680 static VbaFormatInfo pFormatInfoTable[] =
682 VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ),
683 VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
684 VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ),
685 VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
686 VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ),
687 VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ),
688 VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ),
689 VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ),
690 VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
691 VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ),
692 { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 }
695 VbaFormatInfo* getFormatInfo( const String& rFmt )
697 VbaFormatInfo* pInfo = NULL;
698 INT16 i = 0;
699 while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL )
701 if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) )
702 break;
703 i++;
705 return pInfo;
708 #define VBAFORMAT_GENERALDATE "General Date"
709 #define VBAFORMAT_C "c"
710 #define VBAFORMAT_N "n"
711 #define VBAFORMAT_NN "nn"
712 #define VBAFORMAT_W "w"
713 #define VBAFORMAT_Y "y"
714 #define VBAFORMAT_LOWERCASE "<"
715 #define VBAFORMAT_UPPERCASE ">"
717 // From methods1.cxx
718 INT16 implGetWeekDay( double aDate, bool bFirstDayParam = false, INT16 nFirstDay = 0 );
719 // from methods.cxx
720 INT16 implGetMinute( double dDate );
721 INT16 implGetDateYear( double aDate );
722 BOOL implDateSerial( INT16 nYear, INT16 nMonth, INT16 nDay, double& rdRet );
724 void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const
726 short nComma = 0;
727 double d = 0;
729 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
730 // the SvNumberFormatter output is mostly compatible with
731 // VBA output besides the OOo-basic output
732 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
734 String aStr = GetString();
736 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) )
738 rRes = aStr.ToLowerAscii();
739 return;
741 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) )
743 rRes = aStr.ToUpperAscii();
744 return;
747 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
748 com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
749 xFactory = comphelper::getProcessServiceFactory();
750 SvNumberFormatter aFormatter( xFactory, eLangType );
752 sal_uInt32 nIndex;
753 xub_StrLen nCheckPos = 0;
754 short nType;
755 double nNumber;
756 Color* pCol;
758 BOOL bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
760 // number format, use SvNumberFormatter to handle it.
761 if( bSuccess )
763 String aFmtStr = *pFmt;
764 VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
765 if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
767 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
769 nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
771 else
773 aFmtStr.AssignAscii( pInfo->mpOOoFormat );
774 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
776 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
778 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE )
779 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C ))
781 if( nNumber <=-1.0 || nNumber >= 1.0 )
783 // short date
784 nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
785 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
787 // long time
788 if( floor( nNumber ) != nNumber )
790 aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
791 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
792 String aTime;
793 aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
794 rRes.AppendAscii(" ");
795 rRes += aTime;
798 else
800 // long time only
801 aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
802 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
803 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
806 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N )
807 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ))
809 INT32 nMin = implGetMinute( nNumber );
810 if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) )
812 // Minute in two digits
813 sal_Unicode* p = rRes.AllocBuffer( 2 );
814 *p++ = '0';
815 *p = sal_Unicode( '0' + nMin );
817 else
819 rRes = String::CreateFromInt32( nMin );
822 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W ))
824 INT32 nWeekDay = implGetWeekDay( nNumber );
825 rRes = String::CreateFromInt32( nWeekDay );
827 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y ))
829 INT16 nYear = implGetDateYear( nNumber );
830 double dBaseDate;
831 implDateSerial( nYear, 1, 1, dBaseDate );
832 INT32 nYear32 = 1 + INT32( nNumber - dBaseDate );
833 rRes = String::CreateFromInt32( nYear32 );
835 else
837 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
838 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
841 return;
845 SbxDataType eType = GetType();
846 switch( eType )
848 case SbxCHAR:
849 case SbxBYTE:
850 case SbxINTEGER:
851 case SbxUSHORT:
852 case SbxLONG:
853 case SbxULONG:
854 case SbxINT:
855 case SbxUINT:
856 case SbxNULL: // #45929 NULL mit durchschummeln
857 nComma = 0; goto cvt;
858 case SbxSINGLE:
859 nComma = 6; goto cvt;
860 case SbxDOUBLE:
861 nComma = 14;
863 cvt:
864 if( eType != SbxNULL )
865 d = GetDouble();
867 // #45355 weiterer Einsprungpunkt fuer isnumeric-String
868 cvt2:
869 if( pFmt )
871 // hole die 'statischen' Daten f"ur Sbx
872 SbxAppData* pData = GetSbxData_Impl();
874 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
875 if( pData->pBasicFormater )
877 if( pData->eBasicFormaterLangType != eLangType )
879 delete pData->pBasicFormater;
880 pData->pBasicFormater = NULL;
883 pData->eBasicFormaterLangType = eLangType;
885 // falls bisher noch kein BasicFormater-Objekt
886 // existiert, so erzeuge dieses
887 if( !pData->pBasicFormater )
889 SvtSysLocale aSysLocale;
890 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
891 sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0];
892 sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0];
893 String aCurrencyStrg = rData.getCurrSymbol();
895 // Initialisierung des Basic-Formater-Hilfsobjekts:
896 // hole die Resourcen f"ur die vordefinierten Ausgaben
897 // des Format()-Befehls, z.B. f"ur "On/Off".
898 String aOnStrg = String( SbxValueFormatResId(
899 STR_BASICKEY_FORMAT_ON ) );
900 String aOffStrg = String( SbxValueFormatResId(
901 STR_BASICKEY_FORMAT_OFF) );
902 String aYesStrg = String( SbxValueFormatResId(
903 STR_BASICKEY_FORMAT_YES) );
904 String aNoStrg = String( SbxValueFormatResId(
905 STR_BASICKEY_FORMAT_NO) );
906 String aTrueStrg = String( SbxValueFormatResId(
907 STR_BASICKEY_FORMAT_TRUE) );
908 String aFalseStrg = String( SbxValueFormatResId(
909 STR_BASICKEY_FORMAT_FALSE) );
910 String aCurrencyFormatStrg = String( SbxValueFormatResId(
911 STR_BASICKEY_FORMAT_CURRENCY) );
912 // erzeuge das Basic-Formater-Objekt
913 pData->pBasicFormater
914 = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
915 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
916 aCurrencyStrg,aCurrencyFormatStrg );
918 // Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater-
919 // Objekt erzeugt und 'gespeichert', dadurch erspart man
920 // sich das teure Resourcen-Laden (f"ur landesspezifische
921 // vordefinierte Ausgaben, z.B. "On/Off") und die st"andige
922 // String-Erzeugungs Operationen.
923 // ABER: dadurch ist dieser Code NICHT multithreading f"ahig !
925 // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen
926 // wird, wenn der SbxValue eine Zahl ist !!!
927 // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden !
928 if( eType != SbxNULL )
930 rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt );
932 else
934 rRes = pData->pBasicFormater->BasicFormatNull( *pFmt );
937 // Die alte Implementierung:
938 //old: printfmtnum( GetDouble(), rRes, *pFmt );
940 else
941 ImpCvtNum( GetDouble(), nComma, rRes );
942 break;
943 case SbxSTRING:
944 if( pFmt )
946 // #45355 wenn es numerisch ist, muss gewandelt werden
947 if( IsNumericRTL() )
949 ScanNumIntnl( GetString(), d, /*bSingle*/FALSE );
950 goto cvt2;
952 else
954 // Sonst String-Formatierung
955 printfmtstr( GetString(), rRes, *pFmt );
958 else
959 rRes = GetString();
960 break;
961 default:
962 rRes = GetString();