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