xref: /trunk/main/sc/source/core/data/bcaslot.cxx (revision 6d3b264b)
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 #include <boost/mem_fn.hpp>
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 
ScSlotDataScSlotData81     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.
initSlotDistribution(ScSlotDistribution & rSD,SCSIZE & rBSR)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 
ScBroadcastAreaSlot(ScDocument * pDocument,ScBroadcastAreaSlotMachine * pBASMa)123 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
124         ScBroadcastAreaSlotMachine* pBASMa ) :
125     aTmpSeekBroadcastArea( ScRange()),
126     pDoc( pDocument ),
127     pBASM( pBASMa )
128 {
129 }
130 
131 
~ScBroadcastAreaSlot()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 
CheckHardRecalcStateCondition() const149 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 
StartListeningArea(const ScRange & rRange,SvtListener * pListener,ScBroadcastArea * & rpArea)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 
InsertListeningArea(ScBroadcastArea * pArea)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.
EndListeningArea(const ScRange & rRange,SvtListener * pListener,ScBroadcastArea * & rpArea)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::const_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::const_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 
FindBroadcastArea(const ScRange & rRange) const269 ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea(
270         const ScRange& rRange ) const
271 {
272     aTmpSeekBroadcastArea.UpdateRange( rRange);
273     return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
274 }
275 
276 
AreaBroadcast(const ScHint & rHint) const277 sal_Bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const
278 {
279     if (aBroadcastAreaTbl.empty())
280         return sal_False;
281     sal_Bool bIsBroadcasted = sal_False;
282 
283     // issue 118012
284     // do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators
285     // are destroyed during notification.
286     std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() );
287     std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) );
288 
289     const ScAddress& rAddress = rHint.GetAddress();
290     const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() );
291     std::vector< ScBroadcastArea* >::const_iterator aIter;
292     for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
293     {
294         ScBroadcastArea* pArea = *aIter;
295         // check, if copied item has been already removed from <aBroadcastAreaTbl>
296         if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() )
297         {
298             continue;
299         }
300 
301         const ScRange& rAreaRange = pArea->GetRange();
302         if (rAreaRange.In( rAddress))
303         {
304             if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
305             {
306                 pArea->GetBroadcaster().Broadcast( rHint);
307                 bIsBroadcasted = sal_True;
308             }
309         }
310     }
311 
312     // delete no longer referenced <ScBroadcastArea> instances
313     for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
314     {
315         ScBroadcastArea* pArea = *aIter;
316         if ( !pArea->DecRef() )
317         {
318             delete pArea;
319         }
320     }
321 
322     return bIsBroadcasted;
323 }
324 
325 
AreaBroadcastInRange(const ScRange & rRange,const ScHint & rHint) const326 sal_Bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
327         const ScHint& rHint) const
328 {
329     if (aBroadcastAreaTbl.empty())
330         return sal_False;
331     sal_Bool bIsBroadcasted = sal_False;
332 
333     // issue 118012
334     // do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators
335     // are destroyed during notification.
336     std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() );
337     std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) );
338 
339     const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() );
340     std::vector< ScBroadcastArea* >::const_iterator aIter;
341     for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
342     {
343         ScBroadcastArea* pArea = *aIter;
344         // check, if copied item has been already removed from <aBroadcastAreaTbl>
345         if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() )
346         {
347             continue;
348         }
349 
350         const ScRange& rAreaRange = pArea->GetRange();
351         if (rAreaRange.Intersects( rRange ))
352         {
353             if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
354             {
355                 pArea->GetBroadcaster().Broadcast( rHint);
356                 bIsBroadcasted = sal_True;
357             }
358         }
359     }
360 
361     // delete no longer referenced <ScBroadcastArea> instances
362     for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
363     {
364         ScBroadcastArea* pArea = *aIter;
365         if ( !pArea->DecRef() )
366         {
367             delete pArea;
368         }
369     }
370 
371     return bIsBroadcasted;
372 }
373 
374 
DelBroadcastAreasInRange(const ScRange & rRange)375 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
376 {
377     if (aBroadcastAreaTbl.empty())
378         return;
379     for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
380             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
381     {
382         const ScRange& rAreaRange = (*aIter)->GetRange();
383         if (rRange.In( rAreaRange))
384         {
385             ScBroadcastArea* pArea = *aIter;
386             aBroadcastAreaTbl.erase( aIter++);  // erase before modifying
387             if (!pArea->DecRef())
388             {
389                 if (pBASM->IsInBulkBroadcast())
390                     pBASM->RemoveBulkArea( pArea);
391                 delete pArea;
392             }
393         }
394         else
395             ++aIter;
396     }
397 }
398 
399 
UpdateRemove(UpdateRefMode eUpdateRefMode,const ScRange & rRange,SCsCOL nDx,SCsROW nDy,SCsTAB nDz)400 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
401         const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
402 {
403     if (aBroadcastAreaTbl.empty())
404         return;
405 
406     SCCOL nCol1, nCol2, theCol1, theCol2;
407     SCROW nRow1, nRow2, theRow1, theRow2;
408     SCTAB nTab1, nTab2, theTab1, theTab2;
409     rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
410     for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
411             aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
412     {
413         ScBroadcastArea* pArea = *aIter;
414         if ( pArea->IsInUpdateChain() )
415         {
416             aBroadcastAreaTbl.erase( aIter++);
417             pArea->DecRef();
418         }
419         else
420         {
421             pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
422             if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
423                     nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
424                     theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
425             {
426                 aBroadcastAreaTbl.erase( aIter++);
427                 pArea->DecRef();
428                 if (pBASM->IsInBulkBroadcast())
429                     pBASM->RemoveBulkArea( pArea);
430                 pArea->SetInUpdateChain( sal_True );
431                 ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
432                 if ( pUC )
433                     pUC->SetUpdateChainNext( pArea );
434                 else    // no tail => no head
435                     pBASM->SetUpdateChain( pArea );
436                 pBASM->SetEOUpdateChain( pArea );
437             }
438             else
439                 ++aIter;
440         }
441     }
442 }
443 
444 
UpdateRemoveArea(ScBroadcastArea * pArea)445 void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
446 {
447     ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
448     if (aIter == aBroadcastAreaTbl.end())
449         return;
450     if (*aIter != pArea)
451         DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch");
452     else
453     {
454         aBroadcastAreaTbl.erase( aIter);
455         pArea->DecRef();
456     }
457 }
458 
459 
UpdateInsert(ScBroadcastArea * pArea)460 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
461 {
462     ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
463         aBroadcastAreaTbl.insert( pArea );
464     if (aPair.second)
465         pArea->IncRef();
466     else
467     {
468         // Identical area already exists, add listeners.
469         ScBroadcastArea* pTarget = *(aPair.first);
470         if (pArea != pTarget)
471         {
472             SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
473             SvtListenerIter it( pArea->GetBroadcaster());
474             for (SvtListener* pListener = it.GetCurr(); pListener;
475                     pListener = it.GoNext())
476             {
477                 pListener->StartListening( rTarget);
478             }
479         }
480     }
481 }
482 
483 
484 // --- ScBroadcastAreaSlotMachine -------------------------------------
485 
TableSlots()486 ScBroadcastAreaSlotMachine::TableSlots::TableSlots()
487 {
488     ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ];
489     memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
490 }
491 
492 
~TableSlots()493 ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
494 {
495     for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ )
496     {
497         if (*pp)
498             delete *pp;
499     }
500     delete [] ppSlots;
501 }
502 
503 
ScBroadcastAreaSlotMachine(ScDocument * pDocument)504 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
505         ScDocument* pDocument ) :
506     pBCAlways( NULL ),
507     pDoc( pDocument ),
508     pUpdateChain( NULL ),
509     pEOUpdateChain( NULL ),
510     nInBulkBroadcast( 0 )
511 {
512 }
513 
514 
~ScBroadcastAreaSlotMachine()515 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
516 {
517     for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin());
518             iTab != aTableSlotsMap.end(); ++iTab)
519     {
520         delete (*iTab).second;
521     }
522     delete pBCAlways;
523 }
524 
525 
ComputeSlotOffset(const ScAddress & rAddress) const526 inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
527         const ScAddress& rAddress ) const
528 {
529     SCROW nRow = rAddress.Row();
530     SCCOL nCol = rAddress.Col();
531     if ( !ValidRow(nRow) || !ValidCol(nCol) )
532     {
533         DBG_ERRORFILE( "Row/Col invalid, using first slot!" );
534         return 0;
535     }
536     for (size_t i=0; i < aSlotDistribution.size(); ++i)
537     {
538         if (nRow < aSlotDistribution[i].nStopRow)
539         {
540             const ScSlotData& rSD = aSlotDistribution[i];
541             return rSD.nCumulated +
542                 (static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice +
543                 static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow;
544         }
545     }
546     DBG_ERRORFILE( "No slot found, using last!" );
547     return nBcaSlots - 1;
548 }
549 
550 
ComputeAreaPoints(const ScRange & rRange,SCSIZE & rStart,SCSIZE & rEnd,SCSIZE & rRowBreak) const551 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
552         SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
553 {
554     rStart = ComputeSlotOffset( rRange.aStart );
555     rEnd = ComputeSlotOffset( rRange.aEnd );
556     // count of row slots per column minus one
557     rRowBreak = ComputeSlotOffset(
558         ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
559 }
560 
561 
ComputeNextSlot(SCSIZE & nOff,SCSIZE & nBreak,ScBroadcastAreaSlot ** & pp,SCSIZE & nStart,ScBroadcastAreaSlot ** const & ppSlots,SCSIZE const & nRowBreak)562 inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
563         SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak )
564 {
565     if ( nOff < nBreak )
566     {
567         ++nOff;
568         ++pp;
569     }
570     else
571     {
572         nStart += nBcaSlotsRow;
573         nOff = nStart;
574         pp = ppSlots + nOff;
575         nBreak = nOff + nRowBreak;
576     }
577 }
578 
579 
StartListeningArea(const ScRange & rRange,SvtListener * pListener)580 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
581         SvtListener* pListener )
582 {
583     //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());
584     if ( rRange == BCA_LISTEN_ALWAYS  )
585     {
586         if ( !pBCAlways )
587             pBCAlways = new SvtBroadcaster;
588         pListener->StartListening( *pBCAlways );
589     }
590     else
591     {
592         bool bDone = false;
593         for (SCTAB nTab = rRange.aStart.Tab();
594                 !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
595         {
596             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
597             if (iTab == aTableSlotsMap.end())
598                 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
599                             nTab, new TableSlots)).first;
600             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
601             SCSIZE nStart, nEnd, nRowBreak;
602             ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
603             SCSIZE nOff = nStart;
604             SCSIZE nBreak = nOff + nRowBreak;
605             ScBroadcastAreaSlot** pp = ppSlots + nOff;
606             ScBroadcastArea* pArea = NULL;
607             while ( !bDone && nOff <= nEnd )
608             {
609                 if ( !*pp )
610                     *pp = new ScBroadcastAreaSlot( pDoc, this );
611                 if (!pArea)
612                 {
613                     // If the call to StartListeningArea didn't create the
614                     // ScBroadcastArea, listeners were added to an already
615                     // existing identical area that doesn't need to be inserted
616                     // to slots again.
617                     if (!(*pp)->StartListeningArea( rRange, pListener, pArea))
618                         bDone = true;
619                 }
620                 else
621                     (*pp)->InsertListeningArea( pArea);
622                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
623             }
624         }
625     }
626 }
627 
628 
EndListeningArea(const ScRange & rRange,SvtListener * pListener)629 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
630         SvtListener* pListener )
631 {
632     //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());
633     if ( rRange == BCA_LISTEN_ALWAYS  )
634     {
635         DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established");
636         if ( pBCAlways )
637         {
638             pListener->EndListening( *pBCAlways);
639             if (!pBCAlways->HasListeners())
640             {
641                 delete pBCAlways;
642                 pBCAlways = NULL;
643             }
644         }
645     }
646     else
647     {
648         SCTAB nEndTab = rRange.aEnd.Tab();
649         for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
650                 iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
651         {
652             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
653             SCSIZE nStart, nEnd, nRowBreak;
654             ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
655             SCSIZE nOff = nStart;
656             SCSIZE nBreak = nOff + nRowBreak;
657             ScBroadcastAreaSlot** pp = ppSlots + nOff;
658             ScBroadcastArea* pArea = NULL;
659             if (nOff == 0 && nEnd == nBcaSlots-1)
660             {
661                 // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
662                 // happen for insertion and deletion of sheets.
663                 ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
664                 do
665                 {
666                     if ( *pp )
667                         (*pp)->EndListeningArea( rRange, pListener, pArea );
668                 } while (++pp < pStop);
669             }
670             else
671             {
672                 while ( nOff <= nEnd )
673                 {
674                     if ( *pp )
675                         (*pp)->EndListeningArea( rRange, pListener, pArea );
676                     ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
677                 }
678             }
679         }
680     }
681 }
682 
683 
AreaBroadcast(const ScHint & rHint) const684 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
685 {
686     const ScAddress& rAddress = rHint.GetAddress();
687     if ( rAddress == BCA_BRDCST_ALWAYS )
688     {
689         if ( pBCAlways )
690         {
691             pBCAlways->Broadcast( rHint );
692             return sal_True;
693         }
694         else
695             return sal_False;
696     }
697     else
698     {
699         TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
700         if (iTab == aTableSlotsMap.end())
701             return sal_False;
702         ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
703                 ComputeSlotOffset( rAddress));
704         if ( pSlot )
705             return pSlot->AreaBroadcast( rHint );
706         else
707             return sal_False;
708     }
709 }
710 
711 
AreaBroadcastInRange(const ScRange & rRange,const ScHint & rHint) const712 sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
713         const ScHint& rHint ) const
714 {
715     sal_Bool bBroadcasted = sal_False;
716     SCTAB nEndTab = rRange.aEnd.Tab();
717     for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
718             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
719     {
720         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
721         SCSIZE nStart, nEnd, nRowBreak;
722         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
723         SCSIZE nOff = nStart;
724         SCSIZE nBreak = nOff + nRowBreak;
725         ScBroadcastAreaSlot** pp = ppSlots + nOff;
726         while ( nOff <= nEnd )
727         {
728             if ( *pp )
729                 bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
730             ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
731         }
732     }
733     return bBroadcasted;
734 }
735 
736 
DelBroadcastAreasInRange(const ScRange & rRange)737 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
738         const ScRange& rRange )
739 {
740     SCTAB nEndTab = rRange.aEnd.Tab();
741     for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
742             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
743     {
744         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
745         SCSIZE nStart, nEnd, nRowBreak;
746         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
747         SCSIZE nOff = nStart;
748         SCSIZE nBreak = nOff + nRowBreak;
749         ScBroadcastAreaSlot** pp = ppSlots + nOff;
750         if (nOff == 0 && nEnd == nBcaSlots-1)
751         {
752             // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
753             // happen for insertion and deletion of sheets.
754             ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
755             do
756             {
757                 if ( *pp )
758                     (*pp)->DelBroadcastAreasInRange( rRange );
759             } while (++pp < pStop);
760         }
761         else
762         {
763             while ( nOff <= nEnd )
764             {
765                 if ( *pp )
766                     (*pp)->DelBroadcastAreasInRange( rRange );
767                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
768             }
769         }
770     }
771 }
772 
773 
774 // for all affected: remove, chain, update range, insert, and maybe delete
UpdateBroadcastAreas(UpdateRefMode eUpdateRefMode,const ScRange & rRange,SCsCOL nDx,SCsROW nDy,SCsTAB nDz)775 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
776         UpdateRefMode eUpdateRefMode,
777         const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
778 {
779     // remove affected and put in chain
780     SCTAB nEndTab = rRange.aEnd.Tab();
781     for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
782             iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
783     {
784         ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
785         SCSIZE nStart, nEnd, nRowBreak;
786         ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
787         SCSIZE nOff = nStart;
788         SCSIZE nBreak = nOff + nRowBreak;
789         ScBroadcastAreaSlot** pp = ppSlots + nOff;
790         if (nOff == 0 && nEnd == nBcaSlots-1)
791         {
792             // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
793             // happen for insertion and deletion of sheets.
794             ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
795             do
796             {
797                 if ( *pp )
798                     (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
799             } while (++pp < pStop);
800         }
801         else
802         {
803             while ( nOff <= nEnd )
804             {
805                 if ( *pp )
806                     (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
807                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
808             }
809         }
810     }
811 
812     // Updating an area's range will modify the hash key, remove areas from all
813     // affected slots. Will be reinserted later with the updated range.
814     ScBroadcastArea* pChain = pUpdateChain;
815     while (pChain)
816     {
817         ScBroadcastArea* pArea = pChain;
818         pChain = pArea->GetUpdateChainNext();
819         ScRange aRange( pArea->GetRange());
820         // remove from slots
821         for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
822         {
823             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
824             if (iTab == aTableSlotsMap.end())
825             {
826                 DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?");
827                 continue;   // for
828             }
829             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
830             SCSIZE nStart, nEnd, nRowBreak;
831             ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
832             SCSIZE nOff = nStart;
833             SCSIZE nBreak = nOff + nRowBreak;
834             ScBroadcastAreaSlot** pp = ppSlots + nOff;
835             while ( nOff <= nEnd && pArea->GetRef() )
836             {
837                 if (*pp)
838                     (*pp)->UpdateRemoveArea( pArea);
839                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
840             }
841         }
842 
843     }
844 
845     // shift sheets
846     if (nDz)
847     {
848         if (nDz < 0)
849         {
850             TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
851             TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
852             // Remove sheets, if any, iDel or/and iTab may as well point to end().
853             while (iDel != iTab)
854             {
855                 delete (*iDel).second;
856                 aTableSlotsMap.erase( iDel++);
857             }
858             // shift remaining down
859             while (iTab != aTableSlotsMap.end())
860             {
861                 SCTAB nTab = (*iTab).first + nDz;
862                 aTableSlotsMap[nTab] = (*iTab).second;
863                 aTableSlotsMap.erase( iTab++);
864             }
865         }
866         else
867         {
868             TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
869             if (iStop != aTableSlotsMap.end())
870             {
871                 bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
872                 if (!bStopIsBegin)
873                     --iStop;
874                 TableSlotsMap::iterator iTab( aTableSlotsMap.end());
875                 --iTab;
876                 while (iTab != iStop)
877                 {
878                     SCTAB nTab = (*iTab).first + nDz;
879                     aTableSlotsMap[nTab] = (*iTab).second;
880                     aTableSlotsMap.erase( iTab--);
881                 }
882                 // Shift the very first, iTab==iStop in this case.
883                 if (bStopIsBegin)
884                 {
885                     SCTAB nTab = (*iTab).first + nDz;
886                     aTableSlotsMap[nTab] = (*iTab).second;
887                     aTableSlotsMap.erase( iStop);
888                 }
889             }
890         }
891     }
892 
893     // work off chain
894     SCCOL nCol1, nCol2, theCol1, theCol2;
895     SCROW nRow1, nRow2, theRow1, theRow2;
896     SCTAB nTab1, nTab2, theTab1, theTab2;
897     rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
898     while ( pUpdateChain )
899     {
900         ScBroadcastArea* pArea = pUpdateChain;
901         ScRange aRange( pArea->GetRange());
902         pUpdateChain = pArea->GetUpdateChainNext();
903 
904         // update range
905         aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
906         if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
907                 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
908                 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
909         {
910             aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
911             pArea->UpdateRange( aRange );
912             pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );   // for DDE
913         }
914 
915         // insert to slots
916         for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
917         {
918             TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
919             if (iTab == aTableSlotsMap.end())
920                 iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
921                             nTab, new TableSlots)).first;
922             ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
923             SCSIZE nStart, nEnd, nRowBreak;
924             ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
925             SCSIZE nOff = nStart;
926             SCSIZE nBreak = nOff + nRowBreak;
927             ScBroadcastAreaSlot** pp = ppSlots + nOff;
928             while ( nOff <= nEnd )
929             {
930                 if (!*pp)
931                     *pp = new ScBroadcastAreaSlot( pDoc, this );
932                 (*pp)->UpdateInsert( pArea );
933                 ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
934             }
935         }
936 
937         // unchain
938         pArea->SetUpdateChainNext( NULL );
939         pArea->SetInUpdateChain( sal_False );
940 
941         // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
942         // already executed in UpdateRemove().
943         if (!pArea->GetRef())
944             delete pArea;
945     }
946     pEOUpdateChain = NULL;
947 }
948 
949 
EnterBulkBroadcast()950 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
951 {
952     ++nInBulkBroadcast;
953 }
954 
955 
LeaveBulkBroadcast()956 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
957 {
958     if (nInBulkBroadcast > 0)
959     {
960         if (--nInBulkBroadcast == 0)
961             ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
962     }
963 }
964 
965 
InsertBulkArea(const ScBroadcastArea * pArea)966 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
967 {
968     return aBulkBroadcastAreas.insert( pArea ).second;
969 }
970 
971 
RemoveBulkArea(const ScBroadcastArea * pArea)972 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
973 {
974     return aBulkBroadcastAreas.erase( pArea );
975 }
976