1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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),
40 mbInUpdateChain(false),
41 mbGroupListening(false) {}
43 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument
* pDocument
,
44 ScBroadcastAreaSlotMachine
* pBASMa
) :
45 aTmpSeekBroadcastArea( ScRange()),
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
60 ScBroadcastArea
* pArea
= (*aIter
).mpArea
;
61 // Erase all so no hash will be accessed upon destruction of the
63 aIter
= aBroadcastAreaTbl
.erase(aIter
);
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 :-/" );
80 pShell
->SetError(SCWARN_CORE_HARD_RECALC
);
82 pDoc
->SetAutoCalc( false );
83 eState
= ScDocument::HardRecalcState::ETERNAL
;
84 pDoc
->SetHardRecalcState( 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
)
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
;
109 rpArea
= new ScBroadcastArea( rRange
);
110 rpArea
->SetGroupListening(bGroupListening
);
111 if (aBroadcastAreaTbl
.insert( rpArea
).second
)
118 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
124 pListener
->StartListening( rpArea
->GetBroadcaster());
128 if (aBroadcastAreaTbl
.insert( rpArea
).second
)
134 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea
* pArea
)
136 OSL_ENSURE( pArea
, "InsertListeningArea: pArea NULL");
137 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL
)
139 if (aBroadcastAreaTbl
.insert( pArea
).second
)
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");
151 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
, bGroupListening
));
152 if (aIter
== aBroadcastAreaTbl
.end() || isMarkedErased( aIter
))
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
165 if (rpArea
&& !rpArea
->GetBroadcaster().HasListeners())
167 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
, bGroupListening
));
168 if (aIter
== aBroadcastAreaTbl
.end() || isMarkedErased( aIter
))
170 OSL_ENSURE( (*aIter
).mpArea
== rpArea
, "EndListeningArea: area pointer mismatch");
171 if (rpArea
->GetRef() == 1)
172 rpArea
= nullptr; // will be deleted by erase
178 ScBroadcastAreas::iterator
ScBroadcastAreaSlot::FindBroadcastArea(
179 const ScRange
& rRange
, bool bGroupListening
)
181 aTmpSeekBroadcastArea
.UpdateRange( rRange
);
182 aTmpSeekBroadcastArea
.SetGroupListening(bGroupListening
);
183 return aBroadcastAreaTbl
.find( &aTmpSeekBroadcastArea
);
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())
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
))
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())
233 if (pArea
->IsGroupListening())
235 if (pBASM
->IsInBulkBroadcast())
237 pBASM
->InsertBulkGroupArea(pArea
, aIntersection
);
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.
259 return bIsBroadcasted
;
262 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint
& rHint
)
264 if (aBroadcastAreaTbl
.empty())
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
))
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
);
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.
311 return bIsBroadcasted
;
314 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange
& rRange
)
316 if (aBroadcastAreaTbl
.empty())
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
);
338 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode
,
339 const ScRange
& rRange
, SCCOL nDx
, SCROW nDy
, SCTAB nDz
)
341 if (aBroadcastAreaTbl
.empty())
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
);
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
);
366 if (pBASM
->IsInBulkBroadcast())
367 pBASM
->RemoveBulkArea( pArea
);
368 pArea
->SetInUpdateChain( true );
369 ScBroadcastArea
* pUC
= pBASM
->GetEOUpdateChain();
371 pUC
->SetUpdateChainNext( pArea
);
372 else // no tail => no head
373 pBASM
->SetUpdateChain( pArea
);
374 pBASM
->SetEOUpdateChain( pArea
);
382 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea
* pArea
)
384 ScBroadcastAreas::iterator
aIter( aBroadcastAreaTbl
.find( pArea
));
385 if (aIter
== aBroadcastAreaTbl
.end())
387 if ((*aIter
).mpArea
!= pArea
)
388 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
391 aBroadcastAreaTbl
.erase( aIter
);
396 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea
* pArea
)
398 ::std::pair
< ScBroadcastAreas::iterator
, bool > aPair
=
399 aBroadcastAreaTbl
.insert( pArea
);
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
);
429 ScBroadcastArea
* pArea
= (*rIter
).mpArea
;
430 aBroadcastAreaTbl
.erase( rIter
);
431 if (!pArea
->DecRef())
433 if (pBASM
->IsInBulkBroadcast())
434 pBASM
->RemoveBulkGroupArea(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
))
450 ScBroadcastArea
* pArea
= (*aIter
).mpArea
;
451 const ScRange
& rAreaRange
= pArea
->GetRange();
454 case sc::ListenerGroupType::Group
:
455 if (!pArea
->IsGroupListening())
458 case sc::ListenerGroupType::Both
:
465 case sc::AreaOverlapType::Inside
:
466 if (!rRange
.Contains(rAreaRange
))
467 // The range needs to be fully inside specified range.
470 case sc::AreaOverlapType::InsideOrOverlap
:
471 if (!rRange
.Intersects(rAreaRange
))
472 // The range needs to be partially overlapping or fully inside.
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
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
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
);
517 if (auto pFGL
= dynamic_cast<const sc::FormulaGroupAreaListener
*>(pLis
); pFGL
)
519 rLisStore
.emplace_back(pFGL
);
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()
551 for ( ScBroadcastAreaSlot
** pp
= ppSlots
.get() + mnBcaSlots
; --pp
>= ppSlots
.get(); /* nothing */ )
555 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
556 ScDocument
* 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
574 // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
576 sal_Int32 nCol2
= 1024;
577 SCSIZE nSliceCol
= 16;
578 while (nCol2
<= pDoc
->GetMaxColCount())
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
;
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().
597 mnBcaSlotsCol
= nSlotsCol
;
598 assert(nSlotsCol
== mnBcaSlotsCol
);
599 nSlots
+= (nCol2
- nCol1
) / nSliceCol
* nSlotsCol
;
610 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
612 aTableSlotsMap
.clear();
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!" );
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
638 + static_cast<SCSIZE
>(nCol
- rSD
.nStartCol
) / rSD
.nSliceCol
* mnBcaSlotsCol
;
639 assert(slot
< mnBcaSlots
);
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
)
667 nStart
+= nBcaSlotsCol
;
670 nBreak
= nOff
+ nRowBreak
;
675 static void compare(SCSIZE value1
, SCSIZE value2
, int line
)
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 );
749 nBreak
= nOff
+ nRowBreak
;
750 ppSlots
= slots
.get();
752 while ( nOff
<= nEnd
)
754 SCSIZE previous
= nOff
;
755 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
756 compare( nOff
, previous
+ mnBcaSlotsCol
, __LINE__
);
761 void ScBroadcastAreaSlotMachine::StartListeningArea(
762 const ScRange
& rRange
, bool bGroupListening
, SvtListener
* pListener
)
764 if ( rRange
== BCA_LISTEN_ALWAYS
)
767 pBCAlways
.reset( new SvtBroadcaster
);
768 pListener
->StartListening( *pBCAlways
);
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;
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
)
794 *pp
= new ScBroadcastAreaSlot( pDoc
, this );
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
801 if (!(*pp
)->StartListeningArea( rRange
, bGroupListening
, pListener
, pArea
))
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
)
819 pListener
->EndListening( *pBCAlways
);
820 if (!pBCAlways
->HasListeners())
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
;
847 (*pp
)->EndListeningArea( rRange
, bGroupListening
, pListener
, pArea
);
848 } while (++pp
< pStop
);
852 while ( nOff
<= nEnd
)
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
)
879 bBroadcasted
|= (*pp
)->AreaBroadcast( rRange
, nHint
);
880 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
886 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint
& rHint
) const
888 const ScAddress
& rAddress
= rHint
.GetStartAddress();
889 if ( rAddress
== BCA_BRDCST_ALWAYS
)
893 pBCAlways
->Broadcast( rHint
);
901 TableSlotsMap::const_iterator
iTab( aTableSlotsMap
.find( rAddress
.Tab()));
902 if (iTab
== aTableSlotsMap
.end())
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
)
917 bBroadcasted
|= (*pp
)->AreaBroadcast( rHint
);
918 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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
;
945 (*pp
)->DelBroadcastAreasInRange( rRange
);
946 } while (++pp
< pStop
);
950 while ( nOff
<= nEnd
)
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
;
984 (*pp
)->UpdateRemove( eUpdateRefMode
, rRange
, nDx
, nDy
, nDz
);
985 } while (++pp
< pStop
);
989 while ( nOff
<= nEnd
)
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
;
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?!?");
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() )
1024 (*pp
)->UpdateRemoveArea( pArea
);
1025 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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
);
1053 TableSlotsMap::iterator
iStop( aTableSlotsMap
.lower_bound( rRange
.aStart
.Tab()));
1054 if (iStop
!= aTableSlotsMap
.end())
1056 bool bStopIsBegin
= (iStop
== aTableSlotsMap
.begin());
1059 TableSlotsMap::iterator
iTab( aTableSlotsMap
.end());
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.
1070 SCTAB nTab
= (*iTab
).first
+ nDz
;
1071 aTableSlotsMap
.emplace(nTab
, std::move((*iTab
).second
));
1072 aTableSlotsMap
.erase( iStop
);
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();
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
) );
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
)
1117 *pp
= new ScBroadcastAreaSlot( pDoc
, this );
1118 (*pp
)->UpdateInsert( pArea
);
1119 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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())
1132 pEOUpdateChain
= nullptr;
1135 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
1140 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId
)
1142 if (nInBulkBroadcast
<= 0)
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())
1180 sc::BulkDataHint
aHint( *pDoc
);
1182 bool bBroadcasted
= false;
1183 for (const auto& [pArea
, rSpans
] : m_BulkGroupAreas
)
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");
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())
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
);
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
;
1262 p
->GetAllListeners(rRange
, aRet
, eType
, eGroup
);
1263 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
1270 void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState
& rState
) const
1272 for (const auto& [rTab
, rTabSlots
] : aTableSlotsMap
)
1276 ScBroadcastAreaSlot
** pp
= rTabSlots
.getSlots();
1277 for (SCSIZE i
= 0; i
< mnBcaSlots
; ++i
)
1279 const ScBroadcastAreaSlot
* pSlot
= pp
[i
];
1281 pSlot
->CollectBroadcasterState(rState
);
1286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */