Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / bcaslot.cxx
blob92f694c3071b390a4e85079d988781d37502ee28
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 <svl/listener.hxx>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <document.hxx>
25 #include <docsh.hxx>
26 #include <brdcst.hxx>
27 #include <bcaslot.hxx>
28 #include <scerrors.hxx>
29 #include <refupdat.hxx>
30 #include <bulkdatahint.hxx>
31 #include <columnspanset.hxx>
32 #include <formulacell.hxx>
33 #include <grouparealistener.hxx>
34 #include <broadcast.hxx>
36 ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
37 pUpdateChainNext(nullptr),
38 aRange(rRange),
39 nRefCount(0),
40 mbInUpdateChain(false),
41 mbGroupListening(false) {}
43 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
44 ScBroadcastAreaSlotMachine* pBASMa ) :
45 aTmpSeekBroadcastArea( ScRange()),
46 pDoc( pDocument ),
47 pBASM( pBASMa ),
48 mbInBroadcastIteration( false),
49 mbHasErasedArea(false)
53 ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
55 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
56 aIter != aBroadcastAreaTbl.end(); /* none */)
58 // Prevent hash from accessing dangling pointer in case area is
59 // deleted.
60 ScBroadcastArea* pArea = (*aIter).mpArea;
61 // Erase all so no hash will be accessed upon destruction of the
62 // unordered_map.
63 aIter = aBroadcastAreaTbl.erase(aIter);
64 if (!pArea->DecRef())
65 delete pArea;
69 ScDocument::HardRecalcState ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
71 ScDocument::HardRecalcState eState = pDoc->GetHardRecalcState();
72 if (eState == ScDocument::HardRecalcState::OFF)
74 if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
75 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
76 ScDocShell* pShell = pDoc->GetDocumentShell();
77 OSL_ENSURE( pShell, "Missing DocShell :-/" );
79 if ( pShell )
80 pShell->SetError(SCWARN_CORE_HARD_RECALC);
82 pDoc->SetAutoCalc( false );
83 eState = ScDocument::HardRecalcState::ETERNAL;
84 pDoc->SetHardRecalcState( eState );
87 return eState;
90 bool ScBroadcastAreaSlot::StartListeningArea(
91 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
93 bool bNewArea = false;
94 OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
95 assert(!pDoc->IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
96 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
97 return false;
98 if ( !rpArea )
100 // Even if most times the area doesn't exist yet and immediately trying
101 // to new and insert it would save an attempt to find it, on massive
102 // operations like identical large [HV]LOOKUP() areas the new/delete
103 // would add quite some penalty for all but the first formula cell.
104 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
105 if (aIter != aBroadcastAreaTbl.end())
106 rpArea = (*aIter).mpArea;
107 else
109 rpArea = new ScBroadcastArea( rRange);
110 rpArea->SetGroupListening(bGroupListening);
111 if (aBroadcastAreaTbl.insert( rpArea).second)
113 rpArea->IncRef();
114 bNewArea = true;
116 else
118 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
119 delete rpArea;
120 rpArea = nullptr;
123 if (rpArea)
124 pListener->StartListening( rpArea->GetBroadcaster());
126 else
128 if (aBroadcastAreaTbl.insert( rpArea).second)
129 rpArea->IncRef();
131 return bNewArea;
134 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
136 OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
137 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
138 return;
139 if (aBroadcastAreaTbl.insert( pArea).second)
140 pArea->IncRef();
143 // If rpArea != NULL then no listeners are stopped, only the area is removed
144 // and the reference count decremented.
145 void ScBroadcastAreaSlot::EndListeningArea(
146 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
148 OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
149 if ( !rpArea )
151 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
152 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
153 return;
154 rpArea = (*aIter).mpArea;
155 pListener->EndListening( rpArea->GetBroadcaster() );
156 if ( !rpArea->GetBroadcaster().HasListeners() )
157 { // if nobody is listening we can dispose it
158 if (rpArea->GetRef() == 1)
159 rpArea = nullptr; // will be deleted by erase
160 EraseArea( aIter);
163 else
165 if (rpArea && !rpArea->GetBroadcaster().HasListeners())
167 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
168 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
169 return;
170 OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
171 if (rpArea->GetRef() == 1)
172 rpArea = nullptr; // will be deleted by erase
173 EraseArea( aIter);
178 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
179 const ScRange& rRange, bool bGroupListening )
181 aTmpSeekBroadcastArea.UpdateRange( rRange);
182 aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
183 return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
186 namespace {
188 void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
190 ScHint aHint(nHint, ScAddress());
191 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
193 aHint.SetAddressTab(nTab);
194 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
196 aHint.SetAddressCol(nCol);
197 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
199 aHint.SetAddressRow(nRow);
200 rBC.Broadcast(aHint);
208 bool ScBroadcastAreaSlot::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
210 if (aBroadcastAreaTbl.empty())
211 return false;
213 bool bInBroadcast = mbInBroadcastIteration;
214 mbInBroadcastIteration = true;
215 bool bIsBroadcasted = false;
217 mbHasErasedArea = false;
219 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
220 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
222 if (mbHasErasedArea && isMarkedErased( aIter))
223 continue;
225 ScBroadcastArea* pArea = (*aIter).mpArea;
226 const ScRange& rAreaRange = pArea->GetRange();
228 // Take the intersection of the area range and the broadcast range.
229 ScRange aIntersection = rAreaRange.Intersection(rRange);
230 if (!aIntersection.IsValid())
231 continue;
233 if (pArea->IsGroupListening())
235 if (pBASM->IsInBulkBroadcast())
237 pBASM->InsertBulkGroupArea(pArea, aIntersection);
239 else
241 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
242 bIsBroadcasted = true;
245 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
247 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
248 bIsBroadcasted = true;
252 mbInBroadcastIteration = bInBroadcast;
254 // A Notify() during broadcast may call EndListeningArea() and thus dispose
255 // an area if it was the last listener, which would invalidate an iterator
256 // pointing to it, hence the real erase is done afterwards.
257 FinallyEraseAreas();
259 return bIsBroadcasted;
262 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
264 if (aBroadcastAreaTbl.empty())
265 return false;
267 bool bInBroadcast = mbInBroadcastIteration;
268 mbInBroadcastIteration = true;
269 bool bIsBroadcasted = false;
271 mbHasErasedArea = false;
273 const ScRange aRange = rHint.GetRange();
274 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
275 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
277 if (mbHasErasedArea && isMarkedErased( aIter))
278 continue;
280 ScBroadcastArea* pArea = (*aIter).mpArea;
281 const ScRange& rAreaRange = pArea->GetRange();
282 if (rAreaRange.Intersects( aRange))
284 if (pArea->IsGroupListening())
286 if (pBASM->IsInBulkBroadcast())
288 pBASM->InsertBulkGroupArea(pArea, aRange);
290 else
292 pArea->GetBroadcaster().Broadcast( rHint);
293 bIsBroadcasted = true;
296 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
298 pArea->GetBroadcaster().Broadcast( rHint);
299 bIsBroadcasted = true;
304 mbInBroadcastIteration = bInBroadcast;
306 // A Notify() during broadcast may call EndListeningArea() and thus dispose
307 // an area if it was the last listener, which would invalidate an iterator
308 // pointing to it, hence the real erase is done afterwards.
309 FinallyEraseAreas();
311 return bIsBroadcasted;
314 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
316 if (aBroadcastAreaTbl.empty())
317 return;
318 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
319 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
321 const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
322 if (rRange.Contains( rAreaRange))
324 ScBroadcastArea* pArea = (*aIter).mpArea;
325 aIter = aBroadcastAreaTbl.erase(aIter); // erase before modifying
326 if (!pArea->DecRef())
328 if (pBASM->IsInBulkBroadcast())
329 pBASM->RemoveBulkArea( pArea);
330 delete pArea;
333 else
334 ++aIter;
338 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
339 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
341 if (aBroadcastAreaTbl.empty())
342 return;
344 SCCOL nCol1, nCol2, theCol1, theCol2;
345 SCROW nRow1, nRow2, theRow1, theRow2;
346 SCTAB nTab1, nTab2, theTab1, theTab2;
347 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
348 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
349 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
351 ScBroadcastArea* pArea = (*aIter).mpArea;
352 if ( pArea->IsInUpdateChain() )
354 aIter = aBroadcastAreaTbl.erase(aIter);
355 pArea->DecRef();
357 else
359 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
360 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
361 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
362 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
364 aIter = aBroadcastAreaTbl.erase(aIter);
365 pArea->DecRef();
366 if (pBASM->IsInBulkBroadcast())
367 pBASM->RemoveBulkArea( pArea);
368 pArea->SetInUpdateChain( true );
369 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
370 if ( pUC )
371 pUC->SetUpdateChainNext( pArea );
372 else // no tail => no head
373 pBASM->SetUpdateChain( pArea );
374 pBASM->SetEOUpdateChain( pArea );
376 else
377 ++aIter;
382 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
384 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
385 if (aIter == aBroadcastAreaTbl.end())
386 return;
387 if ((*aIter).mpArea != pArea)
388 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
389 else
391 aBroadcastAreaTbl.erase( aIter);
392 pArea->DecRef();
396 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
398 ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
399 aBroadcastAreaTbl.insert( pArea);
400 if (aPair.second)
401 pArea->IncRef();
402 else
404 // Identical area already exists, add listeners.
405 ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
406 if (pArea != pTarget)
408 SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
409 SvtBroadcaster::ListenersType& rListeners = pArea->GetBroadcaster().GetAllListeners();
410 for (auto& pListener : rListeners)
412 SvtListener& rListener = *pListener;
413 rListener.StartListening(rTarget);
419 void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
421 if (mbInBroadcastIteration)
423 (*rIter).mbErasure = true; // mark for erasure
424 mbHasErasedArea = true; // at least one area is marked for erasure.
425 pBASM->PushAreaToBeErased( this, rIter);
427 else
429 ScBroadcastArea* pArea = (*rIter).mpArea;
430 aBroadcastAreaTbl.erase( rIter);
431 if (!pArea->DecRef())
433 if (pBASM->IsInBulkBroadcast())
434 pBASM->RemoveBulkGroupArea(pArea);
435 delete pArea;
440 void ScBroadcastAreaSlot::GetAllListeners(
441 const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
442 sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
444 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
445 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
447 if (isMarkedErased( aIter))
448 continue;
450 ScBroadcastArea* pArea = (*aIter).mpArea;
451 const ScRange& rAreaRange = pArea->GetRange();
452 switch (eGroup)
454 case sc::ListenerGroupType::Group:
455 if (!pArea->IsGroupListening())
456 continue;
457 break;
458 case sc::ListenerGroupType::Both:
459 default:
463 switch (eType)
465 case sc::AreaOverlapType::Inside:
466 if (!rRange.Contains(rAreaRange))
467 // The range needs to be fully inside specified range.
468 continue;
469 break;
470 case sc::AreaOverlapType::InsideOrOverlap:
471 if (!rRange.Intersects(rAreaRange))
472 // The range needs to be partially overlapping or fully inside.
473 continue;
474 break;
475 case sc::AreaOverlapType::OneRowInside:
476 if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.Contains(rAreaRange))
477 // The range needs to be one single row and fully inside
478 // specified range.
479 continue;
480 break;
481 case sc::AreaOverlapType::OneColumnInside:
482 if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.Contains(rAreaRange))
483 // The range needs to be one single column and fully inside
484 // specified range.
485 continue;
486 break;
489 SvtBroadcaster::ListenersType& rLst = pArea->GetBroadcaster().GetAllListeners();
490 for (const auto& pListener : rLst)
492 sc::AreaListener aEntry;
493 aEntry.maArea = rAreaRange;
494 aEntry.mbGroupListening = pArea->IsGroupListening();
495 aEntry.mpListener = pListener;
496 rListeners.push_back(aEntry);
501 void ScBroadcastAreaSlot::CollectBroadcasterState(sc::BroadcasterState& rState) const
503 for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
505 const ScRange& rRange = rEntry.mpArea->GetRange();
506 auto aRes = rState.aAreaListenerStore.try_emplace(rRange);
507 auto& rLisStore = aRes.first->second;
509 for (const SvtListener* pLis : rEntry.mpArea->GetBroadcaster().GetAllListeners())
511 if (auto pFC = dynamic_cast<const ScFormulaCell*>(pLis); pFC)
513 rLisStore.emplace_back(pFC);
514 continue;
517 if (auto pFGL = dynamic_cast<const sc::FormulaGroupAreaListener*>(pLis); pFGL)
519 rLisStore.emplace_back(pFGL);
520 continue;
523 rLisStore.emplace_back(pLis);
528 void ScBroadcastAreaSlot::FinallyEraseAreas()
530 pBASM->FinallyEraseAreas( this);
533 // --- ScBroadcastAreaSlotMachine -------------------------------------
535 ScBroadcastAreaSlotMachine::TableSlots::TableSlots(SCSIZE nBcaSlots)
536 : mnBcaSlots(nBcaSlots)
538 ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
539 memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
542 ScBroadcastAreaSlotMachine::TableSlots::TableSlots(TableSlots&& rOther) noexcept
543 : mnBcaSlots(rOther.mnBcaSlots)
544 , ppSlots( std::move(rOther.ppSlots) )
548 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
550 if (ppSlots)
551 for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
552 delete *pp;
555 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
556 ScDocument* pDocument ) :
557 pDoc( pDocument ),
558 pUpdateChain( nullptr ),
559 pEOUpdateChain( nullptr ),
560 nInBulkBroadcast( 0 )
562 // initSlotDistribution ---------
563 // Logarithmic or any other distribution.
564 // Upper and leftmost sheet part usually is more populated and referenced and gets fine
565 // grained resolution, larger data in larger hunks.
566 // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
567 // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
568 // slice doubles (making more cells share the same slot), this distribution data is stored
569 // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
570 // again with the column slice doubling after some time.
571 // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
572 // calculations.
573 SCSIZE nSlots = 0;
574 // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
575 sal_Int32 nCol1 = 0;
576 sal_Int32 nCol2 = 1024;
577 SCSIZE nSliceCol = 16;
578 while (nCol2 <= pDoc->GetMaxColCount())
580 SCROW nRow1 = 0;
581 SCROW nRow2 = 32*1024;
582 SCSIZE nSliceRow = 128;
583 SCSIZE nSlotsCol = 0;
584 SCSIZE nSlotsStartCol = nSlots;
585 // Must be sorted by row1,row2!
586 while (nRow2 <= pDoc->GetMaxRowCount())
588 maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
589 nSlotsCol += (nRow2 - nRow1) / nSliceRow;
590 nRow1 = nRow2;
591 nRow2 *= 2;
592 nSliceRow *= 2;
594 // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
595 // to the right can be computed quickly in ComputeNextSlot().
596 if(nCol1 == 0)
597 mnBcaSlotsCol = nSlotsCol;
598 assert(nSlotsCol == mnBcaSlotsCol);
599 nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
600 nCol1 = nCol2;
601 nCol2 *= 2;
602 nSliceCol *= 2;
604 mnBcaSlots = nSlots;
605 #ifdef DBG_UTIL
606 DoChecks();
607 #endif
610 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
612 aTableSlotsMap.clear();
613 pBCAlways.reset();
614 // Areas to-be-erased still present is a serious error in handling, but at
615 // this stage there's nothing we can do anymore.
616 SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
619 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
620 const ScAddress& rAddress ) const
622 SCROW nRow = rAddress.Row();
623 SCCOL nCol = rAddress.Col();
624 if ( !pDoc->ValidRow(nRow) || !pDoc->ValidCol(nCol) )
626 OSL_FAIL( "Row/Col invalid, using first slot!" );
627 return 0;
629 for (const ScSlotData& rSD : maSlotDistribution)
631 if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
633 assert(nRow >= rSD.nStartRow);
634 assert(nCol >= rSD.nStartCol);
635 SCSIZE slot = rSD.nCumulatedRow
636 + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
637 + rSD.nCumulatedCol
638 + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
639 assert(slot < mnBcaSlots);
640 return slot;
643 OSL_FAIL( "No slot found, using last!" );
644 return mnBcaSlots - 1;
647 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
648 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
650 rStart = ComputeSlotOffset( rRange.aStart );
651 rEnd = ComputeSlotOffset( rRange.aEnd );
652 // count of row slots per column minus one
653 rRowBreak = ComputeSlotOffset(
654 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
657 static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
658 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
660 if ( nOff < nBreak )
662 ++nOff;
663 ++pp;
665 else
667 nStart += nBcaSlotsCol;
668 nOff = nStart;
669 pp = ppSlots + nOff;
670 nBreak = nOff + nRowBreak;
674 #ifdef DBG_UTIL
675 static void compare(SCSIZE value1, SCSIZE value2, int line)
677 if(value1!=value2)
678 SAL_WARN("sc", "V1:" << value1 << " V2:" << value2 << " (" << line << ")");
679 assert(value1 == value2);
682 // Basic checks that the calculations work correctly.
683 void ScBroadcastAreaSlotMachine::DoChecks()
685 // Copy&paste from the ctor.
686 constexpr SCSIZE nSliceRow = 128;
687 constexpr SCSIZE nSliceCol = 16;
688 // First and second column are in the same slice and so get the same slot.
689 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
690 // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
691 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
692 ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
693 compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
694 ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
695 // Check that last cell is the last slot.
696 compare( ComputeSlotOffset( ScAddress( pDoc->GetMaxColCount() - 1, pDoc->GetMaxRowCount() - 1, 0 )),
697 mnBcaSlots - 1, __LINE__ );
698 // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
699 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
701 const ScSlotData& s1 = maSlotDistribution[ i ];
702 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
703 if( s1.nStartCol == s2.nStartCol )
705 assert( s1.nStopRow == s2.nStartRow );
706 compare( ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow - 1, 0 )),
707 ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
710 // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
711 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
713 const ScSlotData& s1 = maSlotDistribution[ i ];
714 for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
716 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
717 if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
719 assert( s1.nStopRow == s2.nStartRow );
720 compare( ComputeSlotOffset( ScAddress( s1.nStopCol - 1, s1.nStartRow, 0 )),
721 ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
725 // Iterate all slots.
726 ScRange range( ScAddress( 0, 0, 0 ), ScAddress( pDoc->MaxCol(), pDoc->MaxRow(), 0 ));
727 SCSIZE nStart, nEnd, nRowBreak;
728 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
729 assert( nStart == 0 );
730 assert( nEnd == mnBcaSlots - 1 );
731 SCSIZE nOff = nStart;
732 SCSIZE nBreak = nOff + nRowBreak;
733 std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
734 ScBroadcastAreaSlot** ppSlots = slots.get();
735 ScBroadcastAreaSlot** pp = ppSlots;
736 while ( nOff <= nEnd )
738 SCSIZE previous = nOff;
739 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
740 compare( nOff, previous + 1, __LINE__ );
742 // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
743 range = ScRange( ScAddress( 0, pDoc->MaxRow(), 0 ),
744 ScAddress( pDoc->MaxCol(), pDoc->MaxRow() - 1, 0 ));
745 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
746 assert( nStart == mnBcaSlotsCol - 1 );
747 assert( nEnd == mnBcaSlots - 1 );
748 nOff = nStart;
749 nBreak = nOff + nRowBreak;
750 ppSlots = slots.get();
751 pp = ppSlots;
752 while ( nOff <= nEnd )
754 SCSIZE previous = nOff;
755 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
756 compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
759 #endif
761 void ScBroadcastAreaSlotMachine::StartListeningArea(
762 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
764 if ( rRange == BCA_LISTEN_ALWAYS )
766 if ( !pBCAlways )
767 pBCAlways.reset( new SvtBroadcaster );
768 pListener->StartListening( *pBCAlways );
770 else
772 // A new area needs to be inserted to the corresponding slots, for 3D
773 // ranges for all sheets, do not slice into per sheet areas or the
774 // !bDone will break too early (i.e. after the first sheet) if
775 // subsequent listeners are to be added.
776 ScBroadcastArea* pArea = nullptr;
777 bool bDone = false;
778 for (SCTAB nTab = rRange.aStart.Tab();
779 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
781 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
782 if (iTab == aTableSlotsMap.end())
783 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
784 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
785 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
786 SCSIZE nStart, nEnd, nRowBreak;
787 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
788 SCSIZE nOff = nStart;
789 SCSIZE nBreak = nOff + nRowBreak;
790 ScBroadcastAreaSlot** pp = ppSlots + nOff;
791 while ( !bDone && nOff <= nEnd )
793 if ( !*pp )
794 *pp = new ScBroadcastAreaSlot( pDoc, this );
795 if (!pArea)
797 // If the call to StartListeningArea didn't create the
798 // ScBroadcastArea, listeners were added to an already
799 // existing identical area that doesn't need to be inserted
800 // to slots again.
801 if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
802 bDone = true;
804 else
805 (*pp)->InsertListeningArea( pArea);
806 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
812 void ScBroadcastAreaSlotMachine::EndListeningArea(
813 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
815 if ( rRange == BCA_LISTEN_ALWAYS )
817 if ( pBCAlways )
819 pListener->EndListening( *pBCAlways);
820 if (!pBCAlways->HasListeners())
822 pBCAlways.reset();
826 else
828 SCTAB nEndTab = rRange.aEnd.Tab();
829 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
830 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
832 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
833 SCSIZE nStart, nEnd, nRowBreak;
834 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
835 SCSIZE nOff = nStart;
836 SCSIZE nBreak = nOff + nRowBreak;
837 ScBroadcastAreaSlot** pp = ppSlots + nOff;
838 ScBroadcastArea* pArea = nullptr;
839 if (nOff == 0 && nEnd == mnBcaSlots-1)
841 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
842 // happen for insertion and deletion of sheets.
843 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
846 if ( *pp )
847 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
848 } while (++pp < pStop);
850 else
852 while ( nOff <= nEnd )
854 if ( *pp )
855 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
856 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
863 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
865 bool bBroadcasted = false;
866 SCTAB nEndTab = rRange.aEnd.Tab();
867 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
868 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
870 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
871 SCSIZE nStart, nEnd, nRowBreak;
872 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
873 SCSIZE nOff = nStart;
874 SCSIZE nBreak = nOff + nRowBreak;
875 ScBroadcastAreaSlot** pp = ppSlots + nOff;
876 while ( nOff <= nEnd )
878 if ( *pp )
879 bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
880 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
883 return bBroadcasted;
886 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
888 const ScAddress& rAddress = rHint.GetStartAddress();
889 if ( rAddress == BCA_BRDCST_ALWAYS )
891 if ( pBCAlways )
893 pBCAlways->Broadcast( rHint );
894 return true;
896 else
897 return false;
899 else
901 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
902 if (iTab == aTableSlotsMap.end())
903 return false;
904 // Process all slots for the given row range.
905 ScRange broadcastRange( rAddress,
906 ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
907 bool bBroadcasted = false;
908 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
909 SCSIZE nStart, nEnd, nRowBreak;
910 ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
911 SCSIZE nOff = nStart;
912 SCSIZE nBreak = nOff + nRowBreak;
913 ScBroadcastAreaSlot** pp = ppSlots + nOff;
914 while ( nOff <= nEnd )
916 if ( *pp )
917 bBroadcasted |= (*pp)->AreaBroadcast( rHint );
918 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
920 return bBroadcasted;
924 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
925 const ScRange& rRange )
927 SCTAB nEndTab = rRange.aEnd.Tab();
928 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
929 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
931 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
932 SCSIZE nStart, nEnd, nRowBreak;
933 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
934 SCSIZE nOff = nStart;
935 SCSIZE nBreak = nOff + nRowBreak;
936 ScBroadcastAreaSlot** pp = ppSlots + nOff;
937 if (nOff == 0 && nEnd == mnBcaSlots-1)
939 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
940 // happen for insertion and deletion of sheets.
941 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
944 if ( *pp )
945 (*pp)->DelBroadcastAreasInRange( rRange );
946 } while (++pp < pStop);
948 else
950 while ( nOff <= nEnd )
952 if ( *pp )
953 (*pp)->DelBroadcastAreasInRange( rRange );
954 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
960 // for all affected: remove, chain, update range, insert, and maybe delete
961 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
962 UpdateRefMode eUpdateRefMode,
963 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
965 // remove affected and put in chain
966 SCTAB nEndTab = rRange.aEnd.Tab();
967 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
968 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
970 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
971 SCSIZE nStart, nEnd, nRowBreak;
972 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
973 SCSIZE nOff = nStart;
974 SCSIZE nBreak = nOff + nRowBreak;
975 ScBroadcastAreaSlot** pp = ppSlots + nOff;
976 if (nOff == 0 && nEnd == mnBcaSlots-1)
978 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
979 // happen for insertion and deletion of sheets.
980 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
983 if ( *pp )
984 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
985 } while (++pp < pStop);
987 else
989 while ( nOff <= nEnd )
991 if ( *pp )
992 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
993 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
998 // Updating an area's range will modify the hash key, remove areas from all
999 // affected slots. Will be reinserted later with the updated range.
1000 ScBroadcastArea* pChain = pUpdateChain;
1001 while (pChain)
1003 ScBroadcastArea* pArea = pChain;
1004 pChain = pArea->GetUpdateChainNext();
1005 ScRange aRange( pArea->GetRange());
1006 // remove from slots
1007 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
1009 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1010 if (iTab == aTableSlotsMap.end())
1012 OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
1013 continue; // for
1015 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1016 SCSIZE nStart, nEnd, nRowBreak;
1017 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1018 SCSIZE nOff = nStart;
1019 SCSIZE nBreak = nOff + nRowBreak;
1020 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1021 while ( nOff <= nEnd && pArea->GetRef() )
1023 if (*pp)
1024 (*pp)->UpdateRemoveArea( pArea);
1025 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1031 // shift sheets
1032 if (nDz)
1034 if (nDz < 0)
1036 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1037 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
1038 // Remove sheets, if any, iDel or/and iTab may as well point to end().
1039 while (iDel != iTab)
1041 iDel = aTableSlotsMap.erase(iDel);
1043 // shift remaining down
1044 while (iTab != aTableSlotsMap.end())
1046 SCTAB nTab = (*iTab).first + nDz;
1047 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1048 iTab = aTableSlotsMap.erase(iTab);
1051 else
1053 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1054 if (iStop != aTableSlotsMap.end())
1056 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
1057 if (!bStopIsBegin)
1058 --iStop;
1059 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
1060 --iTab;
1061 while (iTab != iStop)
1063 SCTAB nTab = (*iTab).first + nDz;
1064 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1065 aTableSlotsMap.erase( iTab--);
1067 // Shift the very first, iTab==iStop in this case.
1068 if (bStopIsBegin)
1070 SCTAB nTab = (*iTab).first + nDz;
1071 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1072 aTableSlotsMap.erase( iStop);
1078 // work off chain
1079 SCCOL nCol1, nCol2, theCol1, theCol2;
1080 SCROW nRow1, nRow2, theRow1, theRow2;
1081 SCTAB nTab1, nTab2, theTab1, theTab2;
1082 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1083 while ( pUpdateChain )
1085 ScBroadcastArea* pArea = pUpdateChain;
1086 ScRange aRange( pArea->GetRange());
1087 pUpdateChain = pArea->GetUpdateChainNext();
1089 // update range
1090 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
1091 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
1092 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
1093 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
1095 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
1096 pArea->UpdateRange( aRange );
1097 // For DDE and ScLookupCache
1098 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
1101 // insert to slots
1102 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
1104 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1105 if (iTab == aTableSlotsMap.end())
1106 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
1107 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
1108 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1109 SCSIZE nStart, nEnd, nRowBreak;
1110 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1111 SCSIZE nOff = nStart;
1112 SCSIZE nBreak = nOff + nRowBreak;
1113 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1114 while ( nOff <= nEnd )
1116 if (!*pp)
1117 *pp = new ScBroadcastAreaSlot( pDoc, this );
1118 (*pp)->UpdateInsert( pArea );
1119 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1123 // unchain
1124 pArea->SetUpdateChainNext( nullptr );
1125 pArea->SetInUpdateChain( false );
1127 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
1128 // already executed in UpdateRemove().
1129 if (!pArea->GetRef())
1130 delete pArea;
1132 pEOUpdateChain = nullptr;
1135 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
1137 ++nInBulkBroadcast;
1140 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId )
1142 if (nInBulkBroadcast <= 0)
1143 return;
1145 if (--nInBulkBroadcast == 0)
1147 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
1148 bool bBroadcasted = BulkBroadcastGroupAreas();
1149 // Trigger the "final" tracking.
1150 if (pDoc->IsTrackFormulasPending())
1151 pDoc->FinalTrackFormulas( nHintId );
1152 else if (bBroadcasted)
1153 pDoc->TrackFormulas( nHintId );
1157 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
1159 return aBulkBroadcastAreas.insert( pArea ).second;
1162 void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange )
1164 BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
1165 if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
1167 // Insert a new one.
1168 it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
1171 sc::ColumnSpanSet& rSet = it->second;
1172 rSet.set(*pDoc, rRange, true);
1175 bool ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas()
1177 if (m_BulkGroupAreas.empty())
1178 return false;
1180 sc::BulkDataHint aHint( *pDoc );
1182 bool bBroadcasted = false;
1183 for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
1185 assert(pArea);
1186 SvtBroadcaster& rBC = pArea->GetBroadcaster();
1187 if (!rBC.HasListeners())
1189 /* FIXME: find the cause where the last listener is removed and
1190 * this area is still listed here. */
1191 SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
1193 else
1195 aHint.setSpans(&rSpans);
1196 rBC.Broadcast(aHint);
1197 bBroadcasted = true;
1201 m_BulkGroupAreas.clear();
1203 return bBroadcasted;
1206 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
1208 return aBulkBroadcastAreas.erase( pArea );
1211 void ScBroadcastAreaSlotMachine::RemoveBulkGroupArea( ScBroadcastArea* pArea )
1213 m_BulkGroupAreas.erase(pArea);
1216 void ScBroadcastAreaSlotMachine::PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
1217 ScBroadcastAreas::iterator& rIter )
1219 maAreasToBeErased.emplace_back( pSlot, rIter);
1222 void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
1224 SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
1225 "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
1226 if (pSlot->IsInBroadcastIteration())
1227 return;
1229 // maAreasToBeErased is a simple vector so erasing an element may
1230 // invalidate iterators and would be inefficient anyway. Instead, copy
1231 // elements to be preserved (usually none!) to temporary vector and swap.
1232 AreasToBeErased aCopy;
1233 for (auto& rArea : maAreasToBeErased)
1235 if (rArea.first == pSlot)
1236 pSlot->EraseArea( rArea.second);
1237 else
1238 aCopy.push_back( rArea);
1240 maAreasToBeErased.swap( aCopy);
1243 std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
1244 const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
1246 std::vector<sc::AreaListener> aRet;
1248 SCTAB nEndTab = rRange.aEnd.Tab();
1249 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1250 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
1252 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1253 SCSIZE nStart, nEnd, nRowBreak;
1254 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
1255 SCSIZE nOff = nStart;
1256 SCSIZE nBreak = nOff + nRowBreak;
1257 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1258 while ( nOff <= nEnd )
1260 ScBroadcastAreaSlot* p = *pp;
1261 if (p)
1262 p->GetAllListeners(rRange, aRet, eType, eGroup);
1263 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1267 return aRet;
1270 void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState& rState) const
1272 for (const auto& [rTab, rTabSlots] : aTableSlotsMap)
1274 (void)rTab;
1276 ScBroadcastAreaSlot** pp = rTabSlots.getSlots();
1277 for (SCSIZE i = 0; i < mnBcaSlots; ++i)
1279 const ScBroadcastAreaSlot* pSlot = pp[i];
1280 if (pSlot)
1281 pSlot->CollectBroadcasterState(rState);
1286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */