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