1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: bcaslot.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 #include <sfx2/objsh.hxx>
37 #include <svtools/listener.hxx>
39 #include "document.hxx"
41 #include "bcaslot.hxx"
42 #include "scerrors.hxx"
43 #include "docoptio.hxx"
44 #include "refupdat.hxx"
47 // Number of slots per dimension
48 // must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT
49 #define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16)
50 #if MAXROWCOUNT_DEFINE == 32000
51 #define BCA_SLOTS_ROW 256
53 #define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / 128)
55 #define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL)
56 #define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW)
58 #if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE)
59 #error bad BCA_SLOTS_COL value!
61 #if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE)
62 #error bad BCA_SLOTS_ROW value!
65 #define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW)
66 // Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and
67 // sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory
68 // anyway, once you reached these values..
69 #if BCA_SLOTS_DEFINE > 268435456
70 #error BCA_SLOTS_DEFINE DOOMed!
73 const SCSIZE BCA_SLOTS
= BCA_SLOTS_DEFINE
;
75 // STATIC DATA -----------------------------------------------------------
77 TYPEINIT1( ScHint
, SfxSimpleHint
);
78 TYPEINIT1( ScAreaChangedHint
, SfxHint
);
81 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument
* pDocument
,
82 ScBroadcastAreaSlotMachine
* pBASMa
) :
83 aTmpSeekBroadcastArea( ScRange()),
90 ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
92 for ( ScBroadcastAreas::iterator aIter
= aBroadcastAreaTbl
.begin();
93 aIter
!= aBroadcastAreaTbl
.end(); ++aIter
)
95 if (!(*aIter
)->DecRef())
101 // Only here new ScBroadcastArea objects are created, prevention of dupes.
102 // If rpArea != NULL then no listeners are startet, only the area is inserted
103 // and the reference count increased.
104 void ScBroadcastAreaSlot::StartListeningArea( const ScRange
& rRange
,
105 SvtListener
* pListener
, ScBroadcastArea
*& rpArea
108 DBG_ASSERT(pListener
, "StartListeningArea: pListener Null");
109 if ( pDoc
->GetHardRecalcState() )
111 if (aBroadcastAreaTbl
.size() >= aBroadcastAreaTbl
.max_size())
112 { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
113 if ( !pDoc
->GetHardRecalcState() )
115 pDoc
->SetHardRecalcState( 1 );
117 SfxObjectShell
* pShell
= pDoc
->GetDocumentShell();
118 DBG_ASSERT( pShell
, "Missing DocShell :-/" );
121 pShell
->SetError( SCWARN_CORE_HARD_RECALC
);
123 pDoc
->SetAutoCalc( FALSE
);
124 pDoc
->SetHardRecalcState( 2 );
130 rpArea
= new ScBroadcastArea( rRange
);
131 // Most times the area doesn't exist yet, immediately trying to insert
132 // it saves an attempt to find it.
133 if (aBroadcastAreaTbl
.insert( rpArea
).second
)
138 ScBroadcastAreas::const_iterator
aIter( FindBroadcastArea( rRange
));
139 if (aIter
!= aBroadcastAreaTbl
.end())
143 DBG_ERRORFILE("BroadcastArea not inserted and not found?!?");
148 pListener
->StartListening( rpArea
->GetBroadcaster() );
152 aBroadcastAreaTbl
.insert( rpArea
);
158 // If rpArea != NULL then no listeners are stopped, only the area is removed
159 // and the reference count decreased.
160 void ScBroadcastAreaSlot::EndListeningArea( const ScRange
& rRange
,
161 SvtListener
* pListener
, ScBroadcastArea
*& rpArea
164 DBG_ASSERT(pListener
, "EndListeningArea: pListener Null");
167 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
));
168 if (aIter
== aBroadcastAreaTbl
.end())
171 pListener
->EndListening( rpArea
->GetBroadcaster() );
172 if ( !rpArea
->GetBroadcaster().HasListeners() )
173 { // if nobody is listening we can dispose it
174 aBroadcastAreaTbl
.erase( aIter
);
175 if ( !rpArea
->DecRef() )
184 if ( !rpArea
->GetBroadcaster().HasListeners() )
186 ScBroadcastAreas::iterator
aIter( FindBroadcastArea( rRange
));
187 if (aIter
== aBroadcastAreaTbl
.end())
189 aBroadcastAreaTbl
.erase( aIter
);
190 if ( !rpArea
->DecRef() )
200 ScBroadcastAreas::iterator
ScBroadcastAreaSlot::FindBroadcastArea(
201 const ScRange
& rRange
) const
203 aTmpSeekBroadcastArea
.UpdateRange( rRange
);
204 return aBroadcastAreaTbl
.find( &aTmpSeekBroadcastArea
);
208 BOOL
ScBroadcastAreaSlot::AreaBroadcast( const ScHint
& rHint
) const
210 if (aBroadcastAreaTbl
.empty())
212 BOOL bIsBroadcasted
= FALSE
;
213 const ScAddress
& rAddress
= rHint
.GetAddress();
214 // Unfortunately we can't search for the first matching entry.
215 ScBroadcastAreas::const_iterator
aIter( aBroadcastAreaTbl
.begin());
216 while (aIter
!= aBroadcastAreaTbl
.end())
218 ScBroadcastArea
* pArea
= *aIter
;
219 // A Notify() during broadcast may call EndListeningArea() and thus
220 // dispose this area if it was the last listener, which would
221 // invalidate the iterator, hence increment before call.
223 const ScRange
& rAreaRange
= pArea
->GetRange();
224 if (rAreaRange
.In( rAddress
))
226 if (!pBASM
->IsInBulkBroadcast() || pBASM
->InsertBulkArea( pArea
))
228 pArea
->GetBroadcaster().Broadcast( rHint
);
229 bIsBroadcasted
= TRUE
;
232 else if (rAddress
< rAreaRange
.aStart
)
233 break; // while loop, only ranges greater than rAddress follow
235 return bIsBroadcasted
;
239 BOOL
ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange
& rRange
,
240 const ScHint
& rHint
) const
242 if (aBroadcastAreaTbl
.empty())
244 BOOL bIsBroadcasted
= FALSE
;
245 // Unfortunately we can't search for the first matching entry.
246 ScBroadcastAreas::const_iterator
aIter( aBroadcastAreaTbl
.begin());
247 while (aIter
!= aBroadcastAreaTbl
.end())
249 ScBroadcastArea
* pArea
= *aIter
;
250 // A Notify() during broadcast may call EndListeningArea() and thus
251 // dispose this area if it was the last listener, which would
252 // invalidate the iterator, hence increment before call.
254 const ScRange
& rAreaRange
= pArea
->GetRange();
255 if (rAreaRange
.Intersects( rRange
))
257 if (!pBASM
->IsInBulkBroadcast() || pBASM
->InsertBulkArea( pArea
))
259 pArea
->GetBroadcaster().Broadcast( rHint
);
260 bIsBroadcasted
= TRUE
;
263 else if (rRange
.aEnd
< rAreaRange
.aStart
)
264 break; // while loop, only ranges greater than end address follow
266 return bIsBroadcasted
;
270 void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange
& rRange
)
272 if (aBroadcastAreaTbl
.empty())
274 // Searching for areas bound completely within rRange, so it's fine to
275 // exclude all upper left corners smaller than the upper left corner of
276 // rRange and get a lower bound.
277 aTmpSeekBroadcastArea
.UpdateRange( ScRange( rRange
.aStart
));
278 // Search for lower bound, inclusive, not less than.
279 ScBroadcastAreas::iterator
aIter( aBroadcastAreaTbl
.lower_bound(
280 &aTmpSeekBroadcastArea
));
281 for ( ; aIter
!= aBroadcastAreaTbl
.end(); )
283 const ScRange
& rAreaRange
= (*aIter
)->GetRange();
284 if (rRange
.In( rAreaRange
))
286 ScBroadcastArea
* pArea
= *aIter
;
287 if (!pArea
->DecRef())
289 if (pBASM
->IsInBulkBroadcast())
290 pBASM
->RemoveBulkArea( pArea
);
293 ScBroadcastAreas::iterator
aDel( aIter
);
295 aBroadcastAreaTbl
.erase( aDel
);
297 else if (rRange
.aEnd
< rAreaRange
.aStart
)
298 break; // for loop, only ranges greater than end address follow
305 void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode
,
306 const ScRange
& rRange
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
309 if (aBroadcastAreaTbl
.empty())
312 SCCOL nCol1
, nCol2
, theCol1
, theCol2
;
313 SCROW nRow1
, nRow2
, theRow1
, theRow2
;
314 SCTAB nTab1
, nTab2
, theTab1
, theTab2
;
315 nCol1
= rRange
.aStart
.Col();
316 nRow1
= rRange
.aStart
.Row();
317 nTab1
= rRange
.aStart
.Tab();
318 nCol2
= rRange
.aEnd
.Col();
319 nRow2
= rRange
.aEnd
.Row();
320 nTab2
= rRange
.aEnd
.Tab();
321 for ( ScBroadcastAreas::iterator
aIter( aBroadcastAreaTbl
.begin());
322 aIter
!= aBroadcastAreaTbl
.end(); )
324 ScBroadcastArea
* pArea
= *aIter
;
325 ScBroadcastAreas::iterator
aDel( aIter
);
327 if ( pArea
->IsInUpdateChain() )
329 aBroadcastAreaTbl
.erase( aDel
);
334 const ScAddress
& rAdr1
= pArea
->GetStart();
335 theCol1
= rAdr1
.Col();
336 theRow1
= rAdr1
.Row();
337 theTab1
= rAdr1
.Tab();
338 const ScAddress
& rAdr2
= pArea
->GetEnd();
339 theCol2
= rAdr2
.Col();
340 theRow2
= rAdr2
.Row();
341 theTab2
= rAdr2
.Tab();
342 if ( ScRefUpdate::Update( pDoc
, eUpdateRefMode
,
343 nCol1
,nRow1
,nTab1
, nCol2
,nRow2
,nTab2
, nDx
,nDy
,nDz
,
344 theCol1
,theRow1
,theTab1
, theCol2
,theRow2
,theTab2
)
347 aBroadcastAreaTbl
.erase( aDel
);
349 if (pBASM
->IsInBulkBroadcast())
350 pBASM
->RemoveBulkArea( pArea
);
351 pArea
->SetInUpdateChain( TRUE
);
352 ScBroadcastArea
* pUC
= pBASM
->GetEOUpdateChain();
354 pUC
->SetUpdateChainNext( pArea
);
355 else // no tail => no head
356 pBASM
->SetUpdateChain( pArea
);
357 pBASM
->SetEOUpdateChain( pArea
);
364 void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea
* pArea
)
366 aBroadcastAreaTbl
.insert( pArea
);
371 // --- ScBroadcastAreaSlotMachine -------------------------------------
373 ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
374 ScDocument
* pDocument
) :
377 pUpdateChain( NULL
),
378 pEOUpdateChain( NULL
),
379 nInBulkBroadcast( 0 )
381 ppSlots
= new ScBroadcastAreaSlot
* [ BCA_SLOTS
];
382 memset( ppSlots
, 0 , sizeof( ScBroadcastAreaSlot
* ) * BCA_SLOTS
);
386 ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
388 for ( ScBroadcastAreaSlot
** pp
= ppSlots
+ BCA_SLOTS
; --pp
>= ppSlots
; )
399 inline SCSIZE
ScBroadcastAreaSlotMachine::ComputeSlotOffset(
400 const ScAddress
& rAddress
) const
402 SCROW nRow
= rAddress
.Row();
403 SCCOL nCol
= rAddress
.Col();
404 if ( !ValidRow(nRow
) || !ValidCol(nCol
) )
406 DBG_ASSERT( FALSE
, "Row/Col ungueltig!" );
411 static_cast<SCSIZE
>(nRow
) / BCA_SLOT_ROWS
+
412 static_cast<SCSIZE
>(nCol
) / BCA_SLOT_COLS
* BCA_SLOTS_ROW
;
416 void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange
& rRange
,
417 SCSIZE
& rStart
, SCSIZE
& rEnd
, SCSIZE
& rRowBreak
420 rStart
= ComputeSlotOffset( rRange
.aStart
);
421 rEnd
= ComputeSlotOffset( rRange
.aEnd
);
422 // count of row slots per column minus one
423 rRowBreak
= ComputeSlotOffset(
424 ScAddress( rRange
.aStart
.Col(), rRange
.aEnd
.Row(), 0 ) ) - rStart
;
428 void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange
& rRange
,
429 SvtListener
* pListener
432 if ( rRange
== BCA_LISTEN_ALWAYS
)
435 pBCAlways
= new SvtBroadcaster
;
436 pListener
->StartListening( *pBCAlways
);
440 SCSIZE nStart
, nEnd
, nRowBreak
;
441 ComputeAreaPoints( rRange
, nStart
, nEnd
, nRowBreak
);
442 SCSIZE nOff
= nStart
;
443 SCSIZE nBreak
= nOff
+ nRowBreak
;
444 ScBroadcastAreaSlot
** pp
= ppSlots
+ nOff
;
445 ScBroadcastArea
* pArea
= NULL
;
446 while ( nOff
<= nEnd
)
449 *pp
= new ScBroadcastAreaSlot( pDoc
, this );
450 // the first call creates the ScBroadcastArea
451 (*pp
)->StartListeningArea( rRange
, pListener
, pArea
);
459 nStart
+= BCA_SLOTS_ROW
;
462 nBreak
= nOff
+ nRowBreak
;
469 void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange
& rRange
,
470 SvtListener
* pListener
473 if ( rRange
== BCA_LISTEN_ALWAYS
)
475 DBG_ASSERT( pBCAlways
, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established");
478 pListener
->EndListening( *pBCAlways
);
479 if (!pBCAlways
->HasListeners())
488 SCSIZE nStart
, nEnd
, nRowBreak
;
489 ComputeAreaPoints( rRange
, nStart
, nEnd
, nRowBreak
);
490 SCSIZE nOff
= nStart
;
491 SCSIZE nBreak
= nOff
+ nRowBreak
;
492 ScBroadcastAreaSlot
** pp
= ppSlots
+ nOff
;
493 ScBroadcastArea
* pArea
= NULL
;
494 while ( nOff
<= nEnd
)
497 (*pp
)->EndListeningArea( rRange
, pListener
, pArea
);
505 nStart
+= BCA_SLOTS_ROW
;
508 nBreak
= nOff
+ nRowBreak
;
515 BOOL
ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint
& rHint
) const
517 const ScAddress
& rAddress
= rHint
.GetAddress();
518 if ( rAddress
== BCA_BRDCST_ALWAYS
)
522 pBCAlways
->Broadcast( rHint
);
530 ScBroadcastAreaSlot
* pSlot
= ppSlots
[ ComputeSlotOffset( rAddress
) ];
532 return pSlot
->AreaBroadcast( rHint
);
539 BOOL
ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange
& rRange
,
540 const ScHint
& rHint
) const
542 BOOL bBroadcasted
= FALSE
;
543 SCSIZE nStart
, nEnd
, nRowBreak
;
544 ComputeAreaPoints( rRange
, nStart
, nEnd
, nRowBreak
);
545 SCSIZE nOff
= nStart
;
546 SCSIZE nBreak
= nOff
+ nRowBreak
;
547 ScBroadcastAreaSlot
** pp
= ppSlots
+ nOff
;
548 while ( nOff
<= nEnd
)
551 bBroadcasted
|= (*pp
)->AreaBroadcastInRange( rRange
, rHint
);
559 nStart
+= BCA_SLOTS_ROW
;
562 nBreak
= nOff
+ nRowBreak
;
569 void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
570 const ScRange
& rRange
573 SCSIZE nStart
, nEnd
, nRowBreak
;
574 ComputeAreaPoints( rRange
, nStart
, nEnd
, nRowBreak
);
575 SCSIZE nOff
= nStart
;
576 SCSIZE nBreak
= nOff
+ nRowBreak
;
577 ScBroadcastAreaSlot
** pp
= ppSlots
+ nOff
;
578 while ( nOff
<= nEnd
)
581 (*pp
)->DelBroadcastAreasInRange( rRange
);
589 nStart
+= BCA_SLOTS_ROW
;
592 nBreak
= nOff
+ nRowBreak
;
598 // for all affected: remove, chain, update range, insert
599 void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
600 UpdateRefMode eUpdateRefMode
,
601 const ScRange
& rRange
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
604 SCSIZE nStart
, nEnd
, nRowBreak
;
605 // remove affected and put in chain
606 ComputeAreaPoints( rRange
, nStart
, nEnd
, nRowBreak
);
607 SCSIZE nOff
= nStart
;
608 SCSIZE nBreak
= nOff
+ nRowBreak
;
609 ScBroadcastAreaSlot
** pp
= ppSlots
+ nOff
;
610 while ( nOff
<= nEnd
)
613 (*pp
)->UpdateRemove( eUpdateRefMode
, rRange
, nDx
, nDy
, nDz
);
621 nStart
+= BCA_SLOTS_ROW
;
624 nBreak
= nOff
+ nRowBreak
;
628 SCCOL nCol1
, nCol2
, theCol1
, theCol2
;
629 SCROW nRow1
, nRow2
, theRow1
, theRow2
;
630 SCTAB nTab1
, nTab2
, theTab1
, theTab2
;
631 nCol1
= rRange
.aStart
.Col();
632 nRow1
= rRange
.aStart
.Row();
633 nTab1
= rRange
.aStart
.Tab();
634 nCol2
= rRange
.aEnd
.Col();
635 nRow2
= rRange
.aEnd
.Row();
636 nTab2
= rRange
.aEnd
.Tab();
637 while ( pUpdateChain
)
641 ScBroadcastArea
* pArea
= pUpdateChain
;
642 pUpdateChain
= pArea
->GetUpdateChainNext();
645 aAdr
= pArea
->GetStart();
646 theCol1
= aAdr
.Col();
647 theRow1
= aAdr
.Row();
648 theTab1
= aAdr
.Tab();
649 aAdr
= pArea
->GetEnd();
650 theCol2
= aAdr
.Col();
651 theRow2
= aAdr
.Row();
652 theTab2
= aAdr
.Tab();
653 if ( ScRefUpdate::Update( pDoc
, eUpdateRefMode
,
654 nCol1
,nRow1
,nTab1
, nCol2
,nRow2
,nTab2
, nDx
,nDy
,nDz
,
655 theCol1
,theRow1
,theTab1
, theCol2
,theRow2
,theTab2
)
658 aRange
= ScRange( ScAddress( theCol1
,theRow1
,theTab1
),
659 ScAddress( theCol2
,theRow2
,theTab2
) );
660 pArea
->UpdateRange( aRange
);
661 pArea
->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange
) ); // for DDE
665 ComputeAreaPoints( aRange
, nStart
, nEnd
, nRowBreak
);
667 nBreak
= nOff
+ nRowBreak
;
669 while ( nOff
<= nEnd
)
672 (*pp
)->UpdateInsert( pArea
);
680 nStart
+= BCA_SLOTS_ROW
;
683 nBreak
= nOff
+ nRowBreak
;
688 pArea
->SetUpdateChainNext( NULL
);
689 pArea
->SetInUpdateChain( FALSE
);
691 pEOUpdateChain
= NULL
;
695 void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
701 void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
703 if (nInBulkBroadcast
> 0)
705 if (--nInBulkBroadcast
== 0)
706 ScBroadcastAreasBulk().swap( aBulkBroadcastAreas
);
711 bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea
* pArea
)
713 return aBulkBroadcastAreas
.insert( pArea
).second
;
717 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea
* pArea
)
719 return aBulkBroadcastAreas
.erase( pArea
);