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 
38 // Number of slots per dimension
39 // must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT
40 constexpr SCCOL BCA_SLOTS_COL = MAXCOLCOUNT / 16;
42 #if !defined NDEBUG
43 constexpr SCROW BCA_SLICE = 128;
44 static SCROW BCA_SLOTS_ROW(const ScSheetLimits& rLimits) { return rLimits.GetMaxRowCount() / BCA_SLICE; }
45 static SCROW BCA_SLOT_ROWS(const ScSheetLimits& rLimits)
46 {
47  auto nMaxRowCount = rLimits.GetMaxRowCount();
48  auto nSlotsRow = BCA_SLOTS_ROW(rLimits);
49  assert((nMaxRowCount / nSlotsRow * nSlotsRow) == nMaxRowCount && "bad BCA_SLOTS_ROW value");
50  return nMaxRowCount / nSlotsRow;
51 }
52 #endif
53 // multiple?
54 static_assert((BCA_SLOT_COLS * BCA_SLOTS_COL) == MAXCOLCOUNT, "bad BCA_SLOTS_COL value");
55 
56 // size of slot array if linear
57 #if !defined NDEBUG
58 static int BCA_SLOTS(const ScSheetLimits& rLimits) { return BCA_SLOTS_COL * BCA_SLOTS_ROW(rLimits); }
59 #endif
60 
62  pUpdateChainNext(nullptr),
63  aRange(rRange),
64  nRefCount(0),
65  mbInUpdateChain(false),
66  mbGroupListening(false) {}
67 
69  ScBroadcastAreaSlotMachine* pBASMa ) :
70  aTmpSeekBroadcastArea( ScRange()),
71  pDoc( pDocument ),
72  pBASM( pBASMa ),
73  mbInBroadcastIteration( false),
74  mbHasErasedArea(false)
75 {
76 }
77 
79 {
80  for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
81  aIter != aBroadcastAreaTbl.end(); /* none */)
82  {
83  // Prevent hash from accessing dangling pointer in case area is
84  // deleted.
85  ScBroadcastArea* pArea = (*aIter).mpArea;
86  // Erase all so no hash will be accessed upon destruction of the
87  // unordered_map.
88  aBroadcastAreaTbl.erase( aIter++);
89  if (!pArea->DecRef())
90  delete pArea;
91  }
92 }
93 
95 {
98  {
99  if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
100  { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
101  SfxObjectShell* pShell = pDoc->GetDocumentShell();
102  OSL_ENSURE( pShell, "Missing DocShell :-/" );
103 
104  if ( pShell )
106 
107  pDoc->SetAutoCalc( false );
109  pDoc->SetHardRecalcState( eState );
110  }
111  }
112  return eState;
113 }
114 
116  const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
117 {
118  bool bNewArea = false;
119  OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
120  assert(!pDoc->IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
122  return false;
123  if ( !rpArea )
124  {
125  // Even if most times the area doesn't exist yet and immediately trying
126  // to new and insert it would save an attempt to find it, on massive
127  // operations like identical large [HV]LOOKUP() areas the new/delete
128  // would add quite some penalty for all but the first formula cell.
129  ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
130  if (aIter != aBroadcastAreaTbl.end())
131  rpArea = (*aIter).mpArea;
132  else
133  {
134  rpArea = new ScBroadcastArea( rRange);
135  rpArea->SetGroupListening(bGroupListening);
136  if (aBroadcastAreaTbl.insert( rpArea).second)
137  {
138  rpArea->IncRef();
139  bNewArea = true;
140  }
141  else
142  {
143  OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
144  delete rpArea;
145  rpArea = nullptr;
146  }
147  }
148  if (rpArea)
149  pListener->StartListening( rpArea->GetBroadcaster());
150  }
151  else
152  {
153  if (aBroadcastAreaTbl.insert( rpArea).second)
154  rpArea->IncRef();
155  }
156  return bNewArea;
157 }
158 
160 {
161  OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
163  return;
164  if (aBroadcastAreaTbl.insert( pArea).second)
165  pArea->IncRef();
166 }
167 
168 // If rpArea != NULL then no listeners are stopped, only the area is removed
169 // and the reference count decremented.
171  const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
172 {
173  OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
174  if ( !rpArea )
175  {
176  ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
177  if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
178  return;
179  rpArea = (*aIter).mpArea;
180  pListener->EndListening( rpArea->GetBroadcaster() );
181  if ( !rpArea->GetBroadcaster().HasListeners() )
182  { // if nobody is listening we can dispose it
183  if (rpArea->GetRef() == 1)
184  rpArea = nullptr; // will be deleted by erase
185  EraseArea( aIter);
186  }
187  }
188  else
189  {
190  if (rpArea && !rpArea->GetBroadcaster().HasListeners())
191  {
192  ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
193  if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
194  return;
195  OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
196  if (rpArea->GetRef() == 1)
197  rpArea = nullptr; // will be deleted by erase
198  EraseArea( aIter);
199  }
200  }
201 }
202 
203 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
204  const ScRange& rRange, bool bGroupListening )
205 {
207  aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
209 }
210 
211 namespace {
212 
213 void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
214 {
215  ScHint aHint(nHint, ScAddress());
216  ScAddress& rPos = aHint.GetAddress();
217  for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
218  {
219  rPos.SetTab(nTab);
220  for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
221  {
222  rPos.SetCol(nCol);
223  for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
224  {
225  rPos.SetRow(nRow);
226  rBC.Broadcast(aHint);
227  }
228  }
229  }
230 }
231 
232 }
233 
235 {
236  if (aBroadcastAreaTbl.empty())
237  return false;
238 
239  bool bInBroadcast = mbInBroadcastIteration;
240  mbInBroadcastIteration = true;
241  bool bIsBroadcasted = false;
242 
243  mbHasErasedArea = false;
244 
245  for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
246  aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
247  {
248  if (mbHasErasedArea && isMarkedErased( aIter))
249  continue;
250 
251  ScBroadcastArea* pArea = (*aIter).mpArea;
252  const ScRange& rAreaRange = pArea->GetRange();
253 
254  // Take the intersection of the area range and the broadcast range.
255  ScRange aIntersection = rAreaRange.Intersection(rRange);
256  if (!aIntersection.IsValid())
257  continue;
258 
259  if (pArea->IsGroupListening())
260  {
261  if (pBASM->IsInBulkBroadcast())
262  {
263  pBASM->InsertBulkGroupArea(pArea, aIntersection);
264  }
265  else
266  {
267  broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
268  bIsBroadcasted = true;
269  }
270  }
271  else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
272  {
273  broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
274  bIsBroadcasted = true;
275  }
276  }
277 
278  mbInBroadcastIteration = bInBroadcast;
279 
280  // A Notify() during broadcast may call EndListeningArea() and thus dispose
281  // an area if it was the last listener, which would invalidate an iterator
282  // pointing to it, hence the real erase is done afterwards.
284 
285  return bIsBroadcasted;
286 }
287 
289 {
290  if (aBroadcastAreaTbl.empty())
291  return false;
292 
293  bool bInBroadcast = mbInBroadcastIteration;
294  mbInBroadcastIteration = true;
295  bool bIsBroadcasted = false;
296 
297  mbHasErasedArea = false;
298 
299  const ScAddress& rAddress = rHint.GetAddress();
300  for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
301  aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
302  {
303  if (mbHasErasedArea && isMarkedErased( aIter))
304  continue;
305 
306  ScBroadcastArea* pArea = (*aIter).mpArea;
307  const ScRange& rAreaRange = pArea->GetRange();
308  if (rAreaRange.In( rAddress))
309  {
310  if (pArea->IsGroupListening())
311  {
312  if (pBASM->IsInBulkBroadcast())
313  {
314  pBASM->InsertBulkGroupArea(pArea, rAddress);
315  }
316  else
317  {
318  pArea->GetBroadcaster().Broadcast( rHint);
319  bIsBroadcasted = true;
320  }
321  }
322  else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
323  {
324  pArea->GetBroadcaster().Broadcast( rHint);
325  bIsBroadcasted = true;
326  }
327  }
328  }
329 
330  mbInBroadcastIteration = bInBroadcast;
331 
332  // A Notify() during broadcast may call EndListeningArea() and thus dispose
333  // an area if it was the last listener, which would invalidate an iterator
334  // pointing to it, hence the real erase is done afterwards.
336 
337  return bIsBroadcasted;
338 }
339 
341 {
342  if (aBroadcastAreaTbl.empty())
343  return;
344  for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
345  aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
346  {
347  const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
348  if (rRange.In( rAreaRange))
349  {
350  ScBroadcastArea* pArea = (*aIter).mpArea;
351  aBroadcastAreaTbl.erase( aIter++); // erase before modifying
352  if (!pArea->DecRef())
353  {
354  if (pBASM->IsInBulkBroadcast())
355  pBASM->RemoveBulkArea( pArea);
356  delete pArea;
357  }
358  }
359  else
360  ++aIter;
361  }
362 }
363 
365  const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
366 {
367  if (aBroadcastAreaTbl.empty())
368  return;
369 
370  SCCOL nCol1, nCol2, theCol1, theCol2;
371  SCROW nRow1, nRow2, theRow1, theRow2;
372  SCTAB nTab1, nTab2, theTab1, theTab2;
373  rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
374  for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
375  aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
376  {
377  ScBroadcastArea* pArea = (*aIter).mpArea;
378  if ( pArea->IsInUpdateChain() )
379  {
380  aBroadcastAreaTbl.erase( aIter++);
381  pArea->DecRef();
382  }
383  else
384  {
385  pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
386  if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
387  nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
388  theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
389  {
390  aBroadcastAreaTbl.erase( aIter++);
391  pArea->DecRef();
392  if (pBASM->IsInBulkBroadcast())
393  pBASM->RemoveBulkArea( pArea);
394  pArea->SetInUpdateChain( true );
396  if ( pUC )
397  pUC->SetUpdateChainNext( pArea );
398  else // no tail => no head
399  pBASM->SetUpdateChain( pArea );
400  pBASM->SetEOUpdateChain( pArea );
401  }
402  else
403  ++aIter;
404  }
405  }
406 }
407 
409 {
410  ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
411  if (aIter == aBroadcastAreaTbl.end())
412  return;
413  if ((*aIter).mpArea != pArea)
414  OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
415  else
416  {
417  aBroadcastAreaTbl.erase( aIter);
418  pArea->DecRef();
419  }
420 }
421 
423 {
424  ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
425  aBroadcastAreaTbl.insert( pArea);
426  if (aPair.second)
427  pArea->IncRef();
428  else
429  {
430  // Identical area already exists, add listeners.
431  ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
432  if (pArea != pTarget)
433  {
434  SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
436  for (auto& pListener : rListeners)
437  {
438  SvtListener& rListener = *pListener;
439  rListener.StartListening(rTarget);
440  }
441  }
442  }
443 }
444 
445 void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
446 {
448  {
449  (*rIter).mbErasure = true; // mark for erasure
450  mbHasErasedArea = true; // at least one area is marked for erasure.
451  pBASM->PushAreaToBeErased( this, rIter);
452  }
453  else
454  {
455  ScBroadcastArea* pArea = (*rIter).mpArea;
456  aBroadcastAreaTbl.erase( rIter);
457  if (!pArea->DecRef())
458  {
459  if (pBASM->IsInBulkBroadcast())
460  pBASM->RemoveBulkGroupArea(pArea);
461  delete pArea;
462  }
463  }
464 }
465 
467  const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
469 {
470  for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
471  aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
472  {
473  if (isMarkedErased( aIter))
474  continue;
475 
476  ScBroadcastArea* pArea = (*aIter).mpArea;
477  const ScRange& rAreaRange = pArea->GetRange();
478  switch (eGroup)
479  {
481  if (!pArea->IsGroupListening())
482  continue;
483  break;
485  default:
486  ;
487  }
488 
489  switch (eType)
490  {
492  if (!rRange.In(rAreaRange))
493  // The range needs to be fully inside specified range.
494  continue;
495  break;
497  if (!rRange.Intersects(rAreaRange))
498  // The range needs to be partially overlapping or fully inside.
499  continue;
500  break;
502  if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.In(rAreaRange))
503  // The range needs to be one single row and fully inside
504  // specified range.
505  continue;
506  break;
508  if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.In(rAreaRange))
509  // The range needs to be one single column and fully inside
510  // specified range.
511  continue;
512  break;
513  }
514 
516  for (const auto& pListener : rLst)
517  {
518  sc::AreaListener aEntry;
519  aEntry.maArea = rAreaRange;
520  aEntry.mbGroupListening = pArea->IsGroupListening();
521  aEntry.mpListener = pListener;
522  rListeners.push_back(aEntry);
523  }
524  }
525 }
526 
527 #if DEBUG_AREA_BROADCASTER
528 void ScBroadcastAreaSlot::Dump() const
529 {
530  for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
531  {
532  const ScBroadcastArea* pArea = rEntry.mpArea;
533  const SvtBroadcaster& rBC = pArea->GetBroadcaster();
534  const SvtBroadcaster::ListenersType& rListeners = rBC.GetAllListeners();
535  size_t n = rListeners.size();
536 
537  cout << " * range: " << OUStringToOString(pArea->GetRange().Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
538  << ", group: " << pArea->IsGroupListening()
539  << ", listener count: " << n << endl;
540 
541  for (size_t i = 0; i < n; ++i)
542  {
543  const ScFormulaCell* pFC = dynamic_cast<const ScFormulaCell*>(rListeners[i]);
544  if (pFC)
545  {
546  cout << " * listener: formula cell: "
547  << OUStringToOString(pFC->aPos.Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
548  << endl;
549  continue;
550  }
551 
552  const sc::FormulaGroupAreaListener* pFGListener = dynamic_cast<const sc::FormulaGroupAreaListener*>(rListeners[i]);
553  if (pFGListener)
554  {
555  cout << " * listener: formula group: (pos: "
556  << OUStringToOString(pFGListener->getTopCellPos().Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
557  << ", length: " << pFGListener->getGroupLength()
558  << ")" << endl;
559  continue;
560  }
561 
562  cout << " * listener: unknown" << endl;
563  }
564  }
565 }
566 #endif
567 
569 {
570  pBASM->FinallyEraseAreas( this);
571 }
572 
573 // --- ScBroadcastAreaSlotMachine -------------------------------------
574 
576  : mnBcaSlots(nBcaSlots)
577 {
578  ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
579  memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
580 }
581 
583 {
584  for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
585  delete *pp;
586 }
587 
589  ScDocument* pDocument ) :
590  pDoc( pDocument ),
591  pUpdateChain( nullptr ),
592  pEOUpdateChain( nullptr ),
593  nInBulkBroadcast( 0 )
594 {
595  const ScSheetLimits& rSheetLimits = pDoc->GetSheetLimits();
596 
597  assert((BCA_SLOT_ROWS(rSheetLimits) * BCA_SLOTS_ROW(rSheetLimits)) == pDoc->GetSheetLimits().GetMaxRowCount() && "bad BCA_SLOTS_ROW value");
598  // Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and
599  // sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory
600  // anyway, once you reached these values...
601  assert(BCA_SLOTS(rSheetLimits) <= 268435456 && "DOOMed");
602 
603  // initSlotDistribution ---------
604  // Logarithmic or any other distribution.
605  // Upper sheet part usually is more populated and referenced and gets fine
606  // grained resolution, larger data in larger hunks.
607  // Could be further enhanced by also applying a different distribution of
608  // column slots.
609  SCSIZE nSlots = 0;
610  SCROW nRow1 = 0;
611  SCROW nRow2 = 32*1024;
612  SCSIZE nSlice = 128;
613  // Must be sorted by row1,row2!
614  while (nRow2 <= rSheetLimits.GetMaxRowCount())
615  {
616  maSlotDistribution.emplace_back( nRow1, nRow2, nSlice, nSlots);
617  nSlots += (nRow2 - nRow1) / nSlice;
618  nRow1 = nRow2;
619  nRow2 *= 2;
620  nSlice *= 2;
621  }
622  mnBcaSlotsRow = nSlots;
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 & i : maSlotDistribution)
646  {
647  if (nRow < i.nStopRow)
648  {
649  const ScSlotData& rSD = i;
650  return rSD.nCumulated +
651  static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSlice +
652  static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * mnBcaSlotsRow;
653  }
654  }
655  OSL_FAIL( "No slot found, using last!" );
656  return mnBcaSlots - 1;
657 }
658 
660  SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
661 {
662  rStart = ComputeSlotOffset( rRange.aStart );
663  rEnd = ComputeSlotOffset( rRange.aEnd );
664  // count of row slots per column minus one
665  rRowBreak = ComputeSlotOffset(
666  ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
667 }
668 
669 static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
670  SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsRow )
671 {
672  if ( nOff < nBreak )
673  {
674  ++nOff;
675  ++pp;
676  }
677  else
678  {
679  nStart += nBcaSlotsRow;
680  nOff = nStart;
681  pp = ppSlots + nOff;
682  nBreak = nOff + nRowBreak;
683  }
684 }
685 
687  const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
688 {
689  if ( rRange == BCA_LISTEN_ALWAYS )
690  {
691  if ( !pBCAlways )
692  pBCAlways.reset( new SvtBroadcaster );
693  pListener->StartListening( *pBCAlways );
694  }
695  else
696  {
697  // A new area needs to be inserted to the corresponding slots, for 3D
698  // ranges for all sheets, do not slice into per sheet areas or the
699  // !bDone will break too early (i.e. after the first sheet) if
700  // subsequent listeners are to be added.
701  ScBroadcastArea* pArea = nullptr;
702  bool bDone = false;
703  for (SCTAB nTab = rRange.aStart.Tab();
704  !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
705  {
706  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
707  if (iTab == aTableSlotsMap.end())
708  iTab = aTableSlotsMap.emplace(nTab, std::make_unique<TableSlots>(mnBcaSlots)).first;
709  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
710  SCSIZE nStart, nEnd, nRowBreak;
711  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
712  SCSIZE nOff = nStart;
713  SCSIZE nBreak = nOff + nRowBreak;
714  ScBroadcastAreaSlot** pp = ppSlots + nOff;
715  while ( !bDone && nOff <= nEnd )
716  {
717  if ( !*pp )
718  *pp = new ScBroadcastAreaSlot( pDoc, this );
719  if (!pArea)
720  {
721  // If the call to StartListeningArea didn't create the
722  // ScBroadcastArea, listeners were added to an already
723  // existing identical area that doesn't need to be inserted
724  // to slots again.
725  if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
726  bDone = true;
727  }
728  else
729  (*pp)->InsertListeningArea( pArea);
730  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
731  }
732  }
733  }
734 }
735 
737  const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
738 {
739  if ( rRange == BCA_LISTEN_ALWAYS )
740  {
741  if ( pBCAlways )
742  {
743  pListener->EndListening( *pBCAlways);
744  if (!pBCAlways->HasListeners())
745  {
746  pBCAlways.reset();
747  }
748  }
749  }
750  else
751  {
752  SCTAB nEndTab = rRange.aEnd.Tab();
753  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
754  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
755  {
756  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
757  SCSIZE nStart, nEnd, nRowBreak;
758  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
759  SCSIZE nOff = nStart;
760  SCSIZE nBreak = nOff + nRowBreak;
761  ScBroadcastAreaSlot** pp = ppSlots + nOff;
762  ScBroadcastArea* pArea = nullptr;
763  if (nOff == 0 && nEnd == mnBcaSlots-1)
764  {
765  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
766  // happen for insertion and deletion of sheets.
767  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
768  do
769  {
770  if ( *pp )
771  (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
772  } while (++pp < pStop);
773  }
774  else
775  {
776  while ( nOff <= nEnd )
777  {
778  if ( *pp )
779  (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
780  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
781  }
782  }
783  }
784  }
785 }
786 
788 {
789  bool bBroadcasted = false;
790  SCTAB nEndTab = rRange.aEnd.Tab();
791  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
792  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
793  {
794  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
795  SCSIZE nStart, nEnd, nRowBreak;
796  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
797  SCSIZE nOff = nStart;
798  SCSIZE nBreak = nOff + nRowBreak;
799  ScBroadcastAreaSlot** pp = ppSlots + nOff;
800  while ( nOff <= nEnd )
801  {
802  if ( *pp )
803  bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
804  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
805  }
806  }
807  return bBroadcasted;
808 }
809 
811 {
812  const ScAddress& rAddress = rHint.GetAddress();
813  if ( rAddress == BCA_BRDCST_ALWAYS )
814  {
815  if ( pBCAlways )
816  {
817  pBCAlways->Broadcast( rHint );
818  return true;
819  }
820  else
821  return false;
822  }
823  else
824  {
825  TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
826  if (iTab == aTableSlotsMap.end())
827  return false;
828  ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
829  ComputeSlotOffset( rAddress));
830  if ( pSlot )
831  return pSlot->AreaBroadcast( rHint );
832  else
833  return false;
834  }
835 }
836 
838  const ScRange& rRange )
839 {
840  SCTAB nEndTab = rRange.aEnd.Tab();
841  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
842  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
843  {
844  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
845  SCSIZE nStart, nEnd, nRowBreak;
846  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
847  SCSIZE nOff = nStart;
848  SCSIZE nBreak = nOff + nRowBreak;
849  ScBroadcastAreaSlot** pp = ppSlots + nOff;
850  if (nOff == 0 && nEnd == mnBcaSlots-1)
851  {
852  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
853  // happen for insertion and deletion of sheets.
854  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
855  do
856  {
857  if ( *pp )
858  (*pp)->DelBroadcastAreasInRange( rRange );
859  } while (++pp < pStop);
860  }
861  else
862  {
863  while ( nOff <= nEnd )
864  {
865  if ( *pp )
866  (*pp)->DelBroadcastAreasInRange( rRange );
867  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
868  }
869  }
870  }
871 }
872 
873 // for all affected: remove, chain, update range, insert, and maybe delete
875  UpdateRefMode eUpdateRefMode,
876  const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
877 {
878  // remove affected and put in chain
879  SCTAB nEndTab = rRange.aEnd.Tab();
880  for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
881  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
882  {
883  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
884  SCSIZE nStart, nEnd, nRowBreak;
885  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
886  SCSIZE nOff = nStart;
887  SCSIZE nBreak = nOff + nRowBreak;
888  ScBroadcastAreaSlot** pp = ppSlots + nOff;
889  if (nOff == 0 && nEnd == mnBcaSlots-1)
890  {
891  // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
892  // happen for insertion and deletion of sheets.
893  ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
894  do
895  {
896  if ( *pp )
897  (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
898  } while (++pp < pStop);
899  }
900  else
901  {
902  while ( nOff <= nEnd )
903  {
904  if ( *pp )
905  (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
906  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
907  }
908  }
909  }
910 
911  // Updating an area's range will modify the hash key, remove areas from all
912  // affected slots. Will be reinserted later with the updated range.
913  ScBroadcastArea* pChain = pUpdateChain;
914  while (pChain)
915  {
916  ScBroadcastArea* pArea = pChain;
917  pChain = pArea->GetUpdateChainNext();
918  ScRange aRange( pArea->GetRange());
919  // remove from slots
920  for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
921  {
922  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
923  if (iTab == aTableSlotsMap.end())
924  {
925  OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
926  continue; // for
927  }
928  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
929  SCSIZE nStart, nEnd, nRowBreak;
930  ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
931  SCSIZE nOff = nStart;
932  SCSIZE nBreak = nOff + nRowBreak;
933  ScBroadcastAreaSlot** pp = ppSlots + nOff;
934  while ( nOff <= nEnd && pArea->GetRef() )
935  {
936  if (*pp)
937  (*pp)->UpdateRemoveArea( pArea);
938  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
939  }
940  }
941 
942  }
943 
944  // shift sheets
945  if (nDz)
946  {
947  if (nDz < 0)
948  {
949  TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
950  TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
951  // Remove sheets, if any, iDel or/and iTab may as well point to end().
952  while (iDel != iTab)
953  {
954  aTableSlotsMap.erase( iDel++);
955  }
956  // shift remaining down
957  while (iTab != aTableSlotsMap.end())
958  {
959  SCTAB nTab = (*iTab).first + nDz;
960  aTableSlotsMap[nTab] = std::move((*iTab).second);
961  aTableSlotsMap.erase( iTab++);
962  }
963  }
964  else
965  {
966  TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
967  if (iStop != aTableSlotsMap.end())
968  {
969  bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
970  if (!bStopIsBegin)
971  --iStop;
972  TableSlotsMap::iterator iTab( aTableSlotsMap.end());
973  --iTab;
974  while (iTab != iStop)
975  {
976  SCTAB nTab = (*iTab).first + nDz;
977  aTableSlotsMap[nTab] = std::move((*iTab).second);
978  aTableSlotsMap.erase( iTab--);
979  }
980  // Shift the very first, iTab==iStop in this case.
981  if (bStopIsBegin)
982  {
983  SCTAB nTab = (*iTab).first + nDz;
984  aTableSlotsMap[nTab] = std::move((*iTab).second);
985  aTableSlotsMap.erase( iStop);
986  }
987  }
988  }
989  }
990 
991  // work off chain
992  SCCOL nCol1, nCol2, theCol1, theCol2;
993  SCROW nRow1, nRow2, theRow1, theRow2;
994  SCTAB nTab1, nTab2, theTab1, theTab2;
995  rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
996  while ( pUpdateChain )
997  {
998  ScBroadcastArea* pArea = pUpdateChain;
999  ScRange aRange( pArea->GetRange());
1000  pUpdateChain = pArea->GetUpdateChainNext();
1001 
1002  // update range
1003  aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
1004  if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
1005  nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
1006  theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
1007  {
1008  aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
1009  pArea->UpdateRange( aRange );
1010  // For DDE and ScLookupCache
1011  pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
1012  }
1013 
1014  // insert to slots
1015  for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
1016  {
1017  TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
1018  if (iTab == aTableSlotsMap.end())
1019  iTab = aTableSlotsMap.emplace(nTab, std::make_unique<TableSlots>(mnBcaSlots)).first;
1020  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
1021  SCSIZE nStart, nEnd, nRowBreak;
1022  ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
1023  SCSIZE nOff = nStart;
1024  SCSIZE nBreak = nOff + nRowBreak;
1025  ScBroadcastAreaSlot** pp = ppSlots + nOff;
1026  while ( nOff <= nEnd )
1027  {
1028  if (!*pp)
1029  *pp = new ScBroadcastAreaSlot( pDoc, this );
1030  (*pp)->UpdateInsert( pArea );
1031  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
1032  }
1033  }
1034 
1035  // unchain
1036  pArea->SetUpdateChainNext( nullptr );
1037  pArea->SetInUpdateChain( false );
1038 
1039  // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
1040  // already executed in UpdateRemove().
1041  if (!pArea->GetRef())
1042  delete pArea;
1043  }
1044  pEOUpdateChain = nullptr;
1045 }
1046 
1048 {
1049  ++nInBulkBroadcast;
1050 }
1051 
1053 {
1054  if (nInBulkBroadcast <= 0)
1055  return;
1056 
1057  if (--nInBulkBroadcast == 0)
1058  {
1060  bool bBroadcasted = BulkBroadcastGroupAreas( nHintId );
1061  // Trigger the "final" tracking.
1063  pDoc->FinalTrackFormulas( nHintId );
1064  else if (bBroadcasted)
1065  pDoc->TrackFormulas( nHintId );
1066  }
1067 }
1068 
1070 {
1071  return aBulkBroadcastAreas.insert( pArea ).second;
1072 }
1073 
1075 {
1076  BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
1077  if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
1078  {
1079  // Insert a new one.
1080  it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, std::make_unique<sc::ColumnSpanSet>()));
1081  }
1082 
1083  sc::ColumnSpanSet *const pSet = it->second.get();
1084  assert(pSet);
1085  pSet->set(*pDoc, rRange, true);
1086 }
1087 
1089 {
1090  if (m_BulkGroupAreas.empty())
1091  return false;
1092 
1093  sc::BulkDataHint aHint( *pDoc, nHintId);
1094 
1095  bool bBroadcasted = false;
1096  for (const auto& [pArea, rxSpans] : m_BulkGroupAreas)
1097  {
1098  assert(pArea);
1099  SvtBroadcaster& rBC = pArea->GetBroadcaster();
1100  if (!rBC.HasListeners())
1101  {
1102  /* FIXME: find the cause where the last listener is removed and
1103  * this area is still listed here. */
1104  SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
1105  }
1106  else
1107  {
1108  const sc::ColumnSpanSet *const pSpans = rxSpans.get();
1109  assert(pSpans);
1110  aHint.setSpans(pSpans);
1111  rBC.Broadcast(aHint);
1112  bBroadcasted = true;
1113  }
1114  }
1115 
1116  m_BulkGroupAreas.clear();
1117 
1118  return bBroadcasted;
1119 }
1120 
1122 {
1123  return aBulkBroadcastAreas.erase( pArea );
1124 }
1125 
1127 {
1128  m_BulkGroupAreas.erase(pArea);
1129 }
1130 
1132  ScBroadcastAreas::iterator& rIter )
1133 {
1134  maAreasToBeErased.emplace_back( pSlot, rIter);
1135 }
1136 
1138 {
1139  SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
1140  "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
1141  if (pSlot->IsInBroadcastIteration())
1142  return;
1143 
1144  // maAreasToBeErased is a simple vector so erasing an element may
1145  // invalidate iterators and would be inefficient anyway. Instead, copy
1146  // elements to be preserved (usually none!) to temporary vector and swap.
1147  AreasToBeErased aCopy;
1148  for (auto& rArea : maAreasToBeErased)
1149  {
1150  if (rArea.first == pSlot)
1151  pSlot->EraseArea( rArea.second);
1152  else
1153  aCopy.push_back( rArea);
1154  }
1155  maAreasToBeErased.swap( aCopy);
1156 }
1157 
1158 std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
1159  const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
1160 {
1161  std::vector<sc::AreaListener> aRet;
1162 
1163  SCTAB nEndTab = rRange.aEnd.Tab();
1164  for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
1165  iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
1166  {
1167  ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
1168  SCSIZE nStart, nEnd, nRowBreak;
1169  ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
1170  SCSIZE nOff = nStart;
1171  SCSIZE nBreak = nOff + nRowBreak;
1172  ScBroadcastAreaSlot** pp = ppSlots + nOff;
1173  while ( nOff <= nEnd )
1174  {
1175  ScBroadcastAreaSlot* p = *pp;
1176  if (p)
1177  p->GetAllListeners(rRange, aRet, eType, eGroup);
1178  ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsRow);
1179  }
1180  }
1181 
1182  return aRet;
1183 }
1184 
1185 #if DEBUG_AREA_BROADCASTER
1186 void ScBroadcastAreaSlotMachine::Dump() const
1187 {
1188  cout << "slot distribution count: " << nBcaSlots << endl;
1189  for (const auto& [rIndex, pTabSlots] : aTableSlotsMap)
1190  {
1191  cout << "-- sheet (index: " << rIndex << ")" << endl;
1192 
1193  assert(pTabSlots);
1194  ScBroadcastAreaSlot** ppSlots = pTabSlots->getSlots();
1195  for (SCSIZE i = 0; i < nBcaSlots; ++i)
1196  {
1197  const ScBroadcastAreaSlot* pSlot = ppSlots[i];
1198  if (pSlot)
1199  {
1200  cout << "* slot " << i << endl;
1201  pSlot->Dump();
1202  }
1203  }
1204  }
1205 }
1206 #endif
1207 
1208 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsInBulkBroadcast() const
Definition: bcaslot.hxx:347
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2107
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:189
ScAddress aStart
Definition: address.hxx:500
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:952
bool IsInBroadcastIteration() const
Definition: bcaslot.hxx:223
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:445
static void ComputeNextSlot(SCSIZE &nOff, SCSIZE &nBreak, ScBroadcastAreaSlot **&pp, SCSIZE &nStart, ScBroadcastAreaSlot **const &ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsRow)
Definition: bcaslot.cxx:669
sal_Int32 nRefCount
void InsertListeningArea(ScBroadcastArea *pArea)
Insert a ScBroadcastArea obtained via StartListeningArea() to subsequent slots.
Definition: bcaslot.cxx:159
SCROW Row() const
Definition: address.hxx:262
bool IsGroupListening() const
Definition: bcaslot.hxx:82
size_t RemoveBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1121
SC_DLLPUBLIC bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1549
bool mbInBroadcastIteration
Definition: bcaslot.hxx:149
ScBroadcastArea(const ScBroadcastArea &)=delete
FilterGroup & rTarget
sal_Int64 n
ListenersType & GetAllListeners()
bool IsTrackFormulasPending() const
Definition: document.hxx:2295
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener, ScBroadcastArea *&rpArea)
Definition: bcaslot.cxx:170
ScAddress aEnd
Definition: address.hxx:501
void UpdateRemove(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:364
std::unordered_set< const ScBroadcastArea *, ScBroadcastAreaBulkHash, ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk
Definition: bcaslot.hxx:137
constexpr SCCOL BCA_SLOTS_COL
Definition: bcaslot.cxx:40
ScBroadcastArea * GetEOUpdateChain() const
Definition: bcaslot.hxx:345
const SCCOL MAXCOLCOUNT
Definition: address.hxx:64
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1558
SCROW GetMaxRowCount() const
Definition: sheetlimits.hxx:61
void SetHardRecalcState(HardRecalcState eVal)
Definition: document.hxx:2301
void LeaveBulkBroadcast(SfxHintId nHintId)
Definition: bcaslot.cxx:1052
void StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:686
ScBroadcastAreaSlot(ScDocument *pDoc, ScBroadcastAreaSlotMachine *pBASM)
Definition: bcaslot.cxx:68
constexpr SCROW BCA_SLICE
Definition: bcaslot.cxx:43
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:837
const ScAddress & GetAddress() const
Definition: brdcst.hxx:31
Used in a Unique Associative Container.
Definition: bcaslot.hxx:52
#define SCWARN_CORE_HARD_RECALC
Definition: scerrors.hxx:79
bool EndListening(SvtBroadcaster &rBroadcaster)
Collection of BroadcastAreas.
Definition: bcaslot.hxx:142
std::vector< SvtListener * > ListenersType
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:45
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
static SCROW BCA_SLOT_ROWS(const ScSheetLimits &rLimits)
Definition: bcaslot.cxx:45
ScDocument::HardRecalcState CheckHardRecalcStateCondition() const
More hypothetical (memory would probably be doomed anyway) check whether there would be an overflow w...
Definition: bcaslot.cxx:94
void setSpans(const ColumnSpanSet *pSpans)
bool IsInUpdateChain() const
Definition: bcaslot.hxx:79
void set(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
SCTAB Tab() const
Definition: address.hxx:271
void SetRow(SCROW nRowP)
Definition: address.hxx:275
ScBroadcastArea aTmpSeekBroadcastArea
Definition: bcaslot.hxx:146
ScAddress aPos
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:234
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: bcaslot.cxx:736
void SetCol(SCCOL nColP)
Definition: address.hxx:279
TableSlotsMap aTableSlotsMap
Definition: bcaslot.hxx:305
void FinalTrackFormulas(SfxHintId nHintId)
Definition: documen7.cxx:506
void UpdateInsert(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:422
void PushAreaToBeErased(ScBroadcastAreaSlot *pSlot, ScBroadcastAreas::iterator &rIter)
Definition: bcaslot.cxx:1131
bool BulkBroadcastGroupAreas(SfxHintId nHintId)
Definition: bcaslot.cxx:1088
void SetTab(SCTAB nTabP)
Definition: address.hxx:283
std::unique_ptr< ScBroadcastAreaSlot *[]> ppSlots
Definition: bcaslot.hxx:279
void SetUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:344
void Broadcast(const SfxHint &rHint)
int i
void RemoveBulkGroupArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:1126
static int BCA_SLOTS(const ScSheetLimits &rLimits)
Definition: bcaslot.cxx:58
sal_Int16 SCCOL
Definition: types.hxx:22
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:875
SvtListener * mpListener
Definition: bcaslot.hxx:43
SC_DLLPUBLIC void SetAutoCalc(bool bNewAutoCalc)
Definition: documen7.cxx:602
void UpdateRange(const ScRange &rNewRange)
Definition: bcaslot.hxx:71
BulkGroupAreasType m_BulkGroupAreas
Definition: bcaslot.hxx:304
ScRange maArea
Definition: bcaslot.hxx:41
void SetGroupListening(bool b)
Definition: bcaslot.hxx:83
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:874
SC_DLLPUBLIC 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:2203
void DelBroadcastAreasInRange(const ScRange &rRange)
Definition: bcaslot.cxx:340
ScBroadcastAreaSlotMachine * pBASM
Definition: bcaslot.hxx:148
Structure that stores segments of boolean flags per column, and perform custom action on those segmen...
void FinallyEraseAreas(ScBroadcastAreaSlot *pSlot)
Definition: bcaslot.cxx:1137
#define BCA_BRDCST_ALWAYS
Definition: address.hxx:951
ScBroadcastArea * GetUpdateChainNext() const
Definition: bcaslot.hxx:77
HardRecalcState GetHardRecalcState() const
Definition: document.hxx:2300
SvtBroadcaster & GetBroadcaster()
Definition: bcaslot.hxx:69
void TrackFormulas(SfxHintId nHintId=SfxHintId::ScDataChanged)
Definition: documen7.cxx:527
BroadcastAreaSlots and their management, once per document.
Definition: bcaslot.hxx:246
SCSIZE ComputeSlotOffset(const ScAddress &rAddress) const
Definition: bcaslot.cxx:635
void SetUpdateChainNext(ScBroadcastArea *p)
Definition: bcaslot.hxx:78
sal_uLong DecRef()
Definition: bcaslot.hxx:75
ScBroadcastAreas::iterator FindBroadcastArea(const ScRange &rRange, bool bGroupListening)
Definition: bcaslot.cxx:203
void GetVars(SCCOL &nCol1, SCROW &nRow1, SCTAB &nTab1, SCCOL &nCol2, SCROW &nRow2, SCTAB &nTab2) const
Definition: address.hxx:693
bool In(const ScAddress &) const
is Address& in Range?
Definition: address.hxx:733
bool StartListening(SvtBroadcaster &rBroadcaster)
void UpdateBroadcastAreas(UpdateRefMode eUpdateRefMode, const ScRange &rRange, SCCOL nDx, SCROW nDy, SCTAB nDz)
Definition: bcaslot.cxx:874
bool IsValid() const
Definition: address.hxx:547
SCCOL Col() const
Definition: address.hxx:267
::std::vector< ::std::pair< ScBroadcastAreaSlot *, ScBroadcastAreas::iterator > > AreasToBeErased
Definition: bcaslot.hxx:287
void GetAllListeners(const ScRange &rRange, std::vector< sc::AreaListener > &rListeners, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup)
Definition: bcaslot.cxx:466
bool IsDelayedFormulaGrouping() const
Definition: document.hxx:1372
AreaOverlapType
Definition: types.hxx:110
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
UpdateRefMode
Definition: global.hxx:311
ScBroadcastAreasBulk aBulkBroadcastAreas
Definition: bcaslot.hxx:303
sal_Int32 SCROW
Definition: types.hxx:18
bool ValidRow(SCROW nRow) const
Definition: document.hxx:876
ScBroadcastAreaSlotMachine(ScDocument *pDoc)
Definition: bcaslot.cxx:588
ScDocument * pDoc
Definition: bcaslot.hxx:147
#define SAL_WARN_IF(condition, area, stream)
void FinallyEraseAreas()
Finally erase all areas pushed as to-be-erased.
Definition: bcaslot.cxx:568
CalcAll() without broadcast/notify but setting up new listeners.
void SetEOUpdateChain(ScBroadcastArea *p)
Definition: bcaslot.hxx:346
SvStream & endl(SvStream &rStr)
bool InsertBulkArea(const ScBroadcastArea *p)
Definition: bcaslot.cxx:1069
ScBroadcastAreas aBroadcastAreaTbl
Definition: bcaslot.hxx:145
void * p
bool HasListeners() const
ScBroadcastArea * pEOUpdateChain
Definition: bcaslot.hxx:310
bool StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener, ScBroadcastArea *&rpArea)
Only here new ScBroadcastArea objects are created, prevention of dupes.
Definition: bcaslot.cxx:115
bool mbHasErasedArea
If true, the slot has at least one area broadcaster marked for removal.
Definition: bcaslot.hxx:158
bool mbGroupListening
Definition: bcaslot.hxx:42
constexpr SCCOL BCA_SLOT_COLS
Definition: bcaslot.cxx:41
ScSlotDistribution maSlotDistribution
Definition: bcaslot.hxx:300
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1058
AreasToBeErased maAreasToBeErased
Definition: bcaslot.hxx:306
#define SAL_WARN(area, stream)
void IncRef()
Definition: bcaslot.hxx:74
sal_uLong GetRef() const
Definition: bcaslot.hxx:76
std::vector< sc::AreaListener > GetAllListeners(const ScRange &rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup=sc::ListenerGroupType::Both)
Definition: bcaslot.cxx:1158
void ComputeAreaPoints(const ScRange &rRange, SCSIZE &nStart, SCSIZE &nEnd, SCSIZE &nRowBreak) const
Definition: bcaslot.cxx:659
static SCROW BCA_SLOTS_ROW(const ScSheetLimits &rLimits)
Definition: bcaslot.cxx:44
void SetInUpdateChain(bool b)
Definition: bcaslot.hxx:80
bool AreaBroadcast(const ScRange &rRange, SfxHintId nHint)
Definition: bcaslot.cxx:787
void UpdateRemoveArea(ScBroadcastArea *pArea)
Definition: bcaslot.cxx:408
void SetError(ErrCode rErr)
const ScRange & GetRange() const
Definition: bcaslot.hxx:73
std::unique_ptr< SvtBroadcaster > pBCAlways
Definition: bcaslot.hxx:307
ScBroadcastArea * pUpdateChain
Definition: bcaslot.hxx:309
sal_Int16 SCTAB
Definition: types.hxx:23
void InsertBulkGroupArea(ScBroadcastArea *pArea, const ScRange &rRange)
Definition: bcaslot.cxx:1074
static bool isMarkedErased(const ScBroadcastAreas::const_iterator &rIter)
Definition: bcaslot.hxx:175
ListenerGroupType
Definition: types.hxx:118