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 
46  ScBroadcastAreaSlotMachine* pBASMa ) :
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 
180 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
181  const ScRange& rRange, bool bGroupListening )
182 {
184  aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
186 }
187 
188 namespace {
189 
190 void 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;
216  mbInBroadcastIteration = true;
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  {
237  if (pBASM->IsInBulkBroadcast())
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;
270  mbInBroadcastIteration = true;
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  {
288  if (pBASM->IsInBulkBroadcast())
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  {
330  if (pBASM->IsInBulkBroadcast())
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();
368  if (pBASM->IsInBulkBroadcast())
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  {
410  SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
412  for (auto& pListener : rListeners)
413  {
414  SvtListener& rListener = *pListener;
415  rListener.StartListening(rTarget);
416  }
417  }
418  }
419 }
420 
421 void 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  {
435  if (pBASM->IsInBulkBroadcast())
436  pBASM->RemoveBulkGroupArea(pArea);
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
504 void 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 {
560  for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
561  delete *pp;
562 }
563 
565  ScDocument* pDocument ) :
566  pDoc( pDocument ),
567  pUpdateChain( nullptr ),
568  pEOUpdateChain( nullptr ),
569  nInBulkBroadcast( 0 )
570 {
571  // initSlotDistribution ---------
572  // Logarithmic or any other distribution.
573  // Upper and leftmost sheet part usually is more populated and referenced and gets fine
574  // grained resolution, larger data in larger hunks.
575  // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
576  // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
577  // slice doubles (making more cells share the same slot), this distribution data is stored
578  // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
579  // again with the column slice doubling after some time.
580  // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
581  // calculations.
582  SCSIZE nSlots = 0;
583  // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
584  sal_Int32 nCol1 = 0;
585  sal_Int32 nCol2 = 1024;
586  SCSIZE nSliceCol = 16;
587  while (nCol2 <= pDoc->GetMaxColCount())
588  {
589  SCROW nRow1 = 0;
590  SCROW nRow2 = 32*1024;
591  SCSIZE nSliceRow = 128;
592  SCSIZE nSlotsCol = 0;
593  SCSIZE nSlotsStartCol = nSlots;
594  // Must be sorted by row1,row2!
595  while (nRow2 <= pDoc->GetMaxRowCount())
596  {
597  maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
598  nSlotsCol += (nRow2 - nRow1) / nSliceRow;
599  nRow1 = nRow2;
600  nRow2 *= 2;
601  nSliceRow *= 2;
602  }
603  // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
604  // to the right can be computed quickly in ComputeNextSlot().
605  if(nCol1 == 0)
606  mnBcaSlotsCol = nSlotsCol;
607  assert(nSlotsCol == mnBcaSlotsCol);
608  nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
609  nCol1 = nCol2;
610  nCol2 *= 2;
611  nSliceCol *= 2;
612  }
613  mnBcaSlots = nSlots;
614 #ifdef DBG_UTIL
615  DoChecks();
616 #endif
617 }
618 
620 {
621  aTableSlotsMap.clear();
622  pBCAlways.reset();
623  // Areas to-be-erased still present is a serious error in handling, but at
624  // this stage there's nothing we can do anymore.
625  SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
626 }
627 
629  const ScAddress& rAddress ) const
630 {
631  SCROW nRow = rAddress.Row();
632  SCCOL nCol = rAddress.Col();
633  if ( !pDoc->ValidRow(nRow) || !pDoc->ValidCol(nCol) )
634  {
635  OSL_FAIL( "Row/Col invalid, using first slot!" );
636  return 0;
637  }
638  for (const ScSlotData& rSD : maSlotDistribution)
639  {
640  if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
641  {
642  assert(nRow >= rSD.nStartRow);
643  assert(nCol >= rSD.nStartCol);
644  SCSIZE slot = rSD.nCumulatedRow
645  + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
646  + rSD.nCumulatedCol
647  + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
648  assert(slot < mnBcaSlots);
649  return slot;
650  }
651  }
652  OSL_FAIL( "No slot found, using last!" );
653  return mnBcaSlots - 1;
654 }
655 
657  SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
658 {
659  rStart = ComputeSlotOffset( rRange.aStart );
660  rEnd = ComputeSlotOffset( rRange.aEnd );
661  // count of row slots per column minus one
662  rRowBreak = ComputeSlotOffset(
663  ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
664 }
665 
666 static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
667  SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
668 {
669  if ( nOff < nBreak )
670  {
671  ++nOff;
672  ++pp;
673  }
674  else
675  {
676  nStart += nBcaSlotsCol;
677  nOff = nStart;
678  pp = ppSlots + nOff;
679  nBreak = nOff + nRowBreak;
680  }
681 }
682 
683 #ifdef DBG_UTIL
684 static void compare(SCSIZE value1, SCSIZE value2, int line)
685 {
686  if(value1!=value2)
687  SAL_WARN("sc", "V1:" << value1 << " V2:" << value2 << " (" << line << ")");
688  assert(value1 == value2);
689 }
690 
691 // Basic checks that the calculations work correctly.
693 {
694  // Copy&paste from the ctor.
695  constexpr SCSIZE nSliceRow = 128;
696  constexpr SCSIZE nSliceCol = 16;
697  // First and second column are in the same slice and so get the same slot.
698  compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
699  // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
700  compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
701  ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
702  compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
703  ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
704  // Check that last cell is the last slot.
706  mnBcaSlots - 1, __LINE__ );
707  // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
708  for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
709  {
710  const ScSlotData& s1 = maSlotDistribution[ i ];
711  const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
712  if( s1.nStartCol == s2.nStartCol )
713  {
714  assert( s1.nStopRow == s2.nStartRow );
716  ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
717  }
718  }
719  // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
720  for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
721  {
722  const ScSlotData& s1 = maSlotDistribution[ i ];
723  for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
724  {
725  const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
726  if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
727  {
728  assert( s1.nStopRow == s2.nStartRow );
730  ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
731  }
732  }
733  }
734  // Iterate all slots.
735  ScRange range( ScAddress( 0, 0, 0 ), ScAddress( pDoc->MaxCol(), pDoc->MaxRow(), 0 ));
736  SCSIZE nStart, nEnd, nRowBreak;
737  ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
738  assert( nStart == 0 );
739  assert( nEnd == mnBcaSlots - 1 );
740  SCSIZE nOff = nStart;
741  SCSIZE nBreak = nOff + nRowBreak;
742  std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
743  ScBroadcastAreaSlot** ppSlots = slots.get();
744  ScBroadcastAreaSlot** pp = ppSlots;
745  while ( nOff <= nEnd )
746  {
747  SCSIZE previous = nOff;
748  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
749  compare( nOff, previous + 1, __LINE__ );
750  }
751  // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
752  range = ScRange( ScAddress( 0, pDoc->MaxRow(), 0 ),
753  ScAddress( pDoc->MaxCol(), pDoc->MaxRow() - 1, 0 ));
754  ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
755  assert( nStart == mnBcaSlotsCol - 1 );
756  assert( nEnd == mnBcaSlots - 1 );
757  nOff = nStart;
758  nBreak = nOff + nRowBreak;
759  ppSlots = slots.get();
760  pp = ppSlots;
761  while ( nOff <= nEnd )
762  {
763  SCSIZE previous = nOff;
764  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
765  compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
766  }
767 }
768 #endif
769 
771  const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
772 {
773  if ( rRange == BCA_LISTEN_ALWAYS )
774  {
775  if ( !pBCAlways )
776  pBCAlways.reset( new SvtBroadcaster );
777  pListener->StartListening( *pBCAlways );
778  }
779  else
780  {
781  // A new area needs to be inserted to the corresponding slots, for 3D
782  // ranges for all sheets, do not slice into per sheet areas or the
783  // !bDone will break too early (i.e. after the first sheet) if
784  // subsequent listeners are to be added.
785  ScBroadcastArea* pArea = nullptr;
786  bool bDone = false;
787  for (SCTAB nTab = rRange.aStart.Tab();
788  !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
789  {
790  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
791  if (iTab == aTableSlotsMap.end())
792  iTab = aTableSlotsMap.emplace(nTab, std::make_unique<TableSlots>(mnBcaSlots)).first;
793  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
794  SCSIZE nStart, nEnd, nRowBreak;
795  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
796  SCSIZE nOff = nStart;
797  SCSIZE nBreak = nOff + nRowBreak;
798  ScBroadcastAreaSlot** pp = ppSlots + nOff;
799  while ( !bDone && nOff <= nEnd )
800  {
801  if ( !*pp )
802  *pp = new ScBroadcastAreaSlot( pDoc, this );
803  if (!pArea)
804  {
805  // If the call to StartListeningArea didn't create the
806  // ScBroadcastArea, listeners were added to an already
807  // existing identical area that doesn't need to be inserted
808  // to slots again.
809  if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
810  bDone = true;
811  }
812  else
813  (*pp)->InsertListeningArea( pArea);
814  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
815  }
816  }
817  }
818 }
819 
821  const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
822 {
823  if ( rRange == BCA_LISTEN_ALWAYS )
824  {
825  if ( pBCAlways )
826  {
827  pListener->EndListening( *pBCAlways);
828  if (!pBCAlways->HasListeners())
829  {
830  pBCAlways.reset();
831  }
832  }
833  }
834  else
835  {
836  SCTAB nEndTab = rRange.aEnd.Tab();
837  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
838  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
839  {
840  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
841  SCSIZE nStart, nEnd, nRowBreak;
842  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
843  SCSIZE nOff = nStart;
844  SCSIZE nBreak = nOff + nRowBreak;
845  ScBroadcastAreaSlot** pp = ppSlots + nOff;
846  ScBroadcastArea* pArea = nullptr;
847  if (nOff == 0 && nEnd == mnBcaSlots-1)
848  {
849  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
850  // happen for insertion and deletion of sheets.
851  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
852  do
853  {
854  if ( *pp )
855  (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
856  } while (++pp < pStop);
857  }
858  else
859  {
860  while ( nOff <= nEnd )
861  {
862  if ( *pp )
863  (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
864  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
865  }
866  }
867  }
868  }
869 }
870 
872 {
873  bool bBroadcasted = false;
874  SCTAB nEndTab = rRange.aEnd.Tab();
875  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
876  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
877  {
878  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
879  SCSIZE nStart, nEnd, nRowBreak;
880  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
881  SCSIZE nOff = nStart;
882  SCSIZE nBreak = nOff + nRowBreak;
883  ScBroadcastAreaSlot** pp = ppSlots + nOff;
884  while ( nOff <= nEnd )
885  {
886  if ( *pp )
887  bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
888  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
889  }
890  }
891  return bBroadcasted;
892 }
893 
895 {
896  const ScAddress& rAddress = rHint.GetStartAddress();
897  if ( rAddress == BCA_BRDCST_ALWAYS )
898  {
899  if ( pBCAlways )
900  {
901  pBCAlways->Broadcast( rHint );
902  return true;
903  }
904  else
905  return false;
906  }
907  else
908  {
909  TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
910  if (iTab == aTableSlotsMap.end())
911  return false;
912  // Process all slots for the given row range.
913  ScRange broadcastRange( rAddress,
914  ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
915  bool bBroadcasted = false;
916  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
917  SCSIZE nStart, nEnd, nRowBreak;
918  ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
919  SCSIZE nOff = nStart;
920  SCSIZE nBreak = nOff + nRowBreak;
921  ScBroadcastAreaSlot** pp = ppSlots + nOff;
922  while ( nOff <= nEnd )
923  {
924  if ( *pp )
925  bBroadcasted |= (*pp)->AreaBroadcast( rHint );
926  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
927  }
928  return bBroadcasted;
929  }
930 }
931 
933  const ScRange& rRange )
934 {
935  SCTAB nEndTab = rRange.aEnd.Tab();
936  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
937  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
938  {
939  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
940  SCSIZE nStart, nEnd, nRowBreak;
941  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
942  SCSIZE nOff = nStart;
943  SCSIZE nBreak = nOff + nRowBreak;
944  ScBroadcastAreaSlot** pp = ppSlots + nOff;
945  if (nOff == 0 && nEnd == mnBcaSlots-1)
946  {
947  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
948  // happen for insertion and deletion of sheets.
949  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
950  do
951  {
952  if ( *pp )
953  (*pp)->DelBroadcastAreasInRange( rRange );
954  } while (++pp < pStop);
955  }
956  else
957  {
958  while ( nOff <= nEnd )
959  {
960  if ( *pp )
961  (*pp)->DelBroadcastAreasInRange( rRange );
962  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
963  }
964  }
965  }
966 }
967 
968 // for all affected: remove, chain, update range, insert, and maybe delete
970  UpdateRefMode eUpdateRefMode,
971  const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
972 {
973  // remove affected and put in chain
974  SCTAB nEndTab = rRange.aEnd.Tab();
975  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
976  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
977  {
978  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
979  SCSIZE nStart, nEnd, nRowBreak;
980  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
981  SCSIZE nOff = nStart;
982  SCSIZE nBreak = nOff + nRowBreak;
983  ScBroadcastAreaSlot** pp = ppSlots + nOff;
984  if (nOff == 0 && nEnd == mnBcaSlots-1)
985  {
986  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
987  // happen for insertion and deletion of sheets.
988  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
989  do
990  {
991  if ( *pp )
992  (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
993  } while (++pp < pStop);
994  }
995  else
996  {
997  while ( nOff <= nEnd )
998  {
999  if ( *pp )
1000  (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
1001  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1002  }
1003  }
1004  }
1005 
1006  // Updating an area's range will modify the hash key, remove areas from all
1007  // affected slots. Will be reinserted later with the updated range.
1008  ScBroadcastArea* pChain = pUpdateChain;
1009  while (pChain)
1010  {
1011  ScBroadcastArea* pArea = pChain;
1012  pChain = pArea->GetUpdateChainNext();
1013  ScRange aRange( pArea->GetRange());
1014  // remove from slots
1015  for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
1016  {
1017  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1018  if (iTab == aTableSlotsMap.end())
1019  {
1020  OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
1021  continue; // for
1022  }
1023  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
1024  SCSIZE nStart, nEnd, nRowBreak;
1025  ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1026  SCSIZE nOff = nStart;
1027  SCSIZE nBreak = nOff + nRowBreak;
1028  ScBroadcastAreaSlot** pp = ppSlots + nOff;
1029  while ( nOff <= nEnd && pArea->GetRef() )
1030  {
1031  if (*pp)
1032  (*pp)->UpdateRemoveArea( pArea);
1033  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1034  }
1035  }
1036 
1037  }
1038 
1039  // shift sheets
1040  if (nDz)
1041  {
1042  if (nDz < 0)
1043  {
1044  TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1045  TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
1046  // Remove sheets, if any, iDel or/and iTab may as well point to end().
1047  while (iDel != iTab)
1048  {
1049  iDel = aTableSlotsMap.erase(iDel);
1050  }
1051  // shift remaining down
1052  while (iTab != aTableSlotsMap.end())
1053  {
1054  SCTAB nTab = (*iTab).first + nDz;
1055  aTableSlotsMap[nTab] = std::move((*iTab).second);
1056  iTab = aTableSlotsMap.erase(iTab);
1057  }
1058  }
1059  else
1060  {
1061  TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1062  if (iStop != aTableSlotsMap.end())
1063  {
1064  bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
1065  if (!bStopIsBegin)
1066  --iStop;
1067  TableSlotsMap::iterator iTab( aTableSlotsMap.end());
1068  --iTab;
1069  while (iTab != iStop)
1070  {
1071  SCTAB nTab = (*iTab).first + nDz;
1072  aTableSlotsMap[nTab] = std::move((*iTab).second);
1073  aTableSlotsMap.erase( iTab--);
1074  }
1075  // Shift the very first, iTab==iStop in this case.
1076  if (bStopIsBegin)
1077  {
1078  SCTAB nTab = (*iTab).first + nDz;
1079  aTableSlotsMap[nTab] = std::move((*iTab).second);
1080  aTableSlotsMap.erase( iStop);
1081  }
1082  }
1083  }
1084  }
1085 
1086  // work off chain
1087  SCCOL nCol1, nCol2, theCol1, theCol2;
1088  SCROW nRow1, nRow2, theRow1, theRow2;
1089  SCTAB nTab1, nTab2, theTab1, theTab2;
1090  rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1091  while ( pUpdateChain )
1092  {
1093  ScBroadcastArea* pArea = pUpdateChain;
1094  ScRange aRange( pArea->GetRange());
1095  pUpdateChain = pArea->GetUpdateChainNext();
1096 
1097  // update range
1098  aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
1099  if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
1100  nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
1101  theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
1102  {
1103  aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
1104  pArea->UpdateRange( aRange );
1105  // For DDE and ScLookupCache
1106  pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
1107  }
1108 
1109  // insert to slots
1110  for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
1111  {
1112  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1113  if (iTab == aTableSlotsMap.end())
1114  iTab = aTableSlotsMap.emplace(nTab, std::make_unique<TableSlots>(mnBcaSlots)).first;
1115  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
1116  SCSIZE nStart, nEnd, nRowBreak;
1117  ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1118  SCSIZE nOff = nStart;
1119  SCSIZE nBreak = nOff + nRowBreak;
1120  ScBroadcastAreaSlot** pp = ppSlots + nOff;
1121  while ( nOff <= nEnd )
1122  {
1123  if (!*pp)
1124  *pp = new ScBroadcastAreaSlot( pDoc, this );
1125  (*pp)->UpdateInsert( pArea );
1126  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1127  }
1128  }
1129 
1130  // unchain
1131  pArea->SetUpdateChainNext( nullptr );
1132  pArea->SetInUpdateChain( false );
1133 
1134  // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
1135  // already executed in UpdateRemove().
1136  if (!pArea->GetRef())
1137  delete pArea;
1138  }
1139  pEOUpdateChain = nullptr;
1140 }
1141 
1143 {
1144  ++nInBulkBroadcast;
1145 }
1146 
1148 {
1149  if (nInBulkBroadcast <= 0)
1150  return;
1151 
1152  if (--nInBulkBroadcast == 0)
1153  {
1155  bool bBroadcasted = BulkBroadcastGroupAreas( nHintId );
1156  // Trigger the "final" tracking.
1158  pDoc->FinalTrackFormulas( nHintId );
1159  else if (bBroadcasted)
1160  pDoc->TrackFormulas( nHintId );
1161  }
1162 }
1163 
1165 {
1166  return aBulkBroadcastAreas.insert( pArea ).second;
1167 }
1168 
1170 {
1171  BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
1172  if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
1173  {
1174  // Insert a new one.
1175  it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
1176  }
1177 
1178  sc::ColumnSpanSet& rSet = it->second;
1179  rSet.set(*pDoc, rRange, true);
1180 }
1181 
1183 {
1184  if (m_BulkGroupAreas.empty())
1185  return false;
1186 
1187  sc::BulkDataHint aHint( *pDoc, nHintId);
1188 
1189  bool bBroadcasted = false;
1190  for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
1191  {
1192  assert(pArea);
1193  SvtBroadcaster& rBC = pArea->GetBroadcaster();
1194  if (!rBC.HasListeners())
1195  {
1196  /* FIXME: find the cause where the last listener is removed and
1197  * this area is still listed here. */
1198  SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
1199  }
1200  else
1201  {
1202  aHint.setSpans(&rSpans);
1203  rBC.Broadcast(aHint);
1204  bBroadcasted = true;
1205  }
1206  }
1207 
1208  m_BulkGroupAreas.clear();
1209 
1210  return bBroadcasted;
1211 }
1212 
1214 {
1215  return aBulkBroadcastAreas.erase( pArea );
1216 }
1217 
1219 {
1220  m_BulkGroupAreas.erase(pArea);
1221 }
1222 
1224  ScBroadcastAreas::iterator& rIter )
1225 {
1226  maAreasToBeErased.emplace_back( pSlot, rIter);
1227 }
1228 
1230 {
1231  SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
1232  "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
1233  if (pSlot->IsInBroadcastIteration())
1234  return;
1235 
1236  // maAreasToBeErased is a simple vector so erasing an element may
1237  // invalidate iterators and would be inefficient anyway. Instead, copy
1238  // elements to be preserved (usually none!) to temporary vector and swap.
1239  AreasToBeErased aCopy;
1240  for (auto& rArea : maAreasToBeErased)
1241  {
1242  if (rArea.first == pSlot)
1243  pSlot->EraseArea( rArea.second);
1244  else
1245  aCopy.push_back( rArea);
1246  }
1247  maAreasToBeErased.swap( aCopy);
1248 }
1249 
1250 std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
1251  const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
1252 {
1253  std::vector<sc::AreaListener> aRet;
1254 
1255  SCTAB nEndTab = rRange.aEnd.Tab();
1256  for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1257  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
1258  {
1259  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
1260  SCSIZE nStart, nEnd, nRowBreak;
1261  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
1262  SCSIZE nOff = nStart;
1263  SCSIZE nBreak = nOff + nRowBreak;
1264  ScBroadcastAreaSlot** pp = ppSlots + nOff;
1265  while ( nOff <= nEnd )
1266  {
1267  ScBroadcastAreaSlot* p = *pp;
1268  if (p)
1269  p->GetAllListeners(rRange, aRet, eType, eGroup);
1270  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
1271  }
1272  }
1273 
1274  return aRet;
1275 }
1276 
1277 #if DEBUG_AREA_BROADCASTER
1278 void ScBroadcastAreaSlotMachine::Dump() const
1279 {
1280  cout << "slot distribution count: " << nBcaSlots << endl;
1281  for (const auto& [rIndex, pTabSlots] : aTableSlotsMap)
1282  {
1283  cout << "-- sheet (index: " << rIndex << ")" << endl;
1284 
1285  assert(pTabSlots);
1286  ScBroadcastAreaSlot** ppSlots = pTabSlots->getSlots();
1287  for (SCSIZE i = 0; i < nBcaSlots; ++i)
1288  {
1289  const ScBroadcastAreaSlot* pSlot = ppSlots[i];
1290  if (pSlot)
1291  {
1292  cout << "* slot " << i << endl;
1293  pSlot->Dump();
1294  }
1295  }
1296  }
1297 }
1298 #endif
1299 
1300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsInBulkBroadcast() const
Definition: bcaslot.hxx:354
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
SfxHintId
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
ScAddress aStart
Definition: address.hxx:497
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:945
bool IsInBroadcastIteration() const
Definition: bcaslot.hxx:222
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
sal_Int32 nRefCount
void InsertListeningArea(ScBroadcastArea *pArea)
Insert a ScBroadcastArea obtained via StartListeningArea() to subsequent slots.
Definition: bcaslot.cxx:136
SCROW Row() const
Definition: address.hxx:274
SC_DLLPUBLIC SCROW GetMaxRowCount() const
Definition: document.hxx:893
bool IsGroupListening() const
Definition: bcaslot.hxx:81
size_t RemoveBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1213
bool Intersects(const ScRange &rRange) const
Definition: address.hxx:734
bool mbInBroadcastIteration
Definition: bcaslot.hxx:148
ScBroadcastArea(const ScBroadcastArea &)=delete
FilterGroup & rTarget
sal_Int64 n
ListenersType & GetAllListeners()
bool IsTrackFormulasPending() const
Definition: document.hxx:2388
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener, ScBroadcastArea *&rpArea)
Definition: bcaslot.cxx:147
SCROW GetRowCount() const
Definition: brdcst.hxx:32
ScAddress aEnd
Definition: address.hxx:498
void UpdateRemove(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:340
std::unordered_set< const ScBroadcastArea *, ScBroadcastAreaBulkHash, ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk
Definition: bcaslot.hxx:136
ScBroadcastArea * GetEOUpdateChain() const
Definition: bcaslot.hxx:352
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1547
void SetHardRecalcState(HardRecalcState eVal)
Definition: document.hxx:2394
void LeaveBulkBroadcast(SfxHintId nHintId)
Definition: bcaslot.cxx:1147
void StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:770
ScBroadcastAreaSlot(ScDocument *pDoc, ScBroadcastAreaSlotMachine *pBASM)
Definition: bcaslot.cxx:45
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:932
Used in a Unique Associative Container.
Definition: bcaslot.hxx:51
#define SCWARN_CORE_HARD_RECALC
Definition: scerrors.hxx:78
Collection of BroadcastAreas.
Definition: bcaslot.hxx:141
std::vector< SvtListener * > ListenersType
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:891
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
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 setSpans(const ColumnSpanSet *pSpans)
bool IsInUpdateChain() const
Definition: bcaslot.hxx:78
void set(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
SCTAB Tab() const
Definition: address.hxx:283
ScBroadcastArea aTmpSeekBroadcastArea
Definition: bcaslot.hxx:145
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
ScAddress aPos
void EndListening(SvtBroadcaster &rBroadcaster)
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:210
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:820
TableSlotsMap aTableSlotsMap
Definition: bcaslot.hxx:309
void FinalTrackFormulas(SfxHintId nHintId)
Definition: documen7.cxx:509
void UpdateInsert(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:398
ScRange GetRange() const
Definition: brdcst.hxx:33
void PushAreaToBeErased(ScBroadcastAreaSlot *pSlot, ScBroadcastAreas::iterator &rIter)
Definition: bcaslot.cxx:1223
SC_DLLPUBLIC SCCOL GetMaxColCount() const
Definition: document.hxx:892
bool BulkBroadcastGroupAreas(SfxHintId nHintId)
Definition: bcaslot.cxx:1182
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:890
std::unique_ptr< ScBroadcastAreaSlot *[]> ppSlots
Definition: bcaslot.hxx:271
void SetUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:351
void Broadcast(const SfxHint &rHint)
int i
void RemoveBulkGroupArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:1218
static void ComputeNextSlot(SCSIZE &nOff, SCSIZE &nBreak, ScBroadcastAreaSlot **&pp, SCSIZE &nStart, ScBroadcastAreaSlot **const &ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol)
Definition: bcaslot.cxx:666
sal_Int16 SCCOL
Definition: types.hxx:21
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:897
SvtListener * mpListener
Definition: bcaslot.hxx:42
SC_DLLPUBLIC void SetAutoCalc(bool bNewAutoCalc)
Definition: documen7.cxx:608
void UpdateRange(const ScRange &rNewRange)
Definition: bcaslot.hxx:70
BulkGroupAreasType m_BulkGroupAreas
Definition: bcaslot.hxx:308
ScRange maArea
Definition: bcaslot.hxx:40
void SetGroupListening(bool b)
Definition: bcaslot.hxx:82
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
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:316
ScBroadcastAreaSlotMachine * pBASM
Definition: bcaslot.hxx:147
Structure that stores segments of boolean flags per column, and perform custom action on those segmen...
void FinallyEraseAreas(ScBroadcastAreaSlot *pSlot)
Definition: bcaslot.cxx:1229
#define BCA_BRDCST_ALWAYS
Definition: address.hxx:944
ScBroadcastArea * GetUpdateChainNext() const
Definition: bcaslot.hxx:76
HardRecalcState GetHardRecalcState() const
Definition: document.hxx:2393
SvtBroadcaster & GetBroadcaster()
Definition: bcaslot.hxx:68
void TrackFormulas(SfxHintId nHintId=SfxHintId::ScDataChanged)
Definition: documen7.cxx:530
BroadcastAreaSlots and their management, once per document.
Definition: bcaslot.hxx:245
SCSIZE ComputeSlotOffset(const ScAddress &rAddress) const
Definition: bcaslot.cxx:628
void SetUpdateChainNext(ScBroadcastArea *p)
Definition: bcaslot.hxx:77
sal_uLong DecRef()
Definition: bcaslot.hxx:74
ScBroadcastAreas::iterator FindBroadcastArea(const ScRange &rRange, bool bGroupListening)
Definition: bcaslot.cxx:180
void GetVars(SCCOL &nCol1, SCROW &nRow1, SCTAB &nTab1, SCCOL &nCol2, SCROW &nRow2, SCTAB &nTab2) const
Definition: address.hxx:690
bool StartListening(SvtBroadcaster &rBroadcaster)
void UpdateBroadcastAreas(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:969
bool IsValid() const
Definition: address.hxx:544
SCCOL Col() const
Definition: address.hxx:279
::std::vector< ::std::pair< ScBroadcastAreaSlot *, ScBroadcastAreas::iterator > > AreasToBeErased
Definition: bcaslot.hxx:279
void GetAllListeners(const ScRange &rRange, std::vector< sc::AreaListener > &rListeners, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup)
Definition: bcaslot.cxx:442
bool IsDelayedFormulaGrouping() const
Definition: document.hxx:1420
AreaOverlapType
Definition: types.hxx:109
UpdateRefMode
Definition: global.hxx:298
ScBroadcastAreasBulk aBulkBroadcastAreas
Definition: bcaslot.hxx:307
sal_Int32 SCROW
Definition: types.hxx:17
bool ValidRow(SCROW nRow) const
Definition: document.hxx:898
ScBroadcastAreaSlotMachine(ScDocument *pDoc)
Definition: bcaslot.cxx:564
ScDocument * pDoc
Definition: bcaslot.hxx:146
#define SAL_WARN_IF(condition, area, stream)
static void compare(SCSIZE value1, SCSIZE value2, int line)
Definition: bcaslot.cxx:684
void FinallyEraseAreas()
Finally erase all areas pushed as to-be-erased.
Definition: bcaslot.cxx:544
CalcAll() without broadcast/notify but setting up new listeners.
void SetEOUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:353
SvStream & endl(SvStream &rStr)
bool InsertBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1164
ScBroadcastAreas aBroadcastAreaTbl
Definition: bcaslot.hxx:144
void * p
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
bool HasListeners() const
ScBroadcastArea * pEOUpdateChain
Definition: bcaslot.hxx:314
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
bool mbHasErasedArea
If true, the slot has at least one area broadcaster marked for removal.
Definition: bcaslot.hxx:157
bool mbGroupListening
Definition: bcaslot.hxx:41
ScSlotDistribution maSlotDistribution
Definition: bcaslot.hxx:304
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1080
AreasToBeErased maAreasToBeErased
Definition: bcaslot.hxx:310
#define SAL_WARN(area, stream)
void IncRef()
Definition: bcaslot.hxx:73
sal_uLong GetRef() const
Definition: bcaslot.hxx:75
std::vector< sc::AreaListener > GetAllListeners(const ScRange &rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup=sc::ListenerGroupType::Both)
Definition: bcaslot.cxx:1250
void ComputeAreaPoints(const ScRange &rRange, SCSIZE &nStart, SCSIZE &nEnd, SCSIZE &nRowBreak) const
Definition: bcaslot.cxx:656
void SetInUpdateChain(bool b)
Definition: bcaslot.hxx:79
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:871
void UpdateRemoveArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:384
void SetError(ErrCode rErr)
const ScRange & GetRange() const
Definition: bcaslot.hxx:72
std::unique_ptr< SvtBroadcaster > pBCAlways
Definition: bcaslot.hxx:311
ScBroadcastArea * pUpdateChain
Definition: bcaslot.hxx:313
sal_Int16 SCTAB
Definition: types.hxx:22
void InsertBulkGroupArea(ScBroadcastArea *pArea, const ScRange &rRange)
Definition: bcaslot.cxx:1169
static bool isMarkedErased(const ScBroadcastAreas::const_iterator &rIter)
Definition: bcaslot.hxx:174
const ScAddress & GetStartAddress() const
Definition: brdcst.hxx:31
ListenerGroupType
Definition: types.hxx:117
bool m_bDetectedRangeSegmentation false