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 <sfx2/objsh.hxx>
21 #include <svl/listener.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
25 #include <document.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),
41 mbInUpdateChain(false),
42 mbGroupListening(false) {}
44 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument
* pDocument
,
45 ScBroadcastAreaSlotMachine
* pBASMa
) :
46 aTmpSeekBroadcastArea( ScRange()),
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
61 ScBroadcastArea
* pArea
= (*aIter
).mpArea
;
62 // Erase all so no hash will be accessed upon destruction of the
64 aIter
= aBroadcastAreaTbl
.erase(aIter
);
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 :-/" );
81 pShell
->SetError(SCWARN_CORE_HARD_RECALC
);
83 pDoc
->SetAutoCalc( false );
84 eState
= ScDocument::HardRecalcState::ETERNAL
;
85 pDoc
->SetHardRecalcState( 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
)
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
;
110 rpArea
= new ScBroadcastArea( rRange
);
111 rpArea
->SetGroupListening(bGroupListening
);
112 if (aBroadcastAreaTbl
.insert( rpArea
).second
)
119 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
125 pListener
->StartListening( rpArea
->GetBroadcaster());
129 if (aBroadcastAreaTbl
.insert( rpArea
).second
)
135 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea
* pArea
)
137 OSL_ENSURE( pArea
, "InsertListeningArea: pArea NULL");
138 if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL
)
140 if (aBroadcastAreaTbl
.insert( pArea
).second
)
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");
152 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
, bGroupListening
));
153 if (aIter
== aBroadcastAreaTbl
.end() || isMarkedErased( aIter
))
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
166 if (rpArea
&& !rpArea
->GetBroadcaster().HasListeners())
168 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
, bGroupListening
));
169 if (aIter
== aBroadcastAreaTbl
.end() || isMarkedErased( aIter
))
171 OSL_ENSURE( (*aIter
).mpArea
== rpArea
, "EndListeningArea: area pointer mismatch");
172 if (rpArea
->GetRef() == 1)
173 rpArea
= nullptr; // will be deleted by erase
179 ScBroadcastAreas::iterator
ScBroadcastAreaSlot::FindBroadcastArea(
180 const ScRange
& rRange
, bool bGroupListening
)
182 aTmpSeekBroadcastArea
.UpdateRange( rRange
);
183 aTmpSeekBroadcastArea
.SetGroupListening(bGroupListening
);
184 return aBroadcastAreaTbl
.find( &aTmpSeekBroadcastArea
);
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())
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
))
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())
234 if (pArea
->IsGroupListening())
236 if (pBASM
->IsInBulkBroadcast())
238 pBASM
->InsertBulkGroupArea(pArea
, aIntersection
);
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.
260 return bIsBroadcasted
;
263 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint
& rHint
)
265 if (aBroadcastAreaTbl
.empty())
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
))
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
);
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.
312 return bIsBroadcasted
;
315 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange
& rRange
)
317 if (aBroadcastAreaTbl
.empty())
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
);
339 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode
,
340 const ScRange
& rRange
, SCCOL nDx
, SCROW nDy
, SCTAB nDz
)
342 if (aBroadcastAreaTbl
.empty())
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
);
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
);
367 if (pBASM
->IsInBulkBroadcast())
368 pBASM
->RemoveBulkArea( pArea
);
369 pArea
->SetInUpdateChain( true );
370 ScBroadcastArea
* pUC
= pBASM
->GetEOUpdateChain();
372 pUC
->SetUpdateChainNext( pArea
);
373 else // no tail => no head
374 pBASM
->SetUpdateChain( pArea
);
375 pBASM
->SetEOUpdateChain( pArea
);
383 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea
* pArea
)
385 ScBroadcastAreas::iterator
aIter( aBroadcastAreaTbl
.find( pArea
));
386 if (aIter
== aBroadcastAreaTbl
.end())
388 if ((*aIter
).mpArea
!= pArea
)
389 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
392 aBroadcastAreaTbl
.erase( aIter
);
397 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea
* pArea
)
399 ::std::pair
< ScBroadcastAreas::iterator
, bool > aPair
=
400 aBroadcastAreaTbl
.insert( pArea
);
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
);
430 ScBroadcastArea
* pArea
= (*rIter
).mpArea
;
431 aBroadcastAreaTbl
.erase( rIter
);
432 if (!pArea
->DecRef())
434 if (pBASM
->IsInBulkBroadcast())
435 pBASM
->RemoveBulkGroupArea(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
))
451 ScBroadcastArea
* pArea
= (*aIter
).mpArea
;
452 const ScRange
& rAreaRange
= pArea
->GetRange();
455 case sc::ListenerGroupType::Group
:
456 if (!pArea
->IsGroupListening())
459 case sc::ListenerGroupType::Both
:
466 case sc::AreaOverlapType::Inside
:
467 if (!rRange
.Contains(rAreaRange
))
468 // The range needs to be fully inside specified range.
471 case sc::AreaOverlapType::InsideOrOverlap
:
472 if (!rRange
.Intersects(rAreaRange
))
473 // The range needs to be partially overlapping or fully inside.
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
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
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
);
518 if (auto pFGL
= dynamic_cast<const sc::FormulaGroupAreaListener
*>(pLis
); pFGL
)
520 rLisStore
.emplace_back(pFGL
);
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()
552 for ( ScBroadcastAreaSlot
** pp
= ppSlots
.get() + mnBcaSlots
; --pp
>= ppSlots
.get(); /* nothing */ )
556 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
557 ScDocument
* 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
575 // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
577 sal_Int32 nCol2
= 1024;
578 SCSIZE nSliceCol
= 16;
579 while (nCol2
<= pDoc
->GetMaxColCount())
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
;
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().
598 mnBcaSlotsCol
= nSlotsCol
;
599 assert(nSlotsCol
== mnBcaSlotsCol
);
600 nSlots
+= (nCol2
- nCol1
) / nSliceCol
* nSlotsCol
;
611 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
613 aTableSlotsMap
.clear();
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!" );
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
639 + static_cast<SCSIZE
>(nCol
- rSD
.nStartCol
) / rSD
.nSliceCol
* mnBcaSlotsCol
;
640 assert(slot
< mnBcaSlots
);
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
)
668 nStart
+= nBcaSlotsCol
;
671 nBreak
= nOff
+ nRowBreak
;
676 static void compare(SCSIZE value1
, SCSIZE value2
, int line
)
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 );
750 nBreak
= nOff
+ nRowBreak
;
751 ppSlots
= slots
.get();
753 while ( nOff
<= nEnd
)
755 SCSIZE previous
= nOff
;
756 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
757 compare( nOff
, previous
+ mnBcaSlotsCol
, __LINE__
);
762 void ScBroadcastAreaSlotMachine::StartListeningArea(
763 const ScRange
& rRange
, bool bGroupListening
, SvtListener
* pListener
)
765 if ( rRange
== BCA_LISTEN_ALWAYS
)
768 pBCAlways
.reset( new SvtBroadcaster
);
769 pListener
->StartListening( *pBCAlways
);
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;
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
)
795 *pp
= new ScBroadcastAreaSlot( pDoc
, this );
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
802 if (!(*pp
)->StartListeningArea( rRange
, bGroupListening
, pListener
, pArea
))
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
)
820 pListener
->EndListening( *pBCAlways
);
821 if (!pBCAlways
->HasListeners())
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
;
848 (*pp
)->EndListeningArea( rRange
, bGroupListening
, pListener
, pArea
);
849 } while (++pp
< pStop
);
853 while ( nOff
<= nEnd
)
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
)
880 bBroadcasted
|= (*pp
)->AreaBroadcast( rRange
, nHint
);
881 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
887 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint
& rHint
) const
889 const ScAddress
& rAddress
= rHint
.GetStartAddress();
890 if ( rAddress
== BCA_BRDCST_ALWAYS
)
894 pBCAlways
->Broadcast( rHint
);
902 TableSlotsMap::const_iterator
iTab( aTableSlotsMap
.find( rAddress
.Tab()));
903 if (iTab
== aTableSlotsMap
.end())
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
)
918 bBroadcasted
|= (*pp
)->AreaBroadcast( rHint
);
919 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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
;
946 (*pp
)->DelBroadcastAreasInRange( rRange
);
947 } while (++pp
< pStop
);
951 while ( nOff
<= nEnd
)
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
;
985 (*pp
)->UpdateRemove( eUpdateRefMode
, rRange
, nDx
, nDy
, nDz
);
986 } while (++pp
< pStop
);
990 while ( nOff
<= nEnd
)
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
;
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?!?");
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() )
1025 (*pp
)->UpdateRemoveArea( pArea
);
1026 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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
);
1054 TableSlotsMap::iterator
iStop( aTableSlotsMap
.lower_bound( rRange
.aStart
.Tab()));
1055 if (iStop
!= aTableSlotsMap
.end())
1057 bool bStopIsBegin
= (iStop
== aTableSlotsMap
.begin());
1060 TableSlotsMap::iterator
iTab( aTableSlotsMap
.end());
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.
1071 SCTAB nTab
= (*iTab
).first
+ nDz
;
1072 aTableSlotsMap
.emplace(nTab
, std::move((*iTab
).second
));
1073 aTableSlotsMap
.erase( iStop
);
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();
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
) );
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
)
1118 *pp
= new ScBroadcastAreaSlot( pDoc
, this );
1119 (*pp
)->UpdateInsert( pArea
);
1120 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
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())
1133 pEOUpdateChain
= nullptr;
1136 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
1141 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId
)
1143 if (nInBulkBroadcast
<= 0)
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())
1181 sc::BulkDataHint
aHint( *pDoc
, nHintId
);
1183 bool bBroadcasted
= false;
1184 for (const auto& [pArea
, rSpans
] : m_BulkGroupAreas
)
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");
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())
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
);
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
;
1263 p
->GetAllListeners(rRange
, aRet
, eType
, eGroup
);
1264 ComputeNextSlot( nOff
, nBreak
, pp
, nStart
, ppSlots
, nRowBreak
, mnBcaSlotsCol
);
1271 void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState
& rState
) const
1273 for (const auto& [rTab
, rTabSlots
] : aTableSlotsMap
)
1277 ScBroadcastAreaSlot
** pp
= rTabSlots
.getSlots();
1278 for (SCSIZE i
= 0; i
< mnBcaSlots
; ++i
)
1280 const ScBroadcastAreaSlot
* pSlot
= pp
[i
];
1282 pSlot
->CollectBroadcasterState(rState
);
1287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */