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 28 29 #include <sfx2/objsh.hxx> 30 #include <svl/listener.hxx> 31 #include <svl/listeneriter.hxx> 32 33 #include "document.hxx" 34 #include "brdcst.hxx" 35 #include "bcaslot.hxx" 36 #include "scerrors.hxx" 37 #include "docoptio.hxx" 38 #include "refupdat.hxx" 39 #include "table.hxx" 40 41 // Number of slots per dimension 42 // must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT 43 #define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16) 44 #if MAXROWCOUNT_DEFINE == 32000 45 #define BCA_SLOTS_ROW 256 46 #define BCA_SLICE 125 47 #else 48 #define BCA_SLICE 128 49 #define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / BCA_SLICE) 50 #endif 51 #define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL) 52 #define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW) 53 // multiple? 54 #if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE) 55 #error bad BCA_SLOTS_COL value! 56 #endif 57 #if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE) 58 #error bad BCA_SLOTS_ROW value! 59 #endif 60 // size of slot array if linear 61 #define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW) 62 // Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and 63 // sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory 64 // anyway, once you reached these values.. 65 #if BCA_SLOTS_DEFINE > 268435456 66 #error BCA_SLOTS_DEFINE DOOMed! 67 #endif 68 69 // STATIC DATA ----------------------------------------------------------- 70 71 TYPEINIT1( ScHint, SfxSimpleHint ); 72 TYPEINIT1( ScAreaChangedHint, SfxHint ); 73 74 struct ScSlotData 75 { 76 SCROW nStartRow; // first row of this segment 77 SCROW nStopRow; // first row of next segment 78 SCSIZE nSlice; // slice size in this segment 79 SCSIZE nCumulated; // cumulated slots of previous segments 80 81 ScSlotData( SCROW r1, SCROW r2, SCSIZE s, SCSIZE c ) : nStartRow(r1), nStopRow(r2), nSlice(s), nCumulated(c) {} 82 }; 83 typedef ::std::vector< ScSlotData > ScSlotDistribution; 84 #if MAXROWCOUNT_DEFINE <= 65536 85 // Linear distribution. 86 static ScSlotDistribution aSlotDistribution( ScSlotData( 0, MAXROWCOUNT, BCA_SLOT_ROWS, 0)); 87 static SCSIZE nBcaSlotsRow = BCA_SLOTS_ROW; 88 static SCSIZE nBcaSlots = BCA_SLOTS_DEFINE; 89 #else 90 // Logarithmic or any other distribution. 91 // Upper sheet part usually is more populated and referenced and gets fine 92 // grained resolution, larger data in larger hunks. 93 // Could be further enhanced by also applying a different distribution of 94 // column slots. 95 static SCSIZE initSlotDistribution( ScSlotDistribution & rSD, SCSIZE & rBSR ) 96 { 97 SCSIZE nSlots = 0; 98 SCROW nRow1 = 0; 99 SCROW nRow2 = 32*1024; 100 SCSIZE nSlice = 128; 101 // Must be sorted by row1,row2! 102 while (nRow2 <= MAXROWCOUNT) 103 { 104 //fprintf( stderr, "r1,r2,slice,cum: %7zu, %7zu, %7zu, %7zu\n", (size_t)nRow1, (size_t)nRow2, (size_t)nSlice, (size_t)nSlots); 105 // {0,32k,128,0;32k,64k,256,0+256;64k,128k,512,0+256+128;128k,256k,1024,0+256+128+128;256k,512k,2048,...;512k,1M,4096,...} 106 rSD.push_back( ScSlotData( nRow1, nRow2, nSlice, nSlots)); 107 nSlots += (nRow2 - nRow1) / nSlice; 108 nRow1 = nRow2; 109 nRow2 *= 2; 110 nSlice *= 2; 111 } 112 //fprintf( stderr, "Slices: %zu, slots per sheet: %zu, memory per referenced sheet: %zu\n", (size_t) nSlots, (size_t) nSlots * BCA_SLOTS_COL, (size_t) nSlots * BCA_SLOTS_COL * sizeof(void*)); 113 rBSR = nSlots; 114 return nSlots; 115 } 116 static ScSlotDistribution aSlotDistribution; 117 static SCSIZE nBcaSlotsRow; 118 static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow) * BCA_SLOTS_COL; 119 // Ensure that all static variables are initialized with this one call. 120 #endif 121 122 123 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument, 124 ScBroadcastAreaSlotMachine* pBASMa ) : 125 aTmpSeekBroadcastArea( ScRange()), 126 pDoc( pDocument ), 127 pBASM( pBASMa ) 128 { 129 } 130 131 132 ScBroadcastAreaSlot::~ScBroadcastAreaSlot() 133 { 134 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); 135 aIter != aBroadcastAreaTbl.end(); /* none */) 136 { 137 // Prevent hash from accessing dangling pointer in case area is 138 // deleted. 139 ScBroadcastArea* pArea = *aIter; 140 // Erase all so no hash will be accessed upon destruction of the 141 // hash_set. 142 aBroadcastAreaTbl.erase( aIter++); 143 if (!pArea->DecRef()) 144 delete pArea; 145 } 146 } 147 148 149 bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const 150 { 151 if ( pDoc->GetHardRecalcState() ) 152 return true; 153 if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size()) 154 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT 155 if ( !pDoc->GetHardRecalcState() ) 156 { 157 pDoc->SetHardRecalcState( 1 ); 158 159 SfxObjectShell* pShell = pDoc->GetDocumentShell(); 160 DBG_ASSERT( pShell, "Missing DocShell :-/" ); 161 162 if ( pShell ) 163 pShell->SetError( SCWARN_CORE_HARD_RECALC, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); 164 165 pDoc->SetAutoCalc( sal_False ); 166 pDoc->SetHardRecalcState( 2 ); 167 } 168 return true; 169 } 170 return false; 171 } 172 173 174 bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange, 175 SvtListener* pListener, ScBroadcastArea*& rpArea ) 176 { 177 bool bNewArea = false; 178 DBG_ASSERT(pListener, "StartListeningArea: pListener Null"); 179 if (CheckHardRecalcStateCondition()) 180 return false; 181 if ( !rpArea ) 182 { 183 // Even if most times the area doesn't exist yet and immediately trying 184 // to new and insert it would save an attempt to find it, on mass 185 // operations like identical large [HV]LOOKUP() areas the new/delete 186 // would add quite some penalty for all but the first formula cell. 187 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange)); 188 if (aIter != aBroadcastAreaTbl.end()) 189 rpArea = *aIter; 190 else 191 { 192 rpArea = new ScBroadcastArea( rRange); 193 if (aBroadcastAreaTbl.insert( rpArea).second) 194 { 195 rpArea->IncRef(); 196 bNewArea = true; 197 } 198 else 199 { 200 DBG_ERRORFILE("StartListeningArea: area not found and not inserted in slot?!?"); 201 delete rpArea; 202 rpArea = 0; 203 } 204 } 205 if (rpArea) 206 pListener->StartListening( rpArea->GetBroadcaster()); 207 } 208 else 209 { 210 if (aBroadcastAreaTbl.insert( rpArea).second) 211 rpArea->IncRef(); 212 } 213 return bNewArea; 214 } 215 216 217 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea ) 218 { 219 DBG_ASSERT( pArea, "InsertListeningArea: pArea NULL"); 220 if (CheckHardRecalcStateCondition()) 221 return; 222 if (aBroadcastAreaTbl.insert( pArea).second) 223 pArea->IncRef(); 224 } 225 226 227 // If rpArea != NULL then no listeners are stopped, only the area is removed 228 // and the reference count decremented. 229 void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange, 230 SvtListener* pListener, ScBroadcastArea*& rpArea ) 231 { 232 DBG_ASSERT(pListener, "EndListeningArea: pListener Null"); 233 if ( !rpArea ) 234 { 235 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange)); 236 if (aIter == aBroadcastAreaTbl.end()) 237 return; 238 rpArea = *aIter; 239 pListener->EndListening( rpArea->GetBroadcaster() ); 240 if ( !rpArea->GetBroadcaster().HasListeners() ) 241 { // if nobody is listening we can dispose it 242 aBroadcastAreaTbl.erase( aIter); 243 if ( !rpArea->DecRef() ) 244 { 245 delete rpArea; 246 rpArea = NULL; 247 } 248 } 249 } 250 else 251 { 252 if ( !rpArea->GetBroadcaster().HasListeners() ) 253 { 254 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange)); 255 if (aIter == aBroadcastAreaTbl.end()) 256 return; 257 DBG_ASSERT( *aIter == rpArea, "EndListeningArea: area pointer mismatch"); 258 aBroadcastAreaTbl.erase( aIter); 259 if ( !rpArea->DecRef() ) 260 { 261 delete rpArea; 262 rpArea = NULL; 263 } 264 } 265 } 266 } 267 268 269 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea( 270 const ScRange& rRange ) const 271 { 272 aTmpSeekBroadcastArea.UpdateRange( rRange); 273 return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea); 274 } 275 276 277 sal_Bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const 278 { 279 if (aBroadcastAreaTbl.empty()) 280 return sal_False; 281 sal_Bool bIsBroadcasted = sal_False; 282 const ScAddress& rAddress = rHint.GetAddress(); 283 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()); 284 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 285 { 286 ScBroadcastArea* pArea = *aIter; 287 // A Notify() during broadcast may call EndListeningArea() and thus 288 // dispose this area if it was the last listener, which would 289 // invalidate the iterator, hence increment before call. 290 ++aIter; 291 const ScRange& rAreaRange = pArea->GetRange(); 292 if (rAreaRange.In( rAddress)) 293 { 294 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) 295 { 296 pArea->GetBroadcaster().Broadcast( rHint); 297 bIsBroadcasted = sal_True; 298 } 299 } 300 } 301 return bIsBroadcasted; 302 } 303 304 305 sal_Bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange, 306 const ScHint& rHint) const 307 { 308 if (aBroadcastAreaTbl.empty()) 309 return sal_False; 310 sal_Bool bIsBroadcasted = sal_False; 311 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()); 312 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 313 { 314 ScBroadcastArea* pArea = *aIter; 315 // A Notify() during broadcast may call EndListeningArea() and thus 316 // dispose this area if it was the last listener, which would 317 // invalidate the iterator, hence increment before call. 318 ++aIter; 319 const ScRange& rAreaRange = pArea->GetRange(); 320 if (rAreaRange.Intersects( rRange )) 321 { 322 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) 323 { 324 pArea->GetBroadcaster().Broadcast( rHint); 325 bIsBroadcasted = sal_True; 326 } 327 } 328 } 329 return bIsBroadcasted; 330 } 331 332 333 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange ) 334 { 335 if (aBroadcastAreaTbl.empty()) 336 return; 337 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); 338 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 339 { 340 const ScRange& rAreaRange = (*aIter)->GetRange(); 341 if (rRange.In( rAreaRange)) 342 { 343 ScBroadcastArea* pArea = *aIter; 344 aBroadcastAreaTbl.erase( aIter++); // erase before modifying 345 if (!pArea->DecRef()) 346 { 347 if (pBASM->IsInBulkBroadcast()) 348 pBASM->RemoveBulkArea( pArea); 349 delete pArea; 350 } 351 } 352 else 353 ++aIter; 354 } 355 } 356 357 358 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode, 359 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 360 { 361 if (aBroadcastAreaTbl.empty()) 362 return; 363 364 SCCOL nCol1, nCol2, theCol1, theCol2; 365 SCROW nRow1, nRow2, theRow1, theRow2; 366 SCTAB nTab1, nTab2, theTab1, theTab2; 367 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 368 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); 369 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 370 { 371 ScBroadcastArea* pArea = *aIter; 372 if ( pArea->IsInUpdateChain() ) 373 { 374 aBroadcastAreaTbl.erase( aIter++); 375 pArea->DecRef(); 376 } 377 else 378 { 379 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); 380 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, 381 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, 382 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) 383 { 384 aBroadcastAreaTbl.erase( aIter++); 385 pArea->DecRef(); 386 if (pBASM->IsInBulkBroadcast()) 387 pBASM->RemoveBulkArea( pArea); 388 pArea->SetInUpdateChain( sal_True ); 389 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain(); 390 if ( pUC ) 391 pUC->SetUpdateChainNext( pArea ); 392 else // no tail => no head 393 pBASM->SetUpdateChain( pArea ); 394 pBASM->SetEOUpdateChain( pArea ); 395 } 396 else 397 ++aIter; 398 } 399 } 400 } 401 402 403 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea ) 404 { 405 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea)); 406 if (aIter == aBroadcastAreaTbl.end()) 407 return; 408 if (*aIter != pArea) 409 DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch"); 410 else 411 { 412 aBroadcastAreaTbl.erase( aIter); 413 pArea->DecRef(); 414 } 415 } 416 417 418 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea ) 419 { 420 ::std::pair< ScBroadcastAreas::iterator, bool > aPair = 421 aBroadcastAreaTbl.insert( pArea ); 422 if (aPair.second) 423 pArea->IncRef(); 424 else 425 { 426 // Identical area already exists, add listeners. 427 ScBroadcastArea* pTarget = *(aPair.first); 428 if (pArea != pTarget) 429 { 430 SvtBroadcaster& rTarget = pTarget->GetBroadcaster(); 431 SvtListenerIter it( pArea->GetBroadcaster()); 432 for (SvtListener* pListener = it.GetCurr(); pListener; 433 pListener = it.GoNext()) 434 { 435 pListener->StartListening( rTarget); 436 } 437 } 438 } 439 } 440 441 442 // --- ScBroadcastAreaSlotMachine ------------------------------------- 443 444 ScBroadcastAreaSlotMachine::TableSlots::TableSlots() 445 { 446 ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ]; 447 memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots ); 448 } 449 450 451 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots() 452 { 453 for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ ) 454 { 455 if (*pp) 456 delete *pp; 457 } 458 delete [] ppSlots; 459 } 460 461 462 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine( 463 ScDocument* pDocument ) : 464 pBCAlways( NULL ), 465 pDoc( pDocument ), 466 pUpdateChain( NULL ), 467 pEOUpdateChain( NULL ), 468 nInBulkBroadcast( 0 ) 469 { 470 } 471 472 473 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine() 474 { 475 for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin()); 476 iTab != aTableSlotsMap.end(); ++iTab) 477 { 478 delete (*iTab).second; 479 } 480 delete pBCAlways; 481 } 482 483 484 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset( 485 const ScAddress& rAddress ) const 486 { 487 SCROW nRow = rAddress.Row(); 488 SCCOL nCol = rAddress.Col(); 489 if ( !ValidRow(nRow) || !ValidCol(nCol) ) 490 { 491 DBG_ERRORFILE( "Row/Col invalid, using first slot!" ); 492 return 0; 493 } 494 for (size_t i=0; i < aSlotDistribution.size(); ++i) 495 { 496 if (nRow < aSlotDistribution[i].nStopRow) 497 { 498 const ScSlotData& rSD = aSlotDistribution[i]; 499 return rSD.nCumulated + 500 (static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice + 501 static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow; 502 } 503 } 504 DBG_ERRORFILE( "No slot found, using last!" ); 505 return nBcaSlots - 1; 506 } 507 508 509 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange, 510 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const 511 { 512 rStart = ComputeSlotOffset( rRange.aStart ); 513 rEnd = ComputeSlotOffset( rRange.aEnd ); 514 // count of row slots per column minus one 515 rRowBreak = ComputeSlotOffset( 516 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart; 517 } 518 519 520 inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp, 521 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak ) 522 { 523 if ( nOff < nBreak ) 524 { 525 ++nOff; 526 ++pp; 527 } 528 else 529 { 530 nStart += nBcaSlotsRow; 531 nOff = nStart; 532 pp = ppSlots + nOff; 533 nBreak = nOff + nRowBreak; 534 } 535 } 536 537 538 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, 539 SvtListener* pListener ) 540 { 541 //fprintf( stderr, "StartListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab()); 542 if ( rRange == BCA_LISTEN_ALWAYS ) 543 { 544 if ( !pBCAlways ) 545 pBCAlways = new SvtBroadcaster; 546 pListener->StartListening( *pBCAlways ); 547 } 548 else 549 { 550 bool bDone = false; 551 for (SCTAB nTab = rRange.aStart.Tab(); 552 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab) 553 { 554 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 555 if (iTab == aTableSlotsMap.end()) 556 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( 557 nTab, new TableSlots)).first; 558 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 559 SCSIZE nStart, nEnd, nRowBreak; 560 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 561 SCSIZE nOff = nStart; 562 SCSIZE nBreak = nOff + nRowBreak; 563 ScBroadcastAreaSlot** pp = ppSlots + nOff; 564 ScBroadcastArea* pArea = NULL; 565 while ( !bDone && nOff <= nEnd ) 566 { 567 if ( !*pp ) 568 *pp = new ScBroadcastAreaSlot( pDoc, this ); 569 if (!pArea) 570 { 571 // If the call to StartListeningArea didn't create the 572 // ScBroadcastArea, listeners were added to an already 573 // existing identical area that doesn't need to be inserted 574 // to slots again. 575 if (!(*pp)->StartListeningArea( rRange, pListener, pArea)) 576 bDone = true; 577 } 578 else 579 (*pp)->InsertListeningArea( pArea); 580 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 581 } 582 } 583 } 584 } 585 586 587 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, 588 SvtListener* pListener ) 589 { 590 //fprintf( stderr, "EndListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab()); 591 if ( rRange == BCA_LISTEN_ALWAYS ) 592 { 593 DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established"); 594 if ( pBCAlways ) 595 { 596 pListener->EndListening( *pBCAlways); 597 if (!pBCAlways->HasListeners()) 598 { 599 delete pBCAlways; 600 pBCAlways = NULL; 601 } 602 } 603 } 604 else 605 { 606 SCTAB nEndTab = rRange.aEnd.Tab(); 607 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 608 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 609 { 610 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 611 SCSIZE nStart, nEnd, nRowBreak; 612 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 613 SCSIZE nOff = nStart; 614 SCSIZE nBreak = nOff + nRowBreak; 615 ScBroadcastAreaSlot** pp = ppSlots + nOff; 616 ScBroadcastArea* pArea = NULL; 617 if (nOff == 0 && nEnd == nBcaSlots-1) 618 { 619 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 620 // happen for insertion and deletion of sheets. 621 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 622 do 623 { 624 if ( *pp ) 625 (*pp)->EndListeningArea( rRange, pListener, pArea ); 626 } while (++pp < pStop); 627 } 628 else 629 { 630 while ( nOff <= nEnd ) 631 { 632 if ( *pp ) 633 (*pp)->EndListeningArea( rRange, pListener, pArea ); 634 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 635 } 636 } 637 } 638 } 639 } 640 641 642 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const 643 { 644 const ScAddress& rAddress = rHint.GetAddress(); 645 if ( rAddress == BCA_BRDCST_ALWAYS ) 646 { 647 if ( pBCAlways ) 648 { 649 pBCAlways->Broadcast( rHint ); 650 return sal_True; 651 } 652 else 653 return sal_False; 654 } 655 else 656 { 657 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab())); 658 if (iTab == aTableSlotsMap.end()) 659 return sal_False; 660 ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot( 661 ComputeSlotOffset( rAddress)); 662 if ( pSlot ) 663 return pSlot->AreaBroadcast( rHint ); 664 else 665 return sal_False; 666 } 667 } 668 669 670 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange, 671 const ScHint& rHint ) const 672 { 673 sal_Bool bBroadcasted = sal_False; 674 SCTAB nEndTab = rRange.aEnd.Tab(); 675 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 676 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 677 { 678 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 679 SCSIZE nStart, nEnd, nRowBreak; 680 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 681 SCSIZE nOff = nStart; 682 SCSIZE nBreak = nOff + nRowBreak; 683 ScBroadcastAreaSlot** pp = ppSlots + nOff; 684 while ( nOff <= nEnd ) 685 { 686 if ( *pp ) 687 bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint ); 688 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 689 } 690 } 691 return bBroadcasted; 692 } 693 694 695 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange( 696 const ScRange& rRange ) 697 { 698 SCTAB nEndTab = rRange.aEnd.Tab(); 699 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 700 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 701 { 702 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 703 SCSIZE nStart, nEnd, nRowBreak; 704 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 705 SCSIZE nOff = nStart; 706 SCSIZE nBreak = nOff + nRowBreak; 707 ScBroadcastAreaSlot** pp = ppSlots + nOff; 708 if (nOff == 0 && nEnd == nBcaSlots-1) 709 { 710 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 711 // happen for insertion and deletion of sheets. 712 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 713 do 714 { 715 if ( *pp ) 716 (*pp)->DelBroadcastAreasInRange( rRange ); 717 } while (++pp < pStop); 718 } 719 else 720 { 721 while ( nOff <= nEnd ) 722 { 723 if ( *pp ) 724 (*pp)->DelBroadcastAreasInRange( rRange ); 725 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 726 } 727 } 728 } 729 } 730 731 732 // for all affected: remove, chain, update range, insert, and maybe delete 733 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas( 734 UpdateRefMode eUpdateRefMode, 735 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 736 { 737 // remove affected and put in chain 738 SCTAB nEndTab = rRange.aEnd.Tab(); 739 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 740 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 741 { 742 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 743 SCSIZE nStart, nEnd, nRowBreak; 744 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 745 SCSIZE nOff = nStart; 746 SCSIZE nBreak = nOff + nRowBreak; 747 ScBroadcastAreaSlot** pp = ppSlots + nOff; 748 if (nOff == 0 && nEnd == nBcaSlots-1) 749 { 750 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 751 // happen for insertion and deletion of sheets. 752 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 753 do 754 { 755 if ( *pp ) 756 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); 757 } while (++pp < pStop); 758 } 759 else 760 { 761 while ( nOff <= nEnd ) 762 { 763 if ( *pp ) 764 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); 765 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 766 } 767 } 768 } 769 770 // Updating an area's range will modify the hash key, remove areas from all 771 // affected slots. Will be reinserted later with the updated range. 772 ScBroadcastArea* pChain = pUpdateChain; 773 while (pChain) 774 { 775 ScBroadcastArea* pArea = pChain; 776 pChain = pArea->GetUpdateChainNext(); 777 ScRange aRange( pArea->GetRange()); 778 // remove from slots 779 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab) 780 { 781 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 782 if (iTab == aTableSlotsMap.end()) 783 { 784 DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?"); 785 continue; // for 786 } 787 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 788 SCSIZE nStart, nEnd, nRowBreak; 789 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); 790 SCSIZE nOff = nStart; 791 SCSIZE nBreak = nOff + nRowBreak; 792 ScBroadcastAreaSlot** pp = ppSlots + nOff; 793 while ( nOff <= nEnd && pArea->GetRef() ) 794 { 795 if (*pp) 796 (*pp)->UpdateRemoveArea( pArea); 797 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 798 } 799 } 800 801 } 802 803 // shift sheets 804 if (nDz) 805 { 806 if (nDz < 0) 807 { 808 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 809 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz)); 810 // Remove sheets, if any, iDel or/and iTab may as well point to end(). 811 while (iDel != iTab) 812 { 813 delete (*iDel).second; 814 aTableSlotsMap.erase( iDel++); 815 } 816 // shift remaining down 817 while (iTab != aTableSlotsMap.end()) 818 { 819 SCTAB nTab = (*iTab).first + nDz; 820 aTableSlotsMap[nTab] = (*iTab).second; 821 aTableSlotsMap.erase( iTab++); 822 } 823 } 824 else 825 { 826 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 827 if (iStop != aTableSlotsMap.end()) 828 { 829 bool bStopIsBegin = (iStop == aTableSlotsMap.begin()); 830 if (!bStopIsBegin) 831 --iStop; 832 TableSlotsMap::iterator iTab( aTableSlotsMap.end()); 833 --iTab; 834 while (iTab != iStop) 835 { 836 SCTAB nTab = (*iTab).first + nDz; 837 aTableSlotsMap[nTab] = (*iTab).second; 838 aTableSlotsMap.erase( iTab--); 839 } 840 // Shift the very first, iTab==iStop in this case. 841 if (bStopIsBegin) 842 { 843 SCTAB nTab = (*iTab).first + nDz; 844 aTableSlotsMap[nTab] = (*iTab).second; 845 aTableSlotsMap.erase( iStop); 846 } 847 } 848 } 849 } 850 851 // work off chain 852 SCCOL nCol1, nCol2, theCol1, theCol2; 853 SCROW nRow1, nRow2, theRow1, theRow2; 854 SCTAB nTab1, nTab2, theTab1, theTab2; 855 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 856 while ( pUpdateChain ) 857 { 858 ScBroadcastArea* pArea = pUpdateChain; 859 ScRange aRange( pArea->GetRange()); 860 pUpdateChain = pArea->GetUpdateChainNext(); 861 862 // update range 863 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); 864 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, 865 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, 866 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) 867 { 868 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ); 869 pArea->UpdateRange( aRange ); 870 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE 871 } 872 873 // insert to slots 874 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab) 875 { 876 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 877 if (iTab == aTableSlotsMap.end()) 878 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( 879 nTab, new TableSlots)).first; 880 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 881 SCSIZE nStart, nEnd, nRowBreak; 882 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); 883 SCSIZE nOff = nStart; 884 SCSIZE nBreak = nOff + nRowBreak; 885 ScBroadcastAreaSlot** pp = ppSlots + nOff; 886 while ( nOff <= nEnd ) 887 { 888 if (!*pp) 889 *pp = new ScBroadcastAreaSlot( pDoc, this ); 890 (*pp)->UpdateInsert( pArea ); 891 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 892 } 893 } 894 895 // unchain 896 pArea->SetUpdateChainNext( NULL ); 897 pArea->SetInUpdateChain( sal_False ); 898 899 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was 900 // already executed in UpdateRemove(). 901 if (!pArea->GetRef()) 902 delete pArea; 903 } 904 pEOUpdateChain = NULL; 905 } 906 907 908 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast() 909 { 910 ++nInBulkBroadcast; 911 } 912 913 914 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast() 915 { 916 if (nInBulkBroadcast > 0) 917 { 918 if (--nInBulkBroadcast == 0) 919 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas); 920 } 921 } 922 923 924 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea ) 925 { 926 return aBulkBroadcastAreas.insert( pArea ).second; 927 } 928 929 930 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea ) 931 { 932 return aBulkBroadcastAreas.erase( pArea ); 933 } 934