xref: /aoo41x/main/sc/source/core/data/bcaslot.cxx (revision b3f79822)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 
28 
29 #include <sfx2/objsh.hxx>
30 #include <svl/listener.hxx>
31 #include <svl/listeneriter.hxx>
32 
33 #include "document.hxx"
34 #include "brdcst.hxx"
35 #include "bcaslot.hxx"
36 #include "scerrors.hxx"
37 #include "docoptio.hxx"
38 #include "refupdat.hxx"
39 #include "table.hxx"
40 
41 // Number of slots per dimension
42 // must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT
43 #define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16)
44 #if MAXROWCOUNT_DEFINE == 32000
45 #define BCA_SLOTS_ROW 256
46 #define BCA_SLICE 125
47 #else
48 #define BCA_SLICE 128
49 #define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / BCA_SLICE)
50 #endif
51 #define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL)
52 #define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW)
53 // multiple?
54 #if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE)
55 #error bad BCA_SLOTS_COL value!
56 #endif
57 #if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE)
58 #error bad BCA_SLOTS_ROW value!
59 #endif
60 // size of slot array if linear
61 #define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW)
62 // Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and
63 // sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory
64 // anyway, once you reached these values..
65 #if BCA_SLOTS_DEFINE > 268435456
66 #error BCA_SLOTS_DEFINE DOOMed!
67 #endif
68 
69 // STATIC DATA -----------------------------------------------------------
70 
71 TYPEINIT1( ScHint, SfxSimpleHint );
72 TYPEINIT1( ScAreaChangedHint, SfxHint );
73 
74 struct ScSlotData
75 {
76     SCROW  nStartRow;   // first row of this segment
77     SCROW  nStopRow;    // first row of next segment
78     SCSIZE nSlice;      // slice size in this segment
79     SCSIZE nCumulated;  // cumulated slots of previous segments
80 
81     ScSlotData( SCROW r1, SCROW r2, SCSIZE s, SCSIZE c ) : nStartRow(r1), nStopRow(r2), nSlice(s), nCumulated(c) {}
82 };
83 typedef ::std::vector< ScSlotData > ScSlotDistribution;
84 #if MAXROWCOUNT_DEFINE <= 65536
85 // Linear distribution.
86 static ScSlotDistribution aSlotDistribution( ScSlotData( 0, MAXROWCOUNT, BCA_SLOT_ROWS, 0));
87 static SCSIZE nBcaSlotsRow = BCA_SLOTS_ROW;
88 static SCSIZE nBcaSlots = BCA_SLOTS_DEFINE;
89 #else
90 // Logarithmic or any other distribution.
91 // Upper sheet part usually is more populated and referenced and gets fine
92 // grained resolution, larger data in larger hunks.
93 // Could be further enhanced by also applying a different distribution of
94 // column slots.
95 static SCSIZE initSlotDistribution( ScSlotDistribution & rSD, SCSIZE & rBSR )
96 {
97     SCSIZE nSlots = 0;
98     SCROW nRow1 = 0;
99     SCROW nRow2 = 32*1024;
100     SCSIZE nSlice = 128;
101     // Must be sorted by row1,row2!
102     while (nRow2 <= MAXROWCOUNT)
103     {
104         //fprintf( stderr, "r1,r2,slice,cum: %7zu, %7zu, %7zu, %7zu\n", (size_t)nRow1, (size_t)nRow2, (size_t)nSlice, (size_t)nSlots);
105         // {0,32k,128,0;32k,64k,256,0+256;64k,128k,512,0+256+128;128k,256k,1024,0+256+128+128;256k,512k,2048,...;512k,1M,4096,...}
106         rSD.push_back( ScSlotData( nRow1, nRow2, nSlice, nSlots));
107         nSlots += (nRow2 - nRow1) / nSlice;
108         nRow1 = nRow2;
109         nRow2 *= 2;
110         nSlice *= 2;
111     }
112     //fprintf( stderr, "Slices: %zu, slots per sheet: %zu, memory per referenced sheet: %zu\n", (size_t) nSlots, (size_t) nSlots * BCA_SLOTS_COL, (size_t) nSlots * BCA_SLOTS_COL * sizeof(void*));
113     rBSR = nSlots;
114     return nSlots;
115 }
116 static ScSlotDistribution aSlotDistribution;
117 static SCSIZE nBcaSlotsRow;
118 static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow) * BCA_SLOTS_COL;
119 // Ensure that all static variables are initialized with this one call.
120 #endif
121 
122 
123 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
124         ScBroadcastAreaSlotMachine* pBASMa ) :
125     aTmpSeekBroadcastArea( ScRange()),
126     pDoc( pDocument ),
127     pBASM( pBASMa )
128 {
129 }
130 
131 
132 ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
133 {
134     for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
135             aIter != aBroadcastAreaTbl.end(); /* none */)
136     {
137         // Prevent hash from accessing dangling pointer in case area is
138         // deleted.
139         ScBroadcastArea* pArea = *aIter;
140         // Erase all so no hash will be accessed upon destruction of the
141         // hash_set.
142         aBroadcastAreaTbl.erase( aIter++);
143         if (!pArea->DecRef())
144             delete pArea;
145     }
146 }
147 
148 
149 bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
150 {
151     if ( pDoc->GetHardRecalcState() )
152         return true;
153     if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
154     {   // this is more hypothetical now, check existed for old SV_PTRARR_SORT
155         if ( !pDoc->GetHardRecalcState() )
156         {
157             pDoc->SetHardRecalcState( 1 );
158 
159             SfxObjectShell* pShell = pDoc->GetDocumentShell();
160             DBG_ASSERT( pShell, "Missing DocShell :-/" );
161 
162 			if ( pShell )
163 				pShell->SetError( SCWARN_CORE_HARD_RECALC, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) );
164 
165             pDoc->SetAutoCalc( sal_False );
166             pDoc->SetHardRecalcState( 2 );
167         }
168         return true;
169     }
170     return false;
171 }
172 
173 
174 bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
175         SvtListener* pListener, ScBroadcastArea*& rpArea )
176 {
177     bool bNewArea = false;
178     DBG_ASSERT(pListener, "StartListeningArea: pListener Null");
179     if (CheckHardRecalcStateCondition())
180         return false;
181     if ( !rpArea )
182     {
183         // Even if most times the area doesn't exist yet and immediately trying
184         // to new and insert it would save an attempt to find it, on mass
185         // operations like identical large [HV]LOOKUP() areas the new/delete
186         // would add quite some penalty for all but the first formula cell.
187         ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
188         if (aIter != aBroadcastAreaTbl.end())
189             rpArea = *aIter;
190         else
191         {
192             rpArea = new ScBroadcastArea( rRange);
193             if (aBroadcastAreaTbl.insert( rpArea).second)
194             {
195                 rpArea->IncRef();
196                 bNewArea = true;
197             }
198             else
199             {
200                 DBG_ERRORFILE("StartListeningArea: area not found and not inserted in slot?!?");
201                 delete rpArea;
202                 rpArea = 0;
203             }
204         }
205         if (rpArea)
206             pListener->StartListening( rpArea->GetBroadcaster());
207     }
208     else
209     {
210         if (aBroadcastAreaTbl.insert( rpArea).second)
211             rpArea->IncRef();
212     }
213     return bNewArea;
214 }
215 
216 
217 void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
218 {
219     DBG_ASSERT( pArea, "InsertListeningArea: pArea NULL");
220     if (CheckHardRecalcStateCondition())
221         return;
222     if (aBroadcastAreaTbl.insert( pArea).second)
223         pArea->IncRef();
224 }
225 
226 
227 // If rpArea != NULL then no listeners are stopped, only the area is removed
228 // and the reference count decremented.
229 void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
230         SvtListener* pListener, ScBroadcastArea*& rpArea )
231 {
232     DBG_ASSERT(pListener, "EndListeningArea: pListener Null");
233     if ( !rpArea )
234     {
235         ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange));
236         if (aIter == aBroadcastAreaTbl.end())
237             return;
238         rpArea = *aIter;
239         pListener->EndListening( rpArea->GetBroadcaster() );
240         if ( !rpArea->GetBroadcaster().HasListeners() )
241         {   // if nobody is listening we can dispose it
242             aBroadcastAreaTbl.erase( aIter);
243             if ( !rpArea->DecRef() )
244             {
245                 delete rpArea;
246                 rpArea = NULL;
247             }
248         }
249     }
250     else
251     {
252         if ( !rpArea->GetBroadcaster().HasListeners() )
253         {
254             ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange));
255             if (aIter == aBroadcastAreaTbl.end())
256                 return;
257             DBG_ASSERT( *aIter == rpArea, "EndListeningArea: area pointer mismatch");
258             aBroadcastAreaTbl.erase( aIter);
259             if ( !rpArea->DecRef() )
260             {
261                 delete rpArea;
262                 rpArea = NULL;
263             }
264         }
265     }
266 }
267 
268 
269 ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
270         const ScRange& rRange ) const
271 {
272     aTmpSeekBroadcastArea.UpdateRange( rRange);
273     return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
274 }
275 
276 
277 sal_Bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const
278 {
279     if (aBroadcastAreaTbl.empty())
280         return sal_False;
281     sal_Bool bIsBroadcasted = sal_False;
282     const ScAddress& rAddress = rHint.GetAddress();
283     for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
284             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
285     {
286         ScBroadcastArea* pArea = *aIter;
287         // A Notify() during broadcast may call EndListeningArea() and thus
288         // dispose this area if it was the last listener, which would
289         // invalidate the iterator, hence increment before call.
290         ++aIter;
291         const ScRange& rAreaRange = pArea->GetRange();
292         if (rAreaRange.In( rAddress))
293         {
294             if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
295             {
296                 pArea->GetBroadcaster().Broadcast( rHint);
297                 bIsBroadcasted = sal_True;
298             }
299         }
300     }
301     return bIsBroadcasted;
302 }
303 
304 
305 sal_Bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
306         const ScHint& rHint) const
307 {
308     if (aBroadcastAreaTbl.empty())
309         return sal_False;
310     sal_Bool bIsBroadcasted = sal_False;
311     for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
312             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
313     {
314         ScBroadcastArea* pArea = *aIter;
315         // A Notify() during broadcast may call EndListeningArea() and thus
316         // dispose this area if it was the last listener, which would
317         // invalidate the iterator, hence increment before call.
318         ++aIter;
319         const ScRange& rAreaRange = pArea->GetRange();
320         if (rAreaRange.Intersects( rRange ))
321         {
322             if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
323             {
324                 pArea->GetBroadcaster().Broadcast( rHint);
325                 bIsBroadcasted = sal_True;
326             }
327         }
328     }
329     return bIsBroadcasted;
330 }
331 
332 
333 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
334 {
335     if (aBroadcastAreaTbl.empty())
336         return;
337     for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
338             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
339     {
340         const ScRange& rAreaRange = (*aIter)->GetRange();
341         if (rRange.In( rAreaRange))
342         {
343             ScBroadcastArea* pArea = *aIter;
344             aBroadcastAreaTbl.erase( aIter++);  // erase before modifying
345             if (!pArea->DecRef())
346             {
347                 if (pBASM->IsInBulkBroadcast())
348                     pBASM->RemoveBulkArea( pArea);
349                 delete pArea;
350             }
351         }
352         else
353             ++aIter;
354     }
355 }
356 
357 
358 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
359         const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
360 {
361     if (aBroadcastAreaTbl.empty())
362         return;
363 
364     SCCOL nCol1, nCol2, theCol1, theCol2;
365     SCROW nRow1, nRow2, theRow1, theRow2;
366     SCTAB nTab1, nTab2, theTab1, theTab2;
367     rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
368     for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
369             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
370     {
371         ScBroadcastArea* pArea = *aIter;
372         if ( pArea->IsInUpdateChain() )
373         {
374             aBroadcastAreaTbl.erase( aIter++);
375             pArea->DecRef();
376         }
377         else
378         {
379             pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
380             if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
381                     nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
382                     theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
383             {
384                 aBroadcastAreaTbl.erase( aIter++);
385                 pArea->DecRef();
386                 if (pBASM->IsInBulkBroadcast())
387                     pBASM->RemoveBulkArea( pArea);
388                 pArea->SetInUpdateChain( sal_True );
389                 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
390                 if ( pUC )
391                     pUC->SetUpdateChainNext( pArea );
392                 else    // no tail => no head
393                     pBASM->SetUpdateChain( pArea );
394                 pBASM->SetEOUpdateChain( pArea );
395             }
396             else
397                 ++aIter;
398         }
399     }
400 }
401 
402 
403 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
404 {
405     ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
406     if (aIter == aBroadcastAreaTbl.end())
407         return;
408     if (*aIter != pArea)
409         DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch");
410     else
411     {
412         aBroadcastAreaTbl.erase( aIter);
413         pArea->DecRef();
414     }
415 }
416 
417 
418 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
419 {
420     ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
421         aBroadcastAreaTbl.insert( pArea );
422     if (aPair.second)
423         pArea->IncRef();
424     else
425     {
426         // Identical area already exists, add listeners.
427         ScBroadcastArea* pTarget = *(aPair.first);
428         if (pArea != pTarget)
429         {
430             SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
431             SvtListenerIter it( pArea->GetBroadcaster());
432             for (SvtListener* pListener = it.GetCurr(); pListener;
433                     pListener = it.GoNext())
434             {
435                 pListener->StartListening( rTarget);
436             }
437         }
438     }
439 }
440 
441 
442 // --- ScBroadcastAreaSlotMachine -------------------------------------
443 
444 ScBroadcastAreaSlotMachine::TableSlots::TableSlots()
445 {
446     ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ];
447     memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
448 }
449 
450 
451 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
452 {
453     for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ )
454     {
455         if (*pp)
456             delete *pp;
457     }
458     delete [] ppSlots;
459 }
460 
461 
462 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
463         ScDocument* pDocument ) :
464     pBCAlways( NULL ),
465     pDoc( pDocument ),
466     pUpdateChain( NULL ),
467     pEOUpdateChain( NULL ),
468     nInBulkBroadcast( 0 )
469 {
470 }
471 
472 
473 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
474 {
475     for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin());
476             iTab != aTableSlotsMap.end(); ++iTab)
477     {
478         delete (*iTab).second;
479     }
480     delete pBCAlways;
481 }
482 
483 
484 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
485         const ScAddress& rAddress ) const
486 {
487     SCROW nRow = rAddress.Row();
488     SCCOL nCol = rAddress.Col();
489     if ( !ValidRow(nRow) || !ValidCol(nCol) )
490     {
491         DBG_ERRORFILE( "Row/Col invalid, using first slot!" );
492         return 0;
493     }
494     for (size_t i=0; i < aSlotDistribution.size(); ++i)
495     {
496         if (nRow < aSlotDistribution[i].nStopRow)
497         {
498             const ScSlotData& rSD = aSlotDistribution[i];
499             return rSD.nCumulated +
500                 (static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice +
501                 static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow;
502         }
503     }
504     DBG_ERRORFILE( "No slot found, using last!" );
505     return nBcaSlots - 1;
506 }
507 
508 
509 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
510         SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
511 {
512     rStart = ComputeSlotOffset( rRange.aStart );
513     rEnd = ComputeSlotOffset( rRange.aEnd );
514     // count of row slots per column minus one
515     rRowBreak = ComputeSlotOffset(
516         ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
517 }
518 
519 
520 inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
521         SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak )
522 {
523     if ( nOff < nBreak )
524     {
525         ++nOff;
526         ++pp;
527     }
528     else
529     {
530         nStart += nBcaSlotsRow;
531         nOff = nStart;
532         pp = ppSlots + nOff;
533         nBreak = nOff + nRowBreak;
534     }
535 }
536 
537 
538 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
539         SvtListener* pListener )
540 {
541     //fprintf( stderr, "StartListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab());
542     if ( rRange == BCA_LISTEN_ALWAYS  )
543     {
544         if ( !pBCAlways )
545             pBCAlways = new SvtBroadcaster;
546         pListener->StartListening( *pBCAlways );
547     }
548     else
549     {
550         bool bDone = false;
551         for (SCTAB nTab = rRange.aStart.Tab();
552                 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
553         {
554             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
555             if (iTab == aTableSlotsMap.end())
556                 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
557                             nTab, new TableSlots)).first;
558             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
559             SCSIZE nStart, nEnd, nRowBreak;
560             ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
561             SCSIZE nOff = nStart;
562             SCSIZE nBreak = nOff + nRowBreak;
563             ScBroadcastAreaSlot** pp = ppSlots + nOff;
564             ScBroadcastArea* pArea = NULL;
565             while ( !bDone && nOff <= nEnd )
566             {
567                 if ( !*pp )
568                     *pp = new ScBroadcastAreaSlot( pDoc, this );
569                 if (!pArea)
570                 {
571                     // If the call to StartListeningArea didn't create the
572                     // ScBroadcastArea, listeners were added to an already
573                     // existing identical area that doesn't need to be inserted
574                     // to slots again.
575                     if (!(*pp)->StartListeningArea( rRange, pListener, pArea))
576                         bDone = true;
577                 }
578                 else
579                     (*pp)->InsertListeningArea( pArea);
580                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
581             }
582         }
583     }
584 }
585 
586 
587 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
588         SvtListener* pListener )
589 {
590     //fprintf( stderr, "EndListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab());
591     if ( rRange == BCA_LISTEN_ALWAYS  )
592     {
593         DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established");
594         if ( pBCAlways )
595         {
596             pListener->EndListening( *pBCAlways);
597             if (!pBCAlways->HasListeners())
598             {
599                 delete pBCAlways;
600                 pBCAlways = NULL;
601             }
602         }
603     }
604     else
605     {
606         SCTAB nEndTab = rRange.aEnd.Tab();
607         for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
608                 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
609         {
610             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
611             SCSIZE nStart, nEnd, nRowBreak;
612             ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
613             SCSIZE nOff = nStart;
614             SCSIZE nBreak = nOff + nRowBreak;
615             ScBroadcastAreaSlot** pp = ppSlots + nOff;
616             ScBroadcastArea* pArea = NULL;
617             if (nOff == 0 && nEnd == nBcaSlots-1)
618             {
619                 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
620                 // happen for insertion and deletion of sheets.
621                 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
622                 do
623                 {
624                     if ( *pp )
625                         (*pp)->EndListeningArea( rRange, pListener, pArea );
626                 } while (++pp < pStop);
627             }
628             else
629             {
630                 while ( nOff <= nEnd )
631                 {
632                     if ( *pp )
633                         (*pp)->EndListeningArea( rRange, pListener, pArea );
634                     ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
635                 }
636             }
637         }
638     }
639 }
640 
641 
642 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
643 {
644     const ScAddress& rAddress = rHint.GetAddress();
645     if ( rAddress == BCA_BRDCST_ALWAYS )
646     {
647         if ( pBCAlways )
648         {
649             pBCAlways->Broadcast( rHint );
650             return sal_True;
651         }
652         else
653             return sal_False;
654     }
655     else
656     {
657         TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
658         if (iTab == aTableSlotsMap.end())
659             return sal_False;
660         ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
661                 ComputeSlotOffset( rAddress));
662         if ( pSlot )
663             return pSlot->AreaBroadcast( rHint );
664         else
665             return sal_False;
666     }
667 }
668 
669 
670 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
671         const ScHint& rHint ) const
672 {
673     sal_Bool bBroadcasted = sal_False;
674     SCTAB nEndTab = rRange.aEnd.Tab();
675     for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
676             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
677     {
678         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
679         SCSIZE nStart, nEnd, nRowBreak;
680         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
681         SCSIZE nOff = nStart;
682         SCSIZE nBreak = nOff + nRowBreak;
683         ScBroadcastAreaSlot** pp = ppSlots + nOff;
684         while ( nOff <= nEnd )
685         {
686             if ( *pp )
687                 bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
688             ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
689         }
690     }
691     return bBroadcasted;
692 }
693 
694 
695 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
696         const ScRange& rRange )
697 {
698     SCTAB nEndTab = rRange.aEnd.Tab();
699     for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
700             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
701     {
702         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
703         SCSIZE nStart, nEnd, nRowBreak;
704         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
705         SCSIZE nOff = nStart;
706         SCSIZE nBreak = nOff + nRowBreak;
707         ScBroadcastAreaSlot** pp = ppSlots + nOff;
708         if (nOff == 0 && nEnd == nBcaSlots-1)
709         {
710             // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
711             // happen for insertion and deletion of sheets.
712             ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
713             do
714             {
715                 if ( *pp )
716                     (*pp)->DelBroadcastAreasInRange( rRange );
717             } while (++pp < pStop);
718         }
719         else
720         {
721             while ( nOff <= nEnd )
722             {
723                 if ( *pp )
724                     (*pp)->DelBroadcastAreasInRange( rRange );
725                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
726             }
727         }
728     }
729 }
730 
731 
732 // for all affected: remove, chain, update range, insert, and maybe delete
733 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
734         UpdateRefMode eUpdateRefMode,
735         const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
736 {
737     // remove affected and put in chain
738     SCTAB nEndTab = rRange.aEnd.Tab();
739     for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
740             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
741     {
742         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
743         SCSIZE nStart, nEnd, nRowBreak;
744         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
745         SCSIZE nOff = nStart;
746         SCSIZE nBreak = nOff + nRowBreak;
747         ScBroadcastAreaSlot** pp = ppSlots + nOff;
748         if (nOff == 0 && nEnd == nBcaSlots-1)
749         {
750             // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
751             // happen for insertion and deletion of sheets.
752             ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
753             do
754             {
755                 if ( *pp )
756                     (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
757             } while (++pp < pStop);
758         }
759         else
760         {
761             while ( nOff <= nEnd )
762             {
763                 if ( *pp )
764                     (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
765                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
766             }
767         }
768     }
769 
770     // Updating an area's range will modify the hash key, remove areas from all
771     // affected slots. Will be reinserted later with the updated range.
772     ScBroadcastArea* pChain = pUpdateChain;
773     while (pChain)
774     {
775         ScBroadcastArea* pArea = pChain;
776         pChain = pArea->GetUpdateChainNext();
777         ScRange aRange( pArea->GetRange());
778         // remove from slots
779         for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
780         {
781             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
782             if (iTab == aTableSlotsMap.end())
783             {
784                 DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?");
785                 continue;   // for
786             }
787             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
788             SCSIZE nStart, nEnd, nRowBreak;
789             ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
790             SCSIZE nOff = nStart;
791             SCSIZE nBreak = nOff + nRowBreak;
792             ScBroadcastAreaSlot** pp = ppSlots + nOff;
793             while ( nOff <= nEnd && pArea->GetRef() )
794             {
795                 if (*pp)
796                     (*pp)->UpdateRemoveArea( pArea);
797                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
798             }
799         }
800 
801     }
802 
803     // shift sheets
804     if (nDz)
805     {
806         if (nDz < 0)
807         {
808             TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
809             TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
810             // Remove sheets, if any, iDel or/and iTab may as well point to end().
811             while (iDel != iTab)
812             {
813                 delete (*iDel).second;
814                 aTableSlotsMap.erase( iDel++);
815             }
816             // shift remaining down
817             while (iTab != aTableSlotsMap.end())
818             {
819                 SCTAB nTab = (*iTab).first + nDz;
820                 aTableSlotsMap[nTab] = (*iTab).second;
821                 aTableSlotsMap.erase( iTab++);
822             }
823         }
824         else
825         {
826             TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
827             if (iStop != aTableSlotsMap.end())
828             {
829                 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
830                 if (!bStopIsBegin)
831                     --iStop;
832                 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
833                 --iTab;
834                 while (iTab != iStop)
835                 {
836                     SCTAB nTab = (*iTab).first + nDz;
837                     aTableSlotsMap[nTab] = (*iTab).second;
838                     aTableSlotsMap.erase( iTab--);
839                 }
840                 // Shift the very first, iTab==iStop in this case.
841                 if (bStopIsBegin)
842                 {
843                     SCTAB nTab = (*iTab).first + nDz;
844                     aTableSlotsMap[nTab] = (*iTab).second;
845                     aTableSlotsMap.erase( iStop);
846                 }
847             }
848         }
849     }
850 
851     // work off chain
852     SCCOL nCol1, nCol2, theCol1, theCol2;
853     SCROW nRow1, nRow2, theRow1, theRow2;
854     SCTAB nTab1, nTab2, theTab1, theTab2;
855     rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
856     while ( pUpdateChain )
857     {
858         ScBroadcastArea* pArea = pUpdateChain;
859         ScRange aRange( pArea->GetRange());
860         pUpdateChain = pArea->GetUpdateChainNext();
861 
862         // update range
863         aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
864         if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
865                 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
866                 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
867         {
868             aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
869             pArea->UpdateRange( aRange );
870             pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );   // for DDE
871         }
872 
873         // insert to slots
874         for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
875         {
876             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
877             if (iTab == aTableSlotsMap.end())
878                 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
879                             nTab, new TableSlots)).first;
880             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
881             SCSIZE nStart, nEnd, nRowBreak;
882             ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
883             SCSIZE nOff = nStart;
884             SCSIZE nBreak = nOff + nRowBreak;
885             ScBroadcastAreaSlot** pp = ppSlots + nOff;
886             while ( nOff <= nEnd )
887             {
888                 if (!*pp)
889                     *pp = new ScBroadcastAreaSlot( pDoc, this );
890                 (*pp)->UpdateInsert( pArea );
891                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
892             }
893         }
894 
895         // unchain
896         pArea->SetUpdateChainNext( NULL );
897         pArea->SetInUpdateChain( sal_False );
898 
899         // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
900         // already executed in UpdateRemove().
901         if (!pArea->GetRef())
902             delete pArea;
903     }
904     pEOUpdateChain = NULL;
905 }
906 
907 
908 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
909 {
910     ++nInBulkBroadcast;
911 }
912 
913 
914 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
915 {
916     if (nInBulkBroadcast > 0)
917     {
918         if (--nInBulkBroadcast == 0)
919             ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
920     }
921 }
922 
923 
924 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
925 {
926     return aBulkBroadcastAreas.insert( pArea ).second;
927 }
928 
929 
930 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
931 {
932     return aBulkBroadcastAreas.erase( pArea );
933 }
934