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 <vcl/svapp.hxx> 34 35 #if defined( WNT ) && defined( erBEEP ) 36 #include <svwin.h> 37 #define erBEEPER() Beep( 666, 66 ) 38 #else 39 #define erBEEPER() 40 #endif 41 42 #include "document.hxx" 43 #include "brdcst.hxx" 44 #include "bcaslot.hxx" 45 #include "cell.hxx" 46 #include "formula/errorcodes.hxx" // errCircularReference 47 #include "scerrors.hxx" 48 #include "docoptio.hxx" 49 #include "refupdat.hxx" 50 #include "table.hxx" 51 #include "progress.hxx" 52 #include "scmod.hxx" // SC_MOD 53 #include "inputopt.hxx" // GetExpandRefs 54 #include "conditio.hxx" 55 #include "sheetevents.hxx" 56 #include <tools/shl.hxx> 57 58 59 #include "globstr.hrc" 60 61 extern const ScFormulaCell* pLastFormulaTreeTop; // cellform.cxx Err527 WorkAround 62 63 // STATIC DATA ----------------------------------------------------------- 64 65 #ifdef erDEBUG 66 sal_uLong erCountBCAInserts = 0; 67 sal_uLong erCountBCAFinds = 0; 68 #endif 69 70 // ----------------------------------------------------------------------- 71 72 void ScDocument::StartListeningArea( const ScRange& rRange, 73 SvtListener* pListener 74 ) 75 { 76 if ( pBASM ) 77 pBASM->StartListeningArea( rRange, pListener ); 78 } 79 80 81 void ScDocument::EndListeningArea( const ScRange& rRange, 82 SvtListener* pListener 83 ) 84 { 85 if ( pBASM ) 86 pBASM->EndListeningArea( rRange, pListener ); 87 } 88 89 90 void ScDocument::Broadcast( sal_uLong nHint, const ScAddress& rAddr, 91 ScBaseCell* pCell 92 ) 93 { 94 if ( !pBASM ) 95 return ; // Clipboard or Undo 96 ScHint aHint( nHint, rAddr, pCell ); 97 Broadcast( aHint ); 98 } 99 100 101 void ScDocument::Broadcast( const ScHint& rHint ) 102 { 103 if ( !pBASM ) 104 return ; // Clipboard or Undo 105 if ( !nHardRecalcState ) 106 { 107 ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast 108 sal_Bool bIsBroadcasted = sal_False; 109 ScBaseCell* pCell = rHint.GetCell(); 110 if ( pCell ) 111 { 112 SvtBroadcaster* pBC = pCell->GetBroadcaster(); 113 if ( pBC ) 114 { 115 pBC->Broadcast( rHint ); 116 bIsBroadcasted = sal_True; 117 } 118 } 119 if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted ) 120 TrackFormulas( rHint.GetId() ); 121 } 122 123 // Repaint fuer bedingte Formate mit relativen Referenzen: 124 if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS ) 125 pCondFormList->SourceChanged( rHint.GetAddress() ); 126 127 if ( rHint.GetAddress() != BCA_BRDCST_ALWAYS ) 128 { 129 SCTAB nTab = rHint.GetAddress().Tab(); 130 if (pTab[nTab] && pTab[nTab]->IsStreamValid()) 131 pTab[nTab]->SetStreamValid(sal_False); 132 } 133 } 134 135 136 void ScDocument::AreaBroadcast( const ScHint& rHint ) 137 { 138 if ( !pBASM ) 139 return ; // Clipboard or Undo 140 if ( !nHardRecalcState ) 141 { 142 ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast 143 if ( pBASM->AreaBroadcast( rHint ) ) 144 TrackFormulas( rHint.GetId() ); 145 } 146 147 // Repaint fuer bedingte Formate mit relativen Referenzen: 148 if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS ) 149 pCondFormList->SourceChanged( rHint.GetAddress() ); 150 } 151 152 153 void ScDocument::AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) 154 { 155 if ( !pBASM ) 156 return ; // Clipboard or Undo 157 if ( !nHardRecalcState ) 158 { 159 ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast 160 if ( pBASM->AreaBroadcastInRange( rRange, rHint ) ) 161 TrackFormulas( rHint.GetId() ); 162 } 163 164 // Repaint for conditional formats containing relative references. 165 //! This is _THE_ bottle neck! 166 if ( pCondFormList ) 167 { 168 SCCOL nCol; 169 SCROW nRow; 170 SCTAB nTab; 171 SCCOL nCol1; 172 SCROW nRow1; 173 SCTAB nTab1; 174 SCCOL nCol2; 175 SCROW nRow2; 176 SCTAB nTab2; 177 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 178 ScAddress aAddress( rRange.aStart ); 179 for ( nTab = nTab1; nTab <= nTab2; ++nTab ) 180 { 181 aAddress.SetTab( nTab ); 182 for ( nCol = nCol1; nCol <= nCol2; ++nCol ) 183 { 184 aAddress.SetCol( nCol ); 185 for ( nRow = nRow1; nRow <= nRow2; ++nRow ) 186 { 187 aAddress.SetRow( nRow ); 188 pCondFormList->SourceChanged( aAddress ); 189 } 190 } 191 } 192 } 193 } 194 195 196 void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange ) 197 { 198 if ( pBASM ) 199 pBASM->DelBroadcastAreasInRange( rRange ); 200 } 201 202 void ScDocument::StartListeningCell( const ScAddress& rAddress, 203 SvtListener* pListener ) 204 { 205 DBG_ASSERT(pListener, "StartListeningCell: pListener Null"); 206 SCTAB nTab = rAddress.Tab(); 207 if (pTab[nTab]) 208 pTab[nTab]->StartListening( rAddress, pListener ); 209 } 210 211 void ScDocument::EndListeningCell( const ScAddress& rAddress, 212 SvtListener* pListener ) 213 { 214 DBG_ASSERT(pListener, "EndListeningCell: pListener Null"); 215 SCTAB nTab = rAddress.Tab(); 216 if (pTab[nTab]) 217 pTab[nTab]->EndListening( rAddress, pListener ); 218 } 219 220 221 void ScDocument::PutInFormulaTree( ScFormulaCell* pCell ) 222 { 223 DBG_ASSERT( pCell, "PutInFormulaTree: pCell Null" ); 224 RemoveFromFormulaTree( pCell ); 225 // anhaengen 226 if ( pEOFormulaTree ) 227 pEOFormulaTree->SetNext( pCell ); 228 else 229 pFormulaTree = pCell; // kein Ende, kein Anfang.. 230 pCell->SetPrevious( pEOFormulaTree ); 231 pCell->SetNext( 0 ); 232 pEOFormulaTree = pCell; 233 nFormulaCodeInTree += pCell->GetCode()->GetCodeLen(); 234 } 235 236 237 void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell ) 238 { 239 DBG_ASSERT( pCell, "RemoveFromFormulaTree: pCell Null" ); 240 ScFormulaCell* pPrev = pCell->GetPrevious(); 241 // wenn die Zelle die erste oder sonstwo ist 242 if ( pPrev || pFormulaTree == pCell ) 243 { 244 ScFormulaCell* pNext = pCell->GetNext(); 245 if ( pPrev ) 246 pPrev->SetNext( pNext ); // gibt Vorlaeufer 247 else 248 pFormulaTree = pNext; // ist erste Zelle 249 if ( pNext ) 250 pNext->SetPrevious( pPrev ); // gibt Nachfolger 251 else 252 pEOFormulaTree = pPrev; // ist letzte Zelle 253 pCell->SetPrevious( 0 ); 254 pCell->SetNext( 0 ); 255 sal_uInt16 nRPN = pCell->GetCode()->GetCodeLen(); 256 if ( nFormulaCodeInTree >= nRPN ) 257 nFormulaCodeInTree -= nRPN; 258 else 259 { 260 DBG_ERRORFILE( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" ); 261 nFormulaCodeInTree = 0; 262 } 263 } 264 else if ( !pFormulaTree && nFormulaCodeInTree ) 265 { 266 DBG_ERRORFILE( "!pFormulaTree && nFormulaCodeInTree != 0" ); 267 nFormulaCodeInTree = 0; 268 } 269 } 270 271 272 sal_Bool ScDocument::IsInFormulaTree( ScFormulaCell* pCell ) const 273 { 274 return pCell->GetPrevious() || pFormulaTree == pCell; 275 } 276 277 278 void ScDocument::CalcFormulaTree( sal_Bool bOnlyForced, sal_Bool bNoProgress ) 279 { 280 DBG_ASSERT( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" ); 281 // never ever recurse into this, might end up lost in infinity 282 if ( IsCalculatingFormulaTree() ) 283 return ; 284 bCalculatingFormulaTree = sal_True; 285 286 SetForcedFormulaPending( sal_False ); 287 sal_Bool bOldIdleDisabled = IsIdleDisabled(); 288 DisableIdle( sal_True ); 289 sal_Bool bOldAutoCalc = GetAutoCalc(); 290 //! _nicht_ SetAutoCalc( sal_True ) weil das evtl. CalcFormulaTree( sal_True ) 291 //! aufruft, wenn vorher disabled war und bHasForcedFormulas gesetzt ist 292 bAutoCalc = sal_True; 293 if ( nHardRecalcState ) 294 CalcAll(); 295 else 296 { 297 ScFormulaCell* pCell = pFormulaTree; 298 while ( pCell ) 299 { 300 if ( pCell->GetDirty() ) 301 pCell = pCell->GetNext(); // alles klar 302 else 303 { 304 if ( pCell->GetCode()->IsRecalcModeAlways() ) 305 { 306 // pCell wird im SetDirty neu angehaengt! 307 ScFormulaCell* pNext = pCell->GetNext(); 308 pCell->SetDirty(); 309 // falls pNext==0 und neue abhaengige hinten angehaengt 310 // wurden, so macht das nichts, da die alle bDirty sind 311 pCell = pNext; 312 } 313 else 314 { // andere simpel berechnen 315 pCell->SetDirtyVar(); 316 pCell = pCell->GetNext(); 317 } 318 } 319 } 320 sal_Bool bProgress = !bOnlyForced && nFormulaCodeInTree && !bNoProgress; 321 if ( bProgress ) 322 ScProgress::CreateInterpretProgress( this, sal_True ); 323 324 pCell = pFormulaTree; 325 ScFormulaCell* pLastNoGood = 0; 326 while ( pCell ) 327 { 328 // Interpret setzt bDirty zurueck und callt Remove, auch der referierten! 329 // bei RECALCMODE_ALWAYS bleibt die Zelle 330 if ( bOnlyForced ) 331 { 332 if ( pCell->GetCode()->IsRecalcModeForced() ) 333 pCell->Interpret(); 334 } 335 else 336 { 337 pCell->Interpret(); 338 } 339 if ( pCell->GetPrevious() || pCell == pFormulaTree ) 340 { // (IsInFormulaTree(pCell)) kein Remove gewesen => next 341 pLastNoGood = pCell; 342 pCell = pCell->GetNext(); 343 } 344 else 345 { 346 if ( pFormulaTree ) 347 { 348 if ( pFormulaTree->GetDirty() && !bOnlyForced ) 349 { 350 pCell = pFormulaTree; 351 pLastNoGood = 0; 352 } 353 else 354 { 355 // IsInFormulaTree(pLastNoGood) 356 if ( pLastNoGood && (pLastNoGood->GetPrevious() || 357 pLastNoGood == pFormulaTree) ) 358 pCell = pLastNoGood->GetNext(); 359 else 360 { 361 pCell = pFormulaTree; 362 while ( pCell && !pCell->GetDirty() ) 363 pCell = pCell->GetNext(); 364 if ( pCell ) 365 pLastNoGood = pCell->GetPrevious(); 366 } 367 } 368 } 369 else 370 pCell = 0; 371 } 372 if ( ScProgress::IsUserBreak() ) 373 pCell = 0; 374 } 375 if ( bProgress ) 376 ScProgress::DeleteInterpretProgress(); 377 } 378 bAutoCalc = bOldAutoCalc; 379 DisableIdle( bOldIdleDisabled ); 380 bCalculatingFormulaTree = sal_False; 381 } 382 383 384 void ScDocument::ClearFormulaTree() 385 { 386 ScFormulaCell* pCell; 387 ScFormulaCell* pTree = pFormulaTree; 388 while ( pTree ) 389 { 390 pCell = pTree; 391 pTree = pCell->GetNext(); 392 if ( !pCell->GetCode()->IsRecalcModeAlways() ) 393 RemoveFromFormulaTree( pCell ); 394 } 395 } 396 397 398 void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell ) 399 { 400 DBG_ASSERT( pCell, "AppendToFormulaTrack: pCell Null" ); 401 // Zelle kann nicht in beiden Listen gleichzeitig sein 402 RemoveFromFormulaTrack( pCell ); 403 RemoveFromFormulaTree( pCell ); 404 if ( pEOFormulaTrack ) 405 pEOFormulaTrack->SetNextTrack( pCell ); 406 else 407 pFormulaTrack = pCell; // kein Ende, kein Anfang.. 408 pCell->SetPreviousTrack( pEOFormulaTrack ); 409 pCell->SetNextTrack( 0 ); 410 pEOFormulaTrack = pCell; 411 ++nFormulaTrackCount; 412 } 413 414 415 void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell ) 416 { 417 DBG_ASSERT( pCell, "RemoveFromFormulaTrack: pCell Null" ); 418 ScFormulaCell* pPrev = pCell->GetPreviousTrack(); 419 // wenn die Zelle die erste oder sonstwo ist 420 if ( pPrev || pFormulaTrack == pCell ) 421 { 422 ScFormulaCell* pNext = pCell->GetNextTrack(); 423 if ( pPrev ) 424 pPrev->SetNextTrack( pNext ); // gibt Vorlaeufer 425 else 426 pFormulaTrack = pNext; // ist erste Zelle 427 if ( pNext ) 428 pNext->SetPreviousTrack( pPrev ); // gibt Nachfolger 429 else 430 pEOFormulaTrack = pPrev; // ist letzte Zelle 431 pCell->SetPreviousTrack( 0 ); 432 pCell->SetNextTrack( 0 ); 433 --nFormulaTrackCount; 434 } 435 } 436 437 438 sal_Bool ScDocument::IsInFormulaTrack( ScFormulaCell* pCell ) const 439 { 440 return pCell->GetPreviousTrack() || pFormulaTrack == pCell; 441 } 442 443 444 /* 445 Der erste wird gebroadcastet, 446 die dadurch entstehenden werden durch das Notify an den Track gehaengt. 447 Der nachfolgende broadcastet wieder usw. 448 View stoesst Interpret an. 449 */ 450 void ScDocument::TrackFormulas( sal_uLong nHintId ) 451 { 452 453 if ( pFormulaTrack ) 454 { 455 erBEEPER(); 456 // outside the loop, check if any sheet has a "calculate" event script 457 bool bCalcEvent = HasAnySheetEventScript( SC_SHEETEVENT_CALCULATE, true ); 458 SvtBroadcaster* pBC; 459 ScFormulaCell* pTrack; 460 ScFormulaCell* pNext; 461 pTrack = pFormulaTrack; 462 do 463 { 464 ScHint aHint( nHintId, pTrack->aPos, pTrack ); 465 if ( ( pBC = pTrack->GetBroadcaster() ) != NULL ) 466 pBC->Broadcast( aHint ); 467 pBASM->AreaBroadcast( aHint ); 468 // Repaint fuer bedingte Formate mit relativen Referenzen: 469 if ( pCondFormList ) 470 pCondFormList->SourceChanged( pTrack->aPos ); 471 // for "calculate" event, keep track of which sheets are affected by tracked formulas 472 if ( bCalcEvent ) 473 SetCalcNotification( pTrack->aPos.Tab() ); 474 pTrack = pTrack->GetNextTrack(); 475 } while ( pTrack ); 476 pTrack = pFormulaTrack; 477 sal_Bool bHaveForced = sal_False; 478 do 479 { 480 pNext = pTrack->GetNextTrack(); 481 RemoveFromFormulaTrack( pTrack ); 482 PutInFormulaTree( pTrack ); 483 if ( pTrack->GetCode()->IsRecalcModeForced() ) 484 bHaveForced = sal_True; 485 pTrack = pNext; 486 } while ( pTrack ); 487 if ( bHaveForced ) 488 { 489 SetForcedFormulas( sal_True ); 490 if ( bAutoCalc && !IsAutoCalcShellDisabled() && !IsInInterpreter() 491 && !IsCalculatingFormulaTree() ) 492 CalcFormulaTree( sal_True ); 493 else 494 SetForcedFormulaPending( sal_True ); 495 } 496 } 497 DBG_ASSERT( nFormulaTrackCount==0, "TrackFormulas: nFormulaTrackCount!=0" ); 498 } 499 500 501 void ScDocument::StartAllListeners() 502 { 503 for ( SCTAB i = 0; i <= MAXTAB; ++i ) 504 if ( pTab[i] ) 505 pTab[i]->StartAllListeners(); 506 } 507 508 void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode, 509 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz 510 ) 511 { 512 sal_Bool bExpandRefsOld = IsExpandRefs(); 513 if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) ) 514 SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); 515 if ( pBASM ) 516 pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz ); 517 SetExpandRefs( bExpandRefsOld ); 518 } 519 520 void ScDocument::SetAutoCalc( sal_Bool bNewAutoCalc ) 521 { 522 sal_Bool bOld = bAutoCalc; 523 bAutoCalc = bNewAutoCalc; 524 if ( !bOld && bNewAutoCalc && bHasForcedFormulas ) 525 { 526 if ( IsAutoCalcShellDisabled() ) 527 SetForcedFormulaPending( sal_True ); 528 else if ( !IsInInterpreter() ) 529 CalcFormulaTree( sal_True ); 530 } 531 } 532 533 534 535