| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_basic.hxx" |
| #include <tools/errcode.hxx> |
| #include <basic/sbx.hxx> |
| #include "sbxconv.hxx" |
| |
| #include "unotools/syslocale.hxx" |
| |
| #if defined ( UNX ) |
| #include <stdlib.h> |
| #endif |
| |
| #ifndef _APP_HXX //autogen |
| #include <vcl/svapp.hxx> |
| #endif |
| #include <math.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "sbxres.hxx" |
| #include <basic/sbxbase.hxx> |
| #include <basic/sbxform.hxx> |
| #include <svtools/svtools.hrc> |
| |
| #include "basrid.hxx" |
| #include "runtime.hxx" |
| |
| #include <svl/zforlist.hxx> |
| #include <comphelper/processfactory.hxx> |
| |
| |
| void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep ) |
| { |
| SvtSysLocale aSysLocale; |
| const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); |
| rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0]; |
| rcThousandSep = rData.getNumThousandSep().GetBuffer()[0]; |
| } |
| |
| // Scannen eines Strings nach BASIC-Konventionen |
| // Dies entspricht den ueblichen Konventionen, nur dass der Exponent |
| // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt. |
| // Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten. |
| // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp |
| // Fixed ist und das ganze nicht hineinpasst! |
| |
| SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType, |
| sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl ) |
| { |
| ::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) ); |
| |
| // Bei International Komma besorgen |
| char cIntntlComma, cIntntl1000; |
| char cNonIntntlComma = '.'; |
| |
| sal_Unicode cDecimalSep, cThousandSep = 0; |
| if( bAllowIntntl || bOnlyIntntl ) |
| { |
| ImpGetIntntlSep( cDecimalSep, cThousandSep ); |
| cIntntlComma = (char)cDecimalSep; |
| cIntntl1000 = (char)cThousandSep; |
| } |
| // Sonst einfach auch auf . setzen |
| else |
| { |
| cIntntlComma = cNonIntntlComma; |
| cIntntl1000 = cNonIntntlComma; // Unschaedlich machen |
| } |
| // Nur International -> IntnlComma uebernehmen |
| if( bOnlyIntntl ) |
| { |
| cNonIntntlComma = cIntntlComma; |
| cIntntl1000 = (char)cThousandSep; |
| } |
| |
| const char* pStart = aBStr.getStr(); |
| const char* p = pStart; |
| char buf[ 80 ], *q = buf; |
| sal_Bool bRes = sal_True; |
| sal_Bool bMinus = sal_False; |
| nVal = 0; |
| SbxDataType eScanType = SbxSINGLE; |
| // Whitespace wech |
| while( *p &&( *p == ' ' || *p == '\t' ) ) p++; |
| // Zahl? Dann einlesen und konvertieren. |
| if( *p == '-' ) |
| p++, bMinus = sal_True; |
| if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma || |
| *p == cIntntl1000) && isdigit( *(p+1 ) ) ) ) |
| { |
| short exp = 0; // >0: Exponentteil |
| short comma = 0; // >0: Nachkomma |
| short ndig = 0; // Anzahl Ziffern |
| short ncdig = 0; // Anzahl Ziffern nach Komma |
| ByteString aSearchStr( "0123456789DEde" ); |
| // Kommas ergaenzen |
| aSearchStr += cNonIntntlComma; |
| if( cIntntlComma != cNonIntntlComma ) |
| aSearchStr += cIntntlComma; |
| if( bOnlyIntntl ) |
| aSearchStr += cIntntl1000; |
| const char* pSearchStr = aSearchStr.GetBuffer(); |
| while( strchr( pSearchStr, *p ) && *p ) |
| { |
| // 1000er-Trenner ueberlesen |
| if( bOnlyIntntl && *p == cIntntl1000 ) |
| { |
| p++; |
| continue; |
| } |
| |
| // Komma oder Exponent? |
| if( *p == cNonIntntlComma || *p == cIntntlComma ) |
| { |
| // Immer '.' einfuegen, damit atof funktioniert |
| p++; |
| if( ++comma > 1 ) |
| continue; |
| else |
| *q++ = '.'; |
| } |
| else if( strchr( "DdEe", *p ) ) |
| { |
| if( ++exp > 1 ) |
| { |
| p++; continue; |
| } |
| if( toupper( *p ) == 'D' ) |
| eScanType = SbxDOUBLE; |
| *q++ = 'E'; p++; |
| // Vorzeichen hinter Exponent? |
| if( *p == '+' ) |
| p++; |
| else |
| if( *p == '-' ) |
| *q++ = *p++; |
| } |
| else |
| { |
| *q++ = *p++; |
| if( comma && !exp ) ncdig++; |
| } |
| if( !exp ) ndig++; |
| } |
| *q = 0; |
| // Komma, Exponent mehrfach vorhanden? |
| if( comma > 1 || exp > 1 ) |
| bRes = sal_False; |
| // Kann auf Integer gefaltet werden? |
| if( !comma && !exp ) |
| { |
| if( nVal >= SbxMININT && nVal <= SbxMAXINT ) |
| eScanType = SbxINTEGER; |
| else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) |
| eScanType = SbxLONG; |
| } |
| |
| nVal = atof( buf ); |
| ndig = ndig - comma; |
| // zu viele Zahlen fuer SINGLE? |
| if( ndig > 15 || ncdig > 6 ) |
| eScanType = SbxDOUBLE; |
| |
| // Typkennung? |
| if( strchr( "%!&#", *p ) && *p ) p++; |
| } |
| // Hex/Oktalzahl? Einlesen und konvertieren: |
| else if( *p == '&' ) |
| { |
| p++; |
| eScanType = SbxLONG; |
| const char *cmp = "0123456789ABCDEF"; |
| char base = 16; |
| char ndig = 8; |
| char xch = *p++; |
| switch( toupper( xch ) ) |
| { |
| case 'O': cmp = "01234567"; base = 8; ndig = 11; break; |
| case 'H': break; |
| default : bRes = sal_False; |
| } |
| long l = 0; |
| int i; |
| while( isalnum( *p ) ) |
| { |
| char ch = sal::static_int_cast< char >( toupper( *p ) ); |
| p++; |
| if( strchr( cmp, ch ) ) *q++ = ch; |
| else bRes = sal_False; |
| } |
| *q = 0; |
| for( q = buf; *q; q++ ) |
| { |
| i =( *q & 0xFF ) - '0'; |
| if( i > 9 ) i -= 7; |
| l =( l * base ) + i; |
| if( !ndig-- ) |
| bRes = sal_False; |
| } |
| if( *p == '&' ) p++; |
| nVal = (double) l; |
| if( l >= SbxMININT && l <= SbxMAXINT ) |
| eScanType = SbxINTEGER; |
| } |
| else if ( SbiRuntime::isVBAEnabled() ) |
| { |
| OSL_TRACE("Reporting error converting"); |
| return SbxERR_CONVERSION; |
| } |
| if( pLen ) |
| *pLen = (sal_uInt16) ( p - pStart ); |
| if( !bRes ) |
| return SbxERR_CONVERSION; |
| if( bMinus ) |
| nVal = -nVal; |
| rType = eScanType; |
| return SbxERR_OK; |
| } |
| |
| // Schnittstelle fuer CDbl im Basic |
| SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle ) |
| { |
| SbxDataType t; |
| sal_uInt16 nLen = 0; |
| SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen, |
| /*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True ); |
| // Komplett gelesen? |
| if( nRetError == SbxERR_OK && nLen != rSrc.Len() ) |
| nRetError = SbxERR_CONVERSION; |
| |
| if( bSingle ) |
| { |
| SbxValues aValues( nVal ); |
| nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow |
| } |
| return nRetError; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| static double roundArray[] = { |
| 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, |
| 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 }; |
| |
| /*************************************************************************** |
| |* |
| |* void myftoa( double, char *, short, short, sal_Bool, sal_Bool ) |
| |* |
| |* Beschreibung: Konversion double --> ASCII |
| |* Parameter: double die Zahl. |
| |* char * der Zielpuffer |
| |* short Anzahl Nachkommastellen |
| |* short Weite des Exponenten( 0=kein E ) |
| |* sal_Bool sal_True: mit 1000er Punkten |
| |* sal_Bool sal_True: formatfreie Ausgabe |
| |* |
| ***************************************************************************/ |
| |
| static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth, |
| sal_Bool bPt, sal_Bool bFix, sal_Unicode cForceThousandSep = 0 ) |
| { |
| |
| short nExp = 0; // Exponent |
| short nDig = nPrec + 1; // Anzahl Digits in Zahl |
| short nDec; // Anzahl Vorkommastellen |
| register int i, digit; |
| |
| // Komma besorgen |
| sal_Unicode cDecimalSep, cThousandSep; |
| ImpGetIntntlSep( cDecimalSep, cThousandSep ); |
| if( cForceThousandSep ) |
| cThousandSep = cForceThousandSep; |
| |
| // Exponentberechnung: |
| nExp = 0; |
| if( nNum > 0.0 ) |
| { |
| while( nNum < 1.0 ) nNum *= 10.0, nExp--; |
| while( nNum >= 10.0 ) nNum /= 10.0, nExp++; |
| } |
| if( !bFix && !nExpWidth ) |
| nDig = nDig + nExp; |
| else if( bFix && !nPrec ) |
| nDig = nExp + 1; |
| |
| // Zahl runden: |
| if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 ) |
| { |
| nNum = 1.0; |
| ++nExp; |
| if( !nExpWidth ) ++nDig; |
| } |
| |
| // Bestimmung der Vorkommastellen: |
| if( !nExpWidth ) |
| { |
| if( nExp < 0 ) |
| { |
| // #41691: Auch bei bFix eine 0 spendieren |
| *pBuf++ = '0'; |
| if( nPrec ) *pBuf++ = (char)cDecimalSep; |
| i = -nExp - 1; |
| if( nDig <= 0 ) i = nPrec; |
| while( i-- ) *pBuf++ = '0'; |
| nDec = 0; |
| } |
| else |
| nDec = nExp+1; |
| } |
| else |
| nDec = 1; |
| |
| // Zahl ausgeben: |
| if( nDig > 0 ) |
| { |
| for( i = 0 ; ; ++i ) |
| { |
| if( i < 16 ) |
| { |
| digit = (int) nNum; |
| *pBuf++ = sal::static_int_cast< char >(digit + '0'); |
| nNum =( nNum - digit ) * 10.0; |
| } else |
| *pBuf++ = '0'; |
| if( --nDig == 0 ) break; |
| if( nDec ) |
| { |
| nDec--; |
| if( !nDec ) |
| *pBuf++ = (char)cDecimalSep; |
| else if( !(nDec % 3 ) && bPt ) |
| *pBuf++ = (char)cThousandSep; |
| } |
| } |
| } |
| |
| // Exponent ausgeben: |
| if( nExpWidth ) |
| { |
| if( nExpWidth < 3 ) nExpWidth = 3; |
| nExpWidth -= 2; |
| *pBuf++ = 'E'; |
| *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+'; |
| while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--; |
| if( nExp >= 100 || nExpWidth == 3 ) |
| { |
| *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0'); |
| nExp %= 100; |
| } |
| if( nExp/10 || nExpWidth >= 2 ) |
| *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0'); |
| *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0'); |
| } |
| *pBuf = 0; |
| } |
| |
| // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen |
| // aufbereitet. Evtl. wird ein Minus vorangestellt. |
| // Diese Routine ist public, weil sie auch von den Put-Funktionen |
| // der Klasse SbxImpSTRING verwendet wird. |
| |
| #ifdef _MSC_VER |
| #pragma optimize( "", off ) |
| #pragma warning(disable: 4748) // "... because optimizations are disabled ..." |
| #endif |
| |
| void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString ) |
| { |
| char *q; |
| char cBuf[ 40 ], *p = cBuf; |
| |
| sal_Unicode cDecimalSep, cThousandSep; |
| ImpGetIntntlSep( cDecimalSep, cThousandSep ); |
| if( bCoreString ) |
| cDecimalSep = '.'; |
| |
| if( nNum < 0.0 ) { |
| nNum = -nNum; |
| *p++ = '-'; |
| } |
| double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14; |
| myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0, |
| sal_False, sal_True, cDecimalSep ); |
| // Trailing Zeroes weg: |
| for( p = cBuf; *p &&( *p != 'E' ); p++ ) {} |
| q = p; p--; |
| while( nPrec && *p == '0' ) nPrec--, p--; |
| if( *p == cDecimalSep ) p--; |
| while( *q ) *++p = *q++; |
| *++p = 0; |
| rRes = ::rtl::OUString::createFromAscii( cBuf ); |
| } |
| |
| #ifdef _MSC_VER |
| #pragma optimize( "", on ) |
| #endif |
| |
| sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType ) |
| { |
| // Merken, ob ueberhaupt was geaendert wurde |
| sal_Bool bChanged = sal_False; |
| ::rtl::OUString aNewString; |
| |
| // Nur Spezial-Fälle behandeln, als Default tun wir nichts |
| switch( eTargetType ) |
| { |
| // Bei Fliesskomma International beruecksichtigen |
| case SbxSINGLE: |
| case SbxDOUBLE: |
| case SbxCURRENCY: |
| { |
| ::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) ); |
| |
| // Komma besorgen |
| sal_Unicode cDecimalSep, cThousandSep; |
| ImpGetIntntlSep( cDecimalSep, cThousandSep ); |
| aNewString = rSrc; |
| |
| // Ersetzen, wenn DecimalSep kein '.' (nur den ersten) |
| if( cDecimalSep != (sal_Unicode)'.' ) |
| { |
| sal_Int32 nPos = aNewString.indexOf( cDecimalSep ); |
| if( nPos != -1 ) |
| { |
| sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr(); |
| pStr[nPos] = (sal_Unicode)'.'; |
| bChanged = sal_True; |
| } |
| } |
| break; |
| } |
| |
| // Bei sal_Bool sal_True und sal_False als String pruefen |
| case SbxBOOL: |
| { |
| if( rSrc.equalsIgnoreAsciiCaseAscii( "true" ) ) |
| { |
| aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE ); |
| bChanged = sal_True; |
| } |
| else |
| if( rSrc.equalsIgnoreAsciiCaseAscii( "false" ) ) |
| { |
| aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE ); |
| bChanged = sal_True; |
| } |
| break; |
| } |
| default: break; |
| } |
| // String bei Aenderung uebernehmen |
| if( bChanged ) |
| rSrc = aNewString; |
| return bChanged; |
| } |
| |
| |
| // Formatierte Zahlenausgabe |
| // Der Returnwert ist die Anzahl Zeichen, die aus dem |
| // Format verwendt wurden. |
| |
| #ifdef _old_format_code_ |
| // lasse diesen Code vorl"aufig drin, zum 'abgucken' |
| // der bisherigen Implementation |
| |
| static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt ) |
| { |
| const String& rFmt = rWFmt; |
| char cFill = ' '; // Fuellzeichen |
| char cPre = 0; // Startzeichen( evtl. "$" ) |
| short nExpDig= 0; // Anzahl Exponentstellen |
| short nPrec = 0; // Anzahl Nachkommastellen |
| short nWidth = 0; // Zahlenweite gesamnt |
| short nLen; // Laenge konvertierte Zahl |
| sal_Bool bPoint = sal_False; // sal_True: mit 1000er Kommas |
| sal_Bool bTrail = sal_False; // sal_True, wenn folgendes Minus |
| sal_Bool bSign = sal_False; // sal_True: immer mit Vorzeichen |
| sal_Bool bNeg = sal_False; // sal_True: Zahl ist negativ |
| char cBuf [1024]; // Zahlenpuffer |
| char * p; |
| const char* pFmt = rFmt; |
| rRes.Erase(); |
| // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben. |
| if( *pFmt == '$' ) |
| if( *++pFmt != '$' ) rRes += '$'; |
| if( *pFmt == '*' ) |
| if( *++pFmt != '*' ) rRes += '*'; |
| |
| switch( *pFmt++ ) |
| { |
| case 0: |
| break; |
| case '+': |
| bSign = sal_True; nWidth++; break; |
| case '*': |
| nWidth++; cFill = '*'; |
| if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$'; |
| break; |
| case '$': |
| nWidth++; cPre = '$'; break; |
| case '#': |
| case '.': |
| case ',': |
| pFmt--; break; |
| } |
| // Vorkomma: |
| for( ;; ) |
| { |
| while( *pFmt == '#' ) pFmt++, nWidth++; |
| // 1000er Kommas? |
| if( *pFmt == ',' ) |
| { |
| nWidth++; pFmt++; bPoint = sal_True; |
| } else break; |
| } |
| // Nachkomma: |
| if( *pFmt == '.' ) |
| { |
| while( *++pFmt == '#' ) nPrec++; |
| nWidth += nPrec + 1; |
| } |
| // Exponent: |
| while( *pFmt == '^' ) |
| pFmt++, nExpDig++, nWidth++; |
| // Folgendes Minus: |
| if( !bSign && *pFmt == '-' ) |
| pFmt++, bTrail = sal_True; |
| |
| // Zahl konvertieren: |
| if( nPrec > 15 ) nPrec = 15; |
| if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True; |
| p = cBuf; |
| if( bSign ) *p++ = bNeg ? '-' : '+'; |
| myftoa( nNum, p, nPrec, nExpDig, bPoint, sal_False ); |
| nLen = strlen( cBuf ); |
| |
| // Ueberlauf? |
| if( cPre ) nLen++; |
| if( nLen > nWidth ) rRes += '%'; |
| else { |
| nWidth -= nLen; |
| while( nWidth-- ) rRes += (xub_Unicode)cFill; |
| if( cPre ) rRes += (xub_Unicode)cPre; |
| } |
| rRes += (xub_Unicode*)&(cBuf[0]); |
| if( bTrail ) |
| rRes += bNeg ? '-' : ' '; |
| |
| return (sal_uInt16) ( pFmt - (const char*) rFmt ); |
| } |
| |
| #endif //_old_format_code_ |
| |
| static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt ) |
| { |
| const xub_Unicode* pStr = rStr.GetBuffer(); |
| const xub_Unicode* pFmtStart = rFmt.GetBuffer(); |
| const xub_Unicode* pFmt = pFmtStart; |
| rRes.Erase(); |
| switch( *pFmt ) |
| { |
| case '!': |
| rRes += *pStr++; pFmt++; break; |
| case '\\': |
| do |
| { |
| rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); |
| pFmt++; |
| } while( *pFmt != '\\' ); |
| rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); |
| pFmt++; break; |
| case '&': |
| rRes = rStr; |
| pFmt++; break; |
| default: |
| rRes = rStr; |
| break; |
| } |
| return (sal_uInt16) ( pFmt - pFmtStart ); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////// |
| |
| sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen ) |
| { |
| SbxError eRes = SbxERR_OK; |
| if( !CanWrite() ) |
| eRes = SbxERR_PROP_READONLY; |
| else |
| { |
| double n; |
| SbxDataType t; |
| eRes = ImpScan( rSrc, n, t, pLen ); |
| if( eRes == SbxERR_OK ) |
| { |
| if( !IsFixed() ) |
| SetType( t ); |
| PutDouble( n ); |
| } |
| } |
| if( eRes ) |
| { |
| SetError( eRes ); return sal_False; |
| } |
| else |
| return sal_True; |
| } |
| |
| |
| ResMgr* implGetResMgr( void ) |
| { |
| static ResMgr* pResMgr = NULL; |
| if( !pResMgr ) |
| { |
| ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale(); |
| pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale ); |
| } |
| return pResMgr; |
| } |
| |
| class SbxValueFormatResId : public ResId |
| { |
| public: |
| SbxValueFormatResId( sal_uInt16 nId ) |
| : ResId( nId, *implGetResMgr() ) |
| {} |
| }; |
| |
| |
| enum VbaFormatType |
| { |
| VBA_FORMAT_TYPE_OFFSET, // standard number format |
| VBA_FORMAT_TYPE_USERDEFINED, // user defined number format |
| VBA_FORMAT_TYPE_NULL |
| }; |
| |
| struct VbaFormatInfo |
| { |
| VbaFormatType meType; |
| const char* mpVbaFormat; // Format string in vba |
| NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET |
| const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED |
| }; |
| |
| #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \ |
| { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 } |
| |
| #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \ |
| { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 } |
| |
| static VbaFormatInfo pFormatInfoTable[] = |
| { |
| VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ), |
| VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ), |
| VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ), |
| VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ), |
| VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ), |
| VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ), |
| VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ), |
| VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ), |
| VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ), |
| VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ), |
| { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 } |
| }; |
| |
| VbaFormatInfo* getFormatInfo( const String& rFmt ) |
| { |
| VbaFormatInfo* pInfo = NULL; |
| sal_Int16 i = 0; |
| while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL ) |
| { |
| if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) ) |
| break; |
| i++; |
| } |
| return pInfo; |
| } |
| |
| #define VBAFORMAT_GENERALDATE "General Date" |
| #define VBAFORMAT_C "c" |
| #define VBAFORMAT_N "n" |
| #define VBAFORMAT_NN "nn" |
| #define VBAFORMAT_W "w" |
| #define VBAFORMAT_Y "y" |
| #define VBAFORMAT_LOWERCASE "<" |
| #define VBAFORMAT_UPPERCASE ">" |
| |
| // From methods1.cxx |
| sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 ); |
| // from methods.cxx |
| sal_Int16 implGetMinute( double dDate ); |
| sal_Int16 implGetDateYear( double aDate ); |
| sal_Bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet ); |
| |
| void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const |
| { |
| short nComma = 0; |
| double d = 0; |
| |
| // pflin, It is better to use SvNumberFormatter to handle the date/time/number format. |
| // the SvNumberFormatter output is mostly compatible with |
| // VBA output besides the OOo-basic output |
| if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) ) |
| { |
| String aStr = GetString(); |
| |
| SvtSysLocale aSysLocale; |
| const CharClass& rCharClass = aSysLocale.GetCharClass(); |
| |
| if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) ) |
| { |
| rCharClass.toLower( aStr ); |
| rRes = aStr; |
| return; |
| } |
| if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) ) |
| { |
| rCharClass.toUpper( aStr ); |
| rRes = aStr; |
| return; |
| } |
| |
| LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); |
| com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > |
| xFactory = comphelper::getProcessServiceFactory(); |
| SvNumberFormatter aFormatter( xFactory, eLangType ); |
| |
| sal_uInt32 nIndex; |
| xub_StrLen nCheckPos = 0; |
| short nType; |
| double nNumber; |
| Color* pCol; |
| |
| sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber ); |
| |
| // number format, use SvNumberFormatter to handle it. |
| if( bSuccess ) |
| { |
| String aFmtStr = *pFmt; |
| VbaFormatInfo* pInfo = getFormatInfo( aFmtStr ); |
| if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL ) |
| { |
| if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET ) |
| { |
| nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType ); |
| } |
| else |
| { |
| aFmtStr.AssignAscii( pInfo->mpOOoFormat ); |
| aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); |
| } |
| aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); |
| } |
| else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE ) |
| || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C )) |
| { |
| if( nNumber <=-1.0 || nNumber >= 1.0 ) |
| { |
| // short date |
| nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType ); |
| aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); |
| |
| // long time |
| if( floor( nNumber ) != nNumber ) |
| { |
| aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); |
| aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); |
| String aTime; |
| aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol ); |
| rRes.AppendAscii(" "); |
| rRes += aTime; |
| } |
| } |
| else |
| { |
| // long time only |
| aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); |
| aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); |
| aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); |
| } |
| } |
| else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N ) |
| || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN )) |
| { |
| sal_Int32 nMin = implGetMinute( nNumber ); |
| if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) ) |
| { |
| // Minute in two digits |
| sal_Unicode* p = rRes.AllocBuffer( 2 ); |
| *p++ = '0'; |
| *p = sal_Unicode( '0' + nMin ); |
| } |
| else |
| { |
| rRes = String::CreateFromInt32( nMin ); |
| } |
| } |
| else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W )) |
| { |
| sal_Int32 nWeekDay = implGetWeekDay( nNumber ); |
| rRes = String::CreateFromInt32( nWeekDay ); |
| } |
| else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y )) |
| { |
| sal_Int16 nYear = implGetDateYear( nNumber ); |
| double dBaseDate; |
| implDateSerial( nYear, 1, 1, dBaseDate ); |
| sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate ); |
| rRes = String::CreateFromInt32( nYear32 ); |
| } |
| else |
| { |
| aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); |
| aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); |
| } |
| |
| return; |
| } |
| } |
| |
| SbxDataType eType = GetType(); |
| switch( eType ) |
| { |
| case SbxCHAR: |
| case SbxBYTE: |
| case SbxINTEGER: |
| case SbxUSHORT: |
| case SbxLONG: |
| case SbxULONG: |
| case SbxINT: |
| case SbxUINT: |
| case SbxNULL: // #45929 NULL mit durchschummeln |
| nComma = 0; goto cvt; |
| case SbxSINGLE: |
| nComma = 6; goto cvt; |
| case SbxDOUBLE: |
| nComma = 14; |
| |
| cvt: |
| if( eType != SbxNULL ) |
| d = GetDouble(); |
| |
| // #45355 weiterer Einsprungpunkt fuer isnumeric-String |
| cvt2: |
| if( pFmt ) |
| { |
| // hole die 'statischen' Daten f"ur Sbx |
| SbxAppData* pData = GetSbxData_Impl(); |
| |
| LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); |
| if( pData->pBasicFormater ) |
| { |
| if( pData->eBasicFormaterLangType != eLangType ) |
| { |
| delete pData->pBasicFormater; |
| pData->pBasicFormater = NULL; |
| } |
| } |
| pData->eBasicFormaterLangType = eLangType; |
| |
| // falls bisher noch kein BasicFormater-Objekt |
| // existiert, so erzeuge dieses |
| if( !pData->pBasicFormater ) |
| { |
| SvtSysLocale aSysLocale; |
| const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); |
| sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0]; |
| sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0]; |
| String aCurrencyStrg = rData.getCurrSymbol(); |
| |
| // Initialisierung des Basic-Formater-Hilfsobjekts: |
| // hole die Resourcen f"ur die vordefinierten Ausgaben |
| // des Format()-Befehls, z.B. f"ur "On/Off". |
| String aOnStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_ON ) ); |
| String aOffStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_OFF) ); |
| String aYesStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_YES) ); |
| String aNoStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_NO) ); |
| String aTrueStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_TRUE) ); |
| String aFalseStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_FALSE) ); |
| String aCurrencyFormatStrg = String( SbxValueFormatResId( |
| STR_BASICKEY_FORMAT_CURRENCY) ); |
| // erzeuge das Basic-Formater-Objekt |
| pData->pBasicFormater |
| = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg, |
| aYesStrg,aNoStrg,aTrueStrg,aFalseStrg, |
| aCurrencyStrg,aCurrencyFormatStrg ); |
| } |
| // Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater- |
| // Objekt erzeugt und 'gespeichert', dadurch erspart man |
| // sich das teure Resourcen-Laden (f"ur landesspezifische |
| // vordefinierte Ausgaben, z.B. "On/Off") und die st"andige |
| // String-Erzeugungs Operationen. |
| // ABER: dadurch ist dieser Code NICHT multithreading f"ahig ! |
| |
| // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen |
| // wird, wenn der SbxValue eine Zahl ist !!! |
| // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden ! |
| if( eType != SbxNULL ) |
| { |
| rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt ); |
| } |
| else |
| { |
| rRes = pData->pBasicFormater->BasicFormatNull( *pFmt ); |
| } |
| |
| // Die alte Implementierung: |
| //old: printfmtnum( GetDouble(), rRes, *pFmt ); |
| } |
| else |
| { |
| ::rtl::OUString aTmpString( rRes ); |
| ImpCvtNum( GetDouble(), nComma, aTmpString ); |
| rRes = aTmpString; |
| } |
| break; |
| case SbxSTRING: |
| if( pFmt ) |
| { |
| // #45355 wenn es numerisch ist, muss gewandelt werden |
| if( IsNumericRTL() ) |
| { |
| ScanNumIntnl( GetString(), d, /*bSingle*/sal_False ); |
| goto cvt2; |
| } |
| else |
| { |
| // Sonst String-Formatierung |
| printfmtstr( GetString(), rRes, *pFmt ); |
| } |
| } |
| else |
| rRes = GetString(); |
| break; |
| default: |
| rRes = GetString(); |
| } |
| } |
| |
| |