Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / data / bcaslot.cxx
blob4578fd9e55cdf4ac3022731f90ec11218d046ac9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sfx2/objsh.hxx>
21 #include <svl/listener.hxx>
23 #include "document.hxx"
24 #include "brdcst.hxx"
25 #include "bcaslot.hxx"
26 #include "scerrors.hxx"
27 #include "docoptio.hxx"
28 #include "refupdat.hxx"
29 #include "table.hxx"
31 // Number of slots per dimension
32 // must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT
33 #define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16)
34 #if MAXROWCOUNT_DEFINE == 32000
35 #define BCA_SLOTS_ROW 256
36 #define BCA_SLICE 125
37 #else
38 #define BCA_SLICE 128
39 #define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / BCA_SLICE)
40 #endif
41 #define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL)
42 #define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW)
43 // multiple?
44 #if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE)
45 #error bad BCA_SLOTS_COL value!
46 #endif
47 #if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE)
48 #error bad BCA_SLOTS_ROW value!
49 #endif
50 // size of slot array if linear
51 #define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW)
52 // Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and
53 // sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory
54 // anyway, once you reached these values..
55 #if BCA_SLOTS_DEFINE > 268435456
56 #error BCA_SLOTS_DEFINE DOOMed!
57 #endif
59 // STATIC DATA -----------------------------------------------------------
61 TYPEINIT1( ScHint, SfxSimpleHint );
62 TYPEINIT1( ScAreaChangedHint, SfxHint );
64 struct ScSlotData
66 SCROW nStartRow; // first row of this segment
67 SCROW nStopRow; // first row of next segment
68 SCSIZE nSlice; // slice size in this segment
69 SCSIZE nCumulated; // cumulated slots of previous segments
71 ScSlotData( SCROW r1, SCROW r2, SCSIZE s, SCSIZE c ) : nStartRow(r1), nStopRow(r2), nSlice(s), nCumulated(c) {}
73 typedef ::std::vector< ScSlotData > ScSlotDistribution;
74 #if MAXROWCOUNT_DEFINE <= 65536
75 // Linear distribution.
76 static ScSlotDistribution aSlotDistribution( ScSlotData( 0, MAXROWCOUNT, BCA_SLOT_ROWS, 0));
77 static SCSIZE nBcaSlotsRow = BCA_SLOTS_ROW;
78 static SCSIZE nBcaSlots = BCA_SLOTS_DEFINE;
79 #else
80 // Logarithmic or any other distribution.
81 // Upper sheet part usually is more populated and referenced and gets fine
82 // grained resolution, larger data in larger hunks.
83 // Could be further enhanced by also applying a different distribution of
84 // column slots.
85 static SCSIZE initSlotDistribution( ScSlotDistribution & rSD, SCSIZE & rBSR )
87 SCSIZE nSlots = 0;
88 SCROW nRow1 = 0;
89 SCROW nRow2 = 32*1024;
90 SCSIZE nSlice = 128;
91 // Must be sorted by row1,row2!
92 while (nRow2 <= MAXROWCOUNT)
94 rSD.push_back( ScSlotData( nRow1, nRow2, nSlice, nSlots));
95 nSlots += (nRow2 - nRow1) / nSlice;
96 nRow1 = nRow2;
97 nRow2 *= 2;
98 nSlice *= 2;
100 rBSR = nSlots;
101 return nSlots;
103 static ScSlotDistribution aSlotDistribution;
104 static SCSIZE nBcaSlotsRow;
105 static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow) * BCA_SLOTS_COL;
106 // Ensure that all static variables are initialized with this one call.
107 #endif
110 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
111 ScBroadcastAreaSlotMachine* pBASMa ) :
112 aTmpSeekBroadcastArea( ScRange()),
113 pDoc( pDocument ),
114 pBASM( pBASMa ),
115 mbInBroadcastIteration( false)
120 ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
122 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
123 aIter != aBroadcastAreaTbl.end(); /* none */)
125 // Prevent hash from accessing dangling pointer in case area is
126 // deleted.
127 ScBroadcastArea* pArea = (*aIter).mpArea;
128 // Erase all so no hash will be accessed upon destruction of the
129 // boost::unordered_map.
130 aBroadcastAreaTbl.erase( aIter++);
131 if (!pArea->DecRef())
132 delete pArea;
137 bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
139 if ( pDoc->GetHardRecalcState() )
140 return true;
141 if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
142 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
143 if ( !pDoc->GetHardRecalcState() )
145 SfxObjectShell* pShell = pDoc->GetDocumentShell();
146 OSL_ENSURE( pShell, "Missing DocShell :-/" );
148 if ( pShell )
149 pShell->SetError( SCWARN_CORE_HARD_RECALC, OUString( OSL_LOG_PREFIX ) );
151 pDoc->SetAutoCalc( false );
152 pDoc->SetHardRecalcState( true );
154 return true;
156 return false;
160 bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
161 SvtListener* pListener, ScBroadcastArea*& rpArea )
163 bool bNewArea = false;
164 OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
165 if (CheckHardRecalcStateCondition())
166 return false;
167 if ( !rpArea )
169 // Even if most times the area doesn't exist yet and immediately trying
170 // to new and insert it would save an attempt to find it, on mass
171 // operations like identical large [HV]LOOKUP() areas the new/delete
172 // would add quite some penalty for all but the first formula cell.
173 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
174 if (aIter != aBroadcastAreaTbl.end())
175 rpArea = (*aIter).mpArea;
176 else
178 rpArea = new ScBroadcastArea( rRange);
179 if (aBroadcastAreaTbl.insert( rpArea).second)
181 rpArea->IncRef();
182 bNewArea = true;
184 else
186 OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
187 delete rpArea;
188 rpArea = 0;
191 if (rpArea)
192 pListener->StartListening( rpArea->GetBroadcaster());
194 else
196 if (aBroadcastAreaTbl.insert( rpArea).second)
197 rpArea->IncRef();
199 return bNewArea;
203 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
205 OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
206 if (CheckHardRecalcStateCondition())
207 return;
208 if (aBroadcastAreaTbl.insert( pArea).second)
209 pArea->IncRef();
213 // If rpArea != NULL then no listeners are stopped, only the area is removed
214 // and the reference count decremented.
215 void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
216 SvtListener* pListener, ScBroadcastArea*& rpArea )
218 OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
219 if ( !rpArea )
221 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
222 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
223 return;
224 rpArea = (*aIter).mpArea;
225 pListener->EndListening( rpArea->GetBroadcaster() );
226 if ( !rpArea->GetBroadcaster().HasListeners() )
227 { // if nobody is listening we can dispose it
228 if (rpArea->GetRef() == 1)
229 rpArea = NULL; // will be deleted by erase
230 EraseArea( aIter);
233 else
235 if ( !rpArea->GetBroadcaster().HasListeners() )
237 ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
238 if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
239 return;
240 OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
241 if (rpArea->GetRef() == 1)
242 rpArea = NULL; // will be deleted by erase
243 EraseArea( aIter);
249 ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea(
250 const ScRange& rRange ) const
252 aTmpSeekBroadcastArea.UpdateRange( rRange);
253 return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
257 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
259 if (aBroadcastAreaTbl.empty())
260 return false;
261 bool bInBroadcast = mbInBroadcastIteration;
262 mbInBroadcastIteration = true;
263 bool bIsBroadcasted = false;
264 const ScAddress& rAddress = rHint.GetAddress();
265 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
266 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
268 if (isMarkedErased( aIter))
269 continue;
270 ScBroadcastArea* pArea = (*aIter).mpArea;
271 const ScRange& rAreaRange = pArea->GetRange();
272 if (rAreaRange.In( rAddress))
274 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
276 pArea->GetBroadcaster().Broadcast( rHint);
277 bIsBroadcasted = true;
281 mbInBroadcastIteration = bInBroadcast;
282 // A Notify() during broadcast may call EndListeningArea() and thus dispose
283 // an area if it was the last listener, which would invalidate an iterator
284 // pointing to it, hence the real erase is done afterwards.
285 FinallyEraseAreas();
286 return bIsBroadcasted;
290 bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
291 const ScHint& rHint)
293 if (aBroadcastAreaTbl.empty())
294 return false;
295 bool bInBroadcast = mbInBroadcastIteration;
296 mbInBroadcastIteration = true;
297 bool bIsBroadcasted = false;
298 for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
299 aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
301 if (isMarkedErased( aIter))
302 continue;
303 ScBroadcastArea* pArea = (*aIter).mpArea;
304 const ScRange& rAreaRange = pArea->GetRange();
305 if (rAreaRange.Intersects( rRange ))
307 if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
309 pArea->GetBroadcaster().Broadcast( rHint);
310 bIsBroadcasted = true;
314 mbInBroadcastIteration = bInBroadcast;
315 // A Notify() during broadcast may call EndListeningArea() and thus dispose
316 // an area if it was the last listener, which would invalidate an iterator
317 // pointing to it, hence the real erase is done afterwards.
318 FinallyEraseAreas();
319 return bIsBroadcasted;
323 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
325 if (aBroadcastAreaTbl.empty())
326 return;
327 for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
328 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
330 const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
331 if (rRange.In( rAreaRange))
333 ScBroadcastArea* pArea = (*aIter).mpArea;
334 aBroadcastAreaTbl.erase( aIter++); // erase before modifying
335 if (!pArea->DecRef())
337 if (pBASM->IsInBulkBroadcast())
338 pBASM->RemoveBulkArea( pArea);
339 delete pArea;
342 else
343 ++aIter;
348 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
349 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
351 if (aBroadcastAreaTbl.empty())
352 return;
354 SCCOL nCol1, nCol2, theCol1, theCol2;
355 SCROW nRow1, nRow2, theRow1, theRow2;
356 SCTAB nTab1, nTab2, theTab1, theTab2;
357 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
358 for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
359 aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
361 ScBroadcastArea* pArea = (*aIter).mpArea;
362 if ( pArea->IsInUpdateChain() )
364 aBroadcastAreaTbl.erase( aIter++);
365 pArea->DecRef();
367 else
369 pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
370 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
371 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
372 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
374 aBroadcastAreaTbl.erase( aIter++);
375 pArea->DecRef();
376 if (pBASM->IsInBulkBroadcast())
377 pBASM->RemoveBulkArea( pArea);
378 pArea->SetInUpdateChain( true );
379 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
380 if ( pUC )
381 pUC->SetUpdateChainNext( pArea );
382 else // no tail => no head
383 pBASM->SetUpdateChain( pArea );
384 pBASM->SetEOUpdateChain( pArea );
386 else
387 ++aIter;
393 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
395 ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
396 if (aIter == aBroadcastAreaTbl.end())
397 return;
398 if ((*aIter).mpArea != pArea)
399 OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
400 else
402 aBroadcastAreaTbl.erase( aIter);
403 pArea->DecRef();
408 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
410 ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
411 aBroadcastAreaTbl.insert( pArea);
412 if (aPair.second)
413 pArea->IncRef();
414 else
416 // Identical area already exists, add listeners.
417 ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
418 if (pArea != pTarget)
420 SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
421 SvtBroadcaster::ListenersType& rListeners = pArea->GetBroadcaster().GetAllListeners();
422 SvtBroadcaster::ListenersType::iterator it = rListeners.begin(), itEnd = rListeners.end();
423 for (; it != itEnd; ++it)
425 SvtListener& rListener = **it;
426 rListener.StartListening(rTarget);
433 void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
435 if (mbInBroadcastIteration)
437 (*rIter).mbErasure = true; // mark for erasure
438 pBASM->PushAreaToBeErased( this, rIter);
440 else
442 ScBroadcastArea* pArea = (*rIter).mpArea;
443 aBroadcastAreaTbl.erase( rIter);
444 if (!pArea->DecRef())
445 delete pArea;
450 void ScBroadcastAreaSlot::FinallyEraseAreas()
452 pBASM->FinallyEraseAreas( this);
456 // --- ScBroadcastAreaSlotMachine -------------------------------------
458 ScBroadcastAreaSlotMachine::TableSlots::TableSlots()
460 ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ];
461 memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
465 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
467 for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ )
469 if (*pp)
470 delete *pp;
472 delete [] ppSlots;
476 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
477 ScDocument* pDocument ) :
478 pBCAlways( NULL ),
479 pDoc( pDocument ),
480 pUpdateChain( NULL ),
481 pEOUpdateChain( NULL ),
482 nInBulkBroadcast( 0 )
487 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
489 for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin());
490 iTab != aTableSlotsMap.end(); ++iTab)
492 delete (*iTab).second;
494 delete pBCAlways;
495 // Areas to-be-erased still present is a serious error in handling, but at
496 // this stage there's nothing we can do anymore.
497 SAL_WARN_IF( !maAreasToBeErased.empty(), "sc", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
501 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
502 const ScAddress& rAddress ) const
504 SCROW nRow = rAddress.Row();
505 SCCOL nCol = rAddress.Col();
506 if ( !ValidRow(nRow) || !ValidCol(nCol) )
508 OSL_FAIL( "Row/Col invalid, using first slot!" );
509 return 0;
511 for (size_t i=0; i < aSlotDistribution.size(); ++i)
513 if (nRow < aSlotDistribution[i].nStopRow)
515 const ScSlotData& rSD = aSlotDistribution[i];
516 return rSD.nCumulated +
517 (static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice +
518 static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow;
521 OSL_FAIL( "No slot found, using last!" );
522 return nBcaSlots - 1;
526 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
527 SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
529 rStart = ComputeSlotOffset( rRange.aStart );
530 rEnd = ComputeSlotOffset( rRange.aEnd );
531 // count of row slots per column minus one
532 rRowBreak = ComputeSlotOffset(
533 ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
537 inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
538 SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak )
540 if ( nOff < nBreak )
542 ++nOff;
543 ++pp;
545 else
547 nStart += nBcaSlotsRow;
548 nOff = nStart;
549 pp = ppSlots + nOff;
550 nBreak = nOff + nRowBreak;
555 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
556 SvtListener* pListener )
558 if ( rRange == BCA_LISTEN_ALWAYS )
560 if ( !pBCAlways )
561 pBCAlways = new SvtBroadcaster;
562 pListener->StartListening( *pBCAlways );
564 else
566 bool bDone = false;
567 for (SCTAB nTab = rRange.aStart.Tab();
568 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
570 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
571 if (iTab == aTableSlotsMap.end())
572 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
573 nTab, new TableSlots)).first;
574 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
575 SCSIZE nStart, nEnd, nRowBreak;
576 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
577 SCSIZE nOff = nStart;
578 SCSIZE nBreak = nOff + nRowBreak;
579 ScBroadcastAreaSlot** pp = ppSlots + nOff;
580 ScBroadcastArea* pArea = NULL;
581 while ( !bDone && nOff <= nEnd )
583 if ( !*pp )
584 *pp = new ScBroadcastAreaSlot( pDoc, this );
585 if (!pArea)
587 // If the call to StartListeningArea didn't create the
588 // ScBroadcastArea, listeners were added to an already
589 // existing identical area that doesn't need to be inserted
590 // to slots again.
591 if (!(*pp)->StartListeningArea( rRange, pListener, pArea))
592 bDone = true;
594 else
595 (*pp)->InsertListeningArea( pArea);
596 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
603 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
604 SvtListener* pListener )
606 if ( rRange == BCA_LISTEN_ALWAYS )
608 if ( pBCAlways )
610 pListener->EndListening( *pBCAlways);
611 if (!pBCAlways->HasListeners())
613 delete pBCAlways;
614 pBCAlways = NULL;
618 else
620 SCTAB nEndTab = rRange.aEnd.Tab();
621 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
622 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
624 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
625 SCSIZE nStart, nEnd, nRowBreak;
626 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
627 SCSIZE nOff = nStart;
628 SCSIZE nBreak = nOff + nRowBreak;
629 ScBroadcastAreaSlot** pp = ppSlots + nOff;
630 ScBroadcastArea* pArea = NULL;
631 if (nOff == 0 && nEnd == nBcaSlots-1)
633 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
634 // happen for insertion and deletion of sheets.
635 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
638 if ( *pp )
639 (*pp)->EndListeningArea( rRange, pListener, pArea );
640 } while (++pp < pStop);
642 else
644 while ( nOff <= nEnd )
646 if ( *pp )
647 (*pp)->EndListeningArea( rRange, pListener, pArea );
648 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
656 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
658 const ScAddress& rAddress = rHint.GetAddress();
659 if ( rAddress == BCA_BRDCST_ALWAYS )
661 if ( pBCAlways )
663 pBCAlways->Broadcast( rHint );
664 return true;
666 else
667 return false;
669 else
671 TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
672 if (iTab == aTableSlotsMap.end())
673 return false;
674 ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
675 ComputeSlotOffset( rAddress));
676 if ( pSlot )
677 return pSlot->AreaBroadcast( rHint );
678 else
679 return false;
684 bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
685 const ScHint& rHint ) const
687 bool bBroadcasted = false;
688 SCTAB nEndTab = rRange.aEnd.Tab();
689 for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
690 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
692 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
693 SCSIZE nStart, nEnd, nRowBreak;
694 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
695 SCSIZE nOff = nStart;
696 SCSIZE nBreak = nOff + nRowBreak;
697 ScBroadcastAreaSlot** pp = ppSlots + nOff;
698 while ( nOff <= nEnd )
700 if ( *pp )
701 bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
702 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
705 return bBroadcasted;
709 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
710 const ScRange& rRange )
712 SCTAB nEndTab = rRange.aEnd.Tab();
713 for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
714 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
716 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
717 SCSIZE nStart, nEnd, nRowBreak;
718 ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
719 SCSIZE nOff = nStart;
720 SCSIZE nBreak = nOff + nRowBreak;
721 ScBroadcastAreaSlot** pp = ppSlots + nOff;
722 if (nOff == 0 && nEnd == nBcaSlots-1)
724 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
725 // happen for insertion and deletion of sheets.
726 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
729 if ( *pp )
730 (*pp)->DelBroadcastAreasInRange( rRange );
731 } while (++pp < pStop);
733 else
735 while ( nOff <= nEnd )
737 if ( *pp )
738 (*pp)->DelBroadcastAreasInRange( rRange );
739 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
746 // for all affected: remove, chain, update range, insert, and maybe delete
747 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
748 UpdateRefMode eUpdateRefMode,
749 const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
751 // remove affected and put in chain
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)
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 if (nOff == 0 && nEnd == nBcaSlots-1)
764 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
765 // happen for insertion and deletion of sheets.
766 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
769 if ( *pp )
770 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
771 } while (++pp < pStop);
773 else
775 while ( nOff <= nEnd )
777 if ( *pp )
778 (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
779 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
784 // Updating an area's range will modify the hash key, remove areas from all
785 // affected slots. Will be reinserted later with the updated range.
786 ScBroadcastArea* pChain = pUpdateChain;
787 while (pChain)
789 ScBroadcastArea* pArea = pChain;
790 pChain = pArea->GetUpdateChainNext();
791 ScRange aRange( pArea->GetRange());
792 // remove from slots
793 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
795 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
796 if (iTab == aTableSlotsMap.end())
798 OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
799 continue; // for
801 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
802 SCSIZE nStart, nEnd, nRowBreak;
803 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
804 SCSIZE nOff = nStart;
805 SCSIZE nBreak = nOff + nRowBreak;
806 ScBroadcastAreaSlot** pp = ppSlots + nOff;
807 while ( nOff <= nEnd && pArea->GetRef() )
809 if (*pp)
810 (*pp)->UpdateRemoveArea( pArea);
811 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
817 // shift sheets
818 if (nDz)
820 if (nDz < 0)
822 TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
823 TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
824 // Remove sheets, if any, iDel or/and iTab may as well point to end().
825 while (iDel != iTab)
827 delete (*iDel).second;
828 aTableSlotsMap.erase( iDel++);
830 // shift remaining down
831 while (iTab != aTableSlotsMap.end())
833 SCTAB nTab = (*iTab).first + nDz;
834 aTableSlotsMap[nTab] = (*iTab).second;
835 aTableSlotsMap.erase( iTab++);
838 else
840 TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
841 if (iStop != aTableSlotsMap.end())
843 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
844 if (!bStopIsBegin)
845 --iStop;
846 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
847 --iTab;
848 while (iTab != iStop)
850 SCTAB nTab = (*iTab).first + nDz;
851 aTableSlotsMap[nTab] = (*iTab).second;
852 aTableSlotsMap.erase( iTab--);
854 // Shift the very first, iTab==iStop in this case.
855 if (bStopIsBegin)
857 SCTAB nTab = (*iTab).first + nDz;
858 aTableSlotsMap[nTab] = (*iTab).second;
859 aTableSlotsMap.erase( iStop);
865 // work off chain
866 SCCOL nCol1, nCol2, theCol1, theCol2;
867 SCROW nRow1, nRow2, theRow1, theRow2;
868 SCTAB nTab1, nTab2, theTab1, theTab2;
869 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
870 while ( pUpdateChain )
872 ScBroadcastArea* pArea = pUpdateChain;
873 ScRange aRange( pArea->GetRange());
874 pUpdateChain = pArea->GetUpdateChainNext();
876 // update range
877 aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
878 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
879 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
880 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
882 aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
883 pArea->UpdateRange( aRange );
884 pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE
887 // insert to slots
888 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
890 TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
891 if (iTab == aTableSlotsMap.end())
892 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
893 nTab, new TableSlots)).first;
894 ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
895 SCSIZE nStart, nEnd, nRowBreak;
896 ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
897 SCSIZE nOff = nStart;
898 SCSIZE nBreak = nOff + nRowBreak;
899 ScBroadcastAreaSlot** pp = ppSlots + nOff;
900 while ( nOff <= nEnd )
902 if (!*pp)
903 *pp = new ScBroadcastAreaSlot( pDoc, this );
904 (*pp)->UpdateInsert( pArea );
905 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
909 // unchain
910 pArea->SetUpdateChainNext( NULL );
911 pArea->SetInUpdateChain( false );
913 // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
914 // already executed in UpdateRemove().
915 if (!pArea->GetRef())
916 delete pArea;
918 pEOUpdateChain = NULL;
922 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
924 ++nInBulkBroadcast;
928 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
930 if (nInBulkBroadcast > 0)
932 if (--nInBulkBroadcast == 0)
933 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
938 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
940 return aBulkBroadcastAreas.insert( pArea ).second;
944 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
946 return aBulkBroadcastAreas.erase( pArea );
950 void ScBroadcastAreaSlotMachine::PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
951 ScBroadcastAreas::iterator& rIter )
953 maAreasToBeErased.push_back( ::std::make_pair( pSlot, rIter));
957 void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
959 SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc",
960 "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
961 if (pSlot->IsInBroadcastIteration())
962 return;
964 // maAreasToBeErased is a simple vector so erasing an element may
965 // invalidate iterators and would be inefficient anyway. Instead, copy
966 // elements to be preserved (usually none!) to temporary vector and swap.
967 AreasToBeErased aCopy;
968 for (AreasToBeErased::iterator aIt( maAreasToBeErased.begin());
969 aIt != maAreasToBeErased.end(); ++aIt)
971 if ((*aIt).first == pSlot)
972 pSlot->EraseArea( (*aIt).second);
973 else
974 aCopy.push_back( *aIt);
976 maAreasToBeErased.swap( aCopy);
979 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */