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