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