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