Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / data / bcaslot.cxx
blobc8f182e11f38b6915b8d78f6c38ead7fedb6f40f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sfx2/objsh.hxx>
21 #include <svl/listener.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
25 #include <document.hxx>
26 #include <docsh.hxx>
27 #include <brdcst.hxx>
28 #include <bcaslot.hxx>
29 #include <scerrors.hxx>
30 #include <refupdat.hxx>
31 #include <bulkdatahint.hxx>
32 #include <columnspanset.hxx>
33 #include <formulacell.hxx>
34 #include <grouparealistener.hxx>
35 #include <broadcast.hxx>
37 ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
38 pUpdateChainNext(nullptr),
39 aRange(rRange),
40 nRefCount(0),
41 mbInUpdateChain(false),
42 mbGroupListening(false) {}
44 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
45 ScBroadcastAreaSlotMachine* pBASMa ) :
46 aTmpSeekBroadcastArea( ScRange()),
47 pDoc( pDocument ),
48 pBASM( pBASMa ),
49 mbInBroadcastIteration( false),
50 mbHasErasedArea(false)
54 ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
56 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
57 aIter != aBroadcastAreaTbl.end(); /* none */)
59 // Prevent hash from accessing dangling pointer in case area is
60 // deleted.
61 ScBroadcastArea* pArea = (*aIter).mpArea;
62 // Erase all so no hash will be accessed upon destruction of the
63 // unordered_map.
64 aIter = aBroadcastAreaTbl.erase(aIter);
65 if (!pArea->DecRef())
66 delete pArea;
70 ScDocument::HardRecalcState ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
72 ScDocument::HardRecalcState eState = pDoc->GetHardRecalcState();
73 if (eState == ScDocument::HardRecalcState::OFF)
75 if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
76 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
77 ScDocShell* pShell = pDoc->GetDocumentShell();
78 OSL_ENSURE( pShell, "Missing DocShell :-/" );
80 if ( pShell )
81 pShell->SetError(SCWARN_CORE_HARD_RECALC);
83 pDoc->SetAutoCalc( false );
84 eState = ScDocument::HardRecalcState::ETERNAL;
85 pDoc->SetHardRecalcState( eState );
88 return eState;
91 bool ScBroadcastAreaSlot::StartListeningArea(
92 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
94 bool bNewArea = false;
95 OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
96 assert(!pDoc->IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
97 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
98 return false;
99 if ( !rpArea )
101 // Even if most times the area doesn't exist yet and immediately trying
102 // to new and insert it would save an attempt to find it, on massive
103 // operations like identical large [HV]LOOKUP() areas the new/delete
104 // would add quite some penalty for all but the first formula cell.
105 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
106 if (aIter != aBroadcastAreaTbl.end())
107 rpArea = (*aIter).mpArea;
108 else
110 rpArea = new ScBroadcastArea( rRange);
111 rpArea->SetGroupListening(bGroupListening);
112 if (aBroadcastAreaTbl.insert( rpArea).second)
114 rpArea->IncRef();
115 bNewArea = true;
117 else
119 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
120 delete rpArea;
121 rpArea = nullptr;
124 if (rpArea)
125 pListener->StartListening( rpArea->GetBroadcaster());
127 else
129 if (aBroadcastAreaTbl.insert( rpArea).second)
130 rpArea->IncRef();
132 return bNewArea;
135 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
137 OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
138 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
139 return;
140 if (aBroadcastAreaTbl.insert( pArea).second)
141 pArea->IncRef();
144 // If rpArea != NULL then no listeners are stopped, only the area is removed
145 // and the reference count decremented.
146 void ScBroadcastAreaSlot::EndListeningArea(
147 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
149 OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
150 if ( !rpArea )
152 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
153 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
154 return;
155 rpArea = (*aIter).mpArea;
156 pListener->EndListening( rpArea->GetBroadcaster() );
157 if ( !rpArea->GetBroadcaster().HasListeners() )
158 { // if nobody is listening we can dispose it
159 if (rpArea->GetRef() == 1)
160 rpArea = nullptr; // will be deleted by erase
161 EraseArea( aIter);
164 else
166 if (rpArea && !rpArea->GetBroadcaster().HasListeners())
168 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
169 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
170 return;
171 OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
172 if (rpArea->GetRef() == 1)
173 rpArea = nullptr; // will be deleted by erase
174 EraseArea( aIter);
179 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
180 const ScRange& rRange, bool bGroupListening )
182 aTmpSeekBroadcastArea.UpdateRange( rRange);
183 aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
184 return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
187 namespace {
189 void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
191 ScHint aHint(nHint, ScAddress());
192 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
194 aHint.SetAddressTab(nTab);
195 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
197 aHint.SetAddressCol(nCol);
198 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
200 aHint.SetAddressRow(nRow);
201 rBC.Broadcast(aHint);
209 bool ScBroadcastAreaSlot::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
211 if (aBroadcastAreaTbl.empty())
212 return false;
214 bool bInBroadcast = mbInBroadcastIteration;
215 mbInBroadcastIteration = true;
216 bool bIsBroadcasted = false;
218 mbHasErasedArea = false;
220 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
221 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
223 if (mbHasErasedArea && isMarkedErased( aIter))
224 continue;
226 ScBroadcastArea* pArea = (*aIter).mpArea;
227 const ScRange& rAreaRange = pArea->GetRange();
229 // Take the intersection of the area range and the broadcast range.
230 ScRange aIntersection = rAreaRange.Intersection(rRange);
231 if (!aIntersection.IsValid())
232 continue;
234 if (pArea->IsGroupListening())
236 if (pBASM->IsInBulkBroadcast())
238 pBASM->InsertBulkGroupArea(pArea, aIntersection);
240 else
242 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
243 bIsBroadcasted = true;
246 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
248 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
249 bIsBroadcasted = true;
253 mbInBroadcastIteration = bInBroadcast;
255 // A Notify() during broadcast may call EndListeningArea() and thus dispose
256 // an area if it was the last listener, which would invalidate an iterator
257 // pointing to it, hence the real erase is done afterwards.
258 FinallyEraseAreas();
260 return bIsBroadcasted;
263 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
265 if (aBroadcastAreaTbl.empty())
266 return false;
268 bool bInBroadcast = mbInBroadcastIteration;
269 mbInBroadcastIteration = true;
270 bool bIsBroadcasted = false;
272 mbHasErasedArea = false;
274 const ScRange& rRange = rHint.GetRange();
275 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
276 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
278 if (mbHasErasedArea && isMarkedErased( aIter))
279 continue;
281 ScBroadcastArea* pArea = (*aIter).mpArea;
282 const ScRange& rAreaRange = pArea->GetRange();
283 if (rAreaRange.Intersects( rRange))
285 if (pArea->IsGroupListening())
287 if (pBASM->IsInBulkBroadcast())
289 pBASM->InsertBulkGroupArea(pArea, rRange);
291 else
293 pArea->GetBroadcaster().Broadcast( rHint);
294 bIsBroadcasted = true;
297 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
299 pArea->GetBroadcaster().Broadcast( rHint);
300 bIsBroadcasted = true;
305 mbInBroadcastIteration = bInBroadcast;
307 // A Notify() during broadcast may call EndListeningArea() and thus dispose
308 // an area if it was the last listener, which would invalidate an iterator
309 // pointing to it, hence the real erase is done afterwards.
310 FinallyEraseAreas();
312 return bIsBroadcasted;
315 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
317 if (aBroadcastAreaTbl.empty())
318 return;
319 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
320 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
322 const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
323 if (rRange.Contains( rAreaRange))
325 ScBroadcastArea* pArea = (*aIter).mpArea;
326 aIter = aBroadcastAreaTbl.erase(aIter); // erase before modifying
327 if (!pArea->DecRef())
329 if (pBASM->IsInBulkBroadcast())
330 pBASM->RemoveBulkArea( pArea);
331 delete pArea;
334 else
335 ++aIter;
339 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
340 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
342 if (aBroadcastAreaTbl.empty())
343 return;
345 SCCOL nCol1, nCol2, theCol1, theCol2;
346 SCROW nRow1, nRow2, theRow1, theRow2;
347 SCTAB nTab1, nTab2, theTab1, theTab2;
348 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
349 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
350 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
352 ScBroadcastArea* pArea = (*aIter).mpArea;
353 if ( pArea->IsInUpdateChain() )
355 aIter = aBroadcastAreaTbl.erase(aIter);
356 pArea->DecRef();
358 else
360 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
361 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
362 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
363 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
365 aIter = aBroadcastAreaTbl.erase(aIter);
366 pArea->DecRef();
367 if (pBASM->IsInBulkBroadcast())
368 pBASM->RemoveBulkArea( pArea);
369 pArea->SetInUpdateChain( true );
370 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
371 if ( pUC )
372 pUC->SetUpdateChainNext( pArea );
373 else // no tail => no head
374 pBASM->SetUpdateChain( pArea );
375 pBASM->SetEOUpdateChain( pArea );
377 else
378 ++aIter;
383 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
385 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
386 if (aIter == aBroadcastAreaTbl.end())
387 return;
388 if ((*aIter).mpArea != pArea)
389 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
390 else
392 aBroadcastAreaTbl.erase( aIter);
393 pArea->DecRef();
397 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
399 ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
400 aBroadcastAreaTbl.insert( pArea);
401 if (aPair.second)
402 pArea->IncRef();
403 else
405 // Identical area already exists, add listeners.
406 ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
407 if (pArea != pTarget)
409 SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
410 SvtBroadcaster::ListenersType& rListeners = pArea->GetBroadcaster().GetAllListeners();
411 for (auto& pListener : rListeners)
413 SvtListener& rListener = *pListener;
414 rListener.StartListening(rTarget);
420 void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
422 if (mbInBroadcastIteration)
424 (*rIter).mbErasure = true; // mark for erasure
425 mbHasErasedArea = true; // at least one area is marked for erasure.
426 pBASM->PushAreaToBeErased( this, rIter);
428 else
430 ScBroadcastArea* pArea = (*rIter).mpArea;
431 aBroadcastAreaTbl.erase( rIter);
432 if (!pArea->DecRef())
434 if (pBASM->IsInBulkBroadcast())
435 pBASM->RemoveBulkGroupArea(pArea);
436 delete pArea;
441 void ScBroadcastAreaSlot::GetAllListeners(
442 const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
443 sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
445 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
446 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
448 if (isMarkedErased( aIter))
449 continue;
451 ScBroadcastArea* pArea = (*aIter).mpArea;
452 const ScRange& rAreaRange = pArea->GetRange();
453 switch (eGroup)
455 case sc::ListenerGroupType::Group:
456 if (!pArea->IsGroupListening())
457 continue;
458 break;
459 case sc::ListenerGroupType::Both:
460 default:
464 switch (eType)
466 case sc::AreaOverlapType::Inside:
467 if (!rRange.Contains(rAreaRange))
468 // The range needs to be fully inside specified range.
469 continue;
470 break;
471 case sc::AreaOverlapType::InsideOrOverlap:
472 if (!rRange.Intersects(rAreaRange))
473 // The range needs to be partially overlapping or fully inside.
474 continue;
475 break;
476 case sc::AreaOverlapType::OneRowInside:
477 if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.Contains(rAreaRange))
478 // The range needs to be one single row and fully inside
479 // specified range.
480 continue;
481 break;
482 case sc::AreaOverlapType::OneColumnInside:
483 if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.Contains(rAreaRange))
484 // The range needs to be one single column and fully inside
485 // specified range.
486 continue;
487 break;
490 SvtBroadcaster::ListenersType& rLst = pArea->GetBroadcaster().GetAllListeners();
491 for (const auto& pListener : rLst)
493 sc::AreaListener aEntry;
494 aEntry.maArea = rAreaRange;
495 aEntry.mbGroupListening = pArea->IsGroupListening();
496 aEntry.mpListener = pListener;
497 rListeners.push_back(aEntry);
502 void ScBroadcastAreaSlot::CollectBroadcasterState(sc::BroadcasterState& rState) const
504 for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
506 const ScRange& rRange = rEntry.mpArea->GetRange();
507 auto aRes = rState.aAreaListenerStore.try_emplace(rRange);
508 auto& rLisStore = aRes.first->second;
510 for (const SvtListener* pLis : rEntry.mpArea->GetBroadcaster().GetAllListeners())
512 if (auto pFC = dynamic_cast<const ScFormulaCell*>(pLis); pFC)
514 rLisStore.emplace_back(pFC);
515 continue;
518 if (auto pFGL = dynamic_cast<const sc::FormulaGroupAreaListener*>(pLis); pFGL)
520 rLisStore.emplace_back(pFGL);
521 continue;
524 rLisStore.emplace_back(pLis);
529 void ScBroadcastAreaSlot::FinallyEraseAreas()
531 pBASM->FinallyEraseAreas( this);
534 // --- ScBroadcastAreaSlotMachine -------------------------------------
536 ScBroadcastAreaSlotMachine::TableSlots::TableSlots(SCSIZE nBcaSlots)
537 : mnBcaSlots(nBcaSlots)
539 ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
540 memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
543 ScBroadcastAreaSlotMachine::TableSlots::TableSlots(TableSlots&& rOther) noexcept
544 : mnBcaSlots(rOther.mnBcaSlots)
545 , ppSlots( std::move(rOther.ppSlots) )
549 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
551 if (ppSlots)
552 for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
553 delete *pp;
556 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
557 ScDocument* pDocument ) :
558 pDoc( pDocument ),
559 pUpdateChain( nullptr ),
560 pEOUpdateChain( nullptr ),
561 nInBulkBroadcast( 0 )
563 // initSlotDistribution ---------
564 // Logarithmic or any other distribution.
565 // Upper and leftmost sheet part usually is more populated and referenced and gets fine
566 // grained resolution, larger data in larger hunks.
567 // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
568 // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
569 // slice doubles (making more cells share the same slot), this distribution data is stored
570 // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
571 // again with the column slice doubling after some time.
572 // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
573 // calculations.
574 SCSIZE nSlots = 0;
575 // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
576 sal_Int32 nCol1 = 0;
577 sal_Int32 nCol2 = 1024;
578 SCSIZE nSliceCol = 16;
579 while (nCol2 <= pDoc->GetMaxColCount())
581 SCROW nRow1 = 0;
582 SCROW nRow2 = 32*1024;
583 SCSIZE nSliceRow = 128;
584 SCSIZE nSlotsCol = 0;
585 SCSIZE nSlotsStartCol = nSlots;
586 // Must be sorted by row1,row2!
587 while (nRow2 <= pDoc->GetMaxRowCount())
589 maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
590 nSlotsCol += (nRow2 - nRow1) / nSliceRow;
591 nRow1 = nRow2;
592 nRow2 *= 2;
593 nSliceRow *= 2;
595 // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
596 // to the right can be computed quickly in ComputeNextSlot().
597 if(nCol1 == 0)
598 mnBcaSlotsCol = nSlotsCol;
599 assert(nSlotsCol == mnBcaSlotsCol);
600 nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
601 nCol1 = nCol2;
602 nCol2 *= 2;
603 nSliceCol *= 2;
605 mnBcaSlots = nSlots;
606 #ifdef DBG_UTIL
607 DoChecks();
608 #endif
611 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
613 aTableSlotsMap.clear();
614 pBCAlways.reset();
615 // Areas to-be-erased still present is a serious error in handling, but at
616 // this stage there's nothing we can do anymore.
617 SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
620 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
621 const ScAddress& rAddress ) const
623 SCROW nRow = rAddress.Row();
624 SCCOL nCol = rAddress.Col();
625 if ( !pDoc->ValidRow(nRow) || !pDoc->ValidCol(nCol) )
627 OSL_FAIL( "Row/Col invalid, using first slot!" );
628 return 0;
630 for (const ScSlotData& rSD : maSlotDistribution)
632 if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
634 assert(nRow >= rSD.nStartRow);
635 assert(nCol >= rSD.nStartCol);
636 SCSIZE slot = rSD.nCumulatedRow
637 + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
638 + rSD.nCumulatedCol
639 + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
640 assert(slot < mnBcaSlots);
641 return slot;
644 OSL_FAIL( "No slot found, using last!" );
645 return mnBcaSlots - 1;
648 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
649 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
651 rStart = ComputeSlotOffset( rRange.aStart );
652 rEnd = ComputeSlotOffset( rRange.aEnd );
653 // count of row slots per column minus one
654 rRowBreak = ComputeSlotOffset(
655 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
658 static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
659 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
661 if ( nOff < nBreak )
663 ++nOff;
664 ++pp;
666 else
668 nStart += nBcaSlotsCol;
669 nOff = nStart;
670 pp = ppSlots + nOff;
671 nBreak = nOff + nRowBreak;
675 #ifdef DBG_UTIL
676 static void compare(SCSIZE value1, SCSIZE value2, int line)
678 if(value1!=value2)
679 SAL_WARN("sc", "V1:" << value1 << " V2:" << value2 << " (" << line << ")");
680 assert(value1 == value2);
683 // Basic checks that the calculations work correctly.
684 void ScBroadcastAreaSlotMachine::DoChecks()
686 // Copy&paste from the ctor.
687 constexpr SCSIZE nSliceRow = 128;
688 constexpr SCSIZE nSliceCol = 16;
689 // First and second column are in the same slice and so get the same slot.
690 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
691 // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
692 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
693 ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
694 compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
695 ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
696 // Check that last cell is the last slot.
697 compare( ComputeSlotOffset( ScAddress( pDoc->GetMaxColCount() - 1, pDoc->GetMaxRowCount() - 1, 0 )),
698 mnBcaSlots - 1, __LINE__ );
699 // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
700 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
702 const ScSlotData& s1 = maSlotDistribution[ i ];
703 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
704 if( s1.nStartCol == s2.nStartCol )
706 assert( s1.nStopRow == s2.nStartRow );
707 compare( ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow - 1, 0 )),
708 ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
711 // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
712 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
714 const ScSlotData& s1 = maSlotDistribution[ i ];
715 for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
717 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
718 if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
720 assert( s1.nStopRow == s2.nStartRow );
721 compare( ComputeSlotOffset( ScAddress( s1.nStopCol - 1, s1.nStartRow, 0 )),
722 ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
726 // Iterate all slots.
727 ScRange range( ScAddress( 0, 0, 0 ), ScAddress( pDoc->MaxCol(), pDoc->MaxRow(), 0 ));
728 SCSIZE nStart, nEnd, nRowBreak;
729 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
730 assert( nStart == 0 );
731 assert( nEnd == mnBcaSlots - 1 );
732 SCSIZE nOff = nStart;
733 SCSIZE nBreak = nOff + nRowBreak;
734 std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
735 ScBroadcastAreaSlot** ppSlots = slots.get();
736 ScBroadcastAreaSlot** pp = ppSlots;
737 while ( nOff <= nEnd )
739 SCSIZE previous = nOff;
740 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
741 compare( nOff, previous + 1, __LINE__ );
743 // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
744 range = ScRange( ScAddress( 0, pDoc->MaxRow(), 0 ),
745 ScAddress( pDoc->MaxCol(), pDoc->MaxRow() - 1, 0 ));
746 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
747 assert( nStart == mnBcaSlotsCol - 1 );
748 assert( nEnd == mnBcaSlots - 1 );
749 nOff = nStart;
750 nBreak = nOff + nRowBreak;
751 ppSlots = slots.get();
752 pp = ppSlots;
753 while ( nOff <= nEnd )
755 SCSIZE previous = nOff;
756 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
757 compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
760 #endif
762 void ScBroadcastAreaSlotMachine::StartListeningArea(
763 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
765 if ( rRange == BCA_LISTEN_ALWAYS )
767 if ( !pBCAlways )
768 pBCAlways.reset( new SvtBroadcaster );
769 pListener->StartListening( *pBCAlways );
771 else
773 // A new area needs to be inserted to the corresponding slots, for 3D
774 // ranges for all sheets, do not slice into per sheet areas or the
775 // !bDone will break too early (i.e. after the first sheet) if
776 // subsequent listeners are to be added.
777 ScBroadcastArea* pArea = nullptr;
778 bool bDone = false;
779 for (SCTAB nTab = rRange.aStart.Tab();
780 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
782 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
783 if (iTab == aTableSlotsMap.end())
784 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
785 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
786 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
787 SCSIZE nStart, nEnd, nRowBreak;
788 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
789 SCSIZE nOff = nStart;
790 SCSIZE nBreak = nOff + nRowBreak;
791 ScBroadcastAreaSlot** pp = ppSlots + nOff;
792 while ( !bDone && nOff <= nEnd )
794 if ( !*pp )
795 *pp = new ScBroadcastAreaSlot( pDoc, this );
796 if (!pArea)
798 // If the call to StartListeningArea didn't create the
799 // ScBroadcastArea, listeners were added to an already
800 // existing identical area that doesn't need to be inserted
801 // to slots again.
802 if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
803 bDone = true;
805 else
806 (*pp)->InsertListeningArea( pArea);
807 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
813 void ScBroadcastAreaSlotMachine::EndListeningArea(
814 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
816 if ( rRange == BCA_LISTEN_ALWAYS )
818 if ( pBCAlways )
820 pListener->EndListening( *pBCAlways);
821 if (!pBCAlways->HasListeners())
823 pBCAlways.reset();
827 else
829 SCTAB nEndTab = rRange.aEnd.Tab();
830 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
831 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
833 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
834 SCSIZE nStart, nEnd, nRowBreak;
835 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
836 SCSIZE nOff = nStart;
837 SCSIZE nBreak = nOff + nRowBreak;
838 ScBroadcastAreaSlot** pp = ppSlots + nOff;
839 ScBroadcastArea* pArea = nullptr;
840 if (nOff == 0 && nEnd == mnBcaSlots-1)
842 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
843 // happen for insertion and deletion of sheets.
844 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
847 if ( *pp )
848 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
849 } while (++pp < pStop);
851 else
853 while ( nOff <= nEnd )
855 if ( *pp )
856 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
857 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
864 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
866 bool bBroadcasted = false;
867 SCTAB nEndTab = rRange.aEnd.Tab();
868 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
869 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
871 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
872 SCSIZE nStart, nEnd, nRowBreak;
873 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
874 SCSIZE nOff = nStart;
875 SCSIZE nBreak = nOff + nRowBreak;
876 ScBroadcastAreaSlot** pp = ppSlots + nOff;
877 while ( nOff <= nEnd )
879 if ( *pp )
880 bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
881 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
884 return bBroadcasted;
887 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
889 const ScAddress& rAddress = rHint.GetStartAddress();
890 if ( rAddress == BCA_BRDCST_ALWAYS )
892 if ( pBCAlways )
894 pBCAlways->Broadcast( rHint );
895 return true;
897 else
898 return false;
900 else
902 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
903 if (iTab == aTableSlotsMap.end())
904 return false;
905 // Process all slots for the given row range.
906 ScRange broadcastRange( rAddress,
907 ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
908 bool bBroadcasted = false;
909 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
910 SCSIZE nStart, nEnd, nRowBreak;
911 ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
912 SCSIZE nOff = nStart;
913 SCSIZE nBreak = nOff + nRowBreak;
914 ScBroadcastAreaSlot** pp = ppSlots + nOff;
915 while ( nOff <= nEnd )
917 if ( *pp )
918 bBroadcasted |= (*pp)->AreaBroadcast( rHint );
919 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
921 return bBroadcasted;
925 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
926 const ScRange& rRange )
928 SCTAB nEndTab = rRange.aEnd.Tab();
929 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
930 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
932 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
933 SCSIZE nStart, nEnd, nRowBreak;
934 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
935 SCSIZE nOff = nStart;
936 SCSIZE nBreak = nOff + nRowBreak;
937 ScBroadcastAreaSlot** pp = ppSlots + nOff;
938 if (nOff == 0 && nEnd == mnBcaSlots-1)
940 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
941 // happen for insertion and deletion of sheets.
942 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
945 if ( *pp )
946 (*pp)->DelBroadcastAreasInRange( rRange );
947 } while (++pp < pStop);
949 else
951 while ( nOff <= nEnd )
953 if ( *pp )
954 (*pp)->DelBroadcastAreasInRange( rRange );
955 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
961 // for all affected: remove, chain, update range, insert, and maybe delete
962 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
963 UpdateRefMode eUpdateRefMode,
964 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
966 // remove affected and put in chain
967 SCTAB nEndTab = rRange.aEnd.Tab();
968 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
969 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
971 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
972 SCSIZE nStart, nEnd, nRowBreak;
973 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
974 SCSIZE nOff = nStart;
975 SCSIZE nBreak = nOff + nRowBreak;
976 ScBroadcastAreaSlot** pp = ppSlots + nOff;
977 if (nOff == 0 && nEnd == mnBcaSlots-1)
979 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
980 // happen for insertion and deletion of sheets.
981 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
984 if ( *pp )
985 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
986 } while (++pp < pStop);
988 else
990 while ( nOff <= nEnd )
992 if ( *pp )
993 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
994 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
999 // Updating an area's range will modify the hash key, remove areas from all
1000 // affected slots. Will be reinserted later with the updated range.
1001 ScBroadcastArea* pChain = pUpdateChain;
1002 while (pChain)
1004 ScBroadcastArea* pArea = pChain;
1005 pChain = pArea->GetUpdateChainNext();
1006 ScRange aRange( pArea->GetRange());
1007 // remove from slots
1008 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
1010 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1011 if (iTab == aTableSlotsMap.end())
1013 OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
1014 continue; // for
1016 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1017 SCSIZE nStart, nEnd, nRowBreak;
1018 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1019 SCSIZE nOff = nStart;
1020 SCSIZE nBreak = nOff + nRowBreak;
1021 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1022 while ( nOff <= nEnd && pArea->GetRef() )
1024 if (*pp)
1025 (*pp)->UpdateRemoveArea( pArea);
1026 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1032 // shift sheets
1033 if (nDz)
1035 if (nDz < 0)
1037 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1038 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
1039 // Remove sheets, if any, iDel or/and iTab may as well point to end().
1040 while (iDel != iTab)
1042 iDel = aTableSlotsMap.erase(iDel);
1044 // shift remaining down
1045 while (iTab != aTableSlotsMap.end())
1047 SCTAB nTab = (*iTab).first + nDz;
1048 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1049 iTab = aTableSlotsMap.erase(iTab);
1052 else
1054 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1055 if (iStop != aTableSlotsMap.end())
1057 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
1058 if (!bStopIsBegin)
1059 --iStop;
1060 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
1061 --iTab;
1062 while (iTab != iStop)
1064 SCTAB nTab = (*iTab).first + nDz;
1065 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1066 aTableSlotsMap.erase( iTab--);
1068 // Shift the very first, iTab==iStop in this case.
1069 if (bStopIsBegin)
1071 SCTAB nTab = (*iTab).first + nDz;
1072 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1073 aTableSlotsMap.erase( iStop);
1079 // work off chain
1080 SCCOL nCol1, nCol2, theCol1, theCol2;
1081 SCROW nRow1, nRow2, theRow1, theRow2;
1082 SCTAB nTab1, nTab2, theTab1, theTab2;
1083 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1084 while ( pUpdateChain )
1086 ScBroadcastArea* pArea = pUpdateChain;
1087 ScRange aRange( pArea->GetRange());
1088 pUpdateChain = pArea->GetUpdateChainNext();
1090 // update range
1091 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
1092 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
1093 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
1094 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
1096 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
1097 pArea->UpdateRange( aRange );
1098 // For DDE and ScLookupCache
1099 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
1102 // insert to slots
1103 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
1105 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1106 if (iTab == aTableSlotsMap.end())
1107 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
1108 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
1109 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1110 SCSIZE nStart, nEnd, nRowBreak;
1111 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1112 SCSIZE nOff = nStart;
1113 SCSIZE nBreak = nOff + nRowBreak;
1114 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1115 while ( nOff <= nEnd )
1117 if (!*pp)
1118 *pp = new ScBroadcastAreaSlot( pDoc, this );
1119 (*pp)->UpdateInsert( pArea );
1120 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1124 // unchain
1125 pArea->SetUpdateChainNext( nullptr );
1126 pArea->SetInUpdateChain( false );
1128 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
1129 // already executed in UpdateRemove().
1130 if (!pArea->GetRef())
1131 delete pArea;
1133 pEOUpdateChain = nullptr;
1136 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
1138 ++nInBulkBroadcast;
1141 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId )
1143 if (nInBulkBroadcast <= 0)
1144 return;
1146 if (--nInBulkBroadcast == 0)
1148 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
1149 bool bBroadcasted = BulkBroadcastGroupAreas( nHintId );
1150 // Trigger the "final" tracking.
1151 if (pDoc->IsTrackFormulasPending())
1152 pDoc->FinalTrackFormulas( nHintId );
1153 else if (bBroadcasted)
1154 pDoc->TrackFormulas( nHintId );
1158 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
1160 return aBulkBroadcastAreas.insert( pArea ).second;
1163 void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange )
1165 BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
1166 if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
1168 // Insert a new one.
1169 it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
1172 sc::ColumnSpanSet& rSet = it->second;
1173 rSet.set(*pDoc, rRange, true);
1176 bool ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas( SfxHintId nHintId )
1178 if (m_BulkGroupAreas.empty())
1179 return false;
1181 sc::BulkDataHint aHint( *pDoc, nHintId);
1183 bool bBroadcasted = false;
1184 for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
1186 assert(pArea);
1187 SvtBroadcaster& rBC = pArea->GetBroadcaster();
1188 if (!rBC.HasListeners())
1190 /* FIXME: find the cause where the last listener is removed and
1191 * this area is still listed here. */
1192 SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
1194 else
1196 aHint.setSpans(&rSpans);
1197 rBC.Broadcast(aHint);
1198 bBroadcasted = true;
1202 m_BulkGroupAreas.clear();
1204 return bBroadcasted;
1207 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
1209 return aBulkBroadcastAreas.erase( pArea );
1212 void ScBroadcastAreaSlotMachine::RemoveBulkGroupArea( ScBroadcastArea* pArea )
1214 m_BulkGroupAreas.erase(pArea);
1217 void ScBroadcastAreaSlotMachine::PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
1218 ScBroadcastAreas::iterator& rIter )
1220 maAreasToBeErased.emplace_back( pSlot, rIter);
1223 void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
1225 SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
1226 "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
1227 if (pSlot->IsInBroadcastIteration())
1228 return;
1230 // maAreasToBeErased is a simple vector so erasing an element may
1231 // invalidate iterators and would be inefficient anyway. Instead, copy
1232 // elements to be preserved (usually none!) to temporary vector and swap.
1233 AreasToBeErased aCopy;
1234 for (auto& rArea : maAreasToBeErased)
1236 if (rArea.first == pSlot)
1237 pSlot->EraseArea( rArea.second);
1238 else
1239 aCopy.push_back( rArea);
1241 maAreasToBeErased.swap( aCopy);
1244 std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
1245 const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
1247 std::vector<sc::AreaListener> aRet;
1249 SCTAB nEndTab = rRange.aEnd.Tab();
1250 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1251 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
1253 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1254 SCSIZE nStart, nEnd, nRowBreak;
1255 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
1256 SCSIZE nOff = nStart;
1257 SCSIZE nBreak = nOff + nRowBreak;
1258 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1259 while ( nOff <= nEnd )
1261 ScBroadcastAreaSlot* p = *pp;
1262 if (p)
1263 p->GetAllListeners(rRange, aRet, eType, eGroup);
1264 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1268 return aRet;
1271 void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState& rState) const
1273 for (const auto& [rTab, rTabSlots] : aTableSlotsMap)
1275 (void)rTab;
1277 ScBroadcastAreaSlot** pp = rTabSlots.getSlots();
1278 for (SCSIZE i = 0; i < mnBcaSlots; ++i)
1280 const ScBroadcastAreaSlot* pSlot = pp[i];
1281 if (pSlot)
1282 pSlot->CollectBroadcasterState(rState);
1287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */