1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 // INCLUDE --------------------------------------------------------------- 32 33 #include <svl/zforlist.hxx> 34 35 #include "scitems.hxx" 36 #include "attrib.hxx" 37 #include "cell.hxx" 38 #include "compiler.hxx" 39 #include "interpre.hxx" 40 #include "document.hxx" 41 #include "scmatrix.hxx" 42 #include "dociter.hxx" 43 #include "docoptio.hxx" 44 #include "rechead.hxx" 45 #include "rangenam.hxx" 46 #include "brdcst.hxx" 47 #include "ddelink.hxx" 48 #include "validat.hxx" 49 #include "progress.hxx" 50 #include "editutil.hxx" 51 #include "recursionhelper.hxx" 52 #include "postit.hxx" 53 #include "externalrefmgr.hxx" 54 #include <editeng/editobj.hxx> 55 #include <svl/intitem.hxx> 56 #include <editeng/flditem.hxx> 57 #include <svl/broadcast.hxx> 58 59 using namespace formula; 60 // More or less arbitrary, of course all recursions must fit into available 61 // stack space (which is what on all systems we don't know yet?). Choosing a 62 // lower value may be better than trying a much higher value that also isn't 63 // sufficient but temporarily leads to high memory consumption. On the other 64 // hand, if the value fits all recursions, execution is quicker as no resumes 65 // are necessary. Could be made a configurable option. 66 // Allow for a year's calendar (366). 67 const sal_uInt16 MAXRECURSION = 400; 68 69 // STATIC DATA ----------------------------------------------------------- 70 71 #ifdef USE_MEMPOOL 72 // MemPools auf 4k Boundaries - 64 Bytes ausrichten 73 const sal_uInt16 nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell); 74 const sal_uInt16 nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell); 75 const sal_uInt16 nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell); 76 const sal_uInt16 nMemPoolNoteCell = (0x1000 - 64) / sizeof(ScNoteCell); 77 IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell ) 78 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell ) 79 IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell ) 80 IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell ) 81 #endif 82 83 // ============================================================================ 84 85 ScBaseCell::ScBaseCell( CellType eNewType ) : 86 mpNote( 0 ), 87 mpBroadcaster( 0 ), 88 nTextWidth( TEXTWIDTH_DIRTY ), 89 eCellType( sal::static_int_cast<sal_uInt8>(eNewType) ), 90 nScriptType( SC_SCRIPTTYPE_UNKNOWN ) 91 { 92 } 93 94 ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) : 95 mpNote( 0 ), 96 mpBroadcaster( 0 ), 97 nTextWidth( rCell.nTextWidth ), 98 eCellType( rCell.eCellType ), 99 nScriptType( SC_SCRIPTTYPE_UNKNOWN ) 100 { 101 } 102 103 ScBaseCell::~ScBaseCell() 104 { 105 delete mpNote; 106 delete mpBroadcaster; 107 DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" ); 108 } 109 110 namespace { 111 112 ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) 113 { 114 switch( rSrcCell.GetCellType() ) 115 { 116 case CELLTYPE_VALUE: 117 return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) ); 118 case CELLTYPE_STRING: 119 return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) ); 120 case CELLTYPE_EDIT: 121 return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc ); 122 case CELLTYPE_FORMULA: 123 return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags ); 124 case CELLTYPE_NOTE: 125 return new ScNoteCell; 126 default:; 127 } 128 DBG_ERROR( "lclCloneCell - unknown cell type" ); 129 return 0; 130 } 131 132 } // namespace 133 134 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const 135 { 136 // notes will not be cloned -> cell address only needed for formula cells 137 ScAddress aDestPos; 138 if( eCellType == CELLTYPE_FORMULA ) 139 aDestPos = static_cast< const ScFormulaCell* >( this )->aPos; 140 return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags ); 141 } 142 143 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const 144 { 145 return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); 146 } 147 148 ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const 149 { 150 ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); 151 if( mpNote ) 152 { 153 if( !pNewCell ) 154 pNewCell = new ScNoteCell; 155 bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0; 156 pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) ); 157 } 158 return pNewCell; 159 } 160 161 void ScBaseCell::Delete() 162 { 163 DeleteNote(); 164 switch (eCellType) 165 { 166 case CELLTYPE_VALUE: 167 delete (ScValueCell*) this; 168 break; 169 case CELLTYPE_STRING: 170 delete (ScStringCell*) this; 171 break; 172 case CELLTYPE_EDIT: 173 delete (ScEditCell*) this; 174 break; 175 case CELLTYPE_FORMULA: 176 delete (ScFormulaCell*) this; 177 break; 178 case CELLTYPE_NOTE: 179 delete (ScNoteCell*) this; 180 break; 181 default: 182 DBG_ERROR("Unbekannter Zellentyp"); 183 break; 184 } 185 } 186 187 bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const 188 { 189 return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote); 190 } 191 192 void ScBaseCell::TakeNote( ScPostIt* pNote ) 193 { 194 delete mpNote; 195 mpNote = pNote; 196 } 197 198 ScPostIt* ScBaseCell::ReleaseNote() 199 { 200 ScPostIt* pNote = mpNote; 201 mpNote = 0; 202 return pNote; 203 } 204 205 void ScBaseCell::DeleteNote() 206 { 207 DELETEZ( mpNote ); 208 } 209 210 void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster ) 211 { 212 delete mpBroadcaster; 213 mpBroadcaster = pBroadcaster; 214 } 215 216 SvtBroadcaster* ScBaseCell::ReleaseBroadcaster() 217 { 218 SvtBroadcaster* pBroadcaster = mpBroadcaster; 219 mpBroadcaster = 0; 220 return pBroadcaster; 221 } 222 223 void ScBaseCell::DeleteBroadcaster() 224 { 225 DELETEZ( mpBroadcaster ); 226 } 227 228 ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc ) 229 { 230 if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND ) 231 return new ScEditCell( rString, pDoc ); 232 else 233 return new ScStringCell( rString ); 234 } 235 236 void ScBaseCell::StartListeningTo( ScDocument* pDoc ) 237 { 238 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() 239 && !pDoc->GetNoListening() 240 && !((ScFormulaCell*)this)->IsInChangeTrack() 241 ) 242 { 243 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert... 244 245 ScFormulaCell* pFormCell = (ScFormulaCell*)this; 246 ScTokenArray* pArr = pFormCell->GetCode(); 247 if( pArr->IsRecalcModeAlways() ) 248 pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); 249 else 250 { 251 pArr->Reset(); 252 ScToken* t; 253 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) 254 { 255 StackVar eType = t->GetType(); 256 ScSingleRefData& rRef1 = t->GetSingleRef(); 257 ScSingleRefData& rRef2 = (eType == svDoubleRef ? 258 t->GetDoubleRef().Ref2 : rRef1); 259 switch( eType ) 260 { 261 case svSingleRef: 262 rRef1.CalcAbsIfRel( pFormCell->aPos ); 263 if ( rRef1.Valid() ) 264 { 265 pDoc->StartListeningCell( 266 ScAddress( rRef1.nCol, 267 rRef1.nRow, 268 rRef1.nTab ), pFormCell ); 269 } 270 break; 271 case svDoubleRef: 272 t->CalcAbsIfRel( pFormCell->aPos ); 273 if ( rRef1.Valid() && rRef2.Valid() ) 274 { 275 if ( t->GetOpCode() == ocColRowNameAuto ) 276 { // automagically 277 if ( rRef1.IsColRel() ) 278 { // ColName 279 pDoc->StartListeningArea( ScRange ( 280 rRef1.nCol, 281 rRef1.nRow, 282 rRef1.nTab, 283 rRef2.nCol, 284 MAXROW, 285 rRef2.nTab ), pFormCell ); 286 } 287 else 288 { // RowName 289 pDoc->StartListeningArea( ScRange ( 290 rRef1.nCol, 291 rRef1.nRow, 292 rRef1.nTab, 293 MAXCOL, 294 rRef2.nRow, 295 rRef2.nTab ), pFormCell ); 296 } 297 } 298 else 299 { 300 pDoc->StartListeningArea( ScRange ( 301 rRef1.nCol, 302 rRef1.nRow, 303 rRef1.nTab, 304 rRef2.nCol, 305 rRef2.nRow, 306 rRef2.nTab ), pFormCell ); 307 } 308 } 309 break; 310 default: 311 ; // nothing 312 } 313 } 314 } 315 pFormCell->SetNeedsListening( sal_False); 316 } 317 } 318 319 // pArr gesetzt -> Referenzen von anderer Zelle nehmen 320 // dann muss auch aPos uebergeben werden! 321 322 void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, 323 ScAddress aPos ) 324 { 325 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() 326 && !((ScFormulaCell*)this)->IsInChangeTrack() 327 ) 328 { 329 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert... 330 331 ScFormulaCell* pFormCell = (ScFormulaCell*)this; 332 if( pFormCell->GetCode()->IsRecalcModeAlways() ) 333 pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); 334 else 335 { 336 if (!pArr) 337 { 338 pArr = pFormCell->GetCode(); 339 aPos = pFormCell->aPos; 340 } 341 pArr->Reset(); 342 ScToken* t; 343 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) 344 { 345 StackVar eType = t->GetType(); 346 ScSingleRefData& rRef1 = t->GetSingleRef(); 347 ScSingleRefData& rRef2 = (eType == svDoubleRef ? 348 t->GetDoubleRef().Ref2 : rRef1); 349 switch( eType ) 350 { 351 case svSingleRef: 352 rRef1.CalcAbsIfRel( aPos ); 353 if ( rRef1.Valid() ) 354 { 355 pDoc->EndListeningCell( 356 ScAddress( rRef1.nCol, 357 rRef1.nRow, 358 rRef1.nTab ), pFormCell ); 359 } 360 break; 361 case svDoubleRef: 362 t->CalcAbsIfRel( aPos ); 363 if ( rRef1.Valid() && rRef2.Valid() ) 364 { 365 if ( t->GetOpCode() == ocColRowNameAuto ) 366 { // automagically 367 if ( rRef1.IsColRel() ) 368 { // ColName 369 pDoc->EndListeningArea( ScRange ( 370 rRef1.nCol, 371 rRef1.nRow, 372 rRef1.nTab, 373 rRef2.nCol, 374 MAXROW, 375 rRef2.nTab ), pFormCell ); 376 } 377 else 378 { // RowName 379 pDoc->EndListeningArea( ScRange ( 380 rRef1.nCol, 381 rRef1.nRow, 382 rRef1.nTab, 383 MAXCOL, 384 rRef2.nRow, 385 rRef2.nTab ), pFormCell ); 386 } 387 } 388 else 389 { 390 pDoc->EndListeningArea( ScRange ( 391 rRef1.nCol, 392 rRef1.nRow, 393 rRef1.nTab, 394 rRef2.nCol, 395 rRef2.nRow, 396 rRef2.nTab ), pFormCell ); 397 } 398 } 399 break; 400 default: 401 ; // nothing 402 } 403 } 404 } 405 } 406 } 407 408 409 sal_uInt16 ScBaseCell::GetErrorCode() const 410 { 411 switch ( eCellType ) 412 { 413 case CELLTYPE_FORMULA : 414 return ((ScFormulaCell*)this)->GetErrCode(); 415 default: 416 return 0; 417 } 418 } 419 420 421 sal_Bool ScBaseCell::HasEmptyData() const 422 { 423 switch ( eCellType ) 424 { 425 case CELLTYPE_NOTE : 426 return sal_True; 427 case CELLTYPE_FORMULA : 428 return ((ScFormulaCell*)this)->IsEmpty(); 429 default: 430 return sal_False; 431 } 432 } 433 434 435 sal_Bool ScBaseCell::HasValueData() const 436 { 437 switch ( eCellType ) 438 { 439 case CELLTYPE_VALUE : 440 return sal_True; 441 case CELLTYPE_FORMULA : 442 return ((ScFormulaCell*)this)->IsValue(); 443 default: 444 return sal_False; 445 } 446 } 447 448 449 sal_Bool ScBaseCell::HasStringData() const 450 { 451 switch ( eCellType ) 452 { 453 case CELLTYPE_STRING : 454 case CELLTYPE_EDIT : 455 return sal_True; 456 case CELLTYPE_FORMULA : 457 return !((ScFormulaCell*)this)->IsValue(); 458 default: 459 return sal_False; 460 } 461 } 462 463 String ScBaseCell::GetStringData() const 464 { 465 String aStr; 466 switch ( eCellType ) 467 { 468 case CELLTYPE_STRING: 469 ((const ScStringCell*)this)->GetString( aStr ); 470 break; 471 case CELLTYPE_EDIT: 472 ((const ScEditCell*)this)->GetString( aStr ); 473 break; 474 case CELLTYPE_FORMULA: 475 ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const 476 break; 477 } 478 return aStr; 479 } 480 481 // static 482 sal_Bool ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 ) 483 { 484 CellType eType1 = CELLTYPE_NONE; 485 CellType eType2 = CELLTYPE_NONE; 486 if ( pCell1 ) 487 { 488 eType1 = pCell1->GetCellType(); 489 if (eType1 == CELLTYPE_EDIT) 490 eType1 = CELLTYPE_STRING; 491 else if (eType1 == CELLTYPE_NOTE) 492 eType1 = CELLTYPE_NONE; 493 } 494 if ( pCell2 ) 495 { 496 eType2 = pCell2->GetCellType(); 497 if (eType2 == CELLTYPE_EDIT) 498 eType2 = CELLTYPE_STRING; 499 else if (eType2 == CELLTYPE_NOTE) 500 eType2 = CELLTYPE_NONE; 501 } 502 if ( eType1 != eType2 ) 503 return sal_False; 504 505 switch ( eType1 ) // beide Typen gleich 506 { 507 case CELLTYPE_NONE: // beide leer 508 return sal_True; 509 case CELLTYPE_VALUE: // wirklich Value-Zellen 510 return ( ((const ScValueCell*)pCell1)->GetValue() == 511 ((const ScValueCell*)pCell2)->GetValue() ); 512 case CELLTYPE_STRING: // String oder Edit 513 { 514 String aText1; 515 if ( pCell1->GetCellType() == CELLTYPE_STRING ) 516 ((const ScStringCell*)pCell1)->GetString(aText1); 517 else 518 ((const ScEditCell*)pCell1)->GetString(aText1); 519 String aText2; 520 if ( pCell2->GetCellType() == CELLTYPE_STRING ) 521 ((const ScStringCell*)pCell2)->GetString(aText2); 522 else 523 ((const ScEditCell*)pCell2)->GetString(aText2); 524 return ( aText1 == aText2 ); 525 } 526 case CELLTYPE_FORMULA: 527 { 528 //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!! 529 //! Vergleichsfunktion an der Formelzelle ??? 530 //! Abfrage mit ScColumn::SwapRow zusammenfassen! 531 532 ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode(); 533 ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode(); 534 535 if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN 536 { 537 sal_Bool bEqual = sal_True; 538 sal_uInt16 nLen = pCode1->GetLen(); 539 FormulaToken** ppToken1 = pCode1->GetArray(); 540 FormulaToken** ppToken2 = pCode2->GetArray(); 541 for (sal_uInt16 i=0; i<nLen; i++) 542 if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) ) 543 { 544 bEqual = sal_False; 545 break; 546 } 547 548 if (bEqual) 549 return sal_True; 550 } 551 552 return sal_False; // unterschiedlich lang oder unterschiedliche Tokens 553 } 554 default: 555 DBG_ERROR("huch, was fuer Zellen???"); 556 } 557 return sal_False; 558 } 559 560 // ============================================================================ 561 562 ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) : 563 ScBaseCell( CELLTYPE_NOTE ) 564 { 565 TakeBroadcaster( pBC ); 566 } 567 568 ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) : 569 ScBaseCell( CELLTYPE_NOTE ) 570 { 571 TakeNote( pNote ); 572 TakeBroadcaster( pBC ); 573 } 574 575 #ifdef DBG_UTIL 576 ScNoteCell::~ScNoteCell() 577 { 578 eCellType = CELLTYPE_DESTROYED; 579 } 580 #endif 581 582 // ============================================================================ 583 584 ScValueCell::ScValueCell() : 585 ScBaseCell( CELLTYPE_VALUE ), 586 mfValue( 0.0 ) 587 { 588 } 589 590 ScValueCell::ScValueCell( double fValue ) : 591 ScBaseCell( CELLTYPE_VALUE ), 592 mfValue( fValue ) 593 { 594 } 595 596 #ifdef DBG_UTIL 597 ScValueCell::~ScValueCell() 598 { 599 eCellType = CELLTYPE_DESTROYED; 600 } 601 #endif 602 603 // ============================================================================ 604 605 ScStringCell::ScStringCell() : 606 ScBaseCell( CELLTYPE_STRING ) 607 { 608 } 609 610 ScStringCell::ScStringCell( const String& rString ) : 611 ScBaseCell( CELLTYPE_STRING ), 612 maString( rString.intern() ) 613 { 614 } 615 616 #ifdef DBG_UTIL 617 ScStringCell::~ScStringCell() 618 { 619 eCellType = CELLTYPE_DESTROYED; 620 } 621 #endif 622 623 // ============================================================================ 624 625 // 626 // ScFormulaCell 627 // 628 629 ScFormulaCell::ScFormulaCell() : 630 ScBaseCell( CELLTYPE_FORMULA ), 631 eTempGrammar( FormulaGrammar::GRAM_DEFAULT), 632 pCode( NULL ), 633 pDocument( NULL ), 634 pPrevious(0), 635 pNext(0), 636 pPreviousTrack(0), 637 pNextTrack(0), 638 nFormatIndex(0), 639 nFormatType( NUMBERFORMAT_NUMBER ), 640 nSeenInIteration(0), 641 cMatrixFlag ( MM_NONE ), 642 bDirty( sal_False ), 643 bChanged( sal_False ), 644 bRunning( sal_False ), 645 bCompile( sal_False ), 646 bSubTotal( sal_False ), 647 bIsIterCell( sal_False ), 648 bInChangeTrack( sal_False ), 649 bTableOpDirty( sal_False ), 650 bNeedListening( sal_False ), 651 aPos(0,0,0) 652 { 653 } 654 655 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, 656 const String& rFormula, 657 const FormulaGrammar::Grammar eGrammar, 658 sal_uInt8 cMatInd ) : 659 ScBaseCell( CELLTYPE_FORMULA ), 660 eTempGrammar( eGrammar), 661 pCode( NULL ), 662 pDocument( pDoc ), 663 pPrevious(0), 664 pNext(0), 665 pPreviousTrack(0), 666 pNextTrack(0), 667 nFormatIndex(0), 668 nFormatType( NUMBERFORMAT_NUMBER ), 669 nSeenInIteration(0), 670 cMatrixFlag ( cMatInd ), 671 bDirty( sal_True ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0 672 bChanged( sal_False ), 673 bRunning( sal_False ), 674 bCompile( sal_False ), 675 bSubTotal( sal_False ), 676 bIsIterCell( sal_False ), 677 bInChangeTrack( sal_False ), 678 bTableOpDirty( sal_False ), 679 bNeedListening( sal_False ), 680 aPos( rPos ) 681 { 682 Compile( rFormula, sal_True, eGrammar ); // bNoListening, Insert does that 683 } 684 685 // Wird von den Importfiltern verwendet 686 687 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, 688 const ScTokenArray* pArr, 689 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) : 690 ScBaseCell( CELLTYPE_FORMULA ), 691 eTempGrammar( eGrammar), 692 pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ), 693 pDocument( pDoc ), 694 pPrevious(0), 695 pNext(0), 696 pPreviousTrack(0), 697 pNextTrack(0), 698 nFormatIndex(0), 699 nFormatType( NUMBERFORMAT_NUMBER ), 700 nSeenInIteration(0), 701 cMatrixFlag ( cInd ), 702 bDirty( NULL != pArr ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0 703 bChanged( sal_False ), 704 bRunning( sal_False ), 705 bCompile( sal_False ), 706 bSubTotal( sal_False ), 707 bIsIterCell( sal_False ), 708 bInChangeTrack( sal_False ), 709 bTableOpDirty( sal_False ), 710 bNeedListening( sal_False ), 711 aPos( rPos ) 712 { 713 // UPN-Array erzeugen 714 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() ) 715 { 716 ScCompiler aComp( pDocument, aPos, *pCode); 717 aComp.SetGrammar(eTempGrammar); 718 bSubTotal = aComp.CompileTokenArray(); 719 nFormatType = aComp.GetNumFormatType(); 720 } 721 else 722 { 723 pCode->Reset(); 724 if ( pCode->GetNextOpCodeRPN( ocSubTotal ) ) 725 bSubTotal = sal_True; 726 } 727 } 728 729 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) : 730 ScBaseCell( rCell ), 731 SvtListener(), 732 aResult( rCell.aResult ), 733 eTempGrammar( rCell.eTempGrammar), 734 pDocument( &rDoc ), 735 pPrevious(0), 736 pNext(0), 737 pPreviousTrack(0), 738 pNextTrack(0), 739 nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ), 740 nFormatType( rCell.nFormatType ), 741 nSeenInIteration(0), 742 cMatrixFlag ( rCell.cMatrixFlag ), 743 bDirty( rCell.bDirty ), 744 bChanged( rCell.bChanged ), 745 bRunning( sal_False ), 746 bCompile( rCell.bCompile ), 747 bSubTotal( rCell.bSubTotal ), 748 bIsIterCell( sal_False ), 749 bInChangeTrack( sal_False ), 750 bTableOpDirty( sal_False ), 751 bNeedListening( sal_False ), 752 aPos( rPos ) 753 { 754 pCode = rCell.pCode->Clone(); 755 756 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL ) 757 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos ); 758 759 // evtl. Fehler zuruecksetzen und neu kompilieren 760 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben 761 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten 762 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() ) 763 { 764 pCode->SetCodeError( 0 ); 765 bCompile = sal_True; 766 } 767 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference 768 sal_Bool bCompileLater = sal_False; 769 sal_Bool bClipMode = rCell.pDocument->IsClipboard(); 770 if( !bCompile ) 771 { // Name references with references and ColRowNames 772 pCode->Reset(); 773 ScToken* t; 774 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile ) 775 { 776 if ( t->GetOpCode() == ocExternalRef ) 777 { 778 // External name, cell, and area references. 779 bCompile = true; 780 } 781 else if ( t->GetType() == svIndex ) 782 { 783 ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() ); 784 if( pRangeData ) 785 { 786 if( pRangeData->HasReferences() ) 787 bCompile = sal_True; 788 } 789 else 790 bCompile = sal_True; // invalid reference! 791 } 792 else if ( t->GetOpCode() == ocColRowName ) 793 { 794 bCompile = sal_True; // new lookup needed 795 bCompileLater = bClipMode; 796 } 797 } 798 } 799 if( bCompile ) 800 { 801 if ( !bCompileLater && bClipMode ) 802 { 803 // Merging ranges needs the actual positions after UpdateReference. 804 // ColRowNames need new lookup after positions are adjusted. 805 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName); 806 } 807 if ( !bCompileLater ) 808 { 809 // bNoListening, not at all if in Clipboard/Undo, 810 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference. 811 CompileTokenArray( sal_True ); 812 } 813 } 814 815 if( nCloneFlags & SC_CLONECELL_STARTLISTENING ) 816 StartListeningTo( &rDoc ); 817 } 818 819 ScFormulaCell::~ScFormulaCell() 820 { 821 pDocument->RemoveFromFormulaTree( this ); 822 823 if (pDocument->HasExternalRefManager()) 824 pDocument->GetExternalRefManager()->removeRefCell(this); 825 826 delete pCode; 827 #ifdef DBG_UTIL 828 eCellType = CELLTYPE_DESTROYED; 829 #endif 830 } 831 832 void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer, 833 const FormulaGrammar::Grammar eGrammar ) const 834 { 835 if( pCode->GetCodeError() && !pCode->GetLen() ) 836 { 837 rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError())); 838 return; 839 } 840 else if( cMatrixFlag == MM_REFERENCE ) 841 { 842 // Reference to another cell that contains a matrix formula. 843 pCode->Reset(); 844 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 845 if( p ) 846 { 847 /* FIXME: original GetFormula() code obtained 848 * pCell only if (!this->IsInChangeTrack()), 849 * GetEnglishFormula() omitted that test. 850 * Can we live without in all cases? */ 851 ScBaseCell* pCell; 852 ScSingleRefData& rRef = p->GetSingleRef(); 853 rRef.CalcAbsIfRel( aPos ); 854 if ( rRef.Valid() ) 855 pCell = pDocument->GetCell( ScAddress( rRef.nCol, 856 rRef.nRow, rRef.nTab ) ); 857 else 858 pCell = NULL; 859 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) 860 { 861 ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar); 862 return; 863 } 864 else 865 { 866 ScCompiler aComp( pDocument, aPos, *pCode); 867 aComp.SetGrammar(eGrammar); 868 aComp.CreateStringFromTokenArray( rBuffer ); 869 } 870 } 871 else 872 { 873 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix"); 874 } 875 } 876 else 877 { 878 ScCompiler aComp( pDocument, aPos, *pCode); 879 aComp.SetGrammar(eGrammar); 880 aComp.CreateStringFromTokenArray( rBuffer ); 881 } 882 883 sal_Unicode ch('='); 884 rBuffer.insert( 0, &ch, 1 ); 885 if( cMatrixFlag ) 886 { 887 sal_Unicode ch2('{'); 888 rBuffer.insert( 0, &ch2, 1); 889 rBuffer.append( sal_Unicode('}')); 890 } 891 } 892 893 void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const 894 { 895 rtl::OUStringBuffer rBuffer( rFormula ); 896 GetFormula( rBuffer, eGrammar ); 897 rFormula = rBuffer; 898 } 899 900 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ) 901 { 902 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 903 Interpret(); 904 905 const ScMatrix* pMat = NULL; 906 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell && 907 ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0)) 908 pMat->GetDimensions( rCols, rRows ); 909 else 910 { 911 rCols = 0; 912 rRows = 0; 913 } 914 } 915 916 void ScFormulaCell::Compile( const String& rFormula, sal_Bool bNoListening, 917 const FormulaGrammar::Grammar eGrammar ) 918 { 919 if ( 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 1563 // #i102616# For single-sheet saving consider only content changes, not format type, 1564 // because format type isn't set on loading (might be changed later) 1565 sal_Bool bContentChanged = sal_False; 1566 1567 // Do not create a HyperLink() cell if the formula results in an error. 1568 if( p->GetError() && pCode->IsHyperLink()) 1569 pCode->SetHyperLink(sal_False); 1570 1571 if( p->GetError() && p->GetError() != errCircularReference) 1572 { 1573 bDirty = sal_False; 1574 bTableOpDirty = sal_False; 1575 bChanged = sal_True; 1576 } 1577 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty()) 1578 { 1579 bool bIsValue = aResult.IsValue(); // the previous type 1580 // Did it converge? 1581 if ((bIsValue && p->GetResultType() == svDouble && fabs( 1582 p->GetNumResult() - aResult.GetDouble()) <= 1583 pDocument->GetDocOptions().GetIterEps()) || 1584 (!bIsValue && p->GetResultType() == svString && 1585 p->GetStringResult() == aResult.GetString())) 1586 { 1587 // A convergence in the first iteration doesn't necessarily 1588 // mean that it's done, it may be because not all related cells 1589 // of a circle changed their values yet. If the set really 1590 // converges it will do so also during the next iteration. This 1591 // fixes situations like of #i44115#. If this wasn't wanted an 1592 // initial "uncalculated" value would be needed for all cells 1593 // of a circular dependency => graph needed before calculation. 1594 if (nSeenInIteration > 1 || 1595 pDocument->GetDocOptions().GetIterCount() == 1) 1596 { 1597 bDirty = sal_False; 1598 bTableOpDirty = sal_False; 1599 } 1600 } 1601 } 1602 1603 // New error code? 1604 if( p->GetError() != nOldErrCode ) 1605 { 1606 bChanged = sal_True; 1607 // bContentChanged only has to be set if the file content would be changed 1608 if ( aResult.GetCellResultType() != svUnknown ) 1609 bContentChanged = sal_True; 1610 } 1611 // Different number format? 1612 if( nFormatType != p->GetRetFormatType() ) 1613 { 1614 nFormatType = p->GetRetFormatType(); 1615 bChanged = sal_True; 1616 } 1617 if( nFormatIndex != p->GetRetFormatIndex() ) 1618 { 1619 nFormatIndex = p->GetRetFormatIndex(); 1620 bChanged = sal_True; 1621 } 1622 1623 // In case of changes just obtain the result, no temporary and 1624 // comparison needed anymore. 1625 if (bChanged) 1626 { 1627 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving 1628 // Also handle special cases of initial results after loading. 1629 1630 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1631 { 1632 ScFormulaResult aNewResult( p->GetResultToken()); 1633 StackVar eOld = aResult.GetCellResultType(); 1634 StackVar eNew = aNewResult.GetCellResultType(); 1635 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) 1636 { 1637 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0 1638 // -> no change 1639 } 1640 else 1641 { 1642 if ( eOld == svHybridCell ) // string result from SetFormulaResultString? 1643 eOld = svString; // ScHybridCellToken has a valid GetString method 1644 1645 // #i106045# use approxEqual to compare with stored value 1646 bContentChanged = (eOld != eNew || 1647 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) || 1648 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1649 } 1650 } 1651 1652 aResult.SetToken( p->GetResultToken() ); 1653 } 1654 else 1655 { 1656 ScFormulaResult aNewResult( p->GetResultToken()); 1657 StackVar eOld = aResult.GetCellResultType(); 1658 StackVar eNew = aNewResult.GetCellResultType(); 1659 bChanged = (eOld != eNew || 1660 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) || 1661 (eNew == svString && aResult.GetString() != aNewResult.GetString())); 1662 1663 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged) 1664 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) 1665 { 1666 if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) || 1667 ( eOld == svHybridCell && eNew == svString && aResult.GetString() == aNewResult.GetString() ) || 1668 ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) ) 1669 { 1670 // no change, see above 1671 } 1672 else 1673 bContentChanged = sal_True; 1674 } 1675 1676 aResult.Assign( aNewResult); 1677 } 1678 1679 // Precision as shown? 1680 if ( aResult.IsValue() && !p->GetError() 1681 && pDocument->GetDocOptions().IsCalcAsShown() 1682 && nFormatType != NUMBERFORMAT_DATE 1683 && nFormatType != NUMBERFORMAT_TIME 1684 && nFormatType != NUMBERFORMAT_DATETIME ) 1685 { 1686 sal_uLong nFormat = pDocument->GetNumberFormat( aPos ); 1687 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1688 nFormat = nFormatIndex; 1689 if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1690 nFormat = ScGlobal::GetStandardFormat( 1691 *pDocument->GetFormatTable(), nFormat, nFormatType ); 1692 aResult.SetDouble( pDocument->RoundValueAsShown( 1693 aResult.GetDouble(), nFormat)); 1694 } 1695 if (eTailParam == SCITP_NORMAL) 1696 { 1697 bDirty = sal_False; 1698 bTableOpDirty = sal_False; 1699 } 1700 if( aResult.GetMatrix().Is() ) 1701 { 1702 // If the formula wasn't entered as a matrix formula, live on with 1703 // the upper left corner and let reference counting delete the matrix. 1704 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() ) 1705 aResult.SetToken( aResult.GetCellResultToken()); 1706 } 1707 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) 1708 { 1709 // Coded double error may occur via filter import. 1710 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble()); 1711 aResult.SetResultError( nErr); 1712 bChanged = bContentChanged = true; 1713 } 1714 if( bChanged ) 1715 { 1716 SetTextWidth( TEXTWIDTH_DIRTY ); 1717 SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); 1718 } 1719 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab())) 1720 { 1721 // pass bIgnoreLock=sal_True, because even if called from pending row height update, 1722 // a changed result must still reset the stream flag 1723 pDocument->SetStreamValid(aPos.Tab(), sal_False, sal_True); 1724 } 1725 if ( !pCode->IsRecalcModeAlways() ) 1726 pDocument->RemoveFromFormulaTree( this ); 1727 1728 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten) 1729 1730 if ( pCode->IsRecalcModeForced() ) 1731 { 1732 sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr( 1733 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue(); 1734 if ( nValidation ) 1735 { 1736 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation ); 1737 if ( pData && !pData->IsDataValid( this, aPos ) ) 1738 pData->DoCalcError( this ); 1739 } 1740 } 1741 1742 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren 1743 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent( 1744 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); 1745 } 1746 else 1747 { 1748 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen 1749 DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" ); 1750 bDirty = sal_False; 1751 bTableOpDirty = sal_False; 1752 } 1753 } 1754 1755 1756 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows ) 1757 { 1758 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst(); 1759 if (pMat) 1760 pMat->SetMatColsRows( nCols, nRows); 1761 else if (nCols || nRows) 1762 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows)); 1763 } 1764 1765 1766 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const 1767 { 1768 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken(); 1769 if (pMat) 1770 pMat->GetMatColsRows( nCols, nRows); 1771 else 1772 { 1773 nCols = 0; 1774 nRows = 0; 1775 } 1776 } 1777 1778 1779 sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const 1780 { 1781 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1782 return nFormatIndex; 1783 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again. 1784 if ( aResult.IsValue() ) 1785 return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType ); 1786 else 1787 return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType ); 1788 } 1789 1790 1791 void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint) 1792 { 1793 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() ) 1794 { 1795 const ScHint* p = PTR_CAST( ScHint, &rHint ); 1796 sal_uLong nHint = (p ? p->GetId() : 0); 1797 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY)) 1798 { 1799 sal_Bool bForceTrack = sal_False; 1800 if ( nHint & SC_HINT_TABLEOPDIRTY ) 1801 { 1802 bForceTrack = !bTableOpDirty; 1803 if ( !bTableOpDirty ) 1804 { 1805 pDocument->AddTableOpFormulaCell( this ); 1806 bTableOpDirty = sal_True; 1807 } 1808 } 1809 else 1810 { 1811 bForceTrack = !bDirty; 1812 bDirty = sal_True; 1813 } 1814 // #35962# Don't remove from FormulaTree to put in FormulaTrack to 1815 // put in FormulaTree again and again, only if necessary. 1816 // Any other means except RECALCMODE_ALWAYS by which a cell could 1817 // be in FormulaTree if it would notify other cells through 1818 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!? 1819 // #87866# Yes. The new TableOpDirty made it necessary to have a 1820 // forced mode where formulas may still be in FormulaTree from 1821 // TableOpDirty but have to notify dependents for normal dirty. 1822 if ( (bForceTrack || !pDocument->IsInFormulaTree( this ) 1823 || pCode->IsRecalcModeAlways()) 1824 && !pDocument->IsInFormulaTrack( this ) ) 1825 pDocument->AppendToFormulaTrack( this ); 1826 } 1827 } 1828 } 1829 1830 void ScFormulaCell::SetDirty() 1831 { 1832 if ( !IsInChangeTrack() ) 1833 { 1834 if ( pDocument->GetHardRecalcState() ) 1835 bDirty = sal_True; 1836 else 1837 { 1838 // Mehrfach-FormulaTracking in Load und in CompileAll 1839 // nach CopyScenario und CopyBlockFromClip vermeiden. 1840 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=sal_False 1841 // setzen, z.B. in CompileTokenArray 1842 if ( !bDirty || !pDocument->IsInFormulaTree( this ) ) 1843 { 1844 bDirty = sal_True; 1845 pDocument->AppendToFormulaTrack( this ); 1846 pDocument->TrackFormulas(); 1847 } 1848 } 1849 1850 if (pDocument->IsStreamValid(aPos.Tab())) 1851 pDocument->SetStreamValid(aPos.Tab(), sal_False); 1852 } 1853 } 1854 1855 void ScFormulaCell::SetDirtyAfterLoad() 1856 { 1857 bDirty = sal_True; 1858 if ( !pDocument->GetHardRecalcState() ) 1859 pDocument->PutInFormulaTree( this ); 1860 } 1861 1862 void ScFormulaCell::SetTableOpDirty() 1863 { 1864 if ( !IsInChangeTrack() ) 1865 { 1866 if ( pDocument->GetHardRecalcState() ) 1867 bTableOpDirty = sal_True; 1868 else 1869 { 1870 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) ) 1871 { 1872 if ( !bTableOpDirty ) 1873 { 1874 pDocument->AddTableOpFormulaCell( this ); 1875 bTableOpDirty = sal_True; 1876 } 1877 pDocument->AppendToFormulaTrack( this ); 1878 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY ); 1879 } 1880 } 1881 } 1882 } 1883 1884 1885 sal_Bool ScFormulaCell::IsDirtyOrInTableOpDirty() const 1886 { 1887 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp()); 1888 } 1889 1890 1891 void ScFormulaCell::SetErrCode( sal_uInt16 n ) 1892 { 1893 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is 1894 * used whether it is solely for transport of a simple result error and get 1895 * rid of that abuse. */ 1896 pCode->SetCodeError( n ); 1897 // Hard set errors are transported as result type value per convention, 1898 // e.g. via clipboard. ScFormulaResult::IsValue() and 1899 // ScFormulaResult::GetDouble() handle that. 1900 aResult.SetResultError( n ); 1901 } 1902 1903 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits ) 1904 { 1905 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL ) 1906 bDirty = sal_True; 1907 if ( nBits & RECALCMODE_ONLOAD_ONCE ) 1908 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import 1909 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL; 1910 } 1911 pCode->AddRecalcMode( nBits ); 1912 } 1913 1914 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell. 1915 void ScFormulaCell::GetURLResult( String& rURL, String& rCellText ) 1916 { 1917 String aCellString; 1918 1919 Color* pColor; 1920 1921 // Cell Text uses the Cell format while the URL uses 1922 // the default format for the type. 1923 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos ); 1924 SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); 1925 1926 if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) 1927 nCellFormat = GetStandardFormat( *pFormatter,nCellFormat ); 1928 1929 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER); 1930 1931 if ( IsValue() ) 1932 { 1933 double fValue = GetValue(); 1934 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor ); 1935 } 1936 else 1937 { 1938 GetString( aCellString ); 1939 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor ); 1940 } 1941 ScConstMatrixRef xMat( aResult.GetMatrix()); 1942 if (xMat) 1943 { 1944 ScMatValType nMatValType; 1945 // determine if the matrix result is a string or value. 1946 const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType); 1947 if (pMatVal) 1948 { 1949 if (!ScMatrix::IsValueType( nMatValType)) 1950 rURL = pMatVal->GetString(); 1951 else 1952 pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor ); 1953 } 1954 } 1955 1956 if(!rURL.Len()) 1957 { 1958 if(IsValue()) 1959 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor ); 1960 else 1961 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor ); 1962 } 1963 } 1964 1965 bool ScFormulaCell::IsMultilineResult() 1966 { 1967 if (!IsValue()) 1968 return aResult.IsMultiline(); 1969 return false; 1970 } 1971 1972 EditTextObject* ScFormulaCell::CreateURLObject() 1973 { 1974 String aCellText; 1975 String aURL; 1976 GetURLResult( aURL, aCellText ); 1977 1978 SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT); 1979 EditEngine& rEE = pDocument->GetEditEngine(); 1980 rEE.SetText( EMPTY_STRING ); 1981 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) ); 1982 1983 return rEE.CreateTextObject(); 1984 } 1985 1986 // ============================================================================ 1987 1988 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell ) 1989 { 1990 pCode = pCell->GetCode(); 1991 pCode->Reset(); 1992 aPos = pCell->aPos; 1993 } 1994 1995 sal_Bool lcl_ScDetectiveRefIter_SkipRef( ScToken* p ) 1996 { 1997 ScSingleRefData& rRef1 = p->GetSingleRef(); 1998 if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted() 1999 || !rRef1.Valid() ) 2000 return sal_True; 2001 if ( p->GetType() == svDoubleRef ) 2002 { 2003 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 2004 if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted() 2005 || !rRef2.Valid() ) 2006 return sal_True; 2007 } 2008 return sal_False; 2009 } 2010 2011 sal_Bool ScDetectiveRefIter::GetNextRef( ScRange& rRange ) 2012 { 2013 sal_Bool bRet = sal_False; 2014 2015 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2016 if (p) 2017 p->CalcAbsIfRel( aPos ); 2018 2019 while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) ) 2020 { 2021 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 2022 if (p) 2023 p->CalcAbsIfRel( aPos ); 2024 } 2025 2026 if( p ) 2027 { 2028 SingleDoubleRefProvider aProv( *p ); 2029 rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab ); 2030 rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab ); 2031 bRet = sal_True; 2032 } 2033 2034 return bRet; 2035 } 2036 2037 // ============================================================================ 2038