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_sc.hxx" 26 27 28 29 // INCLUDE --------------------------------------------------------------- 30 31 #include "scitems.hxx" 32 #include <sfx2/app.hxx> 33 #include <sfx2/docfile.hxx> 34 #include <sfx2/objsh.hxx> 35 #include <basic/sbmeth.hxx> 36 #include <basic/sbmod.hxx> 37 #include <basic/sbstar.hxx> 38 #include <basic/basmgr.hxx> 39 40 #include <basic/sbx.hxx> 41 #include <svl/zforlist.hxx> 42 #include <vcl/msgbox.hxx> 43 #include <tools/urlobj.hxx> 44 #include <rtl/math.hxx> 45 46 #include "validat.hxx" 47 #include "document.hxx" 48 #include "cell.hxx" 49 #include "patattr.hxx" 50 #include "rechead.hxx" 51 #include "globstr.hrc" 52 #include "rangenam.hxx" 53 #include "dbcolect.hxx" 54 55 #include <math.h> 56 #include <memory> 57 58 using namespace formula; 59 //------------------------------------------------------------------------ 60 61 SV_IMPL_OP_PTRARR_SORT( ScValidationEntries_Impl, ScValidationDataPtr ); 62 63 //------------------------------------------------------------------------ 64 65 // 66 // Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung) 67 // 68 69 ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, 70 const String& rExpr1, const String& rExpr2, 71 ScDocument* pDocument, const ScAddress& rPos, 72 const String& rExprNmsp1, const String& rExprNmsp2, 73 FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) : 74 ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ), 75 nKey( 0 ), 76 eDataMode( eMode ), 77 eErrorStyle( SC_VALERR_STOP ), 78 mnListType( ValidListType::UNSORTED ) 79 { 80 bShowInput = bShowError = sal_False; 81 } 82 83 ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, 84 const ScTokenArray* pArr1, const ScTokenArray* pArr2, 85 ScDocument* pDocument, const ScAddress& rPos ) : 86 ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ), 87 nKey( 0 ), 88 eDataMode( eMode ), 89 eErrorStyle( SC_VALERR_STOP ), 90 mnListType( ValidListType::UNSORTED ) 91 { 92 bShowInput = bShowError = sal_False; 93 } 94 95 ScValidationData::ScValidationData( const ScValidationData& r ) : 96 ScConditionEntry( r ), 97 nKey( r.nKey ), 98 eDataMode( r.eDataMode ), 99 bShowInput( r.bShowInput ), 100 bShowError( r.bShowError ), 101 eErrorStyle( r.eErrorStyle ), 102 mnListType( r.mnListType ), 103 aInputTitle( r.aInputTitle ), 104 aInputMessage( r.aInputMessage ), 105 aErrorTitle( r.aErrorTitle ), 106 aErrorMessage( r.aErrorMessage ) 107 { 108 // Formeln per RefCount kopiert 109 } 110 111 ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r ) : 112 ScConditionEntry( pDocument, r ), 113 nKey( r.nKey ), 114 eDataMode( r.eDataMode ), 115 bShowInput( r.bShowInput ), 116 bShowError( r.bShowError ), 117 eErrorStyle( r.eErrorStyle ), 118 mnListType( r.mnListType ), 119 aInputTitle( r.aInputTitle ), 120 aInputMessage( r.aInputMessage ), 121 aErrorTitle( r.aErrorTitle ), 122 aErrorMessage( r.aErrorMessage ) 123 { 124 // Formeln wirklich kopiert 125 } 126 127 ScValidationData::~ScValidationData() 128 { 129 } 130 131 sal_Bool ScValidationData::IsEmpty() const 132 { 133 String aEmpty; 134 ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() ); 135 return EqualEntries( aDefault ); 136 } 137 138 sal_Bool ScValidationData::EqualEntries( const ScValidationData& r ) const 139 { 140 // gleiche Parameter eingestellt (ohne Key) 141 142 return ScConditionEntry::operator==(r) && 143 eDataMode == r.eDataMode && 144 bShowInput == r.bShowInput && 145 bShowError == r.bShowError && 146 eErrorStyle == r.eErrorStyle && 147 mnListType == r.mnListType && 148 aInputTitle == r.aInputTitle && 149 aInputMessage == r.aInputMessage && 150 aErrorTitle == r.aErrorTitle && 151 aErrorMessage == r.aErrorMessage; 152 } 153 154 void ScValidationData::ResetInput() 155 { 156 bShowInput = sal_False; 157 } 158 159 void ScValidationData::ResetError() 160 { 161 bShowError = sal_False; 162 } 163 164 void ScValidationData::SetInput( const String& rTitle, const String& rMsg ) 165 { 166 bShowInput = sal_True; 167 aInputTitle = rTitle; 168 aInputMessage = rMsg; 169 } 170 171 void ScValidationData::SetError( const String& rTitle, const String& rMsg, 172 ScValidErrorStyle eStyle ) 173 { 174 bShowError = sal_True; 175 eErrorStyle = eStyle; 176 aErrorTitle = rTitle; 177 aErrorMessage = rMsg; 178 } 179 180 sal_Bool ScValidationData::GetErrMsg( String& rTitle, String& rMsg, 181 ScValidErrorStyle& rStyle ) const 182 { 183 rTitle = aErrorTitle; 184 rMsg = aErrorMessage; 185 rStyle = eErrorStyle; 186 return bShowError; 187 } 188 189 sal_Bool ScValidationData::DoScript( const ScAddress& rPos, const String& rInput, 190 ScFormulaCell* pCell, Window* pParent ) const 191 { 192 ScDocument* pDocument = GetDocument(); 193 SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); 194 if ( !pDocSh || !pDocument->CheckMacroWarn() ) 195 return sal_False; 196 197 sal_Bool bScriptReturnedFalse = sal_False; // Standard: kein Abbruch 198 199 // Set up parameters 200 ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2); 201 202 // 1) eingegebener / berechneter Wert 203 String aValStr = rInput; 204 double nValue; 205 sal_Bool bIsValue = sal_False; 206 if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen 207 { 208 bIsValue = pCell->IsValue(); 209 if ( bIsValue ) 210 nValue = pCell->GetValue(); 211 else 212 pCell->GetString( aValStr ); 213 } 214 if ( bIsValue ) 215 aParams[0] = ::com::sun::star::uno::makeAny( nValue ); 216 else 217 aParams[0] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aValStr ) ); 218 219 // 2) Position der Zelle 220 String aPosStr; 221 rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); 222 aParams[1] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aPosStr ) ); 223 224 // use link-update flag to prevent closing the document 225 // while the macro is running 226 sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate(); 227 if ( !bWasInLinkUpdate ) 228 pDocument->SetInLinkUpdate( sal_True ); 229 230 if ( pCell ) 231 pDocument->LockTable( rPos.Tab() ); 232 233 ::com::sun::star::uno::Any aRet; 234 ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex; 235 ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs; 236 237 ErrCode eRet = pDocSh->CallXScript( 238 aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs ); 239 240 if ( pCell ) 241 pDocument->UnlockTable( rPos.Tab() ); 242 243 if ( !bWasInLinkUpdate ) 244 pDocument->SetInLinkUpdate( sal_False ); 245 246 // Check the return value from the script 247 // The contents of the cell get reset if the script returns false 248 sal_Bool bTmp = sal_False; 249 if ( eRet == ERRCODE_NONE && 250 aRet.getValueType() == getCppuBooleanType() && 251 sal_True == ( aRet >>= bTmp ) && 252 bTmp == sal_False ) 253 { 254 bScriptReturnedFalse = sal_True; 255 } 256 257 if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell ) 258 // Makro nicht gefunden (nur bei Eingabe) 259 { 260 //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? 261 262 ErrorBox aBox( pParent, WinBits(WB_OK), 263 ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); 264 aBox.Execute(); 265 } 266 267 return bScriptReturnedFalse; 268 } 269 270 // sal_True -> Abbruch 271 272 sal_Bool ScValidationData::DoMacro( const ScAddress& rPos, const String& rInput, 273 ScFormulaCell* pCell, Window* pParent ) const 274 { 275 if ( SfxApplication::IsXScriptURL( aErrorTitle ) ) 276 { 277 return DoScript( rPos, rInput, pCell, pParent ); 278 } 279 280 ScDocument* pDocument = GetDocument(); 281 SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); 282 if ( !pDocSh || !pDocument->CheckMacroWarn() ) 283 return sal_False; 284 285 sal_Bool bDone = sal_False; 286 sal_Bool bRet = sal_False; // Standard: kein Abbruch 287 288 // Wenn das Dok waehrend eines Basic-Calls geladen wurde, 289 // ist das Sbx-Objekt evtl. nicht angelegt (?) 290 // pDocSh->GetSbxObject(); 291 292 // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic 293 294 #if 0 295 // Makro-Name liegt in folgender Form vor: 296 // "Macroname.Modulname.Libname.Dokumentname" oder 297 // "Macroname.Modulname.Libname.Applikationsname" 298 String aMacroName = aErrorTitle.GetToken(0, '.'); 299 String aModulName = aErrorTitle.GetToken(1, '.'); 300 String aLibName = aErrorTitle.GetToken(2, '.'); 301 String aDocName = aErrorTitle.GetToken(3, '.'); 302 #endif 303 304 // Funktion ueber den einfachen Namen suchen, 305 // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen 306 307 StarBASIC* pRoot = pDocSh->GetBasic(); 308 SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD ); 309 if ( pVar && pVar->ISA(SbMethod) ) 310 { 311 SbMethod* pMethod = (SbMethod*)pVar; 312 SbModule* pModule = pMethod->GetModule(); 313 SbxObject* pObject = pModule->GetParent(); 314 String aMacroStr = pObject->GetName(); 315 aMacroStr += '.'; 316 aMacroStr += pModule->GetName(); 317 aMacroStr += '.'; 318 aMacroStr += pMethod->GetName(); 319 String aBasicStr; 320 321 // #95867# the distinction between document- and app-basic has to be done 322 // by checking the parent (as in ScInterpreter::ScMacro), not by looping 323 // over all open documents, because this may be called from within loading, 324 // when SfxObjectShell::GetFirst/GetNext won't find the document. 325 326 if ( pObject->GetParent() ) 327 aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic 328 else 329 aBasicStr = SFX_APP()->GetName(); // Applikationsbasic 330 331 // Parameter fuer Makro 332 SbxArrayRef refPar = new SbxArray; 333 334 // 1) eingegebener / berechneter Wert 335 String aValStr = rInput; 336 double nValue = 0.0; 337 sal_Bool bIsValue = sal_False; 338 if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen 339 { 340 bIsValue = pCell->IsValue(); 341 if ( bIsValue ) 342 nValue = pCell->GetValue(); 343 else 344 pCell->GetString( aValStr ); 345 } 346 if ( bIsValue ) 347 refPar->Get(1)->PutDouble( nValue ); 348 else 349 refPar->Get(1)->PutString( aValStr ); 350 351 // 2) Position der Zelle 352 String aPosStr; 353 rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); 354 refPar->Get(2)->PutString( aPosStr ); 355 356 // use link-update flag to prevent closing the document 357 // while the macro is running 358 sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate(); 359 if ( !bWasInLinkUpdate ) 360 pDocument->SetInLinkUpdate( sal_True ); 361 362 if ( pCell ) 363 pDocument->LockTable( rPos.Tab() ); 364 SbxVariableRef refRes = new SbxVariable; 365 ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar, refRes ); 366 if ( pCell ) 367 pDocument->UnlockTable( rPos.Tab() ); 368 369 if ( !bWasInLinkUpdate ) 370 pDocument->SetInLinkUpdate( sal_False ); 371 372 // Eingabe abbrechen, wenn Basic-Makro sal_False zurueckgibt 373 if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && refRes->GetBool() == sal_False ) 374 bRet = sal_True; 375 bDone = sal_True; 376 } 377 378 if ( !bDone && !pCell ) // Makro nicht gefunden (nur bei Eingabe) 379 { 380 //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? 381 382 ErrorBox aBox( pParent, WinBits(WB_OK), 383 ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); 384 aBox.Execute(); 385 } 386 387 return bRet; 388 } 389 390 void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const 391 { 392 if ( eErrorStyle == SC_VALERR_MACRO ) 393 DoMacro( pCell->aPos, EMPTY_STRING, pCell, NULL ); 394 } 395 396 // sal_True -> Abbruch 397 398 sal_Bool ScValidationData::DoError( Window* pParent, const String& rInput, 399 const ScAddress& rPos ) const 400 { 401 if ( eErrorStyle == SC_VALERR_MACRO ) 402 return DoMacro( rPos, rInput, NULL, pParent ); 403 404 // Fehlermeldung ausgeben 405 406 String aTitle = aErrorTitle; 407 if (!aTitle.Len()) 408 aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title 409 String aMessage = aErrorMessage; 410 if (!aMessage.Len()) 411 aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR ); 412 413 //! ErrorBox / WarningBox / InfoBox ? 414 //! (bei InfoBox immer nur OK-Button) 415 416 WinBits nStyle = 0; 417 switch (eErrorStyle) 418 { 419 case SC_VALERR_STOP: 420 nStyle = WB_OK | WB_DEF_OK; 421 break; 422 case SC_VALERR_WARNING: 423 nStyle = WB_OK_CANCEL | WB_DEF_CANCEL; 424 break; 425 case SC_VALERR_INFO: 426 nStyle = WB_OK_CANCEL | WB_DEF_OK; 427 break; 428 default: 429 { 430 // added to avoid warnings 431 } 432 } 433 434 MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage ); 435 sal_uInt16 nRet = aBox.Execute(); 436 437 return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL ); 438 } 439 440 441 sal_Bool ScValidationData::IsDataValid( const String& rTest, const ScPatternAttr& rPattern, 442 const ScAddress& rPos ) const 443 { 444 if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed 445 return sal_True; 446 447 if ( rTest.GetChar(0) == '=' ) // formulas do not pass the validity test 448 return sal_False; 449 450 if ( !rTest.Len() ) // check whether empty cells are allowed 451 return IsIgnoreBlank(); 452 453 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); 454 455 // get the value if any 456 sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter ); 457 458 double nVal; 459 sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal ); 460 ScBaseCell* pCell; 461 if (bIsVal) 462 pCell = new ScValueCell( nVal ); 463 else 464 pCell = new ScStringCell( rTest ); 465 466 sal_Bool bRet; 467 if (SC_VALID_TEXTLEN == eDataMode) 468 { 469 const double nLenVal = static_cast<double>( rTest.Len() ); 470 ScValueCell aTmpCell( nLenVal ); 471 bRet = IsCellValid( &aTmpCell, rPos ); 472 } 473 else 474 bRet = IsDataValid( pCell, rPos ); 475 476 pCell->Delete(); 477 return bRet; 478 } 479 480 sal_Bool ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const 481 { 482 if( eDataMode == SC_VALID_LIST ) 483 return IsListValid( pCell, rPos ); 484 485 double nVal = 0.0; 486 String aString; 487 sal_Bool bIsVal = sal_True; 488 489 switch (pCell->GetCellType()) 490 { 491 case CELLTYPE_VALUE: 492 nVal = ((ScValueCell*)pCell)->GetValue(); 493 break; 494 case CELLTYPE_STRING: 495 ((ScStringCell*)pCell)->GetString( aString ); 496 bIsVal = sal_False; 497 break; 498 case CELLTYPE_EDIT: 499 ((ScEditCell*)pCell)->GetString( aString ); 500 bIsVal = sal_False; 501 break; 502 case CELLTYPE_FORMULA: 503 { 504 ScFormulaCell* pFCell = (ScFormulaCell*)pCell; 505 bIsVal = pFCell->IsValue(); 506 if ( bIsVal ) 507 nVal = pFCell->GetValue(); 508 else 509 pFCell->GetString( aString ); 510 } 511 break; 512 default: // Notizen, Broadcaster 513 return IsIgnoreBlank(); // wie eingestellt 514 } 515 516 sal_Bool bOk = sal_True; 517 switch (eDataMode) 518 { 519 // SC_VALID_ANY schon oben 520 521 case SC_VALID_WHOLE: 522 case SC_VALID_DECIMAL: 523 case SC_VALID_DATE: // Date/Time ist nur Formatierung 524 case SC_VALID_TIME: 525 bOk = bIsVal; 526 if ( bOk && eDataMode == SC_VALID_WHOLE ) 527 bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen 528 if ( bOk ) 529 bOk = IsCellValid( pCell, rPos ); 530 break; 531 532 case SC_VALID_CUSTOM: 533 // fuer Custom muss eOp == SC_COND_DIRECT sein 534 //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!! 535 bOk = IsCellValid( pCell, rPos ); 536 break; 537 538 case SC_VALID_TEXTLEN: 539 bOk = !bIsVal; // nur Text 540 if ( bOk ) 541 { 542 double nLenVal = (double) aString.Len(); 543 ScValueCell aTmpCell( nLenVal ); 544 bOk = IsCellValid( &aTmpCell, rPos ); 545 } 546 break; 547 548 default: 549 DBG_ERROR("hammanochnich"); 550 break; 551 } 552 553 return bOk; 554 } 555 556 // ---------------------------------------------------------------------------- 557 558 namespace { 559 560 /** Token array helper. Iterates over all string tokens. 561 @descr The token array must contain separated string tokens only. 562 @param bSkipEmpty true = Ignores string tokens with empty strings. */ 563 class ScStringTokenIterator 564 { 565 public: 566 inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) : 567 mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {} 568 569 /** Returns the string of the first string token or NULL on error or empty token array. */ 570 const String* First(); 571 /** Returns the string of the next string token or NULL on error or end of token array. */ 572 const String* Next(); 573 574 /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */ 575 inline bool Ok() const { return mbOk; } 576 577 private: 578 ScTokenArray& mrTokArr; /// The token array for iteration. 579 bool mbSkipEmpty; /// Ignore empty strings. 580 bool mbOk; /// true = correct token or end of token array. 581 }; 582 583 const String* ScStringTokenIterator::First() 584 { 585 mrTokArr.Reset(); 586 mbOk = true; 587 return Next(); 588 } 589 590 const String* ScStringTokenIterator::Next() 591 { 592 if( !mbOk ) 593 return NULL; 594 595 // seek to next non-separator token 596 const FormulaToken* pToken = mrTokArr.NextNoSpaces(); 597 while( pToken && (pToken->GetOpCode() == ocSep) ) 598 pToken = mrTokArr.NextNoSpaces(); 599 600 mbOk = !pToken || (pToken->GetType() == formula::svString); 601 const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL; 602 // string found but empty -> get next token; otherwise return it 603 return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString; 604 } 605 606 // ---------------------------------------------------------------------------- 607 608 /** Returns the number format of the passed cell, or the standard format. */ 609 sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos ) 610 { 611 const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ); 612 if( !pPattern ) 613 pPattern = rDoc.GetDefPattern(); 614 return pPattern->GetNumberFormat( rDoc.GetFormatTable() ); 615 } 616 617 /** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */ 618 void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted ) 619 { 620 if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) ) 621 delete pData; 622 } 623 624 } // namespace 625 626 // ---------------------------------------------------------------------------- 627 628 bool ScValidationData::HasSelectionList() const 629 { 630 return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE); 631 } 632 633 bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings, 634 ScBaseCell* pCell, 635 const ScAddress& rPos, 636 const ScTokenArray& rTokArr, 637 int& rMatch ) const 638 { 639 bool bOk = true; 640 641 // pDoc is private in condition, use an accessor and a long winded name. 642 ScDocument* pDocument = GetDocument(); 643 if( NULL == pDocument ) 644 return false; 645 646 ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr, 647 formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA); 648 649 // Make sure the formula gets interpreted and a result is delivered, 650 // regardless of the AutoCalc setting. 651 aValidationSrc.Interpret(); 652 653 ScMatrixRef xMatRef; 654 const ScMatrix *pValues = aValidationSrc.GetMatrix(); 655 if (!pValues) 656 { 657 // The somewhat nasty case of either an error occured, or the 658 // dereferenced value of a single cell reference or an immediate result 659 // is stored as a single value. 660 661 // Use an interim matrix to create the TypedStrData below. 662 xMatRef = new ScMatrix(1,1); 663 664 sal_uInt16 nErrCode = aValidationSrc.GetErrCode(); 665 if (nErrCode) 666 { 667 /* TODO : to use later in an alert box? 668 * String rStrResult = "..."; 669 * rStrResult += ScGlobal::GetLongErrorString(nErrCode); 670 */ 671 672 xMatRef->PutError( nErrCode, 0); 673 bOk = false; 674 } 675 else if (aValidationSrc.HasValueData()) 676 xMatRef->PutDouble( aValidationSrc.GetValue(), 0); 677 else 678 { 679 String aStr; 680 aValidationSrc.GetString( aStr); 681 xMatRef->PutString( aStr, 0); 682 } 683 684 pValues = xMatRef; 685 } 686 687 // which index matched. We will want it eventually to pre-select that item. 688 rMatch = -1; 689 690 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); 691 692 bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); 693 SCSIZE nCol, nRow, nCols, nRows, n = 0; 694 pValues->GetDimensions( nCols, nRows ); 695 696 sal_Bool bRef = sal_False; 697 ScRange aRange; 698 699 ScTokenArray* pArr = (ScTokenArray*) &rTokArr; 700 pArr->Reset(); 701 ScToken* t = NULL; 702 if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL) 703 { 704 if (t->GetOpCode() == ocDBArea) 705 { 706 if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) ) 707 { 708 pDBData->GetArea(aRange); 709 bRef = sal_True; 710 } 711 } 712 else if (t->GetOpCode() == ocName) 713 { 714 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 715 if (pName && pName->IsReference(aRange)) 716 { 717 bRef = sal_True; 718 } 719 } 720 else if (t->GetType() != svIndex) 721 { 722 t->CalcAbsIfRel(rPos); 723 if (pArr->IsValidReference(aRange)) 724 { 725 bRef = sal_True; 726 } 727 } 728 } 729 730 /* XL artificially limits things to a single col or row in the UI but does 731 * not list the constraint in MOOXml. If a defined name or INDIRECT 732 * resulting in 1D is entered in the UI and the definition later modified 733 * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead 734 * of the curve and support 2d. In XL, values are listed row-wise, do the 735 * same. */ 736 for( nRow = 0; nRow < nRows ; nRow++ ) 737 { 738 for( nCol = 0; nCol < nCols ; nCol++ ) 739 { 740 ScTokenArray aCondTokArr; 741 TypedStrData* pEntry = NULL; 742 ScMatValType nMatValType; 743 String aValStr; 744 const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType); 745 746 // strings and empties 747 if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) ) 748 { 749 if( NULL != pMatVal ) 750 aValStr = pMatVal->GetString(); 751 752 if( NULL != pStrings ) 753 pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD); 754 755 if( pCell && rMatch < 0 ) 756 aCondTokArr.AddString( aValStr ); 757 } 758 else 759 { 760 sal_uInt16 nErr = pMatVal->GetError(); 761 762 if( 0 != nErr ) 763 { 764 aValStr = ScGlobal::GetErrorString( nErr ); 765 } 766 else 767 { 768 // FIXME FIXME FIXME 769 // Feature regression. Date formats are lost passing through the matrix 770 //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); 771 //For external reference and a formula that results in an area or array, date formats are still lost. 772 if ( bRef ) 773 { 774 pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()), 775 (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr); 776 } 777 else 778 pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); 779 } 780 781 if( pCell && rMatch < 0 ) 782 { 783 // I am not sure errors will work here, but a user can no 784 // manually enter an error yet so the point is somewhat moot. 785 aCondTokArr.AddDouble( pMatVal->fVal ); 786 } 787 if( NULL != pStrings ) 788 pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE); 789 } 790 791 if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) ) 792 { 793 rMatch = n; 794 // short circuit on the first match if not filling the list 795 if( NULL == pStrings ) 796 return true; 797 } 798 799 if( NULL != pEntry ) 800 { 801 lclInsertStringToCollection( *pStrings, pEntry, bSortList ); 802 n++; 803 } 804 } 805 } 806 807 // In case of no match needed and an error occurred, return that error 808 // entry as valid instead of silently failing. 809 return bOk || NULL == pCell; 810 } 811 812 bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const 813 { 814 bool bOk = false; 815 816 if( HasSelectionList() ) 817 { 818 ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); 819 820 // *** try if formula is a string list *** 821 822 bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); 823 sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); 824 ScStringTokenIterator aIt( *pTokArr ); 825 for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) 826 { 827 double fValue; 828 bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ); 829 TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD ); 830 lclInsertStringToCollection( rStrColl, pData, bSortList ); 831 } 832 bOk = aIt.Ok(); 833 834 // *** if not a string list, try if formula results in a cell range or 835 // anything else we recognize as valid *** 836 837 if (!bOk) 838 { 839 int nMatch; 840 bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch ); 841 } 842 } 843 844 return bOk; 845 } 846 847 // ---------------------------------------------------------------------------- 848 849 bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const 850 { 851 // create a condition entry that tests on equality and set the passed token array 852 ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos ); 853 return aCondEntry.IsCellValid( pCell, rPos ); 854 } 855 856 bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const 857 { 858 bool bIsValid = false; 859 860 /* Compare input cell with all supported tokens from the formula. 861 Currently a formula may contain: 862 1) A list of strings (at least one string). 863 2) A single cell or range reference. 864 3) A single defined name (must contain a cell/range reference, another 865 name, or DB range, or a formula resulting in a cell/range reference 866 or matrix/array). 867 4) A single database range. 868 5) A formula resulting in a cell/range reference or matrix/array. 869 */ 870 871 ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); 872 873 // *** try if formula is a string list *** 874 875 sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); 876 ScStringTokenIterator aIt( *pTokArr ); 877 for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) 878 { 879 /* Do not break the loop, if a valid string has been found. 880 This is to find invalid tokens following in the formula. */ 881 if( !bIsValid ) 882 { 883 // create a formula containing a single string or number 884 ScTokenArray aCondTokArr; 885 double fValue; 886 if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) ) 887 aCondTokArr.AddDouble( fValue ); 888 else 889 aCondTokArr.AddString( *pString ); 890 891 bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr ); 892 } 893 } 894 895 if( !aIt.Ok() ) 896 bIsValid = false; 897 898 // *** if not a string list, try if formula results in a cell range or 899 // anything else we recognize as valid *** 900 901 if (!bIsValid) 902 { 903 int nMatch; 904 bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch ); 905 bIsValid = bIsValid && nMatch >= 0; 906 } 907 908 return bIsValid; 909 } 910 911 // ============================================================================ 912 // ============================================================================ 913 914 ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) : 915 ScValidationEntries_Impl() 916 { 917 // fuer Ref-Undo - echte Kopie mit neuen Tokens! 918 919 sal_uInt16 nCount = rList.Count(); 920 921 for (sal_uInt16 i=0; i<nCount; i++) 922 InsertNew( rList[i]->Clone() ); 923 924 //! sortierte Eintraege aus rList schneller einfuegen ??? 925 } 926 927 ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc, 928 const ScValidationDataList& rList) 929 { 930 // fuer neues Dokument - echte Kopie mit neuen Tokens! 931 932 sal_uInt16 nCount = rList.Count(); 933 934 for (sal_uInt16 i=0; i<nCount; i++) 935 InsertNew( rList[i]->Clone(pNewDoc) ); 936 937 //! sortierte Eintraege aus rList schneller einfuegen ??? 938 } 939 940 ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey ) 941 { 942 //! binaer suchen 943 944 sal_uInt16 nCount = Count(); 945 for (sal_uInt16 i=0; i<nCount; i++) 946 if ((*this)[i]->GetKey() == nKey) 947 return (*this)[i]; 948 949 DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden"); 950 return NULL; 951 } 952 953 void ScValidationDataList::CompileXML() 954 { 955 sal_uInt16 nCount = Count(); 956 for (sal_uInt16 i=0; i<nCount; i++) 957 (*this)[i]->CompileXML(); 958 } 959 960 void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode, 961 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 962 { 963 sal_uInt16 nCount = Count(); 964 for (sal_uInt16 i=0; i<nCount; i++) 965 (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz); 966 } 967 968 void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) 969 { 970 sal_uInt16 nCount = Count(); 971 for (sal_uInt16 i=0; i<nCount; i++) 972 (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); 973 } 974 975 bool ScValidationDataList::MarkUsedExternalReferences() const 976 { 977 bool bAllMarked = false; 978 sal_uInt16 nCount = Count(); 979 for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++) 980 bAllMarked = (*this)[i]->MarkUsedExternalReferences(); 981 return bAllMarked; 982 } 983 984 sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const 985 { 986 // fuer Ref-Undo - interne Variablen werden nicht verglichen 987 988 sal_uInt16 nCount = Count(); 989 sal_Bool bEqual = ( nCount == r.Count() ); 990 for (sal_uInt16 i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert 991 if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? 992 bEqual = sal_False; 993 994 return bEqual; 995 } 996 997