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 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_sc.hxx" 24 25 // INCLUDE --------------------------------------------------------------- 26 27 #include <svl/zforlist.hxx> 28 29 #include "scitems.hxx" 30 #include "attrib.hxx" 31 #include "cell.hxx" 32 #include "compiler.hxx" 33 #include "interpre.hxx" 34 #include "document.hxx" 35 #include "scmatrix.hxx" 36 #include "dociter.hxx" 37 #include "docoptio.hxx" 38 #include "rechead.hxx" 39 #include "rangenam.hxx" 40 #include "brdcst.hxx" 41 #include "ddelink.hxx" 42 #include "validat.hxx" 43 #include "progress.hxx" 44 #include "editutil.hxx" 45 #include "recursionhelper.hxx" 46 #include "postit.hxx" 47 #include "externalrefmgr.hxx" 48 #include <editeng/editobj.hxx> 49 #include <svl/intitem.hxx> 50 #include <editeng/flditem.hxx> 51 #include <svl/broadcast.hxx> 52 53 using namespace formula; 54 // More or less arbitrary, of course all recursions must fit into available 55 // stack space (which is what on all systems we don't know yet?). Choosing a 56 // lower value may be better than trying a much higher value that also isn't 57 // sufficient but temporarily leads to high memory consumption. On the other 58 // hand, if the value fits all recursions, execution is quicker as no resumes 59 // are necessary. Could be made a configurable option. 60 // Allow for a year's calendar (366). 61 const sal_uInt16 MAXRECURSION = 400; 62 63 // STATIC DATA ----------------------------------------------------------- 64 65 #ifdef USE_MEMPOOL 66 // MemPools auf 4k Boundaries - 64 Bytes ausrichten 67 const sal_uInt16 nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell); 68 const sal_uInt16 nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell); 69 const sal_uInt16 nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell); 70 const sal_uInt16 nMemPoolNoteCell = (0x1000 - 64) / sizeof(ScNoteCell); 71 IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell ) 72 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell ) 73 IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell ) 74 IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell ) 75 #endif 76 77 // ============================================================================ 78 79 ScBaseCell::ScBaseCell( CellType eNewType ) : 80 mpNote( 0 ), 81 mpBroadcaster( 0 ), 82 nTextWidth( TEXTWIDTH_DIRTY ), 83 eCellType( sal::static_int_cast<sal_uInt8>(eNewType) ), 84 nScriptType( SC_SCRIPTTYPE_UNKNOWN ) 85 { 86 } 87 88 ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) : 89 mpNote( 0 ), 90 mpBroadcaster( 0 ), 91 nTextWidth( rCell.nTextWidth ), 92 eCellType( rCell.eCellType ), 93 nScriptType( SC_SCRIPTTYPE_UNKNOWN ) 94 { 95 } 96 97 ScBaseCell::~ScBaseCell() 98 { 99 delete mpNote; 100 delete mpBroadcaster; 101 DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" ); 102 } 103 104 namespace { 105 106 ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) 107 { 108 switch( rSrcCell.GetCellType() ) 109 { 110 case CELLTYPE_VALUE: 111 return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) ); 112 case CELLTYPE_STRING: 113 return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) ); 114 case CELLTYPE_EDIT: 115 return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc ); 116 case CELLTYPE_FORMULA: 117 return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags ); 118 case CELLTYPE_NOTE: 119 return new ScNoteCell; 120 default:; 121 } 122 DBG_ERROR( "lclCloneCell - unknown cell type" ); 123 return 0; 124 } 125 126 } // namespace 127 128 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const 129 { 130 // notes will not be cloned -> cell address only needed for formula cells 131 ScAddress aDestPos; 132 if( eCellType == CELLTYPE_FORMULA ) 133 aDestPos = static_cast< const ScFormulaCell* >( this )->aPos; 134 return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags ); 135 } 136 137 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const 138 { 139 return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); 140 } 141 142 ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const 143 { 144 ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); 145 if( mpNote ) 146 { 147 if( !pNewCell ) 148 pNewCell = new ScNoteCell; 149 bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0; 150 pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) ); 151 } 152 return pNewCell; 153 } 154 155 void ScBaseCell::Delete() 156 { 157 DeleteNote(); 158 switch (eCellType) 159 { 160 case CELLTYPE_VALUE: 161 delete (ScValueCell*) this; 162 break; 163 case CELLTYPE_STRING: 164 delete (ScStringCell*) this; 165 break; 166 case CELLTYPE_EDIT: 167 delete (ScEditCell*) this; 168 break; 169 case CELLTYPE_FORMULA: 170 delete (ScFormulaCell*) this; 171 break; 172 case CELLTYPE_NOTE: 173 delete (ScNoteCell*) this; 174 break; 175 default: 176 DBG_ERROR("Unbekannter Zellentyp"); 177 break; 178 } 179 } 180 181 bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const 182 { 183 return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote); 184 } 185 186 void ScBaseCell::TakeNote( ScPostIt* pNote ) 187 { 188 delete mpNote; 189 mpNote = pNote; 190 } 191 192 ScPostIt* ScBaseCell::ReleaseNote() 193 { 194 ScPostIt* pNote = mpNote; 195 mpNote = 0; 196 return pNote; 197 } 198 199 void ScBaseCell::DeleteNote() 200 { 201 DELETEZ( mpNote ); 202 } 203 204 void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster ) 205 { 206 delete mpBroadcaster; 207 mpBroadcaster = pBroadcaster; 208 } 209 210 SvtBroadcaster* ScBaseCell::ReleaseBroadcaster() 211 { 212 SvtBroadcaster* pBroadcaster = mpBroadcaster; 213 mpBroadcaster = 0; 214 return pBroadcaster; 215 } 216 217 void ScBaseCell::DeleteBroadcaster() 218 { 219 DELETEZ( mpBroadcaster ); 220 } 221 222 ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc ) 223 { 224 if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND ) 225 return new ScEditCell( rString, pDoc ); 226 else 227 return new ScStringCell( rString ); 228 } 229 230 void ScBaseCell::StartListeningTo( ScDocument* pDoc ) 231 { 232 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() 233 && !pDoc->GetNoListening() 234 && !((ScFormulaCell*)this)->IsInChangeTrack() 235 ) 236 { 237 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert... 238 239 ScFormulaCell* pFormCell = (ScFormulaCell*)this; 240 ScTokenArray* pArr = pFormCell->GetCode(); 241 if( pArr->IsRecalcModeAlways() ) 242 pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); 243 else 244 { 245 pArr->Reset(); 246 ScToken* t; 247 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) 248 { 249 StackVar eType = t->GetType(); 250 ScSingleRefData& rRef1 = t->GetSingleRef(); 251 ScSingleRefData& rRef2 = (eType == svDoubleRef ? 252 t->GetDoubleRef().Ref2 : rRef1); 253 switch( eType ) 254 { 255 case svSingleRef: 256 rRef1.CalcAbsIfRel( pFormCell->aPos ); 257 if ( rRef1.Valid() ) 258 { 259 pDoc->StartListeningCell( 260 ScAddress( rRef1.nCol, 261 rRef1.nRow, 262 rRef1.nTab ), pFormCell ); 263 } 264 break; 265 case svDoubleRef: 266 t->CalcAbsIfRel( pFormCell->aPos ); 267 if ( rRef1.Valid() && rRef2.Valid() ) 268 { 269 if ( t->GetOpCode() == ocColRowNameAuto ) 270 { // automagically 271 if ( rRef1.IsColRel() ) 272 { // ColName 273 pDoc->StartListeningArea( ScRange ( 274 rRef1.nCol, 275 rRef1.nRow, 276 rRef1.nTab, 277 rRef2.nCol, 278 MAXROW, 279 rRef2.nTab ), pFormCell ); 280 } 281 else 282 { // RowName 283 pDoc->StartListeningArea( ScRange ( 284 rRef1.nCol, 285 rRef1.nRow, 286 rRef1.nTab, 287 MAXCOL, 288 rRef2.nRow, 289 rRef2.nTab ), pFormCell ); 290 } 291 } 292 else 293 { 294 pDoc->StartListeningArea( ScRange ( 295 rRef1.nCol, 296 rRef1.nRow, 297 rRef1.nTab, 298 rRef2.nCol, 299 rRef2.nRow, 300 rRef2.nTab ), pFormCell ); 301 } 302 } 303 break; 304 default: 305 ; // nothing 306 } 307 } 308 } 309 pFormCell->SetNeedsListening( sal_False); 310 } 311 } 312 313 // pArr gesetzt -> Referenzen von anderer Zelle nehmen 314 // dann muss auch aPos uebergeben werden! 315 316 void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, 317 ScAddress aPos ) 318 { 319 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() 320 && !((ScFormulaCell*)this)->IsInChangeTrack() 321 ) 322 { 323 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert... 324 325 ScFormulaCell* pFormCell = (ScFormulaCell*)this; 326 if( pFormCell->GetCode()->IsRecalcModeAlways() ) 327 pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); 328 else 329 { 330 if (!pArr) 331 { 332 pArr = pFormCell->GetCode(); 333 aPos = pFormCell->aPos; 334 } 335 pArr->Reset(); 336 ScToken* t; 337 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) 338 { 339 StackVar eType = t->GetType(); 340 ScSingleRefData& rRef1 = t->GetSingleRef(); 341 ScSingleRefData& rRef2 = (eType == svDoubleRef ? 342 t->GetDoubleRef().Ref2 : rRef1); 343 switch( eType ) 344 { 345 case svSingleRef: 346 rRef1.CalcAbsIfRel( aPos ); 347 if ( rRef1.Valid() ) 348 { 349 pDoc->EndListeningCell( 350 ScAddress( rRef1.nCol, 351 rRef1.nRow, 352 rRef1.nTab ), pFormCell ); 353 } 354 break; 355 case svDoubleRef: 356 t->CalcAbsIfRel( aPos ); 357 if ( rRef1.Valid() && rRef2.Valid() ) 358 { 359 if ( t->GetOpCode() == ocColRowNameAuto ) 360 { // automagically 361 if ( rRef1.IsColRel() ) 362 { // ColName 363 pDoc->EndListeningArea( ScRange ( 364 rRef1.nCol, 365 rRef1.nRow, 366 rRef1.nTab, 367 rRef2.nCol, 368 MAXROW, 369 rRef2.nTab ), pFormCell ); 370 } 371 else 372 { // RowName 373 pDoc->EndListeningArea( ScRange ( 374 rRef1.nCol, 375 rRef1.nRow, 376 rRef1.nTab, 377 MAXCOL, 378 rRef2.nRow, 379 rRef2.nTab ), pFormCell ); 380 } 381 } 382 else 383 { 384 pDoc->EndListeningArea( ScRange ( 385 rRef1.nCol, 386 rRef1.nRow, 387 rRef1.nTab, 388 rRef2.nCol, 389 rRef2.nRow, 390 rRef2.nTab ), pFormCell ); 391 } 392 } 393 break; 394 default: 395 ; // nothing 396 } 397 } 398 } 399 } 400 } 401 402 403 sal_uInt16 ScBaseCell::GetErrorCode() const 404 { 405 switch ( eCellType ) 406 { 407 case CELLTYPE_FORMULA : 408 return ((ScFormulaCell*)this)->GetErrCode(); 409 default: 410 return 0; 411 } 412 } 413 414 415 sal_Bool ScBaseCell::HasEmptyData() const 416 { 417 switch ( eCellType ) 418 { 419 case CELLTYPE_NOTE : 420 return sal_True; 421 case CELLTYPE_FORMULA : 422 return ((ScFormulaCell*)this)->IsEmpty(); 423 default: 424 return sal_False; 425 } 426 } 427 428 429 sal_Bool ScBaseCell::HasValueData() const 430 { 431 switch ( eCellType ) 432 { 433 case CELLTYPE_VALUE : 434 return sal_True; 435 case CELLTYPE_FORMULA : 436 return ((ScFormulaCell*)this)->IsValue(); 437 default: 438 return sal_False; 439 } 440 } 441 442 443 sal_Bool ScBaseCell::HasStringData() const 444 { 445 switch ( eCellType ) 446 { 447 case CELLTYPE_STRING : 448 case CELLTYPE_EDIT : 449 return sal_True; 450 case CELLTYPE_FORMULA : 451 return !((ScFormulaCell*)this)->IsValue(); 452 default: 453 return sal_False; 454 } 455 } 456 457 String ScBaseCell::GetStringData() const 458 { 459 String aStr; 460 switch ( eCellType ) 461 { 462 case CELLTYPE_STRING: 463 ((const ScStringCell*)this)->GetString( aStr ); 464 break; 465 case CELLTYPE_EDIT: 466 ((const ScEditCell*)this)->GetString( aStr ); 467 break; 468 case CELLTYPE_FORMULA: 469 ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const 470 break; 471 } 472 return aStr; 473 } 474 475 // static 476 sal_Bool ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 ) 477 { 478 CellType eType1 = CELLTYPE_NONE; 479 CellType eType2 = CELLTYPE_NONE; 480 if ( pCell1 ) 481 { 482 eType1 = pCell1->GetCellType(); 483 if (eType1 == CELLTYPE_EDIT) 484 eType1 = CELLTYPE_STRING; 485 else if (eType1 == CELLTYPE_NOTE) 486 eType1 = CELLTYPE_NONE; 487 } 488 if ( pCell2 ) 489 { 490 eType2 = pCell2->GetCellType(); 491 if (eType2 == CELLTYPE_EDIT) 492 eType2 = CELLTYPE_STRING; 493 else if (eType2 == CELLTYPE_NOTE) 494 eType2 = CELLTYPE_NONE; 495 } 496 if ( eType1 != eType2 ) 497 return sal_False; 498 499 switch ( eType1 ) // beide Typen gleich 500 { 501 case CELLTYPE_NONE: // beide leer 502 return sal_True; 503 case CELLTYPE_VALUE: // wirklich Value-Zellen 504 return ( ((const ScValueCell*)pCell1)->GetValue() == 505 ((const ScValueCell*)pCell2)->GetValue() ); 506 case CELLTYPE_STRING: // String oder Edit 507 { 508 String aText1; 509 if ( pCell1->GetCellType() == CELLTYPE_STRING ) 510 ((const ScStringCell*)pCell1)->GetString(aText1); 511 else 512 ((const ScEditCell*)pCell1)->GetString(aText1); 513 String aText2; 514 if ( pCell2->GetCellType() == CELLTYPE_STRING ) 515 ((const ScStringCell*)pCell2)->GetString(aText2); 516 else 517 ((const ScEditCell*)pCell2)->GetString(aText2); 518 return ( aText1 == aText2 ); 519 } 520 case CELLTYPE_FORMULA: 521 { 522 //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!! 523 //! Vergleichsfunktion an der Formelzelle ??? 524 //! Abfrage mit ScColumn::SwapRow zusammenfassen! 525 526 ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode(); 527 ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode(); 528 529 if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN 530 { 531 sal_Bool bEqual = sal_True; 532 sal_uInt16 nLen = pCode1->GetLen(); 533 FormulaToken** ppToken1 = pCode1->GetArray(); 534 FormulaToken** ppToken2 = pCode2->GetArray(); 535 for (sal_uInt16 i=0; i<nLen; i++) 536 if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) ) 537 { 538 bEqual = sal_False; 539 break; 540 } 541 542 if (bEqual) 543 return sal_True; 544 } 545 546 return sal_False; // unterschiedlich lang oder unterschiedliche Tokens 547 } 548 default: 549 DBG_ERROR("huch, was fuer Zellen???"); 550 } 551 return sal_False; 552 } 553 554 // ============================================================================ 555 556 ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) : 557 ScBaseCell( CELLTYPE_NOTE ) 558 { 559 TakeBroadcaster( pBC ); 560 } 561 562 ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) : 563 ScBaseCell( CELLTYPE_NOTE ) 564 { 565 TakeNote( pNote ); 566 TakeBroadcaster( pBC ); 567 } 568 569 #ifdef DBG_UTIL 570 ScNoteCell::~ScNoteCell() 571 { 572 eCellType = CELLTYPE_DESTROYED; 573 } 574 #endif 575 576 // ============================================================================ 577 578 ScValueCell::ScValueCell() : 579 ScBaseCell( CELLTYPE_VALUE ), 580 mfValue( 0.0 ) 581 { 582 } 583 584 ScValueCell::ScValueCell( double fValue ) : 585 ScBaseCell( CELLTYPE_VALUE ), 586 mfValue( fValue ) 587 { 588 } 589 590 #ifdef DBG_UTIL 591 ScValueCell::~ScValueCell() 592 { 593 eCellType = CELLTYPE_DESTROYED; 594 } 595 #endif 596 597 // ============================================================================ 598 599 ScStringCell::ScStringCell() : 600 ScBaseCell( CELLTYPE_STRING ) 601 { 602 } 603 604 ScStringCell::ScStringCell( const String& rString ) : 605 ScBaseCell( CELLTYPE_STRING ), 606 maString( rString.intern() ) 607 { 608 } 609 610 #ifdef DBG_UTIL 611 ScStringCell::~ScStringCell() 612 { 613 eCellType = CELLTYPE_DESTROYED; 614 } 615 #endif 616 617 // ============================================================================ 618 619 // 620 // ScFormulaCell 621 // 622 623 ScFormulaCell::ScFormulaCell() : 624 ScBaseCell( CELLTYPE_FORMULA ), 625 eTempGrammar( FormulaGrammar::GRAM_DEFAULT), 626 pCode( NULL ), 627 pDocument( NULL ), 628 pPrevious(0), 629 pNext(0), 630 pPreviousTrack(0), 631 pNextTrack(0), 632 nFormatIndex(0), 633 nFormatType( NUMBERFORMAT_NUMBER ), 634 nSeenInIteration(0), 635 cMatrixFlag ( MM_NONE ), 636 bDirty( sal_False ), 637 bChanged( sal_False ), 638 bRunning( sal_False ), 639 bCompile( sal_False ), 640 bSubTotal( sal_False ), 641 bIsIterCell( sal_False ), 642 bInChangeTrack( sal_False ), 643 bTableOpDirty( sal_False ), 644 bNeedListening( sal_False ), 645 pValidRefToken( NULL ), 646 aPos(0,0,0) 647 { 648 } 649 650 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, 651 const String& rFormula, 652 const FormulaGrammar::Grammar eGrammar, 653 sal_uInt8 cMatInd ) : 654 ScBaseCell( CELLTYPE_FORMULA ), 655 eTempGrammar( eGrammar), 656 pCode( NULL ), 657 pDocument( pDoc ), 658 pPrevious(0), 659 pNext(0), 660 pPreviousTrack(0), 661 pNextTrack(0), 662 nFormatIndex(0), 663 nFormatType( NUMBERFORMAT_NUMBER ), 664 nSeenInIteration(0), 665 cMatrixFlag ( cMatInd ), 666 bDirty( sal_True ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0 667 bChanged( sal_False ), 668 bRunning( sal_False ), 669 bCompile( sal_False ), 670 bSubTotal( sal_False ), 671 bIsIterCell( sal_False ), 672 bInChangeTrack( sal_False ), 673 bTableOpDirty( sal_False ), 674 bNeedListening( sal_False ), 675 pValidRefToken( NULL ), 676 aPos( rPos ) 677 { 678 Compile( rFormula, sal_True, eGrammar ); // bNoListening, Insert does that 679 } 680 681 // Wird von den Importfiltern verwendet 682 683 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, 684 const ScTokenArray* pArr, 685 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) : 686 ScBaseCell( CELLTYPE_FORMULA ), 687 eTempGrammar( eGrammar), 688 pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ), 689 pDocument( pDoc ), 690 pPrevious(0), 691 pNext(0), 692 pPreviousTrack(0), 693 pNextTrack(0), 694 nFormatIndex(0), 695 nFormatType( NUMBERFORMAT_NUMBER ), 696 nSeenInIteration(0), 697 cMatrixFlag ( cInd ), 698 bDirty( NULL != pArr ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0 699 bChanged( sal_False ), 700 bRunning( sal_False ), 701 bCompile( sal_False ), 702 bSubTotal( sal_False ), 703 bIsIterCell( sal_False ), 704 bInChangeTrack( sal_False ), 705 bTableOpDirty( sal_False ), 706 bNeedListening( sal_False ), 707 pValidRefToken( NULL ), 708 aPos( rPos ) 709 { 710 // UPN-Array erzeugen 711 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() ) 712 { 713 ScCompiler aComp( pDocument, aPos, *pCode); 714 aComp.SetGrammar(eTempGrammar); 715 bSubTotal = aComp.CompileTokenArray(); 716 nFormatType = aComp.GetNumFormatType(); 717 } 718 else 719 { 720 pCode->Reset(); 721 if ( pCode->GetNextOpCodeRPN( ocSubTotal ) ) 722 bSubTotal = sal_True; 723 } 724 } 725 726 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) : 727 ScBaseCell( rCell ), 728 SvtListener(), 729 aResult( rCell.aResult ), 730 eTempGrammar( rCell.eTempGrammar), 731 pDocument( &rDoc ), 732 pPrevious(0), 733 pNext(0), 734 pPreviousTrack(0), 735 pNextTrack(0), 736 nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ), 737 nFormatType( rCell.nFormatType ), 738 nSeenInIteration(0), 739 cMatrixFlag ( rCell.cMatrixFlag ), 740 bDirty( rCell.bDirty ), 741 bChanged( rCell.bChanged ), 742 bRunning( sal_False ), 743 bCompile( rCell.bCompile ), 744 bSubTotal( rCell.bSubTotal ), 745 bIsIterCell( sal_False ), 746 bInChangeTrack( sal_False ), 747 bTableOpDirty( sal_False ), 748 bNeedListening( sal_False ), 749 aPos( rPos ) 750 { 751 if ( rCell.pValidRefToken ) 752 pValidRefToken = static_cast<ScToken*>(rCell.pValidRefToken->Clone()); 753 else 754 pValidRefToken = NULL; 755 756 pCode = (rCell.pCode) ? rCell.pCode->Clone() : NULL; 757 758 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL ) 759 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos ); 760 761 // evtl. Fehler zuruecksetzen und neu kompilieren 762 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben 763 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten 764 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() ) 765 { 766 pCode->SetCodeError( 0 ); 767 bCompile = sal_True; 768 } 769 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference 770 sal_Bool bCompileLater = sal_False; 771 sal_Bool bClipMode = rCell.pDocument->IsClipboard(); 772 if( !bCompile ) 773 { // Name references with references and ColRowNames 774 pCode->Reset(); 775 ScToken* t; 776 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile ) 777 { 778 if ( t->GetOpCode() == ocExternalRef ) 779 { 780 // External name, cell, and area references. 781 bCompile = true; 782 } 783 else if ( t->GetType() == svIndex ) 784 { 785 ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() ); 786 if( pRangeData ) 787 { 788 if( pRangeData->HasReferences() ) 789 bCompile = sal_True; 790 } 791 else 792 bCompile = sal_True; // invalid reference! 793 } 794 else if ( t->GetOpCode() == ocColRowName ) 795 { 796 bCompile = sal_True; // new lookup needed 797 bCompileLater = bClipMode; 798 } 799 } 800 } 801 if( bCompile ) 802 { 803 if ( !bCompileLater && bClipMode ) 804 { 805 // Merging ranges needs the actual positions after UpdateReference. 806 // ColRowNames need new lookup after positions are adjusted. 807 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName); 808 } 809 if ( !bCompileLater ) 810 { 811 // bNoListening, not at all if in Clipboard/Undo, 812 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference. 813 CompileTokenArray( sal_True ); 814 } 815 } 816 817 if( nCloneFlags & SC_CLONECELL_STARTLISTENING ) 818 StartListeningTo( &rDoc ); 819 } 820 821 ScFormulaCell::~ScFormulaCell() 822 { 823 pDocument->RemoveFromFormulaTree( this ); 824 825 if (pDocument->HasExternalRefManager()) 826 pDocument->GetExternalRefManager()->removeRefCell(this); 827 828 delete pCode; 829 #ifdef DBG_UTIL 830 eCellType = CELLTYPE_DESTROYED; 831 #endif 832 DELETEZ(pValidRefToken); 833 } 834 835 void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer, 836 const FormulaGrammar::Grammar eGrammar ) const 837 { 838 if( pCode->GetCodeError() && !pCode->GetLen() ) 839 { 840 rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError())); 841 return; 842 } 843 else if( cMatrixFlag == MM_REFERENCE ) 844 { 845 // Reference to another cell that contains a matrix formula. 846 pCode->Reset(); 847 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 848 if( p ) 849 { 850 /* FIXME: original GetFormula() code obtained 851 * pCell only if (!this->IsInChangeTrack()), 852 * GetEnglishFormula() omitted that test. 853 * Can we live without in all cases? */ 854 ScBaseCell* pCell; 855 ScSingleRefData& rRef = p->GetSingleRef(); 856 rRef.CalcAbsIfRel( aPos ); 857 if ( rRef.Valid() ) 858 pCell = pDocument->GetCell( ScAddress( rRef.nCol, 859 rRef.nRow, rRef.nTab ) ); 860 else 861 pCell = NULL; 862 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) 863 { 864 ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar); 865 return; 866 } 867 else 868 { 869 ScCompiler aComp( pDocument, aPos, *pCode); 870 aComp.SetGrammar(eGrammar); 871 aComp.CreateStringFromTokenArray( rBuffer ); 872 } 873 } 874 else 875 { 876 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix"); 877 } 878 } 879 else 880 { 881 ScCompiler aComp( pDocument, aPos, *pCode); 882 aComp.SetGrammar(eGrammar); 883 aComp.CreateStringFromTokenArray( rBuffer ); 884 } 885 886 sal_Unicode ch('='); 887 rBuffer.insert( 0, &ch, 1 ); 888 if( cMatrixFlag ) 889 { 890 sal_Unicode ch2('{'); 891 rBuffer.insert( 0, &ch2, 1); 892 rBuffer.append( sal_Unicode('}')); 893 } 894 } 895 896 void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const 897 { 898 rtl::OUStringBuffer rBuffer( rFormula ); 899 GetFormula( rBuffer, eGrammar ); 900 rFormula = rBuffer; 901 } 902 903 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ) 904 { 905 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 906 Interpret(); 907 908 const ScMatrix* pMat = NULL; 909 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell && 910 ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0)) 911 pMat->GetDimensions( rCols, rRows ); 912 else 913 { 914 rCols = 0; 915 rRows = 0; 916 } 917 } 918 919 void ScFormulaCell::Compile( const String& rFormula, sal_Bool bNoListening, 920 const FormulaGrammar::Grammar eGrammar ) 921 { 922 //#118851#, the initialization code for pCode after it can not be gnored if it is still NULL 923 if ( pCode && pDocument->IsClipOrUndo() ) return; 924 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this ); 925 if ( bWasInFormulaTree ) 926 pDocument->RemoveFromFormulaTree( this ); 927 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein 928 if ( pCode ) 929 pCode->Clear(); 930 ScTokenArray* pCodeOld = pCode; 931 ScCompiler aComp( pDocument, aPos); 932 aComp.SetGrammar(eGrammar); 933 pCode = aComp.CompileString( rFormula ); 934 if ( pCodeOld ) 935 delete pCodeOld; 936 if( !pCode->GetCodeError() ) 937 { 938 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() && rFormula == aResult.GetHybridFormula() ) 939 { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray 940 if ( rFormula.GetChar(0) == '=' ) 941 pCode->AddBad( rFormula.GetBuffer() + 1 ); 942 else 943 pCode->AddBad( rFormula.GetBuffer() ); 944 } 945 bCompile = sal_True; 946 CompileTokenArray( bNoListening ); 947 } 948 else 949 { 950 bChanged = sal_True; 951 SetTextWidth( TEXTWIDTH_DIRTY ); 952 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 953 } 954 if ( bWasInFormulaTree ) 955 pDocument->PutInFormulaTree( this ); 956 } 957 958 959 void ScFormulaCell::CompileTokenArray( sal_Bool bNoListening ) 960 { 961 // Not already compiled? 962 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 963 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar); 964 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() ) 965 { 966 // RPN length may get changed 967 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this ); 968 if ( bWasInFormulaTree ) 969 pDocument->RemoveFromFormulaTree( this ); 970 971 // Loading from within filter? No listening yet! 972 if( pDocument->IsInsertingFromOtherDoc() ) 973 bNoListening = sal_True; 974 975 if( !bNoListening && pCode->GetCodeLen() ) 976 EndListeningTo( pDocument ); 977 ScCompiler aComp(pDocument, aPos, *pCode); 978 aComp.SetGrammar(pDocument->GetGrammar()); 979 bSubTotal = aComp.CompileTokenArray(); 980 if( !pCode->GetCodeError() ) 981 { 982 nFormatType = aComp.GetNumFormatType(); 983 nFormatIndex = 0; 984 bChanged = sal_True; 985 aResult.SetToken( NULL); 986 bCompile = sal_False; 987 if ( !bNoListening ) 988 StartListeningTo( pDocument ); 989 } 990 if ( bWasInFormulaTree ) 991 pDocument->PutInFormulaTree( this ); 992 } 993 } 994 995 996 void ScFormulaCell::CompileXML( ScProgress& rProgress ) 997 { 998 if ( cMatrixFlag == MM_REFERENCE ) 999 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula 1000 // just establish listeners 1001 StartListeningTo( pDocument ); 1002 return ; 1003 } 1004 1005 ScCompiler aComp( pDocument, aPos, *pCode); 1006 aComp.SetGrammar(eTempGrammar); 1007 String aFormula, aFormulaNmsp; 1008 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp ); 1009 pDocument->DecXMLImportedFormulaCount( aFormula.Len() ); 1010 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() ); 1011 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein 1012 if ( pCode ) 1013 pCode->Clear(); 1014 ScTokenArray* pCodeOld = pCode; 1015 pCode = aComp.CompileString( aFormula, aFormulaNmsp ); 1016 delete pCodeOld; 1017 if( !pCode->GetCodeError() ) 1018 { 1019 if ( !pCode->GetLen() ) 1020 { 1021 if ( aFormula.GetChar(0) == '=' ) 1022 pCode->AddBad( aFormula.GetBuffer() + 1 ); 1023 else 1024 pCode->AddBad( aFormula.GetBuffer() ); 1025 } 1026 bSubTotal = aComp.CompileTokenArray(); 1027 if( !pCode->GetCodeError() ) 1028 { 1029 nFormatType = aComp.GetNumFormatType(); 1030 nFormatIndex = 0; 1031 bChanged = sal_True; 1032 bCompile = sal_False; 1033 StartListeningTo( pDocument ); 1034 } 1035 } 1036 else 1037 { 1038 bChanged = sal_True; 1039 SetTextWidth( TEXTWIDTH_DIRTY ); 1040 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 1041 } 1042 1043 // Same as in Load: after loading, it must be known if ocMacro is in any formula 1044 // (for macro warning, CompileXML is called at the end of loading XML file) 1045 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) ) 1046 pDocument->SetHasMacroFunc( sal_True ); 1047 } 1048 1049 1050 void ScFormulaCell::CalcAfterLoad() 1051 { 1052 sal_Bool bNewCompiled = sal_False; 1053 // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis, 1054 // aber kein TokenArray 1055 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1056 { 1057 Compile( aResult.GetHybridFormula(), sal_True, eTempGrammar); 1058 aResult.SetToken( NULL); 1059 bDirty = sal_True; 1060 bNewCompiled = sal_True; 1061 } 1062 // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen 1063 // wurde, da die RangeNames erst jetzt existieren. 1064 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() ) 1065 { 1066 ScCompiler aComp(pDocument, aPos, *pCode); 1067 aComp.SetGrammar(pDocument->GetGrammar()); 1068 bSubTotal = aComp.CompileTokenArray(); 1069 nFormatType = aComp.GetNumFormatType(); 1070 nFormatIndex = 0; 1071 bDirty = sal_True; 1072 bCompile = sal_False; 1073 bNewCompiled = sal_True; 1074 } 1075 // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503 1076 // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib 1077 // bei einem fabs(-NAN) abstuerzt (#32739#) 1078 // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht 1079 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) 1080 { 1081 DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?"); 1082 aResult.SetResultError( errIllegalFPOperation ); 1083 bDirty = sal_True; 1084 } 1085 // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix, 1086 // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge 1087 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF && 1088 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() ) 1089 { 1090 cMatrixFlag = MM_FORMULA; 1091 SetMatColsRows( 1, 1); 1092 } 1093 // Muss die Zelle berechnet werden? 1094 // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann 1095 // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL 1096 if( !bNewCompiled || !pCode->GetCodeError() ) 1097 { 1098 StartListeningTo( pDocument ); 1099 if( !pCode->IsRecalcModeNormal() ) 1100 bDirty = sal_True; 1101 } 1102 if ( pCode->IsRecalcModeAlways() ) 1103 { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie 1104 // auch bei jedem F9 berechnet werden. 1105 bDirty = sal_True; 1106 } 1107 // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in 1108 // SetDirtyAfterLoad. 1109 } 1110 1111 1112 bool ScFormulaCell::MarkUsedExternalReferences() 1113 { 1114 return pCode && pDocument->MarkUsedExternalReferences( *pCode); 1115 } 1116 1117 1118 // FIXME: set to 0 1119 #define erDEBUGDOT 0 1120 // If set to 1, write output that's suitable for graphviz tools like dot. 1121 // Only node1 -> node2 entries are written, you'll have to manually surround 1122 // the file content with [strict] digraph name { ... } 1123 // The ``strict'' keyword might be necessary in case of multiple identical 1124 // paths like they occur in iterations, otherwise dot may consume too much 1125 // memory when generating the layout, or you'll get unreadable output. On the 1126 // other hand, information about recurring calculation is lost then. 1127 // Generates output only if variable nDebug is set in debugger, see below. 1128 // FIXME: currently doesn't cope with iterations and recursions. Code fragments 1129 // are a leftover from a previous debug session, meant as a pointer. 1130 #if erDEBUGDOT 1131 #include <cstdio> 1132 using ::std::fopen; 1133 using ::std::fprintf; 1134 #include <vector> 1135 static const char aDebugDotFile[] = "ttt_debug.dot"; 1136 #endif 1137 1138 void ScFormulaCell::Interpret() 1139 { 1140 1141 #if erDEBUGDOT 1142 static int nDebug = 0; 1143 static const int erDEBUGDOTRUN = 3; 1144 static FILE* pDebugFile = 0; 1145 static sal_Int32 nDebugRootCount = 0; 1146 static unsigned int nDebugPathCount = 0; 1147 static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID); 1148 static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID); 1149 typedef ::std::vector< ByteString > DebugVector; 1150 static DebugVector aDebugVec; 1151 class DebugElement 1152 { 1153 public: 1154 static void push( ScFormulaCell* pCell ) 1155 { 1156 aDebugThisPos = pCell->aPos; 1157 if (aDebugVec.empty()) 1158 { 1159 ByteString aR( "root_"); 1160 aR += ByteString::CreateFromInt32( ++nDebugRootCount); 1161 aDebugVec.push_back( aR); 1162 } 1163 String aStr; 1164 pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(), 1165 pCell->GetDocument()->GetAddressConvention() ); 1166 ByteString aB( aStr, RTL_TEXTENCODING_UTF8); 1167 aDebugVec.push_back( aB); 1168 } 1169 static void pop() 1170 { 1171 aDebugLastPos = aDebugThisPos; 1172 if (!aDebugVec.empty()) 1173 { 1174 aDebugVec.pop_back(); 1175 if (aDebugVec.size() == 1) 1176 { 1177 aDebugVec.pop_back(); 1178 aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID); 1179 } 1180 } 1181 } 1182 DebugElement( ScFormulaCell* p ) { push(p); } 1183 ~DebugElement() { pop(); } 1184 }; 1185 class DebugDot 1186 { 1187 public: 1188 static void out( const char* pColor ) 1189 { 1190 if (nDebug != erDEBUGDOTRUN) 1191 return; 1192 char pColorString[256]; 1193 sprintf( pColorString, (*pColor ? 1194 ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor, 1195 pColor); 1196 size_t n = aDebugVec.size(); 1197 fprintf( pDebugFile, 1198 "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n", 1199 aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(), 1200 ++nDebugPathCount, pColorString, n-1); 1201 fflush( pDebugFile); 1202 } 1203 }; 1204 #define erDEBUGDOT_OUT( p ) (DebugDot::out(p)) 1205 #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p)) 1206 #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop()) 1207 #else 1208 #define erDEBUGDOT_OUT( p ) 1209 #define erDEBUGDOT_ELEMENT_PUSH( p ) 1210 #define erDEBUGDOT_ELEMENT_POP() 1211 #endif 1212 1213 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn()) 1214 return; // no double/triple processing 1215 1216 //! HACK: 1217 // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen 1218 // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!! 1219 1220 if ( pDocument->IsInDdeLinkUpdate() ) 1221 return; 1222 1223 #if erDEBUGDOT 1224 // set nDebug=1 in debugger to init things 1225 if (nDebug == 1) 1226 { 1227 ++nDebug; 1228 pDebugFile = fopen( aDebugDotFile, "a"); 1229 if (!pDebugFile) 1230 nDebug = 0; 1231 else 1232 nDebug = erDEBUGDOTRUN; 1233 } 1234 // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output 1235 DebugElement aDebugElem( this); 1236 // set nDebug=5 in debugger to close output 1237 if (nDebug == 5) 1238 { 1239 nDebug = 0; 1240 fclose( pDebugFile); 1241 pDebugFile = 0; 1242 } 1243 #endif 1244 1245 if (bRunning) 1246 { 1247 1248 #if erDEBUGDOT 1249 if (!pDocument->GetRecursionHelper().IsDoingIteration() || 1250 aDebugThisPos != aDebugLastPos) 1251 erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" : 1252 (pDocument->GetRecursionHelper().GetIteration() ? "blue" : 1253 "red")); 1254 #endif 1255 1256 if (!pDocument->GetDocOptions().IsIter()) 1257 { 1258 aResult.SetResultError( errCircularReference ); 1259 return; 1260 } 1261 1262 if (aResult.GetResultError() == errCircularReference) 1263 aResult.SetResultError( 0 ); 1264 1265 // Start or add to iteration list. 1266 if (!pDocument->GetRecursionHelper().IsDoingIteration() || 1267 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell) 1268 pDocument->GetRecursionHelper().SetInIterationReturn( true); 1269 1270 return; 1271 } 1272 // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and 1273 // different entry point recursions. Would also lead to premature 1274 // convergence in iterations. 1275 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration == 1276 pDocument->GetRecursionHelper().GetIteration()) 1277 return ; 1278 1279 erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : ""); 1280 1281 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); 1282 sal_Bool bOldRunning = bRunning; 1283 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION) 1284 { 1285 bRunning = sal_True; 1286 rRecursionHelper.SetInRecursionReturn( true); 1287 } 1288 else 1289 { 1290 InterpretTail( SCITP_NORMAL); 1291 } 1292 1293 // While leaving a recursion or iteration stack, insert its cells to the 1294 // recursion list in reverse order. 1295 if (rRecursionHelper.IsInReturn()) 1296 { 1297 if (rRecursionHelper.GetRecursionCount() > 0 || 1298 !rRecursionHelper.IsDoingRecursion()) 1299 rRecursionHelper.Insert( this, bOldRunning, aResult); 1300 bool bIterationFromRecursion = false; 1301 bool bResumeIteration = false; 1302 do 1303 { 1304 if ((rRecursionHelper.IsInIterationReturn() && 1305 rRecursionHelper.GetRecursionCount() == 0 && 1306 !rRecursionHelper.IsDoingIteration()) || 1307 bIterationFromRecursion || bResumeIteration) 1308 { 1309 ScFormulaCell* pIterCell = this; // scope for debug convenience 1310 bool & rDone = rRecursionHelper.GetConvergingReference(); 1311 rDone = false; 1312 if (!bIterationFromRecursion && bResumeIteration) 1313 { 1314 bResumeIteration = false; 1315 // Resuming iteration expands the range. 1316 ScFormulaRecursionList::const_iterator aOldStart( 1317 rRecursionHelper.GetLastIterationStart()); 1318 rRecursionHelper.ResumeIteration(); 1319 // Mark new cells being in iteration. 1320 for (ScFormulaRecursionList::const_iterator aIter( 1321 rRecursionHelper.GetIterationStart()); aIter != 1322 aOldStart; ++aIter) 1323 { 1324 pIterCell = (*aIter).pCell; 1325 pIterCell->bIsIterCell = sal_True; 1326 } 1327 // Mark older cells dirty again, in case they converted 1328 // without accounting for all remaining cells in the circle 1329 // that weren't touched so far, e.g. conditional. Restore 1330 // backuped result. 1331 sal_uInt16 nIteration = rRecursionHelper.GetIteration(); 1332 for (ScFormulaRecursionList::const_iterator aIter( 1333 aOldStart); aIter != 1334 rRecursionHelper.GetIterationEnd(); ++aIter) 1335 { 1336 pIterCell = (*aIter).pCell; 1337 if (pIterCell->nSeenInIteration == nIteration) 1338 { 1339 if (!pIterCell->bDirty || aIter == aOldStart) 1340 { 1341 pIterCell->aResult = (*aIter).aPreviousResult; 1342 } 1343 --pIterCell->nSeenInIteration; 1344 } 1345 pIterCell->bDirty = sal_True; 1346 } 1347 } 1348 else 1349 { 1350 bResumeIteration = false; 1351 // Close circle once. 1352 rRecursionHelper.GetList().back().pCell->InterpretTail( 1353 SCITP_CLOSE_ITERATION_CIRCLE); 1354 // Start at 1, init things. 1355 rRecursionHelper.StartIteration(); 1356 // Mark all cells being in iteration. 1357 for (ScFormulaRecursionList::const_iterator aIter( 1358 rRecursionHelper.GetIterationStart()); aIter != 1359 rRecursionHelper.GetIterationEnd(); ++aIter) 1360 { 1361 pIterCell = (*aIter).pCell; 1362 pIterCell->bIsIterCell = sal_True; 1363 } 1364 } 1365 bIterationFromRecursion = false; 1366 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount(); 1367 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone; 1368 rRecursionHelper.IncIteration()) 1369 { 1370 rDone = true; 1371 for ( ScFormulaRecursionList::iterator aIter( 1372 rRecursionHelper.GetIterationStart()); aIter != 1373 rRecursionHelper.GetIterationEnd() && 1374 !rRecursionHelper.IsInReturn(); ++aIter) 1375 { 1376 pIterCell = (*aIter).pCell; 1377 if (pIterCell->IsDirtyOrInTableOpDirty() && 1378 rRecursionHelper.GetIteration() != 1379 pIterCell->GetSeenInIteration()) 1380 { 1381 (*aIter).aPreviousResult = pIterCell->aResult; 1382 pIterCell->InterpretTail( SCITP_FROM_ITERATION); 1383 } 1384 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty(); 1385 } 1386 if (rRecursionHelper.IsInReturn()) 1387 { 1388 bResumeIteration = true; 1389 break; // for 1390 // Don't increment iteration. 1391 } 1392 } 1393 if (!bResumeIteration) 1394 { 1395 if (rDone) 1396 { 1397 for (ScFormulaRecursionList::const_iterator aIter( 1398 rRecursionHelper.GetIterationStart()); 1399 aIter != rRecursionHelper.GetIterationEnd(); 1400 ++aIter) 1401 { 1402 pIterCell = (*aIter).pCell; 1403 pIterCell->bIsIterCell = sal_False; 1404 pIterCell->nSeenInIteration = 0; 1405 pIterCell->bRunning = (*aIter).bOldRunning; 1406 } 1407 } 1408 else 1409 { 1410 for (ScFormulaRecursionList::const_iterator aIter( 1411 rRecursionHelper.GetIterationStart()); 1412 aIter != rRecursionHelper.GetIterationEnd(); 1413 ++aIter) 1414 { 1415 pIterCell = (*aIter).pCell; 1416 pIterCell->bIsIterCell = sal_False; 1417 pIterCell->nSeenInIteration = 0; 1418 pIterCell->bRunning = (*aIter).bOldRunning; 1419 // If one cell didn't converge, all cells of this 1420 // circular dependency don't, no matter whether 1421 // single cells did. 1422 pIterCell->bDirty = sal_False; 1423 pIterCell->bTableOpDirty = sal_False; 1424 pIterCell->aResult.SetResultError( errNoConvergence); 1425 pIterCell->bChanged = sal_True; 1426 pIterCell->SetTextWidth( TEXTWIDTH_DIRTY); 1427 pIterCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN); 1428 } 1429 } 1430 // End this iteration and remove entries. 1431 rRecursionHelper.EndIteration(); 1432 bResumeIteration = rRecursionHelper.IsDoingIteration(); 1433 } 1434 } 1435 if (rRecursionHelper.IsInRecursionReturn() && 1436 rRecursionHelper.GetRecursionCount() == 0 && 1437 !rRecursionHelper.IsDoingRecursion()) 1438 { 1439 bIterationFromRecursion = false; 1440 // Iterate over cells known so far, start with the last cell 1441 // encountered, inserting new cells if another recursion limit 1442 // is reached. Repeat until solved. 1443 rRecursionHelper.SetDoingRecursion( true); 1444 do 1445 { 1446 rRecursionHelper.SetInRecursionReturn( false); 1447 for (ScFormulaRecursionList::const_iterator aIter( 1448 rRecursionHelper.GetStart()); 1449 !rRecursionHelper.IsInReturn() && aIter != 1450 rRecursionHelper.GetEnd(); ++aIter) 1451 { 1452 ScFormulaCell* pCell = (*aIter).pCell; 1453 if (pCell->IsDirtyOrInTableOpDirty()) 1454 { 1455 pCell->InterpretTail( SCITP_NORMAL); 1456 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell()) 1457 pCell->bRunning = (*aIter).bOldRunning; 1458 } 1459 } 1460 } while (rRecursionHelper.IsInRecursionReturn()); 1461 rRecursionHelper.SetDoingRecursion( false); 1462 if (rRecursionHelper.IsInIterationReturn()) 1463 { 1464 if (!bResumeIteration) 1465 bIterationFromRecursion = true; 1466 } 1467 else if (bResumeIteration || 1468 rRecursionHelper.IsDoingIteration()) 1469 rRecursionHelper.GetList().erase( 1470 rRecursionHelper.GetStart(), 1471 rRecursionHelper.GetLastIterationStart()); 1472 else 1473 rRecursionHelper.Clear(); 1474 } 1475 } while (bIterationFromRecursion || bResumeIteration); 1476 } 1477 } 1478 1479 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) 1480 { 1481 class RecursionCounter 1482 { 1483 ScRecursionHelper& rRec; 1484 bool bStackedInIteration; 1485 public: 1486 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r) 1487 { 1488 bStackedInIteration = rRec.IsDoingIteration(); 1489 if (bStackedInIteration) 1490 rRec.GetRecursionInIterationStack().push( p); 1491 rRec.IncRecursionCount(); 1492 } 1493 ~RecursionCounter() 1494 { 1495 rRec.DecRecursionCount(); 1496 if (bStackedInIteration) 1497 rRec.GetRecursionInIterationStack().pop(); 1498 } 1499 } aRecursionCounter( pDocument->GetRecursionHelper(), this); 1500 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration(); 1501 if( !pCode->GetCodeLen() && !pCode->GetCodeError() ) 1502 { 1503 // #i11719# no UPN and no error and no token code but result string present 1504 // => interpretation of this cell during name-compilation and unknown names 1505 // => can't exchange underlying code array in CompileTokenArray() / 1506 // Compile() because interpreter's token iterator would crash. 1507 // This should only be a temporary condition and, since we set an 1508 // error, if ran into it again we'd bump into the dirty-clearing 1509 // condition further down. 1510 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1511 { 1512 pCode->SetCodeError( errNoCode ); 1513 // This is worth an assertion; if encountered in daily work 1514 // documents we might need another solution. Or just confirm correctness. 1515 DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" ); 1516 return; 1517 } 1518 CompileTokenArray(); 1519 } 1520 1521 if( pCode->GetCodeLen() && pDocument ) 1522 { 1523 class StackCleaner 1524 { 1525 ScDocument* pDoc; 1526 ScInterpreter* pInt; 1527 public: 1528 StackCleaner( ScDocument* pD, ScInterpreter* pI ) 1529 : pDoc(pD), pInt(pI) 1530 {} 1531 ~StackCleaner() 1532 { 1533 delete pInt; 1534 pDoc->DecInterpretLevel(); 1535 } 1536 }; 1537 pDocument->IncInterpretLevel(); 1538 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode ); 1539 StackCleaner aStackCleaner( pDocument, p); 1540 sal_uInt16 nOldErrCode = aResult.GetResultError(); 1541 if ( nSeenInIteration == 0 ) 1542 { // Only the first time 1543 // With bChanged=sal_False, if a newly compiled cell has a result of 1544 // 0.0, no change is detected and the cell will not be repainted. 1545 // bChanged = sal_False; 1546 aResult.SetResultError( 0 ); 1547 } 1548 1549 switch ( aResult.GetResultError() ) 1550 { 1551 case errCircularReference : // will be determined again if so 1552 aResult.SetResultError( 0 ); 1553 break; 1554 } 1555 1556 sal_Bool bOldRunning = bRunning; 1557 bRunning = sal_True; 1558 p->Interpret(); 1559 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE) 1560 { 1561 if (nSeenInIteration > 0) 1562 --nSeenInIteration; // retry when iteration is resumed 1563 return; 1564 } 1565 bRunning = bOldRunning; 1566 //-> i120962: If the cell was applied reference formula, get the valid token 1567 if (pValidRefToken) 1568 DELETEZ(pValidRefToken); 1569 if (p->IsReferenceFunc() && p->GetLastStackRefToken()) 1570 pValidRefToken = static_cast<ScToken*>(p->GetLastStackRefToken()->Clone()); 1571 //<- i120962 1572 1573 // #i102616# For single-sheet saving consider only content changes, not format type, 1574 // because format type isn't set on loading (might be changed later) 1575 sal_Bool bContentChanged = sal_False; 1576 1577 // Do not create a HyperLink() cell if the formula results in an error. 1578 if( p->GetError() && pCode->IsHyperLink()) 1579 pCode->SetHyperLink(sal_False); 1580 1581 if( p->GetError() && p->GetError() != errCircularReference) 1582 { 1583 bDirty = sal_False; 1584 bTableOpDirty = sal_False; 1585 bChanged = sal_True; 1586 } 1587 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty()) 1588 { 1589 bool bIsValue = aResult.IsValue(); // the previous type 1590 // Did it converge? 1591 if ((bIsValue && p->GetResultType() == svDouble && fabs( 1592 p->GetNumResult() - aResult.GetDouble()) <= 1593 pDocument->GetDocOptions().GetIterEps()) || 1594 (!bIsValue && p->GetResultType() == svString && 1595 p->GetStringResult() == aResult.GetString())) 1596 { 1597 // A convergence in the first iteration doesn't necessarily 1598 // mean that it's done, it may be because not all related cells 1599 // of a circle changed their values yet. If the set really 1600 // converges it will do so also during the next iteration. This 1601 // fixes situations like of #i44115#. If this wasn't wanted an 1602 // initial "uncalculated" value would be needed for all cells 1603 // of a circular dependency => graph needed before calculation. 1604 if (nSeenInIteration > 1 || 1605 pDocument->GetDocOptions().GetIterCount() == 1) 1606 { 1607 bDirty = sal_False; 1608 bTableOpDirty = sal_False; 1609 } 1610 } 1611 } 1612 1613 // New error code? 1614 if( p->GetError() != nOldErrCode ) 1615 { 1616 bChanged = sal_True; 1617 // bContentChanged only has to be set if the file content would be changed 1618 if ( aResult.GetCellResultType() != svUnknown ) 1619 bContentChanged = sal_True; 1620 } 1621 // Different number format? 1622 if( nFormatType != p->GetRetFormatType() ) 1623 { 1624 nFormatType = p->GetRetFormatType(); 1625 bChanged = sal_True; 1626 } 1627 if( nFormatIndex != p->GetRetFormatIndex() ) 1628 { 1629 nFormatIndex = p->GetRetFormatIndex(); 1630 bChanged = sal_True; 1631 } 1632 1633 // In case of changes just obtain the result, no temporary and 1634 // comparison needed anymore. 1635 if (bChanged) 1636 { 1637 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving 1638 // Also handle special cases of initial results after loading. 1639 1640 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1641 { 1642 ScFormulaResult aNewResult( p->GetResultToken()); 1643 StackVar eOld = aResult.GetCellResultType(); 1644 StackVar eNew = aNewResult.GetCellResultType(); 1645 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) 1646 { 1647 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0 1648 // -> no change 1649 } 1650 else 1651 { 1652 if ( eOld == svHybridCell ) // string result from SetFormulaResultString? 1653 eOld = svString; // ScHybridCellToken has a valid GetString method 1654 1655 // #i106045# use approxEqual to compare with stored value 1656 bContentChanged = (eOld != eNew || 1657 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) || 1658 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1659 } 1660 } 1661 1662 aResult.SetToken( p->GetResultToken() ); 1663 } 1664 else 1665 { 1666 ScFormulaResult aNewResult( p->GetResultToken()); 1667 StackVar eOld = aResult.GetCellResultType(); 1668 StackVar eNew = aNewResult.GetCellResultType(); 1669 bChanged = (eOld != eNew || 1670 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) || 1671 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1672 1673 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged) 1674 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1675 { 1676 if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) || 1677 ( eOld == svHybridCell && eNew == svString && aResult.GetString() == aNewResult.GetString() ) || 1678 ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) ) 1679 { 1680 // no change, see above 1681 } 1682 else 1683 bContentChanged = sal_True; 1684 } 1685 1686 aResult.Assign( aNewResult); 1687 } 1688 1689 // Precision as shown? 1690 if ( aResult.IsValue() && !p->GetError() 1691 && pDocument->GetDocOptions().IsCalcAsShown() 1692 && nFormatType != NUMBERFORMAT_DATE 1693 && nFormatType != NUMBERFORMAT_TIME 1694 && nFormatType != NUMBERFORMAT_DATETIME ) 1695 { 1696 sal_uLong nFormat = pDocument->GetNumberFormat( aPos ); 1697 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1698 nFormat = nFormatIndex; 1699 if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1700 nFormat = ScGlobal::GetStandardFormat( 1701 *pDocument->GetFormatTable(), nFormat, nFormatType ); 1702 aResult.SetDouble( pDocument->RoundValueAsShown( 1703 aResult.GetDouble(), nFormat)); 1704 } 1705 if (eTailParam == SCITP_NORMAL) 1706 { 1707 bDirty = sal_False; 1708 bTableOpDirty = sal_False; 1709 } 1710 if( aResult.GetMatrix().Is() ) 1711 { 1712 // If the formula wasn't entered as a matrix formula, live on with 1713 // the upper left corner and let reference counting delete the matrix. 1714 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() ) 1715 aResult.SetToken( aResult.GetCellResultToken()); 1716 } 1717 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) 1718 { 1719 // Coded double error may occur via filter import. 1720 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble()); 1721 aResult.SetResultError( nErr); 1722 bChanged = bContentChanged = true; 1723 } 1724 if( bChanged ) 1725 { 1726 SetTextWidth( TEXTWIDTH_DIRTY ); 1727 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 1728 } 1729 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab())) 1730 { 1731 // pass bIgnoreLock=sal_True, because even if called from pending row height update, 1732 // a changed result must still reset the stream flag 1733 pDocument->SetStreamValid(aPos.Tab(), sal_False, sal_True); 1734 } 1735 if ( !pCode->IsRecalcModeAlways() ) 1736 pDocument->RemoveFromFormulaTree( this ); 1737 1738 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten) 1739 1740 if ( pCode->IsRecalcModeForced() ) 1741 { 1742 sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr( 1743 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue(); 1744 if ( nValidation ) 1745 { 1746 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation ); 1747 if ( pData && !pData->IsDataValid( this, aPos ) ) 1748 pData->DoCalcError( this ); 1749 } 1750 } 1751 1752 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren 1753 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent( 1754 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); 1755 } 1756 else 1757 { 1758 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen 1759 DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" ); 1760 bDirty = sal_False; 1761 bTableOpDirty = sal_False; 1762 } 1763 } 1764 1765 1766 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows ) 1767 { 1768 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst(); 1769 if (pMat) 1770 pMat->SetMatColsRows( nCols, nRows); 1771 else if (nCols || nRows) 1772 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows)); 1773 } 1774 1775 1776 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const 1777 { 1778 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken(); 1779 if (pMat) 1780 pMat->GetMatColsRows( nCols, nRows); 1781 else 1782 { 1783 nCols = 0; 1784 nRows = 0; 1785 } 1786 } 1787 1788 1789 sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const 1790 { 1791 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1792 return nFormatIndex; 1793 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again. 1794 if ( aResult.IsValue() ) 1795 return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType ); 1796 else 1797 return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType ); 1798 } 1799 1800 1801 void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint) 1802 { 1803 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() ) 1804 { 1805 const ScHint* p = PTR_CAST( ScHint, &rHint ); 1806 sal_uLong nHint = (p ? p->GetId() : 0); 1807 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY)) 1808 { 1809 sal_Bool bForceTrack = sal_False; 1810 if ( nHint & SC_HINT_TABLEOPDIRTY ) 1811 { 1812 bForceTrack = !bTableOpDirty; 1813 if ( !bTableOpDirty ) 1814 { 1815 pDocument->AddTableOpFormulaCell( this ); 1816 bTableOpDirty = sal_True; 1817 } 1818 } 1819 else 1820 { 1821 bForceTrack = !bDirty; 1822 bDirty = sal_True; 1823 } 1824 // #35962# Don't remove from FormulaTree to put in FormulaTrack to 1825 // put in FormulaTree again and again, only if necessary. 1826 // Any other means except RECALCMODE_ALWAYS by which a cell could 1827 // be in FormulaTree if it would notify other cells through 1828 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!? 1829 // #87866# Yes. The new TableOpDirty made it necessary to have a 1830 // forced mode where formulas may still be in FormulaTree from 1831 // TableOpDirty but have to notify dependents for normal dirty. 1832 if ( (bForceTrack || !pDocument->IsInFormulaTree( this ) 1833 || pCode->IsRecalcModeAlways()) 1834 && !pDocument->IsInFormulaTrack( this ) ) 1835 pDocument->AppendToFormulaTrack( this ); 1836 } 1837 } 1838 } 1839 1840 void ScFormulaCell::SetDirty() 1841 { 1842 if ( !IsInChangeTrack() ) 1843 { 1844 if ( pDocument->GetHardRecalcState() ) 1845 bDirty = sal_True; 1846 else 1847 { 1848 // Mehrfach-FormulaTracking in Load und in CompileAll 1849 // nach CopyScenario und CopyBlockFromClip vermeiden. 1850 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=sal_False 1851 // setzen, z.B. in CompileTokenArray 1852 if ( !bDirty || !pDocument->IsInFormulaTree( this ) ) 1853 { 1854 bDirty = sal_True; 1855 pDocument->AppendToFormulaTrack( this ); 1856 pDocument->TrackFormulas(); 1857 } 1858 } 1859 1860 if (pDocument->IsStreamValid(aPos.Tab())) 1861 pDocument->SetStreamValid(aPos.Tab(), sal_False); 1862 } 1863 } 1864 1865 void ScFormulaCell::SetDirtyAfterLoad() 1866 { 1867 bDirty = sal_True; 1868 if ( !pDocument->GetHardRecalcState() ) 1869 pDocument->PutInFormulaTree( this ); 1870 } 1871 1872 void ScFormulaCell::SetTableOpDirty() 1873 { 1874 if ( !IsInChangeTrack() ) 1875 { 1876 if ( pDocument->GetHardRecalcState() ) 1877 bTableOpDirty = sal_True; 1878 else 1879 { 1880 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) ) 1881 { 1882 if ( !bTableOpDirty ) 1883 { 1884 pDocument->AddTableOpFormulaCell( this ); 1885 bTableOpDirty = sal_True; 1886 } 1887 pDocument->AppendToFormulaTrack( this ); 1888 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY ); 1889 } 1890 } 1891 } 1892 } 1893 1894 1895 sal_Bool ScFormulaCell::IsDirtyOrInTableOpDirty() const 1896 { 1897 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp()); 1898 } 1899 1900 1901 void ScFormulaCell::SetErrCode( sal_uInt16 n ) 1902 { 1903 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is 1904 * used whether it is solely for transport of a simple result error and get 1905 * rid of that abuse. */ 1906 pCode->SetCodeError( n ); 1907 // Hard set errors are transported as result type value per convention, 1908 // e.g. via clipboard. ScFormulaResult::IsValue() and 1909 // ScFormulaResult::GetDouble() handle that. 1910 aResult.SetResultError( n ); 1911 } 1912 1913 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits ) 1914 { 1915 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL ) 1916 bDirty = sal_True; 1917 if ( nBits & RECALCMODE_ONLOAD_ONCE ) 1918 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import 1919 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL; 1920 } 1921 pCode->AddRecalcMode( nBits ); 1922 } 1923 1924 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell. 1925 void ScFormulaCell::GetURLResult( String& rURL, String& rCellText ) 1926 { 1927 String aCellString; 1928 1929 Color* pColor; 1930 1931 // Cell Text uses the Cell format while the URL uses 1932 // the default format for the type. 1933 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos ); 1934 SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); 1935 1936 if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1937 nCellFormat = GetStandardFormat( *pFormatter,nCellFormat ); 1938 1939 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER); 1940 1941 if ( IsValue() ) 1942 { 1943 double fValue = GetValue(); 1944 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor ); 1945 } 1946 else 1947 { 1948 GetString( aCellString ); 1949 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor ); 1950 } 1951 ScConstMatrixRef xMat( aResult.GetMatrix()); 1952 if (xMat) 1953 { 1954 ScMatValType nMatValType; 1955 // determine if the matrix result is a string or value. 1956 const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType); 1957 if (pMatVal) 1958 { 1959 if (!ScMatrix::IsValueType( nMatValType)) 1960 rURL = pMatVal->GetString(); 1961 else 1962 pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor ); 1963 } 1964 } 1965 1966 if(!rURL.Len()) 1967 { 1968 if(IsValue()) 1969 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor ); 1970 else 1971 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor ); 1972 } 1973 } 1974 1975 bool ScFormulaCell::IsMultilineResult() 1976 { 1977 if (!IsValue()) 1978 return aResult.IsMultiline(); 1979 return false; 1980 } 1981 1982 EditTextObject* ScFormulaCell::CreateURLObject() 1983 { 1984 String aCellText; 1985 String aURL; 1986 GetURLResult( aURL, aCellText ); 1987 1988 SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT); 1989 EditEngine& rEE = pDocument->GetEditEngine(); 1990 rEE.SetText( EMPTY_STRING ); 1991 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) ); 1992 1993 return rEE.CreateTextObject(); 1994 } 1995 1996 // ============================================================================ 1997 1998 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell ) 1999 { 2000 pCode = pCell->GetCode(); 2001 pCode->Reset(); 2002 aPos = pCell->aPos; 2003 } 2004 2005 sal_Bool lcl_ScDetectiveRefIter_SkipRef( ScToken* p ) 2006 { 2007 ScSingleRefData& rRef1 = p->GetSingleRef(); 2008 if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted() 2009 || !rRef1.Valid() ) 2010 return sal_True; 2011 if ( p->GetType() == svDoubleRef ) 2012 { 2013 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 2014 if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted() 2015 || !rRef2.Valid() ) 2016 return sal_True; 2017 } 2018 return sal_False; 2019 } 2020 2021 sal_Bool ScDetectiveRefIter::GetNextRef( ScRange& rRange ) 2022 { 2023 sal_Bool bRet = sal_False; 2024 2025 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2026 if (p) 2027 p->CalcAbsIfRel( aPos ); 2028 2029 while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) ) 2030 { 2031 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2032 if (p) 2033 p->CalcAbsIfRel( aPos ); 2034 } 2035 2036 if( p ) 2037 { 2038 SingleDoubleRefProvider aProv( *p ); 2039 rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab ); 2040 rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab ); 2041 bRet = sal_True; 2042 } 2043 2044 return bRet; 2045 } 2046 2047 // ============================================================================ 2048