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