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 ) 445 return sal_True; // alles erlaubt 446 447 if ( rTest.GetChar(0) == '=' ) 448 return sal_False; // Formeln sind sonst immer ungueltig 449 450 if ( !rTest.Len() ) 451 return IsIgnoreBlank(); // leer: wie eingestellt 452 453 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); 454 455 // Test, was es denn ist - wie in ScColumn::SetString 456 457 sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter ); 458 459 double nVal; 460 sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal ); 461 ScBaseCell* pCell; 462 if (bIsVal) 463 pCell = new ScValueCell( nVal ); 464 else 465 pCell = new ScStringCell( rTest ); 466 467 sal_Bool bRet = IsDataValid( pCell, rPos ); 468 469 pCell->Delete(); 470 return bRet; 471 } 472 473 sal_Bool ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const 474 { 475 if( eDataMode == SC_VALID_LIST ) 476 return IsListValid( pCell, rPos ); 477 478 double nVal = 0.0; 479 String aString; 480 sal_Bool bIsVal = sal_True; 481 482 switch (pCell->GetCellType()) 483 { 484 case CELLTYPE_VALUE: 485 nVal = ((ScValueCell*)pCell)->GetValue(); 486 break; 487 case CELLTYPE_STRING: 488 ((ScStringCell*)pCell)->GetString( aString ); 489 bIsVal = sal_False; 490 break; 491 case CELLTYPE_EDIT: 492 ((ScEditCell*)pCell)->GetString( aString ); 493 bIsVal = sal_False; 494 break; 495 case CELLTYPE_FORMULA: 496 { 497 ScFormulaCell* pFCell = (ScFormulaCell*)pCell; 498 bIsVal = pFCell->IsValue(); 499 if ( bIsVal ) 500 nVal = pFCell->GetValue(); 501 else 502 pFCell->GetString( aString ); 503 } 504 break; 505 default: // Notizen, Broadcaster 506 return IsIgnoreBlank(); // wie eingestellt 507 } 508 509 sal_Bool bOk = sal_True; 510 switch (eDataMode) 511 { 512 // SC_VALID_ANY schon oben 513 514 case SC_VALID_WHOLE: 515 case SC_VALID_DECIMAL: 516 case SC_VALID_DATE: // Date/Time ist nur Formatierung 517 case SC_VALID_TIME: 518 bOk = bIsVal; 519 if ( bOk && eDataMode == SC_VALID_WHOLE ) 520 bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen 521 if ( bOk ) 522 bOk = IsCellValid( pCell, rPos ); 523 break; 524 525 case SC_VALID_CUSTOM: 526 // fuer Custom muss eOp == SC_COND_DIRECT sein 527 //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!! 528 bOk = IsCellValid( pCell, rPos ); 529 break; 530 531 case SC_VALID_TEXTLEN: 532 bOk = !bIsVal; // nur Text 533 if ( bOk ) 534 { 535 double nLenVal = (double) aString.Len(); 536 ScValueCell aTmpCell( nLenVal ); 537 bOk = IsCellValid( &aTmpCell, rPos ); 538 } 539 break; 540 541 default: 542 DBG_ERROR("hammanochnich"); 543 break; 544 } 545 546 return bOk; 547 } 548 549 // ---------------------------------------------------------------------------- 550 551 namespace { 552 553 /** Token array helper. Iterates over all string tokens. 554 @descr The token array must contain separated string tokens only. 555 @param bSkipEmpty true = Ignores string tokens with empty strings. */ 556 class ScStringTokenIterator 557 { 558 public: 559 inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) : 560 mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {} 561 562 /** Returns the string of the first string token or NULL on error or empty token array. */ 563 const String* First(); 564 /** Returns the string of the next string token or NULL on error or end of token array. */ 565 const String* Next(); 566 567 /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */ 568 inline bool Ok() const { return mbOk; } 569 570 private: 571 ScTokenArray& mrTokArr; /// The token array for iteration. 572 bool mbSkipEmpty; /// Ignore empty strings. 573 bool mbOk; /// true = correct token or end of token array. 574 }; 575 576 const String* ScStringTokenIterator::First() 577 { 578 mrTokArr.Reset(); 579 mbOk = true; 580 return Next(); 581 } 582 583 const String* ScStringTokenIterator::Next() 584 { 585 if( !mbOk ) 586 return NULL; 587 588 // seek to next non-separator token 589 const FormulaToken* pToken = mrTokArr.NextNoSpaces(); 590 while( pToken && (pToken->GetOpCode() == ocSep) ) 591 pToken = mrTokArr.NextNoSpaces(); 592 593 mbOk = !pToken || (pToken->GetType() == formula::svString); 594 const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL; 595 // string found but empty -> get next token; otherwise return it 596 return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString; 597 } 598 599 // ---------------------------------------------------------------------------- 600 601 /** Returns the number format of the passed cell, or the standard format. */ 602 sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos ) 603 { 604 const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ); 605 if( !pPattern ) 606 pPattern = rDoc.GetDefPattern(); 607 return pPattern->GetNumberFormat( rDoc.GetFormatTable() ); 608 } 609 610 /** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */ 611 void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted ) 612 { 613 if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) ) 614 delete pData; 615 } 616 617 } // namespace 618 619 // ---------------------------------------------------------------------------- 620 621 bool ScValidationData::HasSelectionList() const 622 { 623 return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE); 624 } 625 626 bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings, 627 ScBaseCell* pCell, 628 const ScAddress& rPos, 629 const ScTokenArray& rTokArr, 630 int& rMatch ) const 631 { 632 bool bOk = true; 633 634 // pDoc is private in condition, use an accessor and a long winded name. 635 ScDocument* pDocument = GetDocument(); 636 if( NULL == pDocument ) 637 return false; 638 639 ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr, 640 formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA); 641 642 // Make sure the formula gets interpreted and a result is delivered, 643 // regardless of the AutoCalc setting. 644 aValidationSrc.Interpret(); 645 646 ScMatrixRef xMatRef; 647 const ScMatrix *pValues = aValidationSrc.GetMatrix(); 648 if (!pValues) 649 { 650 // The somewhat nasty case of either an error occured, or the 651 // dereferenced value of a single cell reference or an immediate result 652 // is stored as a single value. 653 654 // Use an interim matrix to create the TypedStrData below. 655 xMatRef = new ScMatrix(1,1); 656 657 sal_uInt16 nErrCode = aValidationSrc.GetErrCode(); 658 if (nErrCode) 659 { 660 /* TODO : to use later in an alert box? 661 * String rStrResult = "..."; 662 * rStrResult += ScGlobal::GetLongErrorString(nErrCode); 663 */ 664 665 xMatRef->PutError( nErrCode, 0); 666 bOk = false; 667 } 668 else if (aValidationSrc.HasValueData()) 669 xMatRef->PutDouble( aValidationSrc.GetValue(), 0); 670 else 671 { 672 String aStr; 673 aValidationSrc.GetString( aStr); 674 xMatRef->PutString( aStr, 0); 675 } 676 677 pValues = xMatRef; 678 } 679 680 // which index matched. We will want it eventually to pre-select that item. 681 rMatch = -1; 682 683 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); 684 685 bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); 686 SCSIZE nCol, nRow, nCols, nRows, n = 0; 687 pValues->GetDimensions( nCols, nRows ); 688 689 sal_Bool bRef = sal_False; 690 ScRange aRange; 691 692 ScTokenArray* pArr = (ScTokenArray*) &rTokArr; 693 pArr->Reset(); 694 ScToken* t = NULL; 695 if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL) 696 { 697 if (t->GetOpCode() == ocDBArea) 698 { 699 if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) ) 700 { 701 pDBData->GetArea(aRange); 702 bRef = sal_True; 703 } 704 } 705 else if (t->GetOpCode() == ocName) 706 { 707 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 708 if (pName && pName->IsReference(aRange)) 709 { 710 bRef = sal_True; 711 } 712 } 713 else if (t->GetType() != svIndex) 714 { 715 t->CalcAbsIfRel(rPos); 716 if (pArr->IsValidReference(aRange)) 717 { 718 bRef = sal_True; 719 } 720 } 721 } 722 723 /* XL artificially limits things to a single col or row in the UI but does 724 * not list the constraint in MOOXml. If a defined name or INDIRECT 725 * resulting in 1D is entered in the UI and the definition later modified 726 * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead 727 * of the curve and support 2d. In XL, values are listed row-wise, do the 728 * same. */ 729 for( nRow = 0; nRow < nRows ; nRow++ ) 730 { 731 for( nCol = 0; nCol < nCols ; nCol++ ) 732 { 733 ScTokenArray aCondTokArr; 734 TypedStrData* pEntry = NULL; 735 ScMatValType nMatValType; 736 String aValStr; 737 const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType); 738 739 // strings and empties 740 if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) ) 741 { 742 if( NULL != pMatVal ) 743 aValStr = pMatVal->GetString(); 744 745 if( NULL != pStrings ) 746 pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD); 747 748 if( pCell && rMatch < 0 ) 749 aCondTokArr.AddString( aValStr ); 750 } 751 else 752 { 753 sal_uInt16 nErr = pMatVal->GetError(); 754 755 if( 0 != nErr ) 756 { 757 aValStr = ScGlobal::GetErrorString( nErr ); 758 } 759 else 760 { 761 // FIXME FIXME FIXME 762 // Feature regression. Date formats are lost passing through the matrix 763 //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); 764 //For external reference and a formula that results in an area or array, date formats are still lost. 765 if ( bRef ) 766 { 767 pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()), 768 (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr); 769 } 770 else 771 pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); 772 } 773 774 if( pCell && rMatch < 0 ) 775 { 776 // I am not sure errors will work here, but a user can no 777 // manually enter an error yet so the point is somewhat moot. 778 aCondTokArr.AddDouble( pMatVal->fVal ); 779 } 780 if( NULL != pStrings ) 781 pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE); 782 } 783 784 if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) ) 785 { 786 rMatch = n; 787 // short circuit on the first match if not filling the list 788 if( NULL == pStrings ) 789 return true; 790 } 791 792 if( NULL != pEntry ) 793 { 794 lclInsertStringToCollection( *pStrings, pEntry, bSortList ); 795 n++; 796 } 797 } 798 } 799 800 // In case of no match needed and an error occurred, return that error 801 // entry as valid instead of silently failing. 802 return bOk || NULL == pCell; 803 } 804 805 bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const 806 { 807 bool bOk = false; 808 809 if( HasSelectionList() ) 810 { 811 ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); 812 813 // *** try if formula is a string list *** 814 815 bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); 816 sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); 817 ScStringTokenIterator aIt( *pTokArr ); 818 for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) 819 { 820 double fValue; 821 bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ); 822 TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD ); 823 lclInsertStringToCollection( rStrColl, pData, bSortList ); 824 } 825 bOk = aIt.Ok(); 826 827 // *** if not a string list, try if formula results in a cell range or 828 // anything else we recognize as valid *** 829 830 if (!bOk) 831 { 832 int nMatch; 833 bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch ); 834 } 835 } 836 837 return bOk; 838 } 839 840 // ---------------------------------------------------------------------------- 841 842 bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const 843 { 844 // create a condition entry that tests on equality and set the passed token array 845 ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos ); 846 return aCondEntry.IsCellValid( pCell, rPos ); 847 } 848 849 bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const 850 { 851 bool bIsValid = false; 852 853 /* Compare input cell with all supported tokens from the formula. 854 Currently a formula may contain: 855 1) A list of strings (at least one string). 856 2) A single cell or range reference. 857 3) A single defined name (must contain a cell/range reference, another 858 name, or DB range, or a formula resulting in a cell/range reference 859 or matrix/array). 860 4) A single database range. 861 5) A formula resulting in a cell/range reference or matrix/array. 862 */ 863 864 ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); 865 866 // *** try if formula is a string list *** 867 868 sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); 869 ScStringTokenIterator aIt( *pTokArr ); 870 for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) 871 { 872 /* Do not break the loop, if a valid string has been found. 873 This is to find invalid tokens following in the formula. */ 874 if( !bIsValid ) 875 { 876 // create a formula containing a single string or number 877 ScTokenArray aCondTokArr; 878 double fValue; 879 if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) ) 880 aCondTokArr.AddDouble( fValue ); 881 else 882 aCondTokArr.AddString( *pString ); 883 884 bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr ); 885 } 886 } 887 888 if( !aIt.Ok() ) 889 bIsValid = false; 890 891 // *** if not a string list, try if formula results in a cell range or 892 // anything else we recognize as valid *** 893 894 if (!bIsValid) 895 { 896 int nMatch; 897 bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch ); 898 bIsValid = bIsValid && nMatch >= 0; 899 } 900 901 return bIsValid; 902 } 903 904 // ============================================================================ 905 // ============================================================================ 906 907 ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) : 908 ScValidationEntries_Impl() 909 { 910 // fuer Ref-Undo - echte Kopie mit neuen Tokens! 911 912 sal_uInt16 nCount = rList.Count(); 913 914 for (sal_uInt16 i=0; i<nCount; i++) 915 InsertNew( rList[i]->Clone() ); 916 917 //! sortierte Eintraege aus rList schneller einfuegen ??? 918 } 919 920 ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc, 921 const ScValidationDataList& rList) 922 { 923 // fuer neues Dokument - echte Kopie mit neuen Tokens! 924 925 sal_uInt16 nCount = rList.Count(); 926 927 for (sal_uInt16 i=0; i<nCount; i++) 928 InsertNew( rList[i]->Clone(pNewDoc) ); 929 930 //! sortierte Eintraege aus rList schneller einfuegen ??? 931 } 932 933 ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey ) 934 { 935 //! binaer suchen 936 937 sal_uInt16 nCount = Count(); 938 for (sal_uInt16 i=0; i<nCount; i++) 939 if ((*this)[i]->GetKey() == nKey) 940 return (*this)[i]; 941 942 DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden"); 943 return NULL; 944 } 945 946 void ScValidationDataList::CompileXML() 947 { 948 sal_uInt16 nCount = Count(); 949 for (sal_uInt16 i=0; i<nCount; i++) 950 (*this)[i]->CompileXML(); 951 } 952 953 void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode, 954 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 955 { 956 sal_uInt16 nCount = Count(); 957 for (sal_uInt16 i=0; i<nCount; i++) 958 (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz); 959 } 960 961 void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) 962 { 963 sal_uInt16 nCount = Count(); 964 for (sal_uInt16 i=0; i<nCount; i++) 965 (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); 966 } 967 968 bool ScValidationDataList::MarkUsedExternalReferences() const 969 { 970 bool bAllMarked = false; 971 sal_uInt16 nCount = Count(); 972 for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++) 973 bAllMarked = (*this)[i]->MarkUsedExternalReferences(); 974 return bAllMarked; 975 } 976 977 sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const 978 { 979 // fuer Ref-Undo - interne Variablen werden nicht verglichen 980 981 sal_uInt16 nCount = Count(); 982 sal_Bool bEqual = ( nCount == r.Count() ); 983 for (sal_uInt16 i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert 984 if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? 985 bEqual = sal_False; 986 987 return bEqual; 988 } 989 990