LibreOffice Module sc (master) 1
bcaslot.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sfx2/objsh.hxx>
21#include <svl/listener.hxx>
22#include <sal/log.hxx>
23#include <osl/diagnose.h>
24
25#include <document.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
33#if DEBUG_AREA_BROADCASTER
34#include <formulacell.hxx>
35#include <grouparealistener.hxx>
36#endif
37
39 pUpdateChainNext(nullptr),
40 aRange(rRange),
41 nRefCount(0),
42 mbInUpdateChain(false),
43 mbGroupListening(false) {}
44
47 aTmpSeekBroadcastArea( ScRange()),
48 pDoc( pDocument ),
49 pBASM( pBASMa ),
50 mbInBroadcastIteration( false),
51 mbHasErasedArea(false)
52{
53}
54
56{
57 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
58 aIter != aBroadcastAreaTbl.end(); /* none */)
59 {
60 // Prevent hash from accessing dangling pointer in case area is
61 // deleted.
62 ScBroadcastArea* pArea = (*aIter).mpArea;
63 // Erase all so no hash will be accessed upon destruction of the
64 // unordered_map.
65 aIter = aBroadcastAreaTbl.erase(aIter);
66 if (!pArea->DecRef())
67 delete pArea;
68 }
69}
70
72{
75 {
76 if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
77 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
79 OSL_ENSURE( pShell, "Missing DocShell :-/" );
80
81 if ( pShell )
83
84 pDoc->SetAutoCalc( false );
86 pDoc->SetHardRecalcState( eState );
87 }
88 }
89 return eState;
90}
91
93 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
94{
95 bool bNewArea = false;
96 OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
97 assert(!pDoc->IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
99 return false;
100 if ( !rpArea )
101 {
102 // Even if most times the area doesn't exist yet and immediately trying
103 // to new and insert it would save an attempt to find it, on massive
104 // operations like identical large [HV]LOOKUP() areas the new/delete
105 // would add quite some penalty for all but the first formula cell.
106 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
107 if (aIter != aBroadcastAreaTbl.end())
108 rpArea = (*aIter).mpArea;
109 else
110 {
111 rpArea = new ScBroadcastArea( rRange);
112 rpArea->SetGroupListening(bGroupListening);
113 if (aBroadcastAreaTbl.insert( rpArea).second)
114 {
115 rpArea->IncRef();
116 bNewArea = true;
117 }
118 else
119 {
120 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
121 delete rpArea;
122 rpArea = nullptr;
123 }
124 }
125 if (rpArea)
126 pListener->StartListening( rpArea->GetBroadcaster());
127 }
128 else
129 {
130 if (aBroadcastAreaTbl.insert( rpArea).second)
131 rpArea->IncRef();
132 }
133 return bNewArea;
134}
135
137{
138 OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
140 return;
141 if (aBroadcastAreaTbl.insert( pArea).second)
142 pArea->IncRef();
143}
144
145// If rpArea != NULL then no listeners are stopped, only the area is removed
146// and the reference count decremented.
148 const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
149{
150 OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
151 if ( !rpArea )
152 {
153 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
154 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
155 return;
156 rpArea = (*aIter).mpArea;
157 pListener->EndListening( rpArea->GetBroadcaster() );
158 if ( !rpArea->GetBroadcaster().HasListeners() )
159 { // if nobody is listening we can dispose it
160 if (rpArea->GetRef() == 1)
161 rpArea = nullptr; // will be deleted by erase
162 EraseArea( aIter);
163 }
164 }
165 else
166 {
167 if (rpArea && !rpArea->GetBroadcaster().HasListeners())
168 {
169 ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
170 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
171 return;
172 OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
173 if (rpArea->GetRef() == 1)
174 rpArea = nullptr; // will be deleted by erase
175 EraseArea( aIter);
176 }
177 }
178}
179
180ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
181 const ScRange& rRange, bool bGroupListening )
182{
186}
187
188namespace {
189
190void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
191{
192 ScHint aHint(nHint, ScAddress());
193 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
194 {
195 aHint.SetAddressTab(nTab);
196 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
197 {
198 aHint.SetAddressCol(nCol);
199 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
200 {
201 aHint.SetAddressRow(nRow);
202 rBC.Broadcast(aHint);
203 }
204 }
205 }
206}
207
208}
209
211{
212 if (aBroadcastAreaTbl.empty())
213 return false;
214
215 bool bInBroadcast = mbInBroadcastIteration;
217 bool bIsBroadcasted = false;
218
219 mbHasErasedArea = false;
220
221 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
222 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
223 {
224 if (mbHasErasedArea && isMarkedErased( aIter))
225 continue;
226
227 ScBroadcastArea* pArea = (*aIter).mpArea;
228 const ScRange& rAreaRange = pArea->GetRange();
229
230 // Take the intersection of the area range and the broadcast range.
231 ScRange aIntersection = rAreaRange.Intersection(rRange);
232 if (!aIntersection.IsValid())
233 continue;
234
235 if (pArea->IsGroupListening())
236 {
238 {
239 pBASM->InsertBulkGroupArea(pArea, aIntersection);
240 }
241 else
242 {
243 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
244 bIsBroadcasted = true;
245 }
246 }
247 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
248 {
249 broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
250 bIsBroadcasted = true;
251 }
252 }
253
254 mbInBroadcastIteration = bInBroadcast;
255
256 // A Notify() during broadcast may call EndListeningArea() and thus dispose
257 // an area if it was the last listener, which would invalidate an iterator
258 // pointing to it, hence the real erase is done afterwards.
260
261 return bIsBroadcasted;
262}
263
265{
266 if (aBroadcastAreaTbl.empty())
267 return false;
268
269 bool bInBroadcast = mbInBroadcastIteration;
271 bool bIsBroadcasted = false;
272
273 mbHasErasedArea = false;
274
275 const ScRange& rRange = rHint.GetRange();
276 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
277 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
278 {
279 if (mbHasErasedArea && isMarkedErased( aIter))
280 continue;
281
282 ScBroadcastArea* pArea = (*aIter).mpArea;
283 const ScRange& rAreaRange = pArea->GetRange();
284 if (rAreaRange.Intersects( rRange))
285 {
286 if (pArea->IsGroupListening())
287 {
289 {
290 pBASM->InsertBulkGroupArea(pArea, rRange);
291 }
292 else
293 {
294 pArea->GetBroadcaster().Broadcast( rHint);
295 bIsBroadcasted = true;
296 }
297 }
298 else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
299 {
300 pArea->GetBroadcaster().Broadcast( rHint);
301 bIsBroadcasted = true;
302 }
303 }
304 }
305
306 mbInBroadcastIteration = bInBroadcast;
307
308 // A Notify() during broadcast may call EndListeningArea() and thus dispose
309 // an area if it was the last listener, which would invalidate an iterator
310 // pointing to it, hence the real erase is done afterwards.
312
313 return bIsBroadcasted;
314}
315
317{
318 if (aBroadcastAreaTbl.empty())
319 return;
320 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
321 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
322 {
323 const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
324 if (rRange.Contains( rAreaRange))
325 {
326 ScBroadcastArea* pArea = (*aIter).mpArea;
327 aIter = aBroadcastAreaTbl.erase(aIter); // erase before modifying
328 if (!pArea->DecRef())
329 {
331 pBASM->RemoveBulkArea( pArea);
332 delete pArea;
333 }
334 }
335 else
336 ++aIter;
337 }
338}
339
341 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
342{
343 if (aBroadcastAreaTbl.empty())
344 return;
345
346 SCCOL nCol1, nCol2, theCol1, theCol2;
347 SCROW nRow1, nRow2, theRow1, theRow2;
348 SCTAB nTab1, nTab2, theTab1, theTab2;
349 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
350 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
351 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
352 {
353 ScBroadcastArea* pArea = (*aIter).mpArea;
354 if ( pArea->IsInUpdateChain() )
355 {
356 aIter = aBroadcastAreaTbl.erase(aIter);
357 pArea->DecRef();
358 }
359 else
360 {
361 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
362 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
363 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
364 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
365 {
366 aIter = aBroadcastAreaTbl.erase(aIter);
367 pArea->DecRef();
369 pBASM->RemoveBulkArea( pArea);
370 pArea->SetInUpdateChain( true );
372 if ( pUC )
373 pUC->SetUpdateChainNext( pArea );
374 else // no tail => no head
375 pBASM->SetUpdateChain( pArea );
376 pBASM->SetEOUpdateChain( pArea );
377 }
378 else
379 ++aIter;
380 }
381 }
382}
383
385{
386 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
387 if (aIter == aBroadcastAreaTbl.end())
388 return;
389 if ((*aIter).mpArea != pArea)
390 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
391 else
392 {
393 aBroadcastAreaTbl.erase( aIter);
394 pArea->DecRef();
395 }
396}
397
399{
400 ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
401 aBroadcastAreaTbl.insert( pArea);
402 if (aPair.second)
403 pArea->IncRef();
404 else
405 {
406 // Identical area already exists, add listeners.
407 ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
408 if (pArea != pTarget)
409 {
412 for (auto& pListener : rListeners)
413 {
414 SvtListener& rListener = *pListener;
415 rListener.StartListening(rTarget);
416 }
417 }
418 }
419}
420
421void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
422{
424 {
425 (*rIter).mbErasure = true; // mark for erasure
426 mbHasErasedArea = true; // at least one area is marked for erasure.
427 pBASM->PushAreaToBeErased( this, rIter);
428 }
429 else
430 {
431 ScBroadcastArea* pArea = (*rIter).mpArea;
432 aBroadcastAreaTbl.erase( rIter);
433 if (!pArea->DecRef())
434 {
437 delete pArea;
438 }
439 }
440}
441
443 const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
445{
446 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
447 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
448 {
449 if (isMarkedErased( aIter))
450 continue;
451
452 ScBroadcastArea* pArea = (*aIter).mpArea;
453 const ScRange& rAreaRange = pArea->GetRange();
454 switch (eGroup)
455 {
457 if (!pArea->IsGroupListening())
458 continue;
459 break;
461 default:
462 ;
463 }
464
465 switch (eType)
466 {
468 if (!rRange.Contains(rAreaRange))
469 // The range needs to be fully inside specified range.
470 continue;
471 break;
473 if (!rRange.Intersects(rAreaRange))
474 // The range needs to be partially overlapping or fully inside.
475 continue;
476 break;
478 if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.Contains(rAreaRange))
479 // The range needs to be one single row and fully inside
480 // specified range.
481 continue;
482 break;
484 if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.Contains(rAreaRange))
485 // The range needs to be one single column and fully inside
486 // specified range.
487 continue;
488 break;
489 }
490
492 for (const auto& pListener : rLst)
493 {
494 sc::AreaListener aEntry;
495 aEntry.maArea = rAreaRange;
496 aEntry.mbGroupListening = pArea->IsGroupListening();
497 aEntry.mpListener = pListener;
498 rListeners.push_back(aEntry);
499 }
500 }
501}
502
503#if DEBUG_AREA_BROADCASTER
504void ScBroadcastAreaSlot::Dump() const
505{
506 for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
507 {
508 const ScBroadcastArea* pArea = rEntry.mpArea;
509 const SvtBroadcaster& rBC = pArea->GetBroadcaster();
510 const SvtBroadcaster::ListenersType& rListeners = rBC.GetAllListeners();
511 size_t n = rListeners.size();
512
513 cout << " * range: " << OUStringToOString(pArea->GetRange().Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
514 << ", group: " << pArea->IsGroupListening()
515 << ", listener count: " << n << endl;
516
517 for (size_t i = 0; i < n; ++i)
518 {
519 const ScFormulaCell* pFC = dynamic_cast<const ScFormulaCell*>(rListeners[i]);
520 if (pFC)
521 {
522 cout << " * listener: formula cell: "
523 << OUStringToOString(pFC->aPos.Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
524 << endl;
525 continue;
526 }
527
528 const sc::FormulaGroupAreaListener* pFGListener = dynamic_cast<const sc::FormulaGroupAreaListener*>(rListeners[i]);
529 if (pFGListener)
530 {
531 cout << " * listener: formula group: (pos: "
532 << OUStringToOString(pFGListener->getTopCellPos().Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
533 << ", length: " << pFGListener->getGroupLength()
534 << ")" << endl;
535 continue;
536 }
537
538 cout << " * listener: unknown" << endl;
539 }
540 }
541}
542#endif
543
545{
546 pBASM->FinallyEraseAreas( this);
547}
548
549// --- ScBroadcastAreaSlotMachine -------------------------------------
550
552 : mnBcaSlots(nBcaSlots)
553{
554 ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
555 memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
556}
557
559 : mnBcaSlots(rOther.mnBcaSlots)
560 , ppSlots( std::move(rOther.ppSlots) )
561{
562}
563
565{
566 if (ppSlots)
567 for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
568 delete *pp;
569}
570
572 ScDocument* pDocument ) :
573 pDoc( pDocument ),
574 pUpdateChain( nullptr ),
575 pEOUpdateChain( nullptr ),
577{
578 // initSlotDistribution ---------
579 // Logarithmic or any other distribution.
580 // Upper and leftmost sheet part usually is more populated and referenced and gets fine
581 // grained resolution, larger data in larger hunks.
582 // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
583 // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
584 // slice doubles (making more cells share the same slot), this distribution data is stored
585 // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
586 // again with the column slice doubling after some time.
587 // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
588 // calculations.
589 SCSIZE nSlots = 0;
590 // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
591 sal_Int32 nCol1 = 0;
592 sal_Int32 nCol2 = 1024;
593 SCSIZE nSliceCol = 16;
594 while (nCol2 <= pDoc->GetMaxColCount())
595 {
596 SCROW nRow1 = 0;
597 SCROW nRow2 = 32*1024;
598 SCSIZE nSliceRow = 128;
599 SCSIZE nSlotsCol = 0;
600 SCSIZE nSlotsStartCol = nSlots;
601 // Must be sorted by row1,row2!
602 while (nRow2 <= pDoc->GetMaxRowCount())
603 {
604 maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
605 nSlotsCol += (nRow2 - nRow1) / nSliceRow;
606 nRow1 = nRow2;
607 nRow2 *= 2;
608 nSliceRow *= 2;
609 }
610 // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
611 // to the right can be computed quickly in ComputeNextSlot().
612 if(nCol1 == 0)
613 mnBcaSlotsCol = nSlotsCol;
614 assert(nSlotsCol == mnBcaSlotsCol);
615 nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
616 nCol1 = nCol2;
617 nCol2 *= 2;
618 nSliceCol *= 2;
619 }
620 mnBcaSlots = nSlots;
621#ifdef DBG_UTIL
622 DoChecks();
623#endif
624}
625
627{
628 aTableSlotsMap.clear();
629 pBCAlways.reset();
630 // Areas to-be-erased still present is a serious error in handling, but at
631 // this stage there's nothing we can do anymore.
632 SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
633}
634
636 const ScAddress& rAddress ) const
637{
638 SCROW nRow = rAddress.Row();
639 SCCOL nCol = rAddress.Col();
640 if ( !pDoc->ValidRow(nRow) || !pDoc->ValidCol(nCol) )
641 {
642 OSL_FAIL( "Row/Col invalid, using first slot!" );
643 return 0;
644 }
645 for (const ScSlotData& rSD : maSlotDistribution)
646 {
647 if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
648 {
649 assert(nRow >= rSD.nStartRow);
650 assert(nCol >= rSD.nStartCol);
651 SCSIZE slot = rSD.nCumulatedRow
652 + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
653 + rSD.nCumulatedCol
654 + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
655 assert(slot < mnBcaSlots);
656 return slot;
657 }
658 }
659 OSL_FAIL( "No slot found, using last!" );
660 return mnBcaSlots - 1;
661}
662
664 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
665{
666 rStart = ComputeSlotOffset( rRange.aStart );
667 rEnd = ComputeSlotOffset( rRange.aEnd );
668 // count of row slots per column minus one
669 rRowBreak = ComputeSlotOffset(
670 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
671}
672
673static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
674 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
675{
676 if ( nOff < nBreak )
677 {
678 ++nOff;
679 ++pp;
680 }
681 else
682 {
683 nStart += nBcaSlotsCol;
684 nOff = nStart;
685 pp = ppSlots + nOff;
686 nBreak = nOff + nRowBreak;
687 }
688}
689
690#ifdef DBG_UTIL
691static void compare(SCSIZE value1, SCSIZE value2, int line)
692{
693 if(value1!=value2)
694 SAL_WARN("sc", "V1:" << value1 << " V2:" << value2 << " (" << line << ")");
695 assert(value1 == value2);
696}
697
698// Basic checks that the calculations work correctly.
700{
701 // Copy&paste from the ctor.
702 constexpr SCSIZE nSliceRow = 128;
703 constexpr SCSIZE nSliceCol = 16;
704 // First and second column are in the same slice and so get the same slot.
705 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
706 // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
707 compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
708 ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
709 compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
710 ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
711 // Check that last cell is the last slot.
713 mnBcaSlots - 1, __LINE__ );
714 // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
715 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
716 {
717 const ScSlotData& s1 = maSlotDistribution[ i ];
718 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
719 if( s1.nStartCol == s2.nStartCol )
720 {
721 assert( s1.nStopRow == s2.nStartRow );
723 ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
724 }
725 }
726 // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
727 for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
728 {
729 const ScSlotData& s1 = maSlotDistribution[ i ];
730 for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
731 {
732 const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
733 if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
734 {
735 assert( s1.nStopRow == s2.nStartRow );
737 ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
738 }
739 }
740 }
741 // Iterate all slots.
742 ScRange range( ScAddress( 0, 0, 0 ), ScAddress( pDoc->MaxCol(), pDoc->MaxRow(), 0 ));
743 SCSIZE nStart, nEnd, nRowBreak;
744 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
745 assert( nStart == 0 );
746 assert( nEnd == mnBcaSlots - 1 );
747 SCSIZE nOff = nStart;
748 SCSIZE nBreak = nOff + nRowBreak;
749 std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
750 ScBroadcastAreaSlot** ppSlots = slots.get();
751 ScBroadcastAreaSlot** pp = ppSlots;
752 while ( nOff <= nEnd )
753 {
754 SCSIZE previous = nOff;
755 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
756 compare( nOff, previous + 1, __LINE__ );
757 }
758 // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
759 range = ScRange( ScAddress( 0, pDoc->MaxRow(), 0 ),
760 ScAddress( pDoc->MaxCol(), pDoc->MaxRow() - 1, 0 ));
761 ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
762 assert( nStart == mnBcaSlotsCol - 1 );
763 assert( nEnd == mnBcaSlots - 1 );
764 nOff = nStart;
765 nBreak = nOff + nRowBreak;
766 ppSlots = slots.get();
767 pp = ppSlots;
768 while ( nOff <= nEnd )
769 {
770 SCSIZE previous = nOff;
771 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
772 compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
773 }
774}
775#endif
776
778 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
779{
780 if ( rRange == BCA_LISTEN_ALWAYS )
781 {
782 if ( !pBCAlways )
783 pBCAlways.reset( new SvtBroadcaster );
784 pListener->StartListening( *pBCAlways );
785 }
786 else
787 {
788 // A new area needs to be inserted to the corresponding slots, for 3D
789 // ranges for all sheets, do not slice into per sheet areas or the
790 // !bDone will break too early (i.e. after the first sheet) if
791 // subsequent listeners are to be added.
792 ScBroadcastArea* pArea = nullptr;
793 bool bDone = false;
794 for (SCTAB nTab = rRange.aStart.Tab();
795 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
796 {
797 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
798 if (iTab == aTableSlotsMap.end())
799 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
800 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
801 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
802 SCSIZE nStart, nEnd, nRowBreak;
803 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
804 SCSIZE nOff = nStart;
805 SCSIZE nBreak = nOff + nRowBreak;
806 ScBroadcastAreaSlot** pp = ppSlots + nOff;
807 while ( !bDone && nOff <= nEnd )
808 {
809 if ( !*pp )
810 *pp = new ScBroadcastAreaSlot( pDoc, this );
811 if (!pArea)
812 {
813 // If the call to StartListeningArea didn't create the
814 // ScBroadcastArea, listeners were added to an already
815 // existing identical area that doesn't need to be inserted
816 // to slots again.
817 if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
818 bDone = true;
819 }
820 else
821 (*pp)->InsertListeningArea( pArea);
822 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
823 }
824 }
825 }
826}
827
829 const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
830{
831 if ( rRange == BCA_LISTEN_ALWAYS )
832 {
833 if ( pBCAlways )
834 {
835 pListener->EndListening( *pBCAlways);
836 if (!pBCAlways->HasListeners())
837 {
838 pBCAlways.reset();
839 }
840 }
841 }
842 else
843 {
844 SCTAB nEndTab = rRange.aEnd.Tab();
845 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
846 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
847 {
848 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
849 SCSIZE nStart, nEnd, nRowBreak;
850 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
851 SCSIZE nOff = nStart;
852 SCSIZE nBreak = nOff + nRowBreak;
853 ScBroadcastAreaSlot** pp = ppSlots + nOff;
854 ScBroadcastArea* pArea = nullptr;
855 if (nOff == 0 && nEnd == mnBcaSlots-1)
856 {
857 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
858 // happen for insertion and deletion of sheets.
859 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
860 do
861 {
862 if ( *pp )
863 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
864 } while (++pp < pStop);
865 }
866 else
867 {
868 while ( nOff <= nEnd )
869 {
870 if ( *pp )
871 (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
872 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
873 }
874 }
875 }
876 }
877}
878
880{
881 bool bBroadcasted = false;
882 SCTAB nEndTab = rRange.aEnd.Tab();
883 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
884 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
885 {
886 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
887 SCSIZE nStart, nEnd, nRowBreak;
888 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
889 SCSIZE nOff = nStart;
890 SCSIZE nBreak = nOff + nRowBreak;
891 ScBroadcastAreaSlot** pp = ppSlots + nOff;
892 while ( nOff <= nEnd )
893 {
894 if ( *pp )
895 bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
896 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
897 }
898 }
899 return bBroadcasted;
900}
901
903{
904 const ScAddress& rAddress = rHint.GetStartAddress();
905 if ( rAddress == BCA_BRDCST_ALWAYS )
906 {
907 if ( pBCAlways )
908 {
909 pBCAlways->Broadcast( rHint );
910 return true;
911 }
912 else
913 return false;
914 }
915 else
916 {
917 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
918 if (iTab == aTableSlotsMap.end())
919 return false;
920 // Process all slots for the given row range.
921 ScRange broadcastRange( rAddress,
922 ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
923 bool bBroadcasted = false;
924 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
925 SCSIZE nStart, nEnd, nRowBreak;
926 ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
927 SCSIZE nOff = nStart;
928 SCSIZE nBreak = nOff + nRowBreak;
929 ScBroadcastAreaSlot** pp = ppSlots + nOff;
930 while ( nOff <= nEnd )
931 {
932 if ( *pp )
933 bBroadcasted |= (*pp)->AreaBroadcast( rHint );
934 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
935 }
936 return bBroadcasted;
937 }
938}
939
941 const ScRange& rRange )
942{
943 SCTAB nEndTab = rRange.aEnd.Tab();
944 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
945 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
946 {
947 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
948 SCSIZE nStart, nEnd, nRowBreak;
949 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
950 SCSIZE nOff = nStart;
951 SCSIZE nBreak = nOff + nRowBreak;
952 ScBroadcastAreaSlot** pp = ppSlots + nOff;
953 if (nOff == 0 && nEnd == mnBcaSlots-1)
954 {
955 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
956 // happen for insertion and deletion of sheets.
957 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
958 do
959 {
960 if ( *pp )
961 (*pp)->DelBroadcastAreasInRange( rRange );
962 } while (++pp < pStop);
963 }
964 else
965 {
966 while ( nOff <= nEnd )
967 {
968 if ( *pp )
969 (*pp)->DelBroadcastAreasInRange( rRange );
970 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
971 }
972 }
973 }
974}
975
976// for all affected: remove, chain, update range, insert, and maybe delete
978 UpdateRefMode eUpdateRefMode,
979 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
980{
981 // remove affected and put in chain
982 SCTAB nEndTab = rRange.aEnd.Tab();
983 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
984 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
985 {
986 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
987 SCSIZE nStart, nEnd, nRowBreak;
988 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
989 SCSIZE nOff = nStart;
990 SCSIZE nBreak = nOff + nRowBreak;
991 ScBroadcastAreaSlot** pp = ppSlots + nOff;
992 if (nOff == 0 && nEnd == mnBcaSlots-1)
993 {
994 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
995 // happen for insertion and deletion of sheets.
996 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
997 do
998 {
999 if ( *pp )
1000 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
1001 } while (++pp < pStop);
1002 }
1003 else
1004 {
1005 while ( nOff <= nEnd )
1006 {
1007 if ( *pp )
1008 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
1009 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1010 }
1011 }
1012 }
1013
1014 // Updating an area's range will modify the hash key, remove areas from all
1015 // affected slots. Will be reinserted later with the updated range.
1016 ScBroadcastArea* pChain = pUpdateChain;
1017 while (pChain)
1018 {
1019 ScBroadcastArea* pArea = pChain;
1020 pChain = pArea->GetUpdateChainNext();
1021 ScRange aRange( pArea->GetRange());
1022 // remove from slots
1023 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
1024 {
1025 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1026 if (iTab == aTableSlotsMap.end())
1027 {
1028 OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
1029 continue; // for
1030 }
1031 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1032 SCSIZE nStart, nEnd, nRowBreak;
1033 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1034 SCSIZE nOff = nStart;
1035 SCSIZE nBreak = nOff + nRowBreak;
1036 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1037 while ( nOff <= nEnd && pArea->GetRef() )
1038 {
1039 if (*pp)
1040 (*pp)->UpdateRemoveArea( pArea);
1041 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1042 }
1043 }
1044
1045 }
1046
1047 // shift sheets
1048 if (nDz)
1049 {
1050 if (nDz < 0)
1051 {
1052 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1053 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
1054 // Remove sheets, if any, iDel or/and iTab may as well point to end().
1055 while (iDel != iTab)
1056 {
1057 iDel = aTableSlotsMap.erase(iDel);
1058 }
1059 // shift remaining down
1060 while (iTab != aTableSlotsMap.end())
1061 {
1062 SCTAB nTab = (*iTab).first + nDz;
1063 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1064 iTab = aTableSlotsMap.erase(iTab);
1065 }
1066 }
1067 else
1068 {
1069 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1070 if (iStop != aTableSlotsMap.end())
1071 {
1072 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
1073 if (!bStopIsBegin)
1074 --iStop;
1075 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
1076 --iTab;
1077 while (iTab != iStop)
1078 {
1079 SCTAB nTab = (*iTab).first + nDz;
1080 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1081 aTableSlotsMap.erase( iTab--);
1082 }
1083 // Shift the very first, iTab==iStop in this case.
1084 if (bStopIsBegin)
1085 {
1086 SCTAB nTab = (*iTab).first + nDz;
1087 aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
1088 aTableSlotsMap.erase( iStop);
1089 }
1090 }
1091 }
1092 }
1093
1094 // work off chain
1095 SCCOL nCol1, nCol2, theCol1, theCol2;
1096 SCROW nRow1, nRow2, theRow1, theRow2;
1097 SCTAB nTab1, nTab2, theTab1, theTab2;
1098 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1099 while ( pUpdateChain )
1100 {
1102 ScRange aRange( pArea->GetRange());
1104
1105 // update range
1106 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
1107 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
1108 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
1109 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
1110 {
1111 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
1112 pArea->UpdateRange( aRange );
1113 // For DDE and ScLookupCache
1114 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
1115 }
1116
1117 // insert to slots
1118 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
1119 {
1120 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1121 if (iTab == aTableSlotsMap.end())
1122 iTab = aTableSlotsMap.emplace( std::piecewise_construct,
1123 std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
1124 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1125 SCSIZE nStart, nEnd, nRowBreak;
1126 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1127 SCSIZE nOff = nStart;
1128 SCSIZE nBreak = nOff + nRowBreak;
1129 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1130 while ( nOff <= nEnd )
1131 {
1132 if (!*pp)
1133 *pp = new ScBroadcastAreaSlot( pDoc, this );
1134 (*pp)->UpdateInsert( pArea );
1135 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1136 }
1137 }
1138
1139 // unchain
1140 pArea->SetUpdateChainNext( nullptr );
1141 pArea->SetInUpdateChain( false );
1142
1143 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
1144 // already executed in UpdateRemove().
1145 if (!pArea->GetRef())
1146 delete pArea;
1147 }
1148 pEOUpdateChain = nullptr;
1149}
1150
1152{
1154}
1155
1157{
1158 if (nInBulkBroadcast <= 0)
1159 return;
1160
1161 if (--nInBulkBroadcast == 0)
1162 {
1164 bool bBroadcasted = BulkBroadcastGroupAreas( nHintId );
1165 // Trigger the "final" tracking.
1167 pDoc->FinalTrackFormulas( nHintId );
1168 else if (bBroadcasted)
1169 pDoc->TrackFormulas( nHintId );
1170 }
1171}
1172
1174{
1175 return aBulkBroadcastAreas.insert( pArea ).second;
1176}
1177
1179{
1180 BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
1181 if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
1182 {
1183 // Insert a new one.
1184 it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
1185 }
1186
1187 sc::ColumnSpanSet& rSet = it->second;
1188 rSet.set(*pDoc, rRange, true);
1189}
1190
1192{
1193 if (m_BulkGroupAreas.empty())
1194 return false;
1195
1196 sc::BulkDataHint aHint( *pDoc, nHintId);
1197
1198 bool bBroadcasted = false;
1199 for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
1200 {
1201 assert(pArea);
1202 SvtBroadcaster& rBC = pArea->GetBroadcaster();
1203 if (!rBC.HasListeners())
1204 {
1205 /* FIXME: find the cause where the last listener is removed and
1206 * this area is still listed here. */
1207 SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
1208 }
1209 else
1210 {
1211 aHint.setSpans(&rSpans);
1212 rBC.Broadcast(aHint);
1213 bBroadcasted = true;
1214 }
1215 }
1216
1217 m_BulkGroupAreas.clear();
1218
1219 return bBroadcasted;
1220}
1221
1223{
1224 return aBulkBroadcastAreas.erase( pArea );
1225}
1226
1228{
1229 m_BulkGroupAreas.erase(pArea);
1230}
1231
1233 ScBroadcastAreas::iterator& rIter )
1234{
1235 maAreasToBeErased.emplace_back( pSlot, rIter);
1236}
1237
1239{
1240 SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
1241 "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
1242 if (pSlot->IsInBroadcastIteration())
1243 return;
1244
1245 // maAreasToBeErased is a simple vector so erasing an element may
1246 // invalidate iterators and would be inefficient anyway. Instead, copy
1247 // elements to be preserved (usually none!) to temporary vector and swap.
1248 AreasToBeErased aCopy;
1249 for (auto& rArea : maAreasToBeErased)
1250 {
1251 if (rArea.first == pSlot)
1252 pSlot->EraseArea( rArea.second);
1253 else
1254 aCopy.push_back( rArea);
1255 }
1256 maAreasToBeErased.swap( aCopy);
1257}
1258
1260 const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
1261{
1262 std::vector<sc::AreaListener> aRet;
1263
1264 SCTAB nEndTab = rRange.aEnd.Tab();
1265 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1266 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
1267 {
1268 ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
1269 SCSIZE nStart, nEnd, nRowBreak;
1270 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
1271 SCSIZE nOff = nStart;
1272 SCSIZE nBreak = nOff + nRowBreak;
1273 ScBroadcastAreaSlot** pp = ppSlots + nOff;
1274 while ( nOff <= nEnd )
1275 {
1276 ScBroadcastAreaSlot* p = *pp;
1277 if (p)
1278 p->GetAllListeners(rRange, aRet, eType, eGroup);
1279 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1280 }
1281 }
1282
1283 return aRet;
1284}
1285
1286#if DEBUG_AREA_BROADCASTER
1287void ScBroadcastAreaSlotMachine::Dump() const
1288{
1289 cout << "slot distribution count: " << nBcaSlots << endl;
1290 for (const auto& [rIndex, pTabSlots] : aTableSlotsMap)
1291 {
1292 cout << "-- sheet (index: " << rIndex << ")" << endl;
1293
1294 assert(pTabSlots);
1295 ScBroadcastAreaSlot** ppSlots = pTabSlots->getSlots();
1296 for (SCSIZE i = 0; i < nBcaSlots; ++i)
1297 {
1298 const ScBroadcastAreaSlot* pSlot = ppSlots[i];
1299 if (pSlot)
1300 {
1301 cout << "* slot " << i << endl;
1302 pSlot->Dump();
1303 }
1304 }
1305 }
1306}
1307#endif
1308
1309/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:945
#define BCA_BRDCST_ALWAYS
Definition: address.hxx:944
static void ComputeNextSlot(SCSIZE &nOff, SCSIZE &nBreak, ScBroadcastAreaSlot **&pp, SCSIZE &nStart, ScBroadcastAreaSlot **const &ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol)
Definition: bcaslot.cxx:673
static void compare(SCSIZE value1, SCSIZE value2, int line)
Definition: bcaslot.cxx:691
std::unordered_set< const ScBroadcastArea *, ScBroadcastAreaBulkHash, ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk
Definition: bcaslot.hxx:136
SCTAB Tab() const
Definition: address.hxx:283
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
Slot offset arrangement of columns and rows, once per sheet.
Definition: bcaslot.hxx:263
std::unique_ptr< ScBroadcastAreaSlot *[]> ppSlots
Definition: bcaslot.hxx:272
BroadcastAreaSlots and their management, once per document.
Definition: bcaslot.hxx:246
bool BulkBroadcastGroupAreas(SfxHintId nHintId)
Definition: bcaslot.cxx:1191
ScBroadcastAreaSlotMachine(ScDocument *pDoc)
Definition: bcaslot.cxx:571
void ComputeAreaPoints(const ScRange &rRange, SCSIZE &nStart, SCSIZE &nEnd, SCSIZE &nRowBreak) const
Definition: bcaslot.cxx:663
BulkGroupAreasType m_BulkGroupAreas
Definition: bcaslot.hxx:309
AreasToBeErased maAreasToBeErased
Definition: bcaslot.hxx:311
void FinallyEraseAreas(ScBroadcastAreaSlot *pSlot)
Definition: bcaslot.cxx:1238
void LeaveBulkBroadcast(SfxHintId nHintId)
Definition: bcaslot.cxx:1156
ScBroadcastArea * GetEOUpdateChain() const
Definition: bcaslot.hxx:353
std::unique_ptr< SvtBroadcaster > pBCAlways
Definition: bcaslot.hxx:312
void SetEOUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:354
std::vector< sc::AreaListener > GetAllListeners(const ScRange &rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup=sc::ListenerGroupType::Both)
Definition: bcaslot.cxx:1259
TableSlotsMap aTableSlotsMap
Definition: bcaslot.hxx:310
bool IsInBulkBroadcast() const
Definition: bcaslot.hxx:355
ScSlotDistribution maSlotDistribution
Definition: bcaslot.hxx:305
void StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:777
ScBroadcastAreasBulk aBulkBroadcastAreas
Definition: bcaslot.hxx:308
void UpdateBroadcastAreas(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:977
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:828
bool InsertBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1173
size_t RemoveBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1222
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:940
ScBroadcastArea * pEOUpdateChain
Definition: bcaslot.hxx:315
ScBroadcastArea * pUpdateChain
Definition: bcaslot.hxx:314
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:879
::std::vector< ::std::pair< ScBroadcastAreaSlot *, ScBroadcastAreas::iterator > > AreasToBeErased
Definition: bcaslot.hxx:280
void RemoveBulkGroupArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:1227
void SetUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:352
void PushAreaToBeErased(ScBroadcastAreaSlot *pSlot, ScBroadcastAreas::iterator &rIter)
Definition: bcaslot.cxx:1232
void InsertBulkGroupArea(ScBroadcastArea *pArea, const ScRange &rRange)
Definition: bcaslot.cxx:1178
SCSIZE ComputeSlotOffset(const ScAddress &rAddress) const
Definition: bcaslot.cxx:635
Collection of BroadcastAreas.
Definition: bcaslot.hxx:142
ScBroadcastAreas::iterator FindBroadcastArea(const ScRange &rRange, bool bGroupListening)
Definition: bcaslot.cxx:180
bool IsInBroadcastIteration() const
Definition: bcaslot.hxx:222
void UpdateRemove(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:340
bool mbInBroadcastIteration
Definition: bcaslot.hxx:148
ScBroadcastArea aTmpSeekBroadcastArea
Definition: bcaslot.hxx:145
ScBroadcastAreas aBroadcastAreaTbl
Definition: bcaslot.hxx:144
void FinallyEraseAreas()
Finally erase all areas pushed as to-be-erased.
Definition: bcaslot.cxx:544
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:210
static bool isMarkedErased(const ScBroadcastAreas::const_iterator &rIter)
Definition: bcaslot.hxx:174
void UpdateRemoveArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:384
ScBroadcastAreaSlot(ScDocument *pDoc, ScBroadcastAreaSlotMachine *pBASM)
Definition: bcaslot.cxx:45
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:316
ScDocument * pDoc
Definition: bcaslot.hxx:146
bool StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener, ScBroadcastArea *&rpArea)
Only here new ScBroadcastArea objects are created, prevention of dupes.
Definition: bcaslot.cxx:92
void InsertListeningArea(ScBroadcastArea *pArea)
Insert a ScBroadcastArea obtained via StartListeningArea() to subsequent slots.
Definition: bcaslot.cxx:136
ScBroadcastAreaSlotMachine * pBASM
Definition: bcaslot.hxx:147
void EraseArea(ScBroadcastAreas::iterator &rIter)
Erase an area from set and delete it if last reference, or if mbInBroadcastIteration is set push it t...
Definition: bcaslot.cxx:421
ScDocument::HardRecalcState CheckHardRecalcStateCondition() const
More hypothetical (memory would probably be doomed anyway) check whether there would be an overflow w...
Definition: bcaslot.cxx:71
void UpdateInsert(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:398
bool mbHasErasedArea
If true, the slot has at least one area broadcaster marked for removal.
Definition: bcaslot.hxx:157
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener, ScBroadcastArea *&rpArea)
Definition: bcaslot.cxx:147
void GetAllListeners(const ScRange &rRange, std::vector< sc::AreaListener > &rListeners, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup)
Definition: bcaslot.cxx:442
Used in a Unique Associative Container.
Definition: bcaslot.hxx:52
ScBroadcastArea * GetUpdateChainNext() const
Definition: bcaslot.hxx:76
const ScRange & GetRange() const
Definition: bcaslot.hxx:72
bool IsInUpdateChain() const
Definition: bcaslot.hxx:78
ScBroadcastArea(const ScBroadcastArea &)=delete
void SetGroupListening(bool b)
Definition: bcaslot.hxx:82
void UpdateRange(const ScRange &rNewRange)
Definition: bcaslot.hxx:70
void SetInUpdateChain(bool b)
Definition: bcaslot.hxx:79
void SetUpdateChainNext(ScBroadcastArea *p)
Definition: bcaslot.hxx:77
sal_uLong GetRef() const
Definition: bcaslot.hxx:75
bool IsGroupListening() const
Definition: bcaslot.hxx:81
SvtBroadcaster & GetBroadcaster()
Definition: bcaslot.hxx:68
void IncRef()
Definition: bcaslot.hxx:73
sal_uLong DecRef()
Definition: bcaslot.hxx:74
void TrackFormulas(SfxHintId nHintId=SfxHintId::ScDataChanged)
Definition: documen7.cxx:530
void FinalTrackFormulas(SfxHintId nHintId)
Definition: documen7.cxx:509
bool ValidRow(SCROW nRow) const
Definition: document.hxx:899
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:891
SC_DLLPUBLIC SCROW GetMaxRowCount() const
Definition: document.hxx:894
HardRecalcState GetHardRecalcState() const
Definition: document.hxx:2395
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:892
bool IsDelayedFormulaGrouping() const
Definition: document.hxx:1423
@ ETERNAL
CalcAll() without broadcast/notify but setting up new listeners.
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1081
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:898
SC_DLLPUBLIC void SetAutoCalc(bool bNewAutoCalc)
Definition: documen7.cxx:608
void SetHardRecalcState(HardRecalcState eVal)
Definition: document.hxx:2396
SC_DLLPUBLIC SCCOL GetMaxColCount() const
Definition: document.hxx:893
bool IsTrackFormulasPending() const
Definition: document.hxx:2390
ScAddress aPos
ScRange GetRange() const
Definition: brdcst.hxx:33
SCROW GetRowCount() const
Definition: brdcst.hxx:32
const ScAddress & GetStartAddress() const
Definition: brdcst.hxx:31
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1547
void GetVars(SCCOL &nCol1, SCROW &nRow1, SCTAB &nTab1, SCCOL &nCol2, SCROW &nRow2, SCTAB &nTab2) const
Definition: address.hxx:690
OUString Format(const ScDocument &rDocument, ScRefFlags nFlags=ScRefFlags::ZERO, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, bool bFullAddressNotation=false) const
Returns string with formatted cell range from aStart to aEnd, according to provided address conventio...
Definition: address.cxx:2170
ScAddress aEnd
Definition: address.hxx:498
bool Intersects(const ScRange &rRange) const
Definition: address.hxx:734
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
bool IsValid() const
Definition: address.hxx:544
ScAddress aStart
Definition: address.hxx:497
static ScRefUpdateRes Update(const ScDocument *pDoc, UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2, SCCOL nDx, SCROW nDy, SCTAB nDz, SCCOL &theCol1, SCROW &theRow1, SCTAB &theTab1, SCCOL &theCol2, SCROW &theRow2, SCTAB &theTab2)
Definition: refupdat.cxx:188
void SetError(ErrCode rErr)
bool HasListeners() const
void Broadcast(const SfxHint &rHint)
std::vector< SvtListener * > ListenersType
ListenersType & GetAllListeners()
bool StartListening(SvtBroadcaster &rBroadcaster)
void EndListening(SvtBroadcaster &rBroadcaster)
void setSpans(const ColumnSpanSet *pSpans)
Structure that stores segments of boolean flags per column, and perform custom action on those segmen...
sal_Int32 nRefCount
FilterGroup & rTarget
DocumentType eType
UpdateRefMode
Definition: global.hxx:300
SfxHintId
void * p
sal_Int64 n
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
int i
line
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
AreaOverlapType
Definition: types.hxx:110
ListenerGroupType
Definition: types.hxx:118
#define SCWARN_CORE_HARD_RECALC
Definition: scerrors.hxx:78
static SfxItemSet & rSet
TOOLS_DLLPUBLIC SvStream & endl(SvStream &rStr)
bool mbGroupListening
Definition: bcaslot.hxx:41
ScRange maArea
Definition: bcaslot.hxx:40
SvtListener * mpListener
Definition: bcaslot.hxx:42
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17