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