blob: 49784d7a977fae52dd69c1f918f9aa5590a9b911 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include <boost/mem_fn.hpp>
#include <sfx2/objsh.hxx>
#include <svl/listener.hxx>
#include <svl/listeneriter.hxx>
#include "document.hxx"
#include "brdcst.hxx"
#include "bcaslot.hxx"
#include "scerrors.hxx"
#include "docoptio.hxx"
#include "refupdat.hxx"
#include "table.hxx"
// Number of slots per dimension
// must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT
#define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16)
#if MAXROWCOUNT_DEFINE == 32000
#define BCA_SLOTS_ROW 256
#define BCA_SLICE 125
#else
#define BCA_SLICE 128
#define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / BCA_SLICE)
#endif
#define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL)
#define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW)
// multiple?
#if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE)
#error bad BCA_SLOTS_COL value!
#endif
#if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE)
#error bad BCA_SLOTS_ROW value!
#endif
// size of slot array if linear
#define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW)
// Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and
// sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory
// anyway, once you reached these values..
#if BCA_SLOTS_DEFINE > 268435456
#error BCA_SLOTS_DEFINE DOOMed!
#endif
// STATIC DATA -----------------------------------------------------------
TYPEINIT1( ScHint, SfxSimpleHint );
TYPEINIT1( ScAreaChangedHint, SfxHint );
struct ScSlotData
{
SCROW nStartRow; // first row of this segment
SCROW nStopRow; // first row of next segment
SCSIZE nSlice; // slice size in this segment
SCSIZE nCumulated; // cumulated slots of previous segments
ScSlotData( SCROW r1, SCROW r2, SCSIZE s, SCSIZE c ) : nStartRow(r1), nStopRow(r2), nSlice(s), nCumulated(c) {}
};
typedef ::std::vector< ScSlotData > ScSlotDistribution;
#if MAXROWCOUNT_DEFINE <= 65536
// Linear distribution.
static ScSlotDistribution aSlotDistribution( ScSlotData( 0, MAXROWCOUNT, BCA_SLOT_ROWS, 0));
static SCSIZE nBcaSlotsRow = BCA_SLOTS_ROW;
static SCSIZE nBcaSlots = BCA_SLOTS_DEFINE;
#else
// Logarithmic or any other distribution.
// Upper sheet part usually is more populated and referenced and gets fine
// grained resolution, larger data in larger hunks.
// Could be further enhanced by also applying a different distribution of
// column slots.
static SCSIZE initSlotDistribution( ScSlotDistribution & rSD, SCSIZE & rBSR )
{
SCSIZE nSlots = 0;
SCROW nRow1 = 0;
SCROW nRow2 = 32*1024;
SCSIZE nSlice = 128;
// Must be sorted by row1,row2!
while (nRow2 <= MAXROWCOUNT)
{
//fprintf( stderr, "r1,r2,slice,cum: %7zu, %7zu, %7zu, %7zu\n", (size_t)nRow1, (size_t)nRow2, (size_t)nSlice, (size_t)nSlots);
// {0,32k,128,0;32k,64k,256,0+256;64k,128k,512,0+256+128;128k,256k,1024,0+256+128+128;256k,512k,2048,...;512k,1M,4096,...}
rSD.push_back( ScSlotData( nRow1, nRow2, nSlice, nSlots));
nSlots += (nRow2 - nRow1) / nSlice;
nRow1 = nRow2;
nRow2 *= 2;
nSlice *= 2;
}
//fprintf( stderr, "Slices: %zu, slots per sheet: %zu, memory per referenced sheet: %zu\n", (size_t) nSlots, (size_t) nSlots * BCA_SLOTS_COL, (size_t) nSlots * BCA_SLOTS_COL * sizeof(void*));
rBSR = nSlots;
return nSlots;
}
static ScSlotDistribution aSlotDistribution;
static SCSIZE nBcaSlotsRow;
static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow) * BCA_SLOTS_COL;
// Ensure that all static variables are initialized with this one call.
#endif
ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
ScBroadcastAreaSlotMachine* pBASMa ) :
aTmpSeekBroadcastArea( ScRange()),
pDoc( pDocument ),
pBASM( pBASMa )
{
}
ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
{
for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
aIter != aBroadcastAreaTbl.end(); /* none */)
{
// Prevent hash from accessing dangling pointer in case area is
// deleted.
ScBroadcastArea* pArea = *aIter;
// Erase all so no hash will be accessed upon destruction of the
// hash_set.
aBroadcastAreaTbl.erase( aIter++);
if (!pArea->DecRef())
delete pArea;
}
}
bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
{
if ( pDoc->GetHardRecalcState() )
return true;
if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
{ // this is more hypothetical now, check existed for old SV_PTRARR_SORT
if ( !pDoc->GetHardRecalcState() )
{
pDoc->SetHardRecalcState( 1 );
SfxObjectShell* pShell = pDoc->GetDocumentShell();
DBG_ASSERT( pShell, "Missing DocShell :-/" );
if ( pShell )
pShell->SetError( SCWARN_CORE_HARD_RECALC, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) );
pDoc->SetAutoCalc( sal_False );
pDoc->SetHardRecalcState( 2 );
}
return true;
}
return false;
}
bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
SvtListener* pListener, ScBroadcastArea*& rpArea )
{
bool bNewArea = false;
DBG_ASSERT(pListener, "StartListeningArea: pListener Null");
if (CheckHardRecalcStateCondition())
return false;
if ( !rpArea )
{
// Even if most times the area doesn't exist yet and immediately trying
// to new and insert it would save an attempt to find it, on mass
// operations like identical large [HV]LOOKUP() areas the new/delete
// would add quite some penalty for all but the first formula cell.
ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
if (aIter != aBroadcastAreaTbl.end())
rpArea = *aIter;
else
{
rpArea = new ScBroadcastArea( rRange);
if (aBroadcastAreaTbl.insert( rpArea).second)
{
rpArea->IncRef();
bNewArea = true;
}
else
{
DBG_ERRORFILE("StartListeningArea: area not found and not inserted in slot?!?");
delete rpArea;
rpArea = 0;
}
}
if (rpArea)
pListener->StartListening( rpArea->GetBroadcaster());
}
else
{
if (aBroadcastAreaTbl.insert( rpArea).second)
rpArea->IncRef();
}
return bNewArea;
}
void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
{
DBG_ASSERT( pArea, "InsertListeningArea: pArea NULL");
if (CheckHardRecalcStateCondition())
return;
if (aBroadcastAreaTbl.insert( pArea).second)
pArea->IncRef();
}
// If rpArea != NULL then no listeners are stopped, only the area is removed
// and the reference count decremented.
void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
SvtListener* pListener, ScBroadcastArea*& rpArea )
{
DBG_ASSERT(pListener, "EndListeningArea: pListener Null");
if ( !rpArea )
{
ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
if (aIter == aBroadcastAreaTbl.end())
return;
rpArea = *aIter;
pListener->EndListening( rpArea->GetBroadcaster() );
if ( !rpArea->GetBroadcaster().HasListeners() )
{ // if nobody is listening we can dispose it
aBroadcastAreaTbl.erase( aIter);
if ( !rpArea->DecRef() )
{
delete rpArea;
rpArea = NULL;
}
}
}
else
{
if ( !rpArea->GetBroadcaster().HasListeners() )
{
ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
if (aIter == aBroadcastAreaTbl.end())
return;
DBG_ASSERT( *aIter == rpArea, "EndListeningArea: area pointer mismatch");
aBroadcastAreaTbl.erase( aIter);
if ( !rpArea->DecRef() )
{
delete rpArea;
rpArea = NULL;
}
}
}
}
ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea(
const ScRange& rRange ) const
{
aTmpSeekBroadcastArea.UpdateRange( rRange);
return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
}
sal_Bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const
{
if (aBroadcastAreaTbl.empty())
return sal_False;
sal_Bool bIsBroadcasted = sal_False;
// issue 118012
// do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators
// are destroyed during notification.
std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() );
std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) );
const ScAddress& rAddress = rHint.GetAddress();
const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() );
std::vector< ScBroadcastArea* >::const_iterator aIter;
for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
{
ScBroadcastArea* pArea = *aIter;
// check, if copied item has been already removed from <aBroadcastAreaTbl>
if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() )
{
continue;
}
const ScRange& rAreaRange = pArea->GetRange();
if (rAreaRange.In( rAddress))
{
if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
{
pArea->GetBroadcaster().Broadcast( rHint);
bIsBroadcasted = sal_True;
}
}
}
// delete no longer referenced <ScBroadcastArea> instances
for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
{
ScBroadcastArea* pArea = *aIter;
if ( !pArea->DecRef() )
{
delete pArea;
}
}
return bIsBroadcasted;
}
sal_Bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
const ScHint& rHint) const
{
if (aBroadcastAreaTbl.empty())
return sal_False;
sal_Bool bIsBroadcasted = sal_False;
// issue 118012
// do not iterate on <aBoardcastAreaTbl> as its reveals that its iterators
// are destroyed during notification.
std::vector< ScBroadcastArea* > aCopyForIteration( aBroadcastAreaTbl.begin(), aBroadcastAreaTbl.end() );
std::for_each( aCopyForIteration.begin(), aCopyForIteration.end(), boost::mem_fn( &ScBroadcastArea::IncRef ) );
const std::vector< ScBroadcastArea* >::const_iterator aEnd( aCopyForIteration.end() );
std::vector< ScBroadcastArea* >::const_iterator aIter;
for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
{
ScBroadcastArea* pArea = *aIter;
// check, if copied item has been already removed from <aBroadcastAreaTbl>
if ( aBroadcastAreaTbl.find( pArea ) == aBroadcastAreaTbl.end() )
{
continue;
}
const ScRange& rAreaRange = pArea->GetRange();
if (rAreaRange.Intersects( rRange ))
{
if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
{
pArea->GetBroadcaster().Broadcast( rHint);
bIsBroadcasted = sal_True;
}
}
}
// delete no longer referenced <ScBroadcastArea> instances
for ( aIter = aCopyForIteration.begin(); aIter != aEnd; ++aIter )
{
ScBroadcastArea* pArea = *aIter;
if ( !pArea->DecRef() )
{
delete pArea;
}
}
return bIsBroadcasted;
}
void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
{
if (aBroadcastAreaTbl.empty())
return;
for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
const ScRange& rAreaRange = (*aIter)->GetRange();
if (rRange.In( rAreaRange))
{
ScBroadcastArea* pArea = *aIter;
aBroadcastAreaTbl.erase( aIter++); // erase before modifying
if (!pArea->DecRef())
{
if (pBASM->IsInBulkBroadcast())
pBASM->RemoveBulkArea( pArea);
delete pArea;
}
}
else
++aIter;
}
}
void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
if (aBroadcastAreaTbl.empty())
return;
SCCOL nCol1, nCol2, theCol1, theCol2;
SCROW nRow1, nRow2, theRow1, theRow2;
SCTAB nTab1, nTab2, theTab1, theTab2;
rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
ScBroadcastArea* pArea = *aIter;
if ( pArea->IsInUpdateChain() )
{
aBroadcastAreaTbl.erase( aIter++);
pArea->DecRef();
}
else
{
pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
{
aBroadcastAreaTbl.erase( aIter++);
pArea->DecRef();
if (pBASM->IsInBulkBroadcast())
pBASM->RemoveBulkArea( pArea);
pArea->SetInUpdateChain( sal_True );
ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
if ( pUC )
pUC->SetUpdateChainNext( pArea );
else // no tail => no head
pBASM->SetUpdateChain( pArea );
pBASM->SetEOUpdateChain( pArea );
}
else
++aIter;
}
}
}
void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
{
ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
if (aIter == aBroadcastAreaTbl.end())
return;
if (*aIter != pArea)
DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch");
else
{
aBroadcastAreaTbl.erase( aIter);
pArea->DecRef();
}
}
void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
{
::std::pair< ScBroadcastAreas::iterator, bool > aPair =
aBroadcastAreaTbl.insert( pArea );
if (aPair.second)
pArea->IncRef();
else
{
// Identical area already exists, add listeners.
ScBroadcastArea* pTarget = *(aPair.first);
if (pArea != pTarget)
{
SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
SvtListenerIter it( pArea->GetBroadcaster());
for (SvtListener* pListener = it.GetCurr(); pListener;
pListener = it.GoNext())
{
pListener->StartListening( rTarget);
}
}
}
}
// --- ScBroadcastAreaSlotMachine -------------------------------------
ScBroadcastAreaSlotMachine::TableSlots::TableSlots()
{
ppSlots = new ScBroadcastAreaSlot* [ nBcaSlots ];
memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
}
ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
{
for ( ScBroadcastAreaSlot** pp = ppSlots + nBcaSlots; --pp >= ppSlots; /* nothing */ )
{
if (*pp)
delete *pp;
}
delete [] ppSlots;
}
ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
ScDocument* pDocument ) :
pBCAlways( NULL ),
pDoc( pDocument ),
pUpdateChain( NULL ),
pEOUpdateChain( NULL ),
nInBulkBroadcast( 0 )
{
}
ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
{
for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin());
iTab != aTableSlotsMap.end(); ++iTab)
{
delete (*iTab).second;
}
delete pBCAlways;
}
inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
const ScAddress& rAddress ) const
{
SCROW nRow = rAddress.Row();
SCCOL nCol = rAddress.Col();
if ( !ValidRow(nRow) || !ValidCol(nCol) )
{
DBG_ERRORFILE( "Row/Col invalid, using first slot!" );
return 0;
}
for (size_t i=0; i < aSlotDistribution.size(); ++i)
{
if (nRow < aSlotDistribution[i].nStopRow)
{
const ScSlotData& rSD = aSlotDistribution[i];
return rSD.nCumulated +
(static_cast<SCSIZE>(nRow - rSD.nStartRow)) / rSD.nSlice +
static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * nBcaSlotsRow;
}
}
DBG_ERRORFILE( "No slot found, using last!" );
return nBcaSlots - 1;
}
void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
{
rStart = ComputeSlotOffset( rRange.aStart );
rEnd = ComputeSlotOffset( rRange.aEnd );
// count of row slots per column minus one
rRowBreak = ComputeSlotOffset(
ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
}
inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE const & nRowBreak )
{
if ( nOff < nBreak )
{
++nOff;
++pp;
}
else
{
nStart += nBcaSlotsRow;
nOff = nStart;
pp = ppSlots + nOff;
nBreak = nOff + nRowBreak;
}
}
void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
SvtListener* pListener )
{
//fprintf( stderr, "StartListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab());
if ( rRange == BCA_LISTEN_ALWAYS )
{
if ( !pBCAlways )
pBCAlways = new SvtBroadcaster;
pListener->StartListening( *pBCAlways );
}
else
{
bool bDone = false;
for (SCTAB nTab = rRange.aStart.Tab();
!bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
{
TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
if (iTab == aTableSlotsMap.end())
iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
nTab, new TableSlots)).first;
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
ScBroadcastArea* pArea = NULL;
while ( !bDone && nOff <= nEnd )
{
if ( !*pp )
*pp = new ScBroadcastAreaSlot( pDoc, this );
if (!pArea)
{
// If the call to StartListeningArea didn't create the
// ScBroadcastArea, listeners were added to an already
// existing identical area that doesn't need to be inserted
// to slots again.
if (!(*pp)->StartListeningArea( rRange, pListener, pArea))
bDone = true;
}
else
(*pp)->InsertListeningArea( pArea);
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
}
}
void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
SvtListener* pListener )
{
//fprintf( stderr, "EndListeningArea (c,r,t): %d, %d, %d, %d, %d, %d\n", (int)rRange.aStart.Col(), (int)rRange.aStart.Row(), (int)rRange.aStart.Tab(), (int)rRange.aEnd.Col(), (int)rRange.aEnd.Row(), (int)rRange.aEnd.Tab());
if ( rRange == BCA_LISTEN_ALWAYS )
{
DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established");
if ( pBCAlways )
{
pListener->EndListening( *pBCAlways);
if (!pBCAlways->HasListeners())
{
delete pBCAlways;
pBCAlways = NULL;
}
}
}
else
{
SCTAB nEndTab = rRange.aEnd.Tab();
for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
ScBroadcastArea* pArea = NULL;
if (nOff == 0 && nEnd == nBcaSlots-1)
{
// Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
// happen for insertion and deletion of sheets.
ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
do
{
if ( *pp )
(*pp)->EndListeningArea( rRange, pListener, pArea );
} while (++pp < pStop);
}
else
{
while ( nOff <= nEnd )
{
if ( *pp )
(*pp)->EndListeningArea( rRange, pListener, pArea );
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
}
}
}
sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
{
const ScAddress& rAddress = rHint.GetAddress();
if ( rAddress == BCA_BRDCST_ALWAYS )
{
if ( pBCAlways )
{
pBCAlways->Broadcast( rHint );
return sal_True;
}
else
return sal_False;
}
else
{
TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
if (iTab == aTableSlotsMap.end())
return sal_False;
ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
ComputeSlotOffset( rAddress));
if ( pSlot )
return pSlot->AreaBroadcast( rHint );
else
return sal_False;
}
}
sal_Bool ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
const ScHint& rHint ) const
{
sal_Bool bBroadcasted = sal_False;
SCTAB nEndTab = rRange.aEnd.Tab();
for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
while ( nOff <= nEnd )
{
if ( *pp )
bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
return bBroadcasted;
}
void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
const ScRange& rRange )
{
SCTAB nEndTab = rRange.aEnd.Tab();
for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
if (nOff == 0 && nEnd == nBcaSlots-1)
{
// Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
// happen for insertion and deletion of sheets.
ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
do
{
if ( *pp )
(*pp)->DelBroadcastAreasInRange( rRange );
} while (++pp < pStop);
}
else
{
while ( nOff <= nEnd )
{
if ( *pp )
(*pp)->DelBroadcastAreasInRange( rRange );
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
}
}
// for all affected: remove, chain, update range, insert, and maybe delete
void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
UpdateRefMode eUpdateRefMode,
const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
// remove affected and put in chain
SCTAB nEndTab = rRange.aEnd.Tab();
for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
if (nOff == 0 && nEnd == nBcaSlots-1)
{
// Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
// happen for insertion and deletion of sheets.
ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
do
{
if ( *pp )
(*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
} while (++pp < pStop);
}
else
{
while ( nOff <= nEnd )
{
if ( *pp )
(*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
}
// Updating an area's range will modify the hash key, remove areas from all
// affected slots. Will be reinserted later with the updated range.
ScBroadcastArea* pChain = pUpdateChain;
while (pChain)
{
ScBroadcastArea* pArea = pChain;
pChain = pArea->GetUpdateChainNext();
ScRange aRange( pArea->GetRange());
// remove from slots
for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
{
TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
if (iTab == aTableSlotsMap.end())
{
DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?");
continue; // for
}
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
while ( nOff <= nEnd && pArea->GetRef() )
{
if (*pp)
(*pp)->UpdateRemoveArea( pArea);
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
}
// shift sheets
if (nDz)
{
if (nDz < 0)
{
TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
// Remove sheets, if any, iDel or/and iTab may as well point to end().
while (iDel != iTab)
{
delete (*iDel).second;
aTableSlotsMap.erase( iDel++);
}
// shift remaining down
while (iTab != aTableSlotsMap.end())
{
SCTAB nTab = (*iTab).first + nDz;
aTableSlotsMap[nTab] = (*iTab).second;
aTableSlotsMap.erase( iTab++);
}
}
else
{
TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
if (iStop != aTableSlotsMap.end())
{
bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
if (!bStopIsBegin)
--iStop;
TableSlotsMap::iterator iTab( aTableSlotsMap.end());
--iTab;
while (iTab != iStop)
{
SCTAB nTab = (*iTab).first + nDz;
aTableSlotsMap[nTab] = (*iTab).second;
aTableSlotsMap.erase( iTab--);
}
// Shift the very first, iTab==iStop in this case.
if (bStopIsBegin)
{
SCTAB nTab = (*iTab).first + nDz;
aTableSlotsMap[nTab] = (*iTab).second;
aTableSlotsMap.erase( iStop);
}
}
}
}
// work off chain
SCCOL nCol1, nCol2, theCol1, theCol2;
SCROW nRow1, nRow2, theRow1, theRow2;
SCTAB nTab1, nTab2, theTab1, theTab2;
rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
while ( pUpdateChain )
{
ScBroadcastArea* pArea = pUpdateChain;
ScRange aRange( pArea->GetRange());
pUpdateChain = pArea->GetUpdateChainNext();
// update range
aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
{
aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
pArea->UpdateRange( aRange );
pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE
}
// insert to slots
for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
{
TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
if (iTab == aTableSlotsMap.end())
iTab = aTableSlotsMap.insert( TableSlotsMap::value_type(
nTab, new TableSlots)).first;
ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
SCSIZE nStart, nEnd, nRowBreak;
ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
SCSIZE nOff = nStart;
SCSIZE nBreak = nOff + nRowBreak;
ScBroadcastAreaSlot** pp = ppSlots + nOff;
while ( nOff <= nEnd )
{
if (!*pp)
*pp = new ScBroadcastAreaSlot( pDoc, this );
(*pp)->UpdateInsert( pArea );
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
// unchain
pArea->SetUpdateChainNext( NULL );
pArea->SetInUpdateChain( sal_False );
// Delete if not inserted to any slot. RemoveBulkArea(pArea) was
// already executed in UpdateRemove().
if (!pArea->GetRef())
delete pArea;
}
pEOUpdateChain = NULL;
}
void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
{
++nInBulkBroadcast;
}
void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
{
if (nInBulkBroadcast > 0)
{
if (--nInBulkBroadcast == 0)
ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
}
}
bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
{
return aBulkBroadcastAreas.insert( pArea ).second;
}
size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
{
return aBulkBroadcastAreas.erase( pArea );
}