1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_basic.hxx" 26 #include <tools/errcode.hxx> 27 #include <basic/sbx.hxx> 28 #include "sbxconv.hxx" 29 30 #include "unotools/syslocale.hxx" 31 32 #if defined ( UNX ) 33 #include <stdlib.h> 34 #endif 35 36 #ifndef _APP_HXX //autogen 37 #include <vcl/svapp.hxx> 38 #endif 39 #include <math.h> 40 #include <string.h> 41 #include <ctype.h> 42 43 #include "sbxres.hxx" 44 #include <basic/sbxbase.hxx> 45 #include <basic/sbxform.hxx> 46 #include <svtools/svtools.hrc> 47 48 #include "basrid.hxx" 49 #include "runtime.hxx" 50 51 #include <svl/zforlist.hxx> 52 #include <comphelper/processfactory.hxx> 53 54 55 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep ) 56 { 57 SvtSysLocale aSysLocale; 58 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); 59 rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0]; 60 rcThousandSep = rData.getNumThousandSep().GetBuffer()[0]; 61 } 62 63 // Scannen eines Strings nach BASIC-Konventionen 64 // Dies entspricht den ueblichen Konventionen, nur dass der Exponent 65 // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt. 66 // Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten. 67 // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp 68 // Fixed ist und das ganze nicht hineinpasst! 69 70 SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType, 71 sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl ) 72 { 73 ::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) ); 74 75 // Bei International Komma besorgen 76 char cIntntlComma, cIntntl1000; 77 char cNonIntntlComma = '.'; 78 79 sal_Unicode cDecimalSep, cThousandSep = 0; 80 if( bAllowIntntl || bOnlyIntntl ) 81 { 82 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 83 cIntntlComma = (char)cDecimalSep; 84 cIntntl1000 = (char)cThousandSep; 85 } 86 // Sonst einfach auch auf . setzen 87 else 88 { 89 cIntntlComma = cNonIntntlComma; 90 cIntntl1000 = cNonIntntlComma; // Unschaedlich machen 91 } 92 // Nur International -> IntnlComma uebernehmen 93 if( bOnlyIntntl ) 94 { 95 cNonIntntlComma = cIntntlComma; 96 cIntntl1000 = (char)cThousandSep; 97 } 98 99 const char* pStart = aBStr.getStr(); 100 const char* p = pStart; 101 char buf[ 80 ], *q = buf; 102 sal_Bool bRes = sal_True; 103 sal_Bool bMinus = sal_False; 104 nVal = 0; 105 SbxDataType eScanType = SbxSINGLE; 106 // Whitespace wech 107 while( *p &&( *p == ' ' || *p == '\t' ) ) p++; 108 // Zahl? Dann einlesen und konvertieren. 109 if( *p == '-' ) 110 p++, bMinus = sal_True; 111 if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma || 112 *p == cIntntl1000) && isdigit( *(p+1 ) ) ) ) 113 { 114 short exp = 0; // >0: Exponentteil 115 short comma = 0; // >0: Nachkomma 116 short ndig = 0; // Anzahl Ziffern 117 short ncdig = 0; // Anzahl Ziffern nach Komma 118 ByteString aSearchStr( "0123456789DEde" ); 119 // Kommas ergaenzen 120 aSearchStr += cNonIntntlComma; 121 if( cIntntlComma != cNonIntntlComma ) 122 aSearchStr += cIntntlComma; 123 if( bOnlyIntntl ) 124 aSearchStr += cIntntl1000; 125 const char* pSearchStr = aSearchStr.GetBuffer(); 126 while( strchr( pSearchStr, *p ) && *p ) 127 { 128 // 1000er-Trenner ueberlesen 129 if( bOnlyIntntl && *p == cIntntl1000 ) 130 { 131 p++; 132 continue; 133 } 134 135 // Komma oder Exponent? 136 if( *p == cNonIntntlComma || *p == cIntntlComma ) 137 { 138 // Immer '.' einfuegen, damit atof funktioniert 139 p++; 140 if( ++comma > 1 ) 141 continue; 142 else 143 *q++ = '.'; 144 } 145 else if( strchr( "DdEe", *p ) ) 146 { 147 if( ++exp > 1 ) 148 { 149 p++; continue; 150 } 151 if( toupper( *p ) == 'D' ) 152 eScanType = SbxDOUBLE; 153 *q++ = 'E'; p++; 154 // Vorzeichen hinter Exponent? 155 if( *p == '+' ) 156 p++; 157 else 158 if( *p == '-' ) 159 *q++ = *p++; 160 } 161 else 162 { 163 *q++ = *p++; 164 if( comma && !exp ) ncdig++; 165 } 166 if( !exp ) ndig++; 167 } 168 *q = 0; 169 // Komma, Exponent mehrfach vorhanden? 170 if( comma > 1 || exp > 1 ) 171 bRes = sal_False; 172 // Kann auf Integer gefaltet werden? 173 if( !comma && !exp ) 174 { 175 if( nVal >= SbxMININT && nVal <= SbxMAXINT ) 176 eScanType = SbxINTEGER; 177 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) 178 eScanType = SbxLONG; 179 } 180 181 nVal = atof( buf ); 182 ndig = ndig - comma; 183 // zu viele Zahlen fuer SINGLE? 184 if( ndig > 15 || ncdig > 6 ) 185 eScanType = SbxDOUBLE; 186 187 // Typkennung? 188 if( strchr( "%!&#", *p ) && *p ) p++; 189 } 190 // Hex/Oktalzahl? Einlesen und konvertieren: 191 else if( *p == '&' ) 192 { 193 p++; 194 eScanType = SbxLONG; 195 const char *cmp = "0123456789ABCDEF"; 196 char base = 16; 197 char ndig = 8; 198 char xch = *p++; 199 switch( toupper( xch ) ) 200 { 201 case 'O': cmp = "01234567"; base = 8; ndig = 11; break; 202 case 'H': break; 203 default : bRes = sal_False; 204 } 205 long l = 0; 206 int i; 207 while( isalnum( *p ) ) 208 { 209 char ch = sal::static_int_cast< char >( toupper( *p ) ); 210 p++; 211 if( strchr( cmp, ch ) ) *q++ = ch; 212 else bRes = sal_False; 213 } 214 *q = 0; 215 for( q = buf; *q; q++ ) 216 { 217 i =( *q & 0xFF ) - '0'; 218 if( i > 9 ) i -= 7; 219 l =( l * base ) + i; 220 if( !ndig-- ) 221 bRes = sal_False; 222 } 223 if( *p == '&' ) p++; 224 nVal = (double) l; 225 if( l >= SbxMININT && l <= SbxMAXINT ) 226 eScanType = SbxINTEGER; 227 } 228 else if ( SbiRuntime::isVBAEnabled() ) 229 { 230 OSL_TRACE("Reporting error converting"); 231 return SbxERR_CONVERSION; 232 } 233 if( pLen ) 234 *pLen = (sal_uInt16) ( p - pStart ); 235 if( !bRes ) 236 return SbxERR_CONVERSION; 237 if( bMinus ) 238 nVal = -nVal; 239 rType = eScanType; 240 return SbxERR_OK; 241 } 242 243 // Schnittstelle fuer CDbl im Basic 244 SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle ) 245 { 246 SbxDataType t; 247 sal_uInt16 nLen = 0; 248 SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen, 249 /*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True ); 250 // Komplett gelesen? 251 if( nRetError == SbxERR_OK && nLen != rSrc.Len() ) 252 nRetError = SbxERR_CONVERSION; 253 254 if( bSingle ) 255 { 256 SbxValues aValues( nVal ); 257 nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow 258 } 259 return nRetError; 260 } 261 262 //////////////////////////////////////////////////////////////////////////// 263 264 static double roundArray[] = { 265 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, 266 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 }; 267 268 /*************************************************************************** 269 |* 270 |* void myftoa( double, char *, short, short, sal_Bool, sal_Bool ) 271 |* 272 |* Beschreibung: Konversion double --> ASCII 273 |* Parameter: double die Zahl. 274 |* char * der Zielpuffer 275 |* short Anzahl Nachkommastellen 276 |* short Weite des Exponenten( 0=kein E ) 277 |* sal_Bool sal_True: mit 1000er Punkten 278 |* sal_Bool sal_True: formatfreie Ausgabe 279 |* 280 ***************************************************************************/ 281 282 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth, 283 sal_Bool bPt, sal_Bool bFix, sal_Unicode cForceThousandSep = 0 ) 284 { 285 286 short nExp = 0; // Exponent 287 short nDig = nPrec + 1; // Anzahl Digits in Zahl 288 short nDec; // Anzahl Vorkommastellen 289 register int i, digit; 290 291 // Komma besorgen 292 sal_Unicode cDecimalSep, cThousandSep; 293 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 294 if( cForceThousandSep ) 295 cThousandSep = cForceThousandSep; 296 297 // Exponentberechnung: 298 nExp = 0; 299 if( nNum > 0.0 ) 300 { 301 while( nNum < 1.0 ) nNum *= 10.0, nExp--; 302 while( nNum >= 10.0 ) nNum /= 10.0, nExp++; 303 } 304 if( !bFix && !nExpWidth ) 305 nDig = nDig + nExp; 306 else if( bFix && !nPrec ) 307 nDig = nExp + 1; 308 309 // Zahl runden: 310 if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 ) 311 { 312 nNum = 1.0; 313 ++nExp; 314 if( !nExpWidth ) ++nDig; 315 } 316 317 // Bestimmung der Vorkommastellen: 318 if( !nExpWidth ) 319 { 320 if( nExp < 0 ) 321 { 322 // #41691: Auch bei bFix eine 0 spendieren 323 *pBuf++ = '0'; 324 if( nPrec ) *pBuf++ = (char)cDecimalSep; 325 i = -nExp - 1; 326 if( nDig <= 0 ) i = nPrec; 327 while( i-- ) *pBuf++ = '0'; 328 nDec = 0; 329 } 330 else 331 nDec = nExp+1; 332 } 333 else 334 nDec = 1; 335 336 // Zahl ausgeben: 337 if( nDig > 0 ) 338 { 339 for( i = 0 ; ; ++i ) 340 { 341 if( i < 16 ) 342 { 343 digit = (int) nNum; 344 *pBuf++ = sal::static_int_cast< char >(digit + '0'); 345 nNum =( nNum - digit ) * 10.0; 346 } else 347 *pBuf++ = '0'; 348 if( --nDig == 0 ) break; 349 if( nDec ) 350 { 351 nDec--; 352 if( !nDec ) 353 *pBuf++ = (char)cDecimalSep; 354 else if( !(nDec % 3 ) && bPt ) 355 *pBuf++ = (char)cThousandSep; 356 } 357 } 358 } 359 360 // Exponent ausgeben: 361 if( nExpWidth ) 362 { 363 if( nExpWidth < 3 ) nExpWidth = 3; 364 nExpWidth -= 2; 365 *pBuf++ = 'E'; 366 *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+'; 367 while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--; 368 if( nExp >= 100 || nExpWidth == 3 ) 369 { 370 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0'); 371 nExp %= 100; 372 } 373 if( nExp/10 || nExpWidth >= 2 ) 374 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0'); 375 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0'); 376 } 377 *pBuf = 0; 378 } 379 380 // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen 381 // aufbereitet. Evtl. wird ein Minus vorangestellt. 382 // Diese Routine ist public, weil sie auch von den Put-Funktionen 383 // der Klasse SbxImpSTRING verwendet wird. 384 385 #ifdef _MSC_VER 386 #pragma optimize( "", off ) 387 #pragma warning(disable: 4748) // "... because optimizations are disabled ..." 388 #endif 389 390 void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString ) 391 { 392 char *q; 393 char cBuf[ 40 ], *p = cBuf; 394 395 sal_Unicode cDecimalSep, cThousandSep; 396 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 397 if( bCoreString ) 398 cDecimalSep = '.'; 399 400 if( nNum < 0.0 ) { 401 nNum = -nNum; 402 *p++ = '-'; 403 } 404 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14; 405 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum > dMaxNumWithoutExp ) ) ? 4:0, 406 sal_False, sal_True, cDecimalSep ); 407 // Trailing Zeroes weg: 408 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {} 409 q = p; p--; 410 while( nPrec && *p == '0' ) nPrec--, p--; 411 if( *p == cDecimalSep ) p--; 412 while( *q ) *++p = *q++; 413 *++p = 0; 414 rRes = ::rtl::OUString::createFromAscii( cBuf ); 415 } 416 417 #ifdef _MSC_VER 418 #pragma optimize( "", on ) 419 #endif 420 421 sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType ) 422 { 423 // Merken, ob ueberhaupt was geaendert wurde 424 sal_Bool bChanged = sal_False; 425 ::rtl::OUString aNewString; 426 427 // Nur Spezial-F�lle behandeln, als Default tun wir nichts 428 switch( eTargetType ) 429 { 430 // Bei Fliesskomma International beruecksichtigen 431 case SbxSINGLE: 432 case SbxDOUBLE: 433 case SbxCURRENCY: 434 { 435 ::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) ); 436 437 // Komma besorgen 438 sal_Unicode cDecimalSep, cThousandSep; 439 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 440 aNewString = rSrc; 441 442 // Ersetzen, wenn DecimalSep kein '.' (nur den ersten) 443 if( cDecimalSep != (sal_Unicode)'.' ) 444 { 445 sal_Int32 nPos = aNewString.indexOf( cDecimalSep ); 446 if( nPos != -1 ) 447 { 448 sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr(); 449 pStr[nPos] = (sal_Unicode)'.'; 450 bChanged = sal_True; 451 } 452 } 453 break; 454 } 455 456 // Bei sal_Bool sal_True und sal_False als String pruefen 457 case SbxBOOL: 458 { 459 if( rSrc.equalsIgnoreAsciiCaseAscii( "true" ) ) 460 { 461 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE ); 462 bChanged = sal_True; 463 } 464 else 465 if( rSrc.equalsIgnoreAsciiCaseAscii( "false" ) ) 466 { 467 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE ); 468 bChanged = sal_True; 469 } 470 break; 471 } 472 default: break; 473 } 474 // String bei Aenderung uebernehmen 475 if( bChanged ) 476 rSrc = aNewString; 477 return bChanged; 478 } 479 480 481 // Formatierte Zahlenausgabe 482 // Der Returnwert ist die Anzahl Zeichen, die aus dem 483 // Format verwendt wurden. 484 485 #ifdef _old_format_code_ 486 // lasse diesen Code vorl"aufig drin, zum 'abgucken' 487 // der bisherigen Implementation 488 489 static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt ) 490 { 491 const String& rFmt = rWFmt; 492 char cFill = ' '; // Fuellzeichen 493 char cPre = 0; // Startzeichen( evtl. "$" ) 494 short nExpDig= 0; // Anzahl Exponentstellen 495 short nPrec = 0; // Anzahl Nachkommastellen 496 short nWidth = 0; // Zahlenweite gesamnt 497 short nLen; // Laenge konvertierte Zahl 498 sal_Bool bPoint = sal_False; // sal_True: mit 1000er Kommas 499 sal_Bool bTrail = sal_False; // sal_True, wenn folgendes Minus 500 sal_Bool bSign = sal_False; // sal_True: immer mit Vorzeichen 501 sal_Bool bNeg = sal_False; // sal_True: Zahl ist negativ 502 char cBuf [1024]; // Zahlenpuffer 503 char * p; 504 const char* pFmt = rFmt; 505 rRes.Erase(); 506 // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben. 507 if( *pFmt == '$' ) 508 if( *++pFmt != '$' ) rRes += '$'; 509 if( *pFmt == '*' ) 510 if( *++pFmt != '*' ) rRes += '*'; 511 512 switch( *pFmt++ ) 513 { 514 case 0: 515 break; 516 case '+': 517 bSign = sal_True; nWidth++; break; 518 case '*': 519 nWidth++; cFill = '*'; 520 if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$'; 521 break; 522 case '$': 523 nWidth++; cPre = '$'; break; 524 case '#': 525 case '.': 526 case ',': 527 pFmt--; break; 528 } 529 // Vorkomma: 530 for( ;; ) 531 { 532 while( *pFmt == '#' ) pFmt++, nWidth++; 533 // 1000er Kommas? 534 if( *pFmt == ',' ) 535 { 536 nWidth++; pFmt++; bPoint = sal_True; 537 } else break; 538 } 539 // Nachkomma: 540 if( *pFmt == '.' ) 541 { 542 while( *++pFmt == '#' ) nPrec++; 543 nWidth += nPrec + 1; 544 } 545 // Exponent: 546 while( *pFmt == '^' ) 547 pFmt++, nExpDig++, nWidth++; 548 // Folgendes Minus: 549 if( !bSign && *pFmt == '-' ) 550 pFmt++, bTrail = sal_True; 551 552 // Zahl konvertieren: 553 if( nPrec > 15 ) nPrec = 15; 554 if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True; 555 p = cBuf; 556 if( bSign ) *p++ = bNeg ? '-' : '+'; 557 myftoa( nNum, p, nPrec, nExpDig, bPoint, sal_False ); 558 nLen = strlen( cBuf ); 559 560 // Ueberlauf? 561 if( cPre ) nLen++; 562 if( nLen > nWidth ) rRes += '%'; 563 else { 564 nWidth -= nLen; 565 while( nWidth-- ) rRes += (xub_Unicode)cFill; 566 if( cPre ) rRes += (xub_Unicode)cPre; 567 } 568 rRes += (xub_Unicode*)&(cBuf[0]); 569 if( bTrail ) 570 rRes += bNeg ? '-' : ' '; 571 572 return (sal_uInt16) ( pFmt - (const char*) rFmt ); 573 } 574 575 #endif //_old_format_code_ 576 577 static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt ) 578 { 579 const xub_Unicode* pStr = rStr.GetBuffer(); 580 const xub_Unicode* pFmtStart = rFmt.GetBuffer(); 581 const xub_Unicode* pFmt = pFmtStart; 582 rRes.Erase(); 583 switch( *pFmt ) 584 { 585 case '!': 586 rRes += *pStr++; pFmt++; break; 587 case '\\': 588 do 589 { 590 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); 591 pFmt++; 592 } while( *pFmt != '\\' ); 593 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); 594 pFmt++; break; 595 case '&': 596 rRes = rStr; 597 pFmt++; break; 598 default: 599 rRes = rStr; 600 break; 601 } 602 return (sal_uInt16) ( pFmt - pFmtStart ); 603 } 604 605 ///////////////////////////////////////////////////////////////////////// 606 607 sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen ) 608 { 609 SbxError eRes = SbxERR_OK; 610 if( !CanWrite() ) 611 eRes = SbxERR_PROP_READONLY; 612 else 613 { 614 double n; 615 SbxDataType t; 616 eRes = ImpScan( rSrc, n, t, pLen ); 617 if( eRes == SbxERR_OK ) 618 { 619 if( !IsFixed() ) 620 SetType( t ); 621 PutDouble( n ); 622 } 623 } 624 if( eRes ) 625 { 626 SetError( eRes ); return sal_False; 627 } 628 else 629 return sal_True; 630 } 631 632 633 ResMgr* implGetResMgr( void ) 634 { 635 static ResMgr* pResMgr = NULL; 636 if( !pResMgr ) 637 { 638 ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale(); 639 pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale ); 640 } 641 return pResMgr; 642 } 643 644 class SbxValueFormatResId : public ResId 645 { 646 public: 647 SbxValueFormatResId( sal_uInt16 nId ) 648 : ResId( nId, *implGetResMgr() ) 649 {} 650 }; 651 652 653 enum VbaFormatType 654 { 655 VBA_FORMAT_TYPE_OFFSET, // standard number format 656 VBA_FORMAT_TYPE_USERDEFINED, // user defined number format 657 VBA_FORMAT_TYPE_NULL 658 }; 659 660 struct VbaFormatInfo 661 { 662 VbaFormatType meType; 663 const char* mpVbaFormat; // Format string in vba 664 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET 665 const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED 666 }; 667 668 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \ 669 { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 } 670 671 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \ 672 { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 } 673 674 static VbaFormatInfo pFormatInfoTable[] = 675 { 676 VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ), 677 VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ), 678 VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ), 679 VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ), 680 VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ), 681 VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ), 682 VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ), 683 VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ), 684 VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ), 685 VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ), 686 { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 } 687 }; 688 689 VbaFormatInfo* getFormatInfo( const String& rFmt ) 690 { 691 VbaFormatInfo* pInfo = NULL; 692 sal_Int16 i = 0; 693 while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL ) 694 { 695 if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) ) 696 break; 697 i++; 698 } 699 return pInfo; 700 } 701 702 #define VBAFORMAT_GENERALDATE "General Date" 703 #define VBAFORMAT_C "c" 704 #define VBAFORMAT_N "n" 705 #define VBAFORMAT_NN "nn" 706 #define VBAFORMAT_W "w" 707 #define VBAFORMAT_Y "y" 708 #define VBAFORMAT_LOWERCASE "<" 709 #define VBAFORMAT_UPPERCASE ">" 710 711 // From methods1.cxx 712 sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 ); 713 // from methods.cxx 714 sal_Int16 implGetMinute( double dDate ); 715 sal_Int16 implGetDateYear( double aDate ); 716 sal_Bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet ); 717 718 void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const 719 { 720 short nComma = 0; 721 double d = 0; 722 723 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format. 724 // the SvNumberFormatter output is mostly compatible with 725 // VBA output besides the OOo-basic output 726 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) ) 727 { 728 String aStr = GetString(); 729 730 SvtSysLocale aSysLocale; 731 const CharClass& rCharClass = aSysLocale.GetCharClass(); 732 733 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) ) 734 { 735 rCharClass.toLower( aStr ); 736 rRes = aStr; 737 return; 738 } 739 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) ) 740 { 741 rCharClass.toUpper( aStr ); 742 rRes = aStr; 743 return; 744 } 745 746 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); 747 com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > 748 xFactory = comphelper::getProcessServiceFactory(); 749 SvNumberFormatter aFormatter( xFactory, eLangType ); 750 751 sal_uInt32 nIndex; 752 xub_StrLen nCheckPos = 0; 753 short nType; 754 double nNumber; 755 Color* pCol; 756 757 sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber ); 758 759 // number format, use SvNumberFormatter to handle it. 760 if( bSuccess ) 761 { 762 String aFmtStr = *pFmt; 763 VbaFormatInfo* pInfo = getFormatInfo( aFmtStr ); 764 if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL ) 765 { 766 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET ) 767 { 768 nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType ); 769 } 770 else 771 { 772 aFmtStr.AssignAscii( pInfo->mpOOoFormat ); 773 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 774 } 775 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 776 } 777 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE ) 778 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C )) 779 { 780 if( nNumber <=-1.0 || nNumber >= 1.0 ) 781 { 782 // short date 783 nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType ); 784 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 785 786 // long time 787 if( floor( nNumber ) != nNumber ) 788 { 789 aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); 790 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 791 String aTime; 792 aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol ); 793 rRes.AppendAscii(" "); 794 rRes += aTime; 795 } 796 } 797 else 798 { 799 // long time only 800 aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); 801 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 802 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 803 } 804 } 805 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N ) 806 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN )) 807 { 808 sal_Int32 nMin = implGetMinute( nNumber ); 809 if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) ) 810 { 811 // Minute in two digits 812 sal_Unicode* p = rRes.AllocBuffer( 2 ); 813 *p++ = '0'; 814 *p = sal_Unicode( '0' + nMin ); 815 } 816 else 817 { 818 rRes = String::CreateFromInt32( nMin ); 819 } 820 } 821 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W )) 822 { 823 sal_Int32 nWeekDay = implGetWeekDay( nNumber ); 824 rRes = String::CreateFromInt32( nWeekDay ); 825 } 826 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y )) 827 { 828 sal_Int16 nYear = implGetDateYear( nNumber ); 829 double dBaseDate; 830 implDateSerial( nYear, 1, 1, dBaseDate ); 831 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate ); 832 rRes = String::CreateFromInt32( nYear32 ); 833 } 834 else 835 { 836 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 837 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 838 } 839 840 return; 841 } 842 } 843 844 SbxDataType eType = GetType(); 845 switch( eType ) 846 { 847 case SbxCHAR: 848 case SbxBYTE: 849 case SbxINTEGER: 850 case SbxUSHORT: 851 case SbxLONG: 852 case SbxULONG: 853 case SbxINT: 854 case SbxUINT: 855 case SbxNULL: // #45929 NULL mit durchschummeln 856 nComma = 0; goto cvt; 857 case SbxSINGLE: 858 nComma = 6; goto cvt; 859 case SbxDOUBLE: 860 nComma = 14; 861 862 cvt: 863 if( eType != SbxNULL ) 864 d = GetDouble(); 865 866 // #45355 weiterer Einsprungpunkt fuer isnumeric-String 867 cvt2: 868 if( pFmt ) 869 { 870 // hole die 'statischen' Daten f"ur Sbx 871 SbxAppData* pData = GetSbxData_Impl(); 872 873 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); 874 if( pData->pBasicFormater ) 875 { 876 if( pData->eBasicFormaterLangType != eLangType ) 877 { 878 delete pData->pBasicFormater; 879 pData->pBasicFormater = NULL; 880 } 881 } 882 pData->eBasicFormaterLangType = eLangType; 883 884 // falls bisher noch kein BasicFormater-Objekt 885 // existiert, so erzeuge dieses 886 if( !pData->pBasicFormater ) 887 { 888 SvtSysLocale aSysLocale; 889 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); 890 sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0]; 891 sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0]; 892 String aCurrencyStrg = rData.getCurrSymbol(); 893 894 // Initialisierung des Basic-Formater-Hilfsobjekts: 895 // hole die Resourcen f"ur die vordefinierten Ausgaben 896 // des Format()-Befehls, z.B. f"ur "On/Off". 897 String aOnStrg = String( SbxValueFormatResId( 898 STR_BASICKEY_FORMAT_ON ) ); 899 String aOffStrg = String( SbxValueFormatResId( 900 STR_BASICKEY_FORMAT_OFF) ); 901 String aYesStrg = String( SbxValueFormatResId( 902 STR_BASICKEY_FORMAT_YES) ); 903 String aNoStrg = String( SbxValueFormatResId( 904 STR_BASICKEY_FORMAT_NO) ); 905 String aTrueStrg = String( SbxValueFormatResId( 906 STR_BASICKEY_FORMAT_TRUE) ); 907 String aFalseStrg = String( SbxValueFormatResId( 908 STR_BASICKEY_FORMAT_FALSE) ); 909 String aCurrencyFormatStrg = String( SbxValueFormatResId( 910 STR_BASICKEY_FORMAT_CURRENCY) ); 911 // erzeuge das Basic-Formater-Objekt 912 pData->pBasicFormater 913 = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg, 914 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg, 915 aCurrencyStrg,aCurrencyFormatStrg ); 916 } 917 // Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater- 918 // Objekt erzeugt und 'gespeichert', dadurch erspart man 919 // sich das teure Resourcen-Laden (f"ur landesspezifische 920 // vordefinierte Ausgaben, z.B. "On/Off") und die st"andige 921 // String-Erzeugungs Operationen. 922 // ABER: dadurch ist dieser Code NICHT multithreading f"ahig ! 923 924 // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen 925 // wird, wenn der SbxValue eine Zahl ist !!! 926 // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden ! 927 if( eType != SbxNULL ) 928 { 929 rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt ); 930 } 931 else 932 { 933 rRes = pData->pBasicFormater->BasicFormatNull( *pFmt ); 934 } 935 936 // Die alte Implementierung: 937 //old: printfmtnum( GetDouble(), rRes, *pFmt ); 938 } 939 else 940 { 941 ::rtl::OUString aTmpString( rRes ); 942 ImpCvtNum( GetDouble(), nComma, aTmpString ); 943 rRes = aTmpString; 944 } 945 break; 946 case SbxSTRING: 947 if( pFmt ) 948 { 949 // #45355 wenn es numerisch ist, muss gewandelt werden 950 if( IsNumericRTL() ) 951 { 952 ScanNumIntnl( GetString(), d, /*bSingle*/sal_False ); 953 goto cvt2; 954 } 955 else 956 { 957 // Sonst String-Formatierung 958 printfmtstr( GetString(), rRes, *pFmt ); 959 } 960 } 961 else 962 rRes = GetString(); 963 break; 964 default: 965 rRes = GetString(); 966 } 967 } 968 969 970