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 aPos(0,0,0), 646 pValidRefToken( NULL ) 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 aPos( rPos ), 676 pValidRefToken( NULL ) 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 aPos( rPos ), 708 pValidRefToken( NULL ) 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 pValidRefToken( rCell.pValidRefToken ) 751 { 752 pCode = (rCell.pCode) ? rCell.pCode->Clone() : NULL; 753 754 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL ) 755 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos ); 756 757 // evtl. Fehler zuruecksetzen und neu kompilieren 758 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben 759 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten 760 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() ) 761 { 762 pCode->SetCodeError( 0 ); 763 bCompile = sal_True; 764 } 765 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference 766 sal_Bool bCompileLater = sal_False; 767 sal_Bool bClipMode = rCell.pDocument->IsClipboard(); 768 if( !bCompile ) 769 { // Name references with references and ColRowNames 770 pCode->Reset(); 771 ScToken* t; 772 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile ) 773 { 774 if ( t->GetOpCode() == ocExternalRef ) 775 { 776 // External name, cell, and area references. 777 bCompile = true; 778 } 779 else if ( t->GetType() == svIndex ) 780 { 781 ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() ); 782 if( pRangeData ) 783 { 784 if( pRangeData->HasReferences() ) 785 bCompile = sal_True; 786 } 787 else 788 bCompile = sal_True; // invalid reference! 789 } 790 else if ( t->GetOpCode() == ocColRowName ) 791 { 792 bCompile = sal_True; // new lookup needed 793 bCompileLater = bClipMode; 794 } 795 } 796 } 797 if( bCompile ) 798 { 799 if ( !bCompileLater && bClipMode ) 800 { 801 // Merging ranges needs the actual positions after UpdateReference. 802 // ColRowNames need new lookup after positions are adjusted. 803 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName); 804 } 805 if ( !bCompileLater ) 806 { 807 // bNoListening, not at all if in Clipboard/Undo, 808 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference. 809 CompileTokenArray( sal_True ); 810 } 811 } 812 813 if( nCloneFlags & SC_CLONECELL_STARTLISTENING ) 814 StartListeningTo( &rDoc ); 815 } 816 817 ScFormulaCell::~ScFormulaCell() 818 { 819 pDocument->RemoveFromFormulaTree( this ); 820 821 if (pDocument->HasExternalRefManager()) 822 pDocument->GetExternalRefManager()->removeRefCell(this); 823 824 delete pCode; 825 #ifdef DBG_UTIL 826 eCellType = CELLTYPE_DESTROYED; 827 #endif 828 DELETEZ(pValidRefToken); 829 } 830 831 void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer, 832 const FormulaGrammar::Grammar eGrammar ) const 833 { 834 if( pCode->GetCodeError() && !pCode->GetLen() ) 835 { 836 rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError())); 837 return; 838 } 839 else if( cMatrixFlag == MM_REFERENCE ) 840 { 841 // Reference to another cell that contains a matrix formula. 842 pCode->Reset(); 843 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 844 if( p ) 845 { 846 /* FIXME: original GetFormula() code obtained 847 * pCell only if (!this->IsInChangeTrack()), 848 * GetEnglishFormula() omitted that test. 849 * Can we live without in all cases? */ 850 ScBaseCell* pCell; 851 ScSingleRefData& rRef = p->GetSingleRef(); 852 rRef.CalcAbsIfRel( aPos ); 853 if ( rRef.Valid() ) 854 pCell = pDocument->GetCell( ScAddress( rRef.nCol, 855 rRef.nRow, rRef.nTab ) ); 856 else 857 pCell = NULL; 858 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) 859 { 860 ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar); 861 return; 862 } 863 else 864 { 865 ScCompiler aComp( pDocument, aPos, *pCode); 866 aComp.SetGrammar(eGrammar); 867 aComp.CreateStringFromTokenArray( rBuffer ); 868 } 869 } 870 else 871 { 872 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix"); 873 } 874 } 875 else 876 { 877 ScCompiler aComp( pDocument, aPos, *pCode); 878 aComp.SetGrammar(eGrammar); 879 aComp.CreateStringFromTokenArray( rBuffer ); 880 } 881 882 sal_Unicode ch('='); 883 rBuffer.insert( 0, &ch, 1 ); 884 if( cMatrixFlag ) 885 { 886 sal_Unicode ch2('{'); 887 rBuffer.insert( 0, &ch2, 1); 888 rBuffer.append( sal_Unicode('}')); 889 } 890 } 891 892 void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const 893 { 894 rtl::OUStringBuffer rBuffer( rFormula ); 895 GetFormula( rBuffer, eGrammar ); 896 rFormula = rBuffer; 897 } 898 899 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ) 900 { 901 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 902 Interpret(); 903 904 const ScMatrix* pMat = NULL; 905 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell && 906 ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0)) 907 pMat->GetDimensions( rCols, rRows ); 908 else 909 { 910 rCols = 0; 911 rRows = 0; 912 } 913 } 914 915 void ScFormulaCell::Compile( const String& rFormula, sal_Bool bNoListening, 916 const FormulaGrammar::Grammar eGrammar ) 917 { 918 //#118851#, the initialization code for pCode after it can not be gnored if it is still NULL 919 if ( pCode && pDocument->IsClipOrUndo() ) return; 920 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this ); 921 if ( bWasInFormulaTree ) 922 pDocument->RemoveFromFormulaTree( this ); 923 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein 924 if ( pCode ) 925 pCode->Clear(); 926 ScTokenArray* pCodeOld = pCode; 927 ScCompiler aComp( pDocument, aPos); 928 aComp.SetGrammar(eGrammar); 929 pCode = aComp.CompileString( rFormula ); 930 if ( pCodeOld ) 931 delete pCodeOld; 932 if( !pCode->GetCodeError() ) 933 { 934 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() && rFormula == aResult.GetHybridFormula() ) 935 { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray 936 if ( rFormula.GetChar(0) == '=' ) 937 pCode->AddBad( rFormula.GetBuffer() + 1 ); 938 else 939 pCode->AddBad( rFormula.GetBuffer() ); 940 } 941 bCompile = sal_True; 942 CompileTokenArray( bNoListening ); 943 } 944 else 945 { 946 bChanged = sal_True; 947 SetTextWidth( TEXTWIDTH_DIRTY ); 948 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 949 } 950 if ( bWasInFormulaTree ) 951 pDocument->PutInFormulaTree( this ); 952 } 953 954 955 void ScFormulaCell::CompileTokenArray( sal_Bool bNoListening ) 956 { 957 // Not already compiled? 958 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 959 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar); 960 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() ) 961 { 962 // RPN length may get changed 963 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this ); 964 if ( bWasInFormulaTree ) 965 pDocument->RemoveFromFormulaTree( this ); 966 967 // Loading from within filter? No listening yet! 968 if( pDocument->IsInsertingFromOtherDoc() ) 969 bNoListening = sal_True; 970 971 if( !bNoListening && pCode->GetCodeLen() ) 972 EndListeningTo( pDocument ); 973 ScCompiler aComp(pDocument, aPos, *pCode); 974 aComp.SetGrammar(pDocument->GetGrammar()); 975 bSubTotal = aComp.CompileTokenArray(); 976 if( !pCode->GetCodeError() ) 977 { 978 nFormatType = aComp.GetNumFormatType(); 979 nFormatIndex = 0; 980 bChanged = sal_True; 981 aResult.SetToken( NULL); 982 bCompile = sal_False; 983 if ( !bNoListening ) 984 StartListeningTo( pDocument ); 985 } 986 if ( bWasInFormulaTree ) 987 pDocument->PutInFormulaTree( this ); 988 } 989 } 990 991 992 void ScFormulaCell::CompileXML( ScProgress& rProgress ) 993 { 994 if ( cMatrixFlag == MM_REFERENCE ) 995 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula 996 // just establish listeners 997 StartListeningTo( pDocument ); 998 return ; 999 } 1000 1001 ScCompiler aComp( pDocument, aPos, *pCode); 1002 aComp.SetGrammar(eTempGrammar); 1003 String aFormula, aFormulaNmsp; 1004 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp ); 1005 pDocument->DecXMLImportedFormulaCount( aFormula.Len() ); 1006 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() ); 1007 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein 1008 if ( pCode ) 1009 pCode->Clear(); 1010 ScTokenArray* pCodeOld = pCode; 1011 pCode = aComp.CompileString( aFormula, aFormulaNmsp ); 1012 delete pCodeOld; 1013 if( !pCode->GetCodeError() ) 1014 { 1015 if ( !pCode->GetLen() ) 1016 { 1017 if ( aFormula.GetChar(0) == '=' ) 1018 pCode->AddBad( aFormula.GetBuffer() + 1 ); 1019 else 1020 pCode->AddBad( aFormula.GetBuffer() ); 1021 } 1022 bSubTotal = aComp.CompileTokenArray(); 1023 if( !pCode->GetCodeError() ) 1024 { 1025 nFormatType = aComp.GetNumFormatType(); 1026 nFormatIndex = 0; 1027 bChanged = sal_True; 1028 bCompile = sal_False; 1029 StartListeningTo( pDocument ); 1030 } 1031 } 1032 else 1033 { 1034 bChanged = sal_True; 1035 SetTextWidth( TEXTWIDTH_DIRTY ); 1036 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 1037 } 1038 1039 // Same as in Load: after loading, it must be known if ocMacro is in any formula 1040 // (for macro warning, CompileXML is called at the end of loading XML file) 1041 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) ) 1042 pDocument->SetHasMacroFunc( sal_True ); 1043 } 1044 1045 1046 void ScFormulaCell::CalcAfterLoad() 1047 { 1048 sal_Bool bNewCompiled = sal_False; 1049 // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis, 1050 // aber kein TokenArray 1051 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1052 { 1053 Compile( aResult.GetHybridFormula(), sal_True, eTempGrammar); 1054 aResult.SetToken( NULL); 1055 bDirty = sal_True; 1056 bNewCompiled = sal_True; 1057 } 1058 // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen 1059 // wurde, da die RangeNames erst jetzt existieren. 1060 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() ) 1061 { 1062 ScCompiler aComp(pDocument, aPos, *pCode); 1063 aComp.SetGrammar(pDocument->GetGrammar()); 1064 bSubTotal = aComp.CompileTokenArray(); 1065 nFormatType = aComp.GetNumFormatType(); 1066 nFormatIndex = 0; 1067 bDirty = sal_True; 1068 bCompile = sal_False; 1069 bNewCompiled = sal_True; 1070 } 1071 // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503 1072 // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib 1073 // bei einem fabs(-NAN) abstuerzt (#32739#) 1074 // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht 1075 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) 1076 { 1077 DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?"); 1078 aResult.SetResultError( errIllegalFPOperation ); 1079 bDirty = sal_True; 1080 } 1081 // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix, 1082 // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge 1083 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF && 1084 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() ) 1085 { 1086 cMatrixFlag = MM_FORMULA; 1087 SetMatColsRows( 1, 1); 1088 } 1089 // Muss die Zelle berechnet werden? 1090 // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann 1091 // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL 1092 if( !bNewCompiled || !pCode->GetCodeError() ) 1093 { 1094 StartListeningTo( pDocument ); 1095 if( !pCode->IsRecalcModeNormal() ) 1096 bDirty = sal_True; 1097 } 1098 if ( pCode->IsRecalcModeAlways() ) 1099 { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie 1100 // auch bei jedem F9 berechnet werden. 1101 bDirty = sal_True; 1102 } 1103 // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in 1104 // SetDirtyAfterLoad. 1105 } 1106 1107 1108 bool ScFormulaCell::MarkUsedExternalReferences() 1109 { 1110 return pCode && pDocument->MarkUsedExternalReferences( *pCode); 1111 } 1112 1113 1114 // FIXME: set to 0 1115 #define erDEBUGDOT 0 1116 // If set to 1, write output that's suitable for graphviz tools like dot. 1117 // Only node1 -> node2 entries are written, you'll have to manually surround 1118 // the file content with [strict] digraph name { ... } 1119 // The ``strict'' keyword might be necessary in case of multiple identical 1120 // paths like they occur in iterations, otherwise dot may consume too much 1121 // memory when generating the layout, or you'll get unreadable output. On the 1122 // other hand, information about recurring calculation is lost then. 1123 // Generates output only if variable nDebug is set in debugger, see below. 1124 // FIXME: currently doesn't cope with iterations and recursions. Code fragments 1125 // are a leftover from a previous debug session, meant as a pointer. 1126 #if erDEBUGDOT 1127 #include <cstdio> 1128 using ::std::fopen; 1129 using ::std::fprintf; 1130 #include <vector> 1131 static const char aDebugDotFile[] = "ttt_debug.dot"; 1132 #endif 1133 1134 void ScFormulaCell::Interpret() 1135 { 1136 1137 #if erDEBUGDOT 1138 static int nDebug = 0; 1139 static const int erDEBUGDOTRUN = 3; 1140 static FILE* pDebugFile = 0; 1141 static sal_Int32 nDebugRootCount = 0; 1142 static unsigned int nDebugPathCount = 0; 1143 static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID); 1144 static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID); 1145 typedef ::std::vector< ByteString > DebugVector; 1146 static DebugVector aDebugVec; 1147 class DebugElement 1148 { 1149 public: 1150 static void push( ScFormulaCell* pCell ) 1151 { 1152 aDebugThisPos = pCell->aPos; 1153 if (aDebugVec.empty()) 1154 { 1155 ByteString aR( "root_"); 1156 aR += ByteString::CreateFromInt32( ++nDebugRootCount); 1157 aDebugVec.push_back( aR); 1158 } 1159 String aStr; 1160 pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(), 1161 pCell->GetDocument()->GetAddressConvention() ); 1162 ByteString aB( aStr, RTL_TEXTENCODING_UTF8); 1163 aDebugVec.push_back( aB); 1164 } 1165 static void pop() 1166 { 1167 aDebugLastPos = aDebugThisPos; 1168 if (!aDebugVec.empty()) 1169 { 1170 aDebugVec.pop_back(); 1171 if (aDebugVec.size() == 1) 1172 { 1173 aDebugVec.pop_back(); 1174 aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID); 1175 } 1176 } 1177 } 1178 DebugElement( ScFormulaCell* p ) { push(p); } 1179 ~DebugElement() { pop(); } 1180 }; 1181 class DebugDot 1182 { 1183 public: 1184 static void out( const char* pColor ) 1185 { 1186 if (nDebug != erDEBUGDOTRUN) 1187 return; 1188 char pColorString[256]; 1189 sprintf( pColorString, (*pColor ? 1190 ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor, 1191 pColor); 1192 size_t n = aDebugVec.size(); 1193 fprintf( pDebugFile, 1194 "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n", 1195 aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(), 1196 ++nDebugPathCount, pColorString, n-1); 1197 fflush( pDebugFile); 1198 } 1199 }; 1200 #define erDEBUGDOT_OUT( p ) (DebugDot::out(p)) 1201 #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p)) 1202 #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop()) 1203 #else 1204 #define erDEBUGDOT_OUT( p ) 1205 #define erDEBUGDOT_ELEMENT_PUSH( p ) 1206 #define erDEBUGDOT_ELEMENT_POP() 1207 #endif 1208 1209 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn()) 1210 return; // no double/triple processing 1211 1212 //! HACK: 1213 // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen 1214 // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!! 1215 1216 if ( pDocument->IsInDdeLinkUpdate() ) 1217 return; 1218 1219 #if erDEBUGDOT 1220 // set nDebug=1 in debugger to init things 1221 if (nDebug == 1) 1222 { 1223 ++nDebug; 1224 pDebugFile = fopen( aDebugDotFile, "a"); 1225 if (!pDebugFile) 1226 nDebug = 0; 1227 else 1228 nDebug = erDEBUGDOTRUN; 1229 } 1230 // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output 1231 DebugElement aDebugElem( this); 1232 // set nDebug=5 in debugger to close output 1233 if (nDebug == 5) 1234 { 1235 nDebug = 0; 1236 fclose( pDebugFile); 1237 pDebugFile = 0; 1238 } 1239 #endif 1240 1241 if (bRunning) 1242 { 1243 1244 #if erDEBUGDOT 1245 if (!pDocument->GetRecursionHelper().IsDoingIteration() || 1246 aDebugThisPos != aDebugLastPos) 1247 erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" : 1248 (pDocument->GetRecursionHelper().GetIteration() ? "blue" : 1249 "red")); 1250 #endif 1251 1252 if (!pDocument->GetDocOptions().IsIter()) 1253 { 1254 aResult.SetResultError( errCircularReference ); 1255 return; 1256 } 1257 1258 if (aResult.GetResultError() == errCircularReference) 1259 aResult.SetResultError( 0 ); 1260 1261 // Start or add to iteration list. 1262 if (!pDocument->GetRecursionHelper().IsDoingIteration() || 1263 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell) 1264 pDocument->GetRecursionHelper().SetInIterationReturn( true); 1265 1266 return; 1267 } 1268 // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and 1269 // different entry point recursions. Would also lead to premature 1270 // convergence in iterations. 1271 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration == 1272 pDocument->GetRecursionHelper().GetIteration()) 1273 return ; 1274 1275 erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : ""); 1276 1277 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); 1278 sal_Bool bOldRunning = bRunning; 1279 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION) 1280 { 1281 bRunning = sal_True; 1282 rRecursionHelper.SetInRecursionReturn( true); 1283 } 1284 else 1285 { 1286 InterpretTail( SCITP_NORMAL); 1287 } 1288 1289 // While leaving a recursion or iteration stack, insert its cells to the 1290 // recursion list in reverse order. 1291 if (rRecursionHelper.IsInReturn()) 1292 { 1293 if (rRecursionHelper.GetRecursionCount() > 0 || 1294 !rRecursionHelper.IsDoingRecursion()) 1295 rRecursionHelper.Insert( this, bOldRunning, aResult); 1296 bool bIterationFromRecursion = false; 1297 bool bResumeIteration = false; 1298 do 1299 { 1300 if ((rRecursionHelper.IsInIterationReturn() && 1301 rRecursionHelper.GetRecursionCount() == 0 && 1302 !rRecursionHelper.IsDoingIteration()) || 1303 bIterationFromRecursion || bResumeIteration) 1304 { 1305 ScFormulaCell* pIterCell = this; // scope for debug convenience 1306 bool & rDone = rRecursionHelper.GetConvergingReference(); 1307 rDone = false; 1308 if (!bIterationFromRecursion && bResumeIteration) 1309 { 1310 bResumeIteration = false; 1311 // Resuming iteration expands the range. 1312 ScFormulaRecursionList::const_iterator aOldStart( 1313 rRecursionHelper.GetLastIterationStart()); 1314 rRecursionHelper.ResumeIteration(); 1315 // Mark new cells being in iteration. 1316 for (ScFormulaRecursionList::const_iterator aIter( 1317 rRecursionHelper.GetIterationStart()); aIter != 1318 aOldStart; ++aIter) 1319 { 1320 pIterCell = (*aIter).pCell; 1321 pIterCell->bIsIterCell = sal_True; 1322 } 1323 // Mark older cells dirty again, in case they converted 1324 // without accounting for all remaining cells in the circle 1325 // that weren't touched so far, e.g. conditional. Restore 1326 // backuped result. 1327 sal_uInt16 nIteration = rRecursionHelper.GetIteration(); 1328 for (ScFormulaRecursionList::const_iterator aIter( 1329 aOldStart); aIter != 1330 rRecursionHelper.GetIterationEnd(); ++aIter) 1331 { 1332 pIterCell = (*aIter).pCell; 1333 if (pIterCell->nSeenInIteration == nIteration) 1334 { 1335 if (!pIterCell->bDirty || aIter == aOldStart) 1336 { 1337 pIterCell->aResult = (*aIter).aPreviousResult; 1338 } 1339 --pIterCell->nSeenInIteration; 1340 } 1341 pIterCell->bDirty = sal_True; 1342 } 1343 } 1344 else 1345 { 1346 bResumeIteration = false; 1347 // Close circle once. 1348 rRecursionHelper.GetList().back().pCell->InterpretTail( 1349 SCITP_CLOSE_ITERATION_CIRCLE); 1350 // Start at 1, init things. 1351 rRecursionHelper.StartIteration(); 1352 // Mark all cells being in iteration. 1353 for (ScFormulaRecursionList::const_iterator aIter( 1354 rRecursionHelper.GetIterationStart()); aIter != 1355 rRecursionHelper.GetIterationEnd(); ++aIter) 1356 { 1357 pIterCell = (*aIter).pCell; 1358 pIterCell->bIsIterCell = sal_True; 1359 } 1360 } 1361 bIterationFromRecursion = false; 1362 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount(); 1363 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone; 1364 rRecursionHelper.IncIteration()) 1365 { 1366 rDone = true; 1367 for ( ScFormulaRecursionList::iterator aIter( 1368 rRecursionHelper.GetIterationStart()); aIter != 1369 rRecursionHelper.GetIterationEnd() && 1370 !rRecursionHelper.IsInReturn(); ++aIter) 1371 { 1372 pIterCell = (*aIter).pCell; 1373 if (pIterCell->IsDirtyOrInTableOpDirty() && 1374 rRecursionHelper.GetIteration() != 1375 pIterCell->GetSeenInIteration()) 1376 { 1377 (*aIter).aPreviousResult = pIterCell->aResult; 1378 pIterCell->InterpretTail( SCITP_FROM_ITERATION); 1379 } 1380 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty(); 1381 } 1382 if (rRecursionHelper.IsInReturn()) 1383 { 1384 bResumeIteration = true; 1385 break; // for 1386 // Don't increment iteration. 1387 } 1388 } 1389 if (!bResumeIteration) 1390 { 1391 if (rDone) 1392 { 1393 for (ScFormulaRecursionList::const_iterator aIter( 1394 rRecursionHelper.GetIterationStart()); 1395 aIter != rRecursionHelper.GetIterationEnd(); 1396 ++aIter) 1397 { 1398 pIterCell = (*aIter).pCell; 1399 pIterCell->bIsIterCell = sal_False; 1400 pIterCell->nSeenInIteration = 0; 1401 pIterCell->bRunning = (*aIter).bOldRunning; 1402 } 1403 } 1404 else 1405 { 1406 for (ScFormulaRecursionList::const_iterator aIter( 1407 rRecursionHelper.GetIterationStart()); 1408 aIter != rRecursionHelper.GetIterationEnd(); 1409 ++aIter) 1410 { 1411 pIterCell = (*aIter).pCell; 1412 pIterCell->bIsIterCell = sal_False; 1413 pIterCell->nSeenInIteration = 0; 1414 pIterCell->bRunning = (*aIter).bOldRunning; 1415 // If one cell didn't converge, all cells of this 1416 // circular dependency don't, no matter whether 1417 // single cells did. 1418 pIterCell->bDirty = sal_False; 1419 pIterCell->bTableOpDirty = sal_False; 1420 pIterCell->aResult.SetResultError( errNoConvergence); 1421 pIterCell->bChanged = sal_True; 1422 pIterCell->SetTextWidth( TEXTWIDTH_DIRTY); 1423 pIterCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN); 1424 } 1425 } 1426 // End this iteration and remove entries. 1427 rRecursionHelper.EndIteration(); 1428 bResumeIteration = rRecursionHelper.IsDoingIteration(); 1429 } 1430 } 1431 if (rRecursionHelper.IsInRecursionReturn() && 1432 rRecursionHelper.GetRecursionCount() == 0 && 1433 !rRecursionHelper.IsDoingRecursion()) 1434 { 1435 bIterationFromRecursion = false; 1436 // Iterate over cells known so far, start with the last cell 1437 // encountered, inserting new cells if another recursion limit 1438 // is reached. Repeat until solved. 1439 rRecursionHelper.SetDoingRecursion( true); 1440 do 1441 { 1442 rRecursionHelper.SetInRecursionReturn( false); 1443 for (ScFormulaRecursionList::const_iterator aIter( 1444 rRecursionHelper.GetStart()); 1445 !rRecursionHelper.IsInReturn() && aIter != 1446 rRecursionHelper.GetEnd(); ++aIter) 1447 { 1448 ScFormulaCell* pCell = (*aIter).pCell; 1449 if (pCell->IsDirtyOrInTableOpDirty()) 1450 { 1451 pCell->InterpretTail( SCITP_NORMAL); 1452 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell()) 1453 pCell->bRunning = (*aIter).bOldRunning; 1454 } 1455 } 1456 } while (rRecursionHelper.IsInRecursionReturn()); 1457 rRecursionHelper.SetDoingRecursion( false); 1458 if (rRecursionHelper.IsInIterationReturn()) 1459 { 1460 if (!bResumeIteration) 1461 bIterationFromRecursion = true; 1462 } 1463 else if (bResumeIteration || 1464 rRecursionHelper.IsDoingIteration()) 1465 rRecursionHelper.GetList().erase( 1466 rRecursionHelper.GetStart(), 1467 rRecursionHelper.GetLastIterationStart()); 1468 else 1469 rRecursionHelper.Clear(); 1470 } 1471 } while (bIterationFromRecursion || bResumeIteration); 1472 } 1473 } 1474 1475 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) 1476 { 1477 class RecursionCounter 1478 { 1479 ScRecursionHelper& rRec; 1480 bool bStackedInIteration; 1481 public: 1482 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r) 1483 { 1484 bStackedInIteration = rRec.IsDoingIteration(); 1485 if (bStackedInIteration) 1486 rRec.GetRecursionInIterationStack().push( p); 1487 rRec.IncRecursionCount(); 1488 } 1489 ~RecursionCounter() 1490 { 1491 rRec.DecRecursionCount(); 1492 if (bStackedInIteration) 1493 rRec.GetRecursionInIterationStack().pop(); 1494 } 1495 } aRecursionCounter( pDocument->GetRecursionHelper(), this); 1496 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration(); 1497 if( !pCode->GetCodeLen() && !pCode->GetCodeError() ) 1498 { 1499 // #i11719# no UPN and no error and no token code but result string present 1500 // => interpretation of this cell during name-compilation and unknown names 1501 // => can't exchange underlying code array in CompileTokenArray() / 1502 // Compile() because interpreter's token iterator would crash. 1503 // This should only be a temporary condition and, since we set an 1504 // error, if ran into it again we'd bump into the dirty-clearing 1505 // condition further down. 1506 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1507 { 1508 pCode->SetCodeError( errNoCode ); 1509 // This is worth an assertion; if encountered in daily work 1510 // documents we might need another solution. Or just confirm correctness. 1511 DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" ); 1512 return; 1513 } 1514 CompileTokenArray(); 1515 } 1516 1517 if( pCode->GetCodeLen() && pDocument ) 1518 { 1519 class StackCleaner 1520 { 1521 ScDocument* pDoc; 1522 ScInterpreter* pInt; 1523 public: 1524 StackCleaner( ScDocument* pD, ScInterpreter* pI ) 1525 : pDoc(pD), pInt(pI) 1526 {} 1527 ~StackCleaner() 1528 { 1529 delete pInt; 1530 pDoc->DecInterpretLevel(); 1531 } 1532 }; 1533 pDocument->IncInterpretLevel(); 1534 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode ); 1535 StackCleaner aStackCleaner( pDocument, p); 1536 sal_uInt16 nOldErrCode = aResult.GetResultError(); 1537 if ( nSeenInIteration == 0 ) 1538 { // Only the first time 1539 // With bChanged=sal_False, if a newly compiled cell has a result of 1540 // 0.0, no change is detected and the cell will not be repainted. 1541 // bChanged = sal_False; 1542 aResult.SetResultError( 0 ); 1543 } 1544 1545 switch ( aResult.GetResultError() ) 1546 { 1547 case errCircularReference : // will be determined again if so 1548 aResult.SetResultError( 0 ); 1549 break; 1550 } 1551 1552 sal_Bool bOldRunning = bRunning; 1553 bRunning = sal_True; 1554 p->Interpret(); 1555 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE) 1556 { 1557 if (nSeenInIteration > 0) 1558 --nSeenInIteration; // retry when iteration is resumed 1559 return; 1560 } 1561 bRunning = bOldRunning; 1562 //-> i120962: If the cell was applied reference formula, get the valid token 1563 if (pValidRefToken) 1564 DELETEZ(pValidRefToken); 1565 if (p->IsReferenceFunc() && p->GetLastStackRefToken()) 1566 pValidRefToken = static_cast<ScToken*>(p->GetLastStackRefToken()->Clone()); 1567 //<- i120962 1568 1569 // #i102616# For single-sheet saving consider only content changes, not format type, 1570 // because format type isn't set on loading (might be changed later) 1571 sal_Bool bContentChanged = sal_False; 1572 1573 // Do not create a HyperLink() cell if the formula results in an error. 1574 if( p->GetError() && pCode->IsHyperLink()) 1575 pCode->SetHyperLink(sal_False); 1576 1577 if( p->GetError() && p->GetError() != errCircularReference) 1578 { 1579 bDirty = sal_False; 1580 bTableOpDirty = sal_False; 1581 bChanged = sal_True; 1582 } 1583 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty()) 1584 { 1585 bool bIsValue = aResult.IsValue(); // the previous type 1586 // Did it converge? 1587 if ((bIsValue && p->GetResultType() == svDouble && fabs( 1588 p->GetNumResult() - aResult.GetDouble()) <= 1589 pDocument->GetDocOptions().GetIterEps()) || 1590 (!bIsValue && p->GetResultType() == svString && 1591 p->GetStringResult() == aResult.GetString())) 1592 { 1593 // A convergence in the first iteration doesn't necessarily 1594 // mean that it's done, it may be because not all related cells 1595 // of a circle changed their values yet. If the set really 1596 // converges it will do so also during the next iteration. This 1597 // fixes situations like of #i44115#. If this wasn't wanted an 1598 // initial "uncalculated" value would be needed for all cells 1599 // of a circular dependency => graph needed before calculation. 1600 if (nSeenInIteration > 1 || 1601 pDocument->GetDocOptions().GetIterCount() == 1) 1602 { 1603 bDirty = sal_False; 1604 bTableOpDirty = sal_False; 1605 } 1606 } 1607 } 1608 1609 // New error code? 1610 if( p->GetError() != nOldErrCode ) 1611 { 1612 bChanged = sal_True; 1613 // bContentChanged only has to be set if the file content would be changed 1614 if ( aResult.GetCellResultType() != svUnknown ) 1615 bContentChanged = sal_True; 1616 } 1617 // Different number format? 1618 if( nFormatType != p->GetRetFormatType() ) 1619 { 1620 nFormatType = p->GetRetFormatType(); 1621 bChanged = sal_True; 1622 } 1623 if( nFormatIndex != p->GetRetFormatIndex() ) 1624 { 1625 nFormatIndex = p->GetRetFormatIndex(); 1626 bChanged = sal_True; 1627 } 1628 1629 // In case of changes just obtain the result, no temporary and 1630 // comparison needed anymore. 1631 if (bChanged) 1632 { 1633 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving 1634 // Also handle special cases of initial results after loading. 1635 1636 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1637 { 1638 ScFormulaResult aNewResult( p->GetResultToken()); 1639 StackVar eOld = aResult.GetCellResultType(); 1640 StackVar eNew = aNewResult.GetCellResultType(); 1641 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) 1642 { 1643 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0 1644 // -> no change 1645 } 1646 else 1647 { 1648 if ( eOld == svHybridCell ) // string result from SetFormulaResultString? 1649 eOld = svString; // ScHybridCellToken has a valid GetString method 1650 1651 // #i106045# use approxEqual to compare with stored value 1652 bContentChanged = (eOld != eNew || 1653 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) || 1654 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1655 } 1656 } 1657 1658 aResult.SetToken( p->GetResultToken() ); 1659 } 1660 else 1661 { 1662 ScFormulaResult aNewResult( p->GetResultToken()); 1663 StackVar eOld = aResult.GetCellResultType(); 1664 StackVar eNew = aNewResult.GetCellResultType(); 1665 bChanged = (eOld != eNew || 1666 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) || 1667 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1668 1669 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged) 1670 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1671 { 1672 if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) || 1673 ( eOld == svHybridCell && eNew == svString && aResult.GetString() == aNewResult.GetString() ) || 1674 ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) ) 1675 { 1676 // no change, see above 1677 } 1678 else 1679 bContentChanged = sal_True; 1680 } 1681 1682 aResult.Assign( aNewResult); 1683 } 1684 1685 // Precision as shown? 1686 if ( aResult.IsValue() && !p->GetError() 1687 && pDocument->GetDocOptions().IsCalcAsShown() 1688 && nFormatType != NUMBERFORMAT_DATE 1689 && nFormatType != NUMBERFORMAT_TIME 1690 && nFormatType != NUMBERFORMAT_DATETIME ) 1691 { 1692 sal_uLong nFormat = pDocument->GetNumberFormat( aPos ); 1693 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1694 nFormat = nFormatIndex; 1695 if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1696 nFormat = ScGlobal::GetStandardFormat( 1697 *pDocument->GetFormatTable(), nFormat, nFormatType ); 1698 aResult.SetDouble( pDocument->RoundValueAsShown( 1699 aResult.GetDouble(), nFormat)); 1700 } 1701 if (eTailParam == SCITP_NORMAL) 1702 { 1703 bDirty = sal_False; 1704 bTableOpDirty = sal_False; 1705 } 1706 if( aResult.GetMatrix().Is() ) 1707 { 1708 // If the formula wasn't entered as a matrix formula, live on with 1709 // the upper left corner and let reference counting delete the matrix. 1710 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() ) 1711 aResult.SetToken( aResult.GetCellResultToken()); 1712 } 1713 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) 1714 { 1715 // Coded double error may occur via filter import. 1716 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble()); 1717 aResult.SetResultError( nErr); 1718 bChanged = bContentChanged = true; 1719 } 1720 if( bChanged ) 1721 { 1722 SetTextWidth( TEXTWIDTH_DIRTY ); 1723 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 1724 } 1725 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab())) 1726 { 1727 // pass bIgnoreLock=sal_True, because even if called from pending row height update, 1728 // a changed result must still reset the stream flag 1729 pDocument->SetStreamValid(aPos.Tab(), sal_False, sal_True); 1730 } 1731 if ( !pCode->IsRecalcModeAlways() ) 1732 pDocument->RemoveFromFormulaTree( this ); 1733 1734 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten) 1735 1736 if ( pCode->IsRecalcModeForced() ) 1737 { 1738 sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr( 1739 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue(); 1740 if ( nValidation ) 1741 { 1742 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation ); 1743 if ( pData && !pData->IsDataValid( this, aPos ) ) 1744 pData->DoCalcError( this ); 1745 } 1746 } 1747 1748 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren 1749 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent( 1750 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); 1751 } 1752 else 1753 { 1754 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen 1755 DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" ); 1756 bDirty = sal_False; 1757 bTableOpDirty = sal_False; 1758 } 1759 } 1760 1761 1762 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows ) 1763 { 1764 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst(); 1765 if (pMat) 1766 pMat->SetMatColsRows( nCols, nRows); 1767 else if (nCols || nRows) 1768 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows)); 1769 } 1770 1771 1772 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const 1773 { 1774 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken(); 1775 if (pMat) 1776 pMat->GetMatColsRows( nCols, nRows); 1777 else 1778 { 1779 nCols = 0; 1780 nRows = 0; 1781 } 1782 } 1783 1784 1785 sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const 1786 { 1787 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1788 return nFormatIndex; 1789 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again. 1790 if ( aResult.IsValue() ) 1791 return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType ); 1792 else 1793 return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType ); 1794 } 1795 1796 1797 void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint) 1798 { 1799 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() ) 1800 { 1801 const ScHint* p = PTR_CAST( ScHint, &rHint ); 1802 sal_uLong nHint = (p ? p->GetId() : 0); 1803 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY)) 1804 { 1805 sal_Bool bForceTrack = sal_False; 1806 if ( nHint & SC_HINT_TABLEOPDIRTY ) 1807 { 1808 bForceTrack = !bTableOpDirty; 1809 if ( !bTableOpDirty ) 1810 { 1811 pDocument->AddTableOpFormulaCell( this ); 1812 bTableOpDirty = sal_True; 1813 } 1814 } 1815 else 1816 { 1817 bForceTrack = !bDirty; 1818 bDirty = sal_True; 1819 } 1820 // #35962# Don't remove from FormulaTree to put in FormulaTrack to 1821 // put in FormulaTree again and again, only if necessary. 1822 // Any other means except RECALCMODE_ALWAYS by which a cell could 1823 // be in FormulaTree if it would notify other cells through 1824 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!? 1825 // #87866# Yes. The new TableOpDirty made it necessary to have a 1826 // forced mode where formulas may still be in FormulaTree from 1827 // TableOpDirty but have to notify dependents for normal dirty. 1828 if ( (bForceTrack || !pDocument->IsInFormulaTree( this ) 1829 || pCode->IsRecalcModeAlways()) 1830 && !pDocument->IsInFormulaTrack( this ) ) 1831 pDocument->AppendToFormulaTrack( this ); 1832 } 1833 } 1834 } 1835 1836 void ScFormulaCell::SetDirty() 1837 { 1838 if ( !IsInChangeTrack() ) 1839 { 1840 if ( pDocument->GetHardRecalcState() ) 1841 bDirty = sal_True; 1842 else 1843 { 1844 // Mehrfach-FormulaTracking in Load und in CompileAll 1845 // nach CopyScenario und CopyBlockFromClip vermeiden. 1846 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=sal_False 1847 // setzen, z.B. in CompileTokenArray 1848 if ( !bDirty || !pDocument->IsInFormulaTree( this ) ) 1849 { 1850 bDirty = sal_True; 1851 pDocument->AppendToFormulaTrack( this ); 1852 pDocument->TrackFormulas(); 1853 } 1854 } 1855 1856 if (pDocument->IsStreamValid(aPos.Tab())) 1857 pDocument->SetStreamValid(aPos.Tab(), sal_False); 1858 } 1859 } 1860 1861 void ScFormulaCell::SetDirtyAfterLoad() 1862 { 1863 bDirty = sal_True; 1864 if ( !pDocument->GetHardRecalcState() ) 1865 pDocument->PutInFormulaTree( this ); 1866 } 1867 1868 void ScFormulaCell::SetTableOpDirty() 1869 { 1870 if ( !IsInChangeTrack() ) 1871 { 1872 if ( pDocument->GetHardRecalcState() ) 1873 bTableOpDirty = sal_True; 1874 else 1875 { 1876 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) ) 1877 { 1878 if ( !bTableOpDirty ) 1879 { 1880 pDocument->AddTableOpFormulaCell( this ); 1881 bTableOpDirty = sal_True; 1882 } 1883 pDocument->AppendToFormulaTrack( this ); 1884 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY ); 1885 } 1886 } 1887 } 1888 } 1889 1890 1891 sal_Bool ScFormulaCell::IsDirtyOrInTableOpDirty() const 1892 { 1893 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp()); 1894 } 1895 1896 1897 void ScFormulaCell::SetErrCode( sal_uInt16 n ) 1898 { 1899 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is 1900 * used whether it is solely for transport of a simple result error and get 1901 * rid of that abuse. */ 1902 pCode->SetCodeError( n ); 1903 // Hard set errors are transported as result type value per convention, 1904 // e.g. via clipboard. ScFormulaResult::IsValue() and 1905 // ScFormulaResult::GetDouble() handle that. 1906 aResult.SetResultError( n ); 1907 } 1908 1909 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits ) 1910 { 1911 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL ) 1912 bDirty = sal_True; 1913 if ( nBits & RECALCMODE_ONLOAD_ONCE ) 1914 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import 1915 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL; 1916 } 1917 pCode->AddRecalcMode( nBits ); 1918 } 1919 1920 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell. 1921 void ScFormulaCell::GetURLResult( String& rURL, String& rCellText ) 1922 { 1923 String aCellString; 1924 1925 Color* pColor; 1926 1927 // Cell Text uses the Cell format while the URL uses 1928 // the default format for the type. 1929 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos ); 1930 SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); 1931 1932 if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1933 nCellFormat = GetStandardFormat( *pFormatter,nCellFormat ); 1934 1935 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER); 1936 1937 if ( IsValue() ) 1938 { 1939 double fValue = GetValue(); 1940 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor ); 1941 } 1942 else 1943 { 1944 GetString( aCellString ); 1945 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor ); 1946 } 1947 ScConstMatrixRef xMat( aResult.GetMatrix()); 1948 if (xMat) 1949 { 1950 ScMatValType nMatValType; 1951 // determine if the matrix result is a string or value. 1952 const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType); 1953 if (pMatVal) 1954 { 1955 if (!ScMatrix::IsValueType( nMatValType)) 1956 rURL = pMatVal->GetString(); 1957 else 1958 pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor ); 1959 } 1960 } 1961 1962 if(!rURL.Len()) 1963 { 1964 if(IsValue()) 1965 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor ); 1966 else 1967 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor ); 1968 } 1969 } 1970 1971 bool ScFormulaCell::IsMultilineResult() 1972 { 1973 if (!IsValue()) 1974 return aResult.IsMultiline(); 1975 return false; 1976 } 1977 1978 EditTextObject* ScFormulaCell::CreateURLObject() 1979 { 1980 String aCellText; 1981 String aURL; 1982 GetURLResult( aURL, aCellText ); 1983 1984 SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT); 1985 EditEngine& rEE = pDocument->GetEditEngine(); 1986 rEE.SetText( EMPTY_STRING ); 1987 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) ); 1988 1989 return rEE.CreateTextObject(); 1990 } 1991 1992 // ============================================================================ 1993 1994 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell ) 1995 { 1996 pCode = pCell->GetCode(); 1997 pCode->Reset(); 1998 aPos = pCell->aPos; 1999 } 2000 2001 sal_Bool lcl_ScDetectiveRefIter_SkipRef( ScToken* p ) 2002 { 2003 ScSingleRefData& rRef1 = p->GetSingleRef(); 2004 if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted() 2005 || !rRef1.Valid() ) 2006 return sal_True; 2007 if ( p->GetType() == svDoubleRef ) 2008 { 2009 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 2010 if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted() 2011 || !rRef2.Valid() ) 2012 return sal_True; 2013 } 2014 return sal_False; 2015 } 2016 2017 sal_Bool ScDetectiveRefIter::GetNextRef( ScRange& rRange ) 2018 { 2019 sal_Bool bRet = sal_False; 2020 2021 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2022 if (p) 2023 p->CalcAbsIfRel( aPos ); 2024 2025 while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) ) 2026 { 2027 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2028 if (p) 2029 p->CalcAbsIfRel( aPos ); 2030 } 2031 2032 if( p ) 2033 { 2034 SingleDoubleRefProvider aProv( *p ); 2035 rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab ); 2036 rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab ); 2037 bRet = sal_True; 2038 } 2039 2040 return bRet; 2041 } 2042 2043 // ============================================================================ 2044