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 <boost/mem_fn.hpp> 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 283 // issue 118012 284 // do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators 285 // are destroyed during notification. 286 std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() ); 287 std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) ); 288 289 const ScAddress& rAddress = rHint.GetAddress(); 290 const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() ); 291 std::vector< ScBroadcastArea* >::const_iterator aIter; 292 for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter ) 293 { 294 ScBroadcastArea* pArea = *aIter; 295 // check, if copied item has been already removed from <aBroadcastAreaTbl> 296 if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() ) 297 { 298 continue; 299 } 300 301 const ScRange& rAreaRange = pArea->GetRange(); 302 if (rAreaRange.In( rAddress)) 303 { 304 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) 305 { 306 pArea->GetBroadcaster().Broadcast( rHint); 307 bIsBroadcasted = sal_True; 308 } 309 } 310 } 311 312 // delete no longer referenced <ScBroadcastArea> instances 313 for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter ) 314 { 315 ScBroadcastArea* pArea = *aIter; 316 if ( !pArea->DecRef() ) 317 { 318 delete pArea; 319 } 320 } 321 322 return bIsBroadcasted; 323 } 324 325 326 sal_Bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange, 327 const ScHint& rHint) const 328 { 329 if (aBroadcastAreaTbl.empty()) 330 return sal_False; 331 sal_Bool bIsBroadcasted = sal_False; 332 333 // issue 118012 334 // do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators 335 // are destroyed during notification. 336 std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() ); 337 std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) ); 338 339 const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() ); 340 std::vector< ScBroadcastArea* >::const_iterator aIter; 341 for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter ) 342 { 343 ScBroadcastArea* pArea = *aIter; 344 // check, if copied item has been already removed from <aBroadcastAreaTbl> 345 if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() ) 346 { 347 continue; 348 } 349 350 const ScRange& rAreaRange = pArea->GetRange(); 351 if (rAreaRange.Intersects( rRange )) 352 { 353 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) 354 { 355 pArea->GetBroadcaster().Broadcast( rHint); 356 bIsBroadcasted = sal_True; 357 } 358 } 359 } 360 361 // delete no longer referenced <ScBroadcastArea> instances 362 for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter ) 363 { 364 ScBroadcastArea* pArea = *aIter; 365 if ( !pArea->DecRef() ) 366 { 367 delete pArea; 368 } 369 } 370 371 return bIsBroadcasted; 372 } 373 374 375 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange ) 376 { 377 if (aBroadcastAreaTbl.empty()) 378 return; 379 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); 380 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 381 { 382 const ScRange& rAreaRange = (*aIter)->GetRange(); 383 if (rRange.In( rAreaRange)) 384 { 385 ScBroadcastArea* pArea = *aIter; 386 aBroadcastAreaTbl.erase( aIter++); // erase before modifying 387 if (!pArea->DecRef()) 388 { 389 if (pBASM->IsInBulkBroadcast()) 390 pBASM->RemoveBulkArea( pArea); 391 delete pArea; 392 } 393 } 394 else 395 ++aIter; 396 } 397 } 398 399 400 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode, 401 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 402 { 403 if (aBroadcastAreaTbl.empty()) 404 return; 405 406 SCCOL nCol1, nCol2, theCol1, theCol2; 407 SCROW nRow1, nRow2, theRow1, theRow2; 408 SCTAB nTab1, nTab2, theTab1, theTab2; 409 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 410 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); 411 aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) 412 { 413 ScBroadcastArea* pArea = *aIter; 414 if ( pArea->IsInUpdateChain() ) 415 { 416 aBroadcastAreaTbl.erase( aIter++); 417 pArea->DecRef(); 418 } 419 else 420 { 421 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); 422 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, 423 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, 424 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) 425 { 426 aBroadcastAreaTbl.erase( aIter++); 427 pArea->DecRef(); 428 if (pBASM->IsInBulkBroadcast()) 429 pBASM->RemoveBulkArea( pArea); 430 pArea->SetInUpdateChain( sal_True ); 431 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain(); 432 if ( pUC ) 433 pUC->SetUpdateChainNext( pArea ); 434 else // no tail => no head 435 pBASM->SetUpdateChain( pArea ); 436 pBASM->SetEOUpdateChain( pArea ); 437 } 438 else 439 ++aIter; 440 } 441 } 442 } 443 444 445 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea ) 446 { 447 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea)); 448 if (aIter == aBroadcastAreaTbl.end()) 449 return; 450 if (*aIter != pArea) 451 DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch"); 452 else 453 { 454 aBroadcastAreaTbl.erase( aIter); 455 pArea->DecRef(); 456 } 457 } 458 459 460 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea ) 461 { 462 ::std::pair< ScBroadcastAreas::iterator, bool > aPair = 463 aBroadcastAreaTbl.insert( pArea ); 464 if (aPair.second) 465 pArea->IncRef(); 466 else 467 { 468 // Identical area already exists, add listeners. 469 ScBroadcastArea* pTarget = *(aPair.first); 470 if (pArea != pTarget) 471 { 472 SvtBroadcaster& rTarget = pTarget->GetBroadcaster(); 473 SvtListenerIter it( pArea->GetBroadcaster()); 474 for (SvtListener* pListener = it.GetCurr(); pListener; 475 pListener = it.GoNext()) 476 { 477 pListener->StartListening( rTarget); 478 } 479 } 480 } 481 } 482 483 484 // --- ScBroadcastAreaSlotMachine ------------------------------------- 485 486 ScBroadcastAreaSlotMachine::TableSlots::TableSlots() 487 { 488 ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ]; 489 memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots ); 490 } 491 492 493 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots() 494 { 495 for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ ) 496 { 497 if (*pp) 498 delete *pp; 499 } 500 delete [] ppSlots; 501 } 502 503 504 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine( 505 ScDocument* pDocument ) : 506 pBCAlways( NULL ), 507 pDoc( pDocument ), 508 pUpdateChain( NULL ), 509 pEOUpdateChain( NULL ), 510 nInBulkBroadcast( 0 ) 511 { 512 } 513 514 515 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine() 516 { 517 for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin()); 518 iTab != aTableSlotsMap.end(); ++iTab) 519 { 520 delete (*iTab).second; 521 } 522 delete pBCAlways; 523 } 524 525 526 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset( 527 const ScAddress& rAddress ) const 528 { 529 SCROW nRow = rAddress.Row(); 530 SCCOL nCol = rAddress.Col(); 531 if ( !ValidRow(nRow) || !ValidCol(nCol) ) 532 { 533 DBG_ERRORFILE( "Row/Col invalid, using first slot!" ); 534 return 0; 535 } 536 for (size_t i=0; i < aSlotDistribution.size(); ++i) 537 { 538 if (nRow < aSlotDistribution[i].nStopRow) 539 { 540 const ScSlotData& rSD = aSlotDistribution[i]; 541 return rSD.nCumulated + 542 (static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice + 543 static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow; 544 } 545 } 546 DBG_ERRORFILE( "No slot found, using last!" ); 547 return nBcaSlots - 1; 548 } 549 550 551 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange, 552 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const 553 { 554 rStart = ComputeSlotOffset( rRange.aStart ); 555 rEnd = ComputeSlotOffset( rRange.aEnd ); 556 // count of row slots per column minus one 557 rRowBreak = ComputeSlotOffset( 558 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart; 559 } 560 561 562 inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp, 563 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak ) 564 { 565 if ( nOff < nBreak ) 566 { 567 ++nOff; 568 ++pp; 569 } 570 else 571 { 572 nStart += nBcaSlotsRow; 573 nOff = nStart; 574 pp = ppSlots + nOff; 575 nBreak = nOff + nRowBreak; 576 } 577 } 578 579 580 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, 581 SvtListener* pListener ) 582 { 583 //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()); 584 if ( rRange == BCA_LISTEN_ALWAYS ) 585 { 586 if ( !pBCAlways ) 587 pBCAlways = new SvtBroadcaster; 588 pListener->StartListening( *pBCAlways ); 589 } 590 else 591 { 592 bool bDone = false; 593 for (SCTAB nTab = rRange.aStart.Tab(); 594 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab) 595 { 596 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 597 if (iTab == aTableSlotsMap.end()) 598 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( 599 nTab, new TableSlots)).first; 600 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 601 SCSIZE nStart, nEnd, nRowBreak; 602 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 603 SCSIZE nOff = nStart; 604 SCSIZE nBreak = nOff + nRowBreak; 605 ScBroadcastAreaSlot** pp = ppSlots + nOff; 606 ScBroadcastArea* pArea = NULL; 607 while ( !bDone && nOff <= nEnd ) 608 { 609 if ( !*pp ) 610 *pp = new ScBroadcastAreaSlot( pDoc, this ); 611 if (!pArea) 612 { 613 // If the call to StartListeningArea didn't create the 614 // ScBroadcastArea, listeners were added to an already 615 // existing identical area that doesn't need to be inserted 616 // to slots again. 617 if (!(*pp)->StartListeningArea( rRange, pListener, pArea)) 618 bDone = true; 619 } 620 else 621 (*pp)->InsertListeningArea( pArea); 622 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 623 } 624 } 625 } 626 } 627 628 629 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, 630 SvtListener* pListener ) 631 { 632 //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()); 633 if ( rRange == BCA_LISTEN_ALWAYS ) 634 { 635 DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established"); 636 if ( pBCAlways ) 637 { 638 pListener->EndListening( *pBCAlways); 639 if (!pBCAlways->HasListeners()) 640 { 641 delete pBCAlways; 642 pBCAlways = NULL; 643 } 644 } 645 } 646 else 647 { 648 SCTAB nEndTab = rRange.aEnd.Tab(); 649 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 650 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 651 { 652 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 653 SCSIZE nStart, nEnd, nRowBreak; 654 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 655 SCSIZE nOff = nStart; 656 SCSIZE nBreak = nOff + nRowBreak; 657 ScBroadcastAreaSlot** pp = ppSlots + nOff; 658 ScBroadcastArea* pArea = NULL; 659 if (nOff == 0 && nEnd == nBcaSlots-1) 660 { 661 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 662 // happen for insertion and deletion of sheets. 663 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 664 do 665 { 666 if ( *pp ) 667 (*pp)->EndListeningArea( rRange, pListener, pArea ); 668 } while (++pp < pStop); 669 } 670 else 671 { 672 while ( nOff <= nEnd ) 673 { 674 if ( *pp ) 675 (*pp)->EndListeningArea( rRange, pListener, pArea ); 676 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 677 } 678 } 679 } 680 } 681 } 682 683 684 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const 685 { 686 const ScAddress& rAddress = rHint.GetAddress(); 687 if ( rAddress == BCA_BRDCST_ALWAYS ) 688 { 689 if ( pBCAlways ) 690 { 691 pBCAlways->Broadcast( rHint ); 692 return sal_True; 693 } 694 else 695 return sal_False; 696 } 697 else 698 { 699 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab())); 700 if (iTab == aTableSlotsMap.end()) 701 return sal_False; 702 ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot( 703 ComputeSlotOffset( rAddress)); 704 if ( pSlot ) 705 return pSlot->AreaBroadcast( rHint ); 706 else 707 return sal_False; 708 } 709 } 710 711 712 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange, 713 const ScHint& rHint ) const 714 { 715 sal_Bool bBroadcasted = sal_False; 716 SCTAB nEndTab = rRange.aEnd.Tab(); 717 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 718 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 719 { 720 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 721 SCSIZE nStart, nEnd, nRowBreak; 722 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 723 SCSIZE nOff = nStart; 724 SCSIZE nBreak = nOff + nRowBreak; 725 ScBroadcastAreaSlot** pp = ppSlots + nOff; 726 while ( nOff <= nEnd ) 727 { 728 if ( *pp ) 729 bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint ); 730 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 731 } 732 } 733 return bBroadcasted; 734 } 735 736 737 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange( 738 const ScRange& rRange ) 739 { 740 SCTAB nEndTab = rRange.aEnd.Tab(); 741 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 742 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 743 { 744 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 745 SCSIZE nStart, nEnd, nRowBreak; 746 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 747 SCSIZE nOff = nStart; 748 SCSIZE nBreak = nOff + nRowBreak; 749 ScBroadcastAreaSlot** pp = ppSlots + nOff; 750 if (nOff == 0 && nEnd == nBcaSlots-1) 751 { 752 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 753 // happen for insertion and deletion of sheets. 754 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 755 do 756 { 757 if ( *pp ) 758 (*pp)->DelBroadcastAreasInRange( rRange ); 759 } while (++pp < pStop); 760 } 761 else 762 { 763 while ( nOff <= nEnd ) 764 { 765 if ( *pp ) 766 (*pp)->DelBroadcastAreasInRange( rRange ); 767 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 768 } 769 } 770 } 771 } 772 773 774 // for all affected: remove, chain, update range, insert, and maybe delete 775 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas( 776 UpdateRefMode eUpdateRefMode, 777 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) 778 { 779 // remove affected and put in chain 780 SCTAB nEndTab = rRange.aEnd.Tab(); 781 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 782 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) 783 { 784 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 785 SCSIZE nStart, nEnd, nRowBreak; 786 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); 787 SCSIZE nOff = nStart; 788 SCSIZE nBreak = nOff + nRowBreak; 789 ScBroadcastAreaSlot** pp = ppSlots + nOff; 790 if (nOff == 0 && nEnd == nBcaSlots-1) 791 { 792 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they 793 // happen for insertion and deletion of sheets. 794 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; 795 do 796 { 797 if ( *pp ) 798 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); 799 } while (++pp < pStop); 800 } 801 else 802 { 803 while ( nOff <= nEnd ) 804 { 805 if ( *pp ) 806 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); 807 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 808 } 809 } 810 } 811 812 // Updating an area's range will modify the hash key, remove areas from all 813 // affected slots. Will be reinserted later with the updated range. 814 ScBroadcastArea* pChain = pUpdateChain; 815 while (pChain) 816 { 817 ScBroadcastArea* pArea = pChain; 818 pChain = pArea->GetUpdateChainNext(); 819 ScRange aRange( pArea->GetRange()); 820 // remove from slots 821 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab) 822 { 823 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 824 if (iTab == aTableSlotsMap.end()) 825 { 826 DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?"); 827 continue; // for 828 } 829 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 830 SCSIZE nStart, nEnd, nRowBreak; 831 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); 832 SCSIZE nOff = nStart; 833 SCSIZE nBreak = nOff + nRowBreak; 834 ScBroadcastAreaSlot** pp = ppSlots + nOff; 835 while ( nOff <= nEnd && pArea->GetRef() ) 836 { 837 if (*pp) 838 (*pp)->UpdateRemoveArea( pArea); 839 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 840 } 841 } 842 843 } 844 845 // shift sheets 846 if (nDz) 847 { 848 if (nDz < 0) 849 { 850 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 851 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz)); 852 // Remove sheets, if any, iDel or/and iTab may as well point to end(). 853 while (iDel != iTab) 854 { 855 delete (*iDel).second; 856 aTableSlotsMap.erase( iDel++); 857 } 858 // shift remaining down 859 while (iTab != aTableSlotsMap.end()) 860 { 861 SCTAB nTab = (*iTab).first + nDz; 862 aTableSlotsMap[nTab] = (*iTab).second; 863 aTableSlotsMap.erase( iTab++); 864 } 865 } 866 else 867 { 868 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); 869 if (iStop != aTableSlotsMap.end()) 870 { 871 bool bStopIsBegin = (iStop == aTableSlotsMap.begin()); 872 if (!bStopIsBegin) 873 --iStop; 874 TableSlotsMap::iterator iTab( aTableSlotsMap.end()); 875 --iTab; 876 while (iTab != iStop) 877 { 878 SCTAB nTab = (*iTab).first + nDz; 879 aTableSlotsMap[nTab] = (*iTab).second; 880 aTableSlotsMap.erase( iTab--); 881 } 882 // Shift the very first, iTab==iStop in this case. 883 if (bStopIsBegin) 884 { 885 SCTAB nTab = (*iTab).first + nDz; 886 aTableSlotsMap[nTab] = (*iTab).second; 887 aTableSlotsMap.erase( iStop); 888 } 889 } 890 } 891 } 892 893 // work off chain 894 SCCOL nCol1, nCol2, theCol1, theCol2; 895 SCROW nRow1, nRow2, theRow1, theRow2; 896 SCTAB nTab1, nTab2, theTab1, theTab2; 897 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 898 while ( pUpdateChain ) 899 { 900 ScBroadcastArea* pArea = pUpdateChain; 901 ScRange aRange( pArea->GetRange()); 902 pUpdateChain = pArea->GetUpdateChainNext(); 903 904 // update range 905 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); 906 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, 907 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, 908 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) 909 { 910 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ); 911 pArea->UpdateRange( aRange ); 912 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE 913 } 914 915 // insert to slots 916 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab) 917 { 918 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); 919 if (iTab == aTableSlotsMap.end()) 920 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( 921 nTab, new TableSlots)).first; 922 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); 923 SCSIZE nStart, nEnd, nRowBreak; 924 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); 925 SCSIZE nOff = nStart; 926 SCSIZE nBreak = nOff + nRowBreak; 927 ScBroadcastAreaSlot** pp = ppSlots + nOff; 928 while ( nOff <= nEnd ) 929 { 930 if (!*pp) 931 *pp = new ScBroadcastAreaSlot( pDoc, this ); 932 (*pp)->UpdateInsert( pArea ); 933 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); 934 } 935 } 936 937 // unchain 938 pArea->SetUpdateChainNext( NULL ); 939 pArea->SetInUpdateChain( sal_False ); 940 941 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was 942 // already executed in UpdateRemove(). 943 if (!pArea->GetRef()) 944 delete pArea; 945 } 946 pEOUpdateChain = NULL; 947 } 948 949 950 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast() 951 { 952 ++nInBulkBroadcast; 953 } 954 955 956 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast() 957 { 958 if (nInBulkBroadcast > 0) 959 { 960 if (--nInBulkBroadcast == 0) 961 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas); 962 } 963 } 964 965 966 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea ) 967 { 968 return aBulkBroadcastAreas.insert( pArea ).second; 969 } 970 971 972 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea ) 973 { 974 return aBulkBroadcastAreas.erase( pArea ); 975 } 976