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