summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorKurt Zenker <kz@openoffice.org>2009-06-02 11:23:01 +0000
committerKurt Zenker <kz@openoffice.org>2009-06-02 11:23:01 +0000
commit92288a66306167c908c86064a8e8cacf4f592841 (patch)
treed04d1118fa0d5eb77d348a5442fc0876ec2961ab /sc
parentbb6071dfa4ca367fa268c77880852e3d4f6e54e3 (diff)
CWS-TOOLING: integrate CWS calcperf04
2009-04-23 12:47:25 +0200 er r271155 : CWS-TOOLING: rebase CWS calcperf04 to trunk@270723 (milestone: DEV300:m46) 2009-04-22 13:49:45 +0200 er r271104 : #i101254# performance area broadcasters: replace ::std::set with ::std::hash_set; have broadcaster slots per sheet instead of document wide lumps 2009-04-16 15:04:58 +0200 nn r270886 : #i101126# ScFormatRangeStyles::GetStyleNameIndex: pass row before which to remove 2009-04-16 11:26:33 +0200 nn r270874 : #i101126# ScUniqueCellFormatsObj: collect ranges without ScRangeList::Join 2009-04-15 15:07:34 +0200 er r270846 : merged #i95967# changesets 263482,263483 for performance testcases 2009-03-06 12:48:52 +0100 nn r268996 : #i99960# don't get null date from the model repeatedly 2009-03-06 12:47:21 +0100 nn r268995 : #i99959# don't use GetInputString in ScXMLExport::WriteCell 2009-03-03 17:30:25 +0100 er r268772 : #i99828# NextNewToken: some string handling optimization, i.e. less toUpper calls
Diffstat (limited to 'sc')
-rw-r--r--sc/inc/address.hxx39
-rw-r--r--sc/inc/cellsuno.hxx1
-rw-r--r--sc/source/core/data/bcaslot.cxx592
-rw-r--r--sc/source/core/data/document.cxx8
-rw-r--r--sc/source/core/inc/bcaslot.hxx136
-rw-r--r--sc/source/core/tool/compiler.cxx256
-rw-r--r--sc/source/filter/xml/XMLExportIterator.cxx6
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.cxx4
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.hxx2
-rw-r--r--sc/source/filter/xml/xmlexprt.cxx36
-rw-r--r--sc/source/ui/unoobj/cellsuno.cxx145
11 files changed, 809 insertions, 416 deletions
diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx
index 7b128b4b1abd..7ba5aa9c1f32 100644
--- a/sc/inc/address.hxx
+++ b/sc/inc/address.hxx
@@ -434,6 +434,22 @@ inline size_t ScAddress::hash() const
(static_cast<size_t>(nCol) << 24) ^ static_cast<size_t>(nRow);
}
+struct ScAddressHashFunctor
+{
+ size_t operator()( const ScAddress & rAdr ) const
+ {
+ return rAdr.hash();
+ }
+};
+
+struct ScAddressEqualFunctor
+{
+ bool operator()( const ScAddress & rAdr1, const ScAddress & rAdr2 ) const
+ {
+ return rAdr1 == rAdr2;
+ }
+};
+
// === ScRange ===============================================================
@@ -521,7 +537,9 @@ public:
inline bool operator>( const ScRange& r ) const;
inline bool operator>=( const ScRange& r ) const;
- inline size_t hash() const;
+ /// Hash 2D area ignoring table number.
+ inline size_t hashArea() const;
+ /// Hash start column and start and end rows.
inline size_t hashStartColumn() const;
};
@@ -580,7 +598,7 @@ inline bool ScRange::In( const ScRange& r ) const
}
-inline size_t ScRange::hash() const
+inline size_t ScRange::hashArea() const
{
// Assume that there are not that many ranges with identical corners so we
// won't have too many collisions. Also assume that more lower row and
@@ -609,6 +627,23 @@ inline size_t ScRange::hashStartColumn() const
}
+struct ScRangeHashAreaFunctor
+{
+ size_t operator()( const ScRange & rRange ) const
+ {
+ return rRange.hashArea();
+ }
+};
+
+struct ScRangeEqualFunctor
+{
+ bool operator()( const ScRange & rRange1, const ScRange & rRange2 ) const
+ {
+ return rRange1 == rRange2;
+ }
+};
+
+
// === ScRangePair ===========================================================
class ScRangePair
diff --git a/sc/inc/cellsuno.hxx b/sc/inc/cellsuno.hxx
index 51822f6c75c9..07789d47f363 100644
--- a/sc/inc/cellsuno.hxx
+++ b/sc/inc/cellsuno.hxx
@@ -980,7 +980,6 @@ public:
throw(::com::sun::star::uno::RuntimeException);
virtual sal_Int16 SAL_CALL resetActionLocks() throw(::com::sun::star::uno::RuntimeException);
- static String GetInputString_Impl(ScDocument* pDoc, const ScAddress& aPos, BOOL bEnglish);
static String GetOutputString_Impl(ScDocument* pDoc, const ScAddress& aPos);
};
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
index 34b19662debf..75699e8b0a10 100644
--- a/sc/source/core/data/bcaslot.cxx
+++ b/sc/source/core/data/bcaslot.cxx
@@ -35,6 +35,7 @@
#include <sfx2/objsh.hxx>
#include <svtools/listener.hxx>
+#include <svtools/listeneriter.hxx>
#include "document.hxx"
#include "brdcst.hxx"
@@ -89,7 +90,7 @@ ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
{
- for ( ScBroadcastAreas::iterator aIter = aBroadcastAreaTbl.begin();
+ for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
aIter != aBroadcastAreaTbl.end(); ++aIter)
{
if (!(*aIter)->DecRef())
@@ -98,16 +99,10 @@ ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
}
-// Only here new ScBroadcastArea objects are created, prevention of dupes.
-// If rpArea != NULL then no listeners are startet, only the area is inserted
-// and the reference count increased.
-void ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
- SvtListener* pListener, ScBroadcastArea*& rpArea
- )
+bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
{
- DBG_ASSERT(pListener, "StartListeningArea: pListener Null");
if ( pDoc->GetHardRecalcState() )
- return;
+ return true;
if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
{ // this is more hypothetical now, check existed for old SV_PTRARR_SORT
if ( !pDoc->GetHardRecalcState() )
@@ -123,43 +118,69 @@ void ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
pDoc->SetAutoCalc( FALSE );
pDoc->SetHardRecalcState( 2 );
}
- return;
+ 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 )
{
- rpArea = new ScBroadcastArea( rRange );
- // Most times the area doesn't exist yet, immediately trying to insert
- // it saves an attempt to find it.
- if (aBroadcastAreaTbl.insert( rpArea).second)
- rpArea->IncRef();
+ // 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
{
- delete rpArea;
- ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
- if (aIter != aBroadcastAreaTbl.end())
- rpArea = *aIter;
+ rpArea = new ScBroadcastArea( rRange);
+ if (aBroadcastAreaTbl.insert( rpArea).second)
+ {
+ rpArea->IncRef();
+ bNewArea = true;
+ }
else
{
- DBG_ERRORFILE("BroadcastArea not inserted and not found?!?");
+ DBG_ERRORFILE("StartListeningArea: area not found and not inserted in slot?!?");
+ delete rpArea;
rpArea = 0;
}
}
if (rpArea)
- pListener->StartListening( rpArea->GetBroadcaster() );
+ pListener->StartListening( rpArea->GetBroadcaster());
}
else
{
- aBroadcastAreaTbl.insert( rpArea );
- rpArea->IncRef();
+ 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 decreased.
+// and the reference count decremented.
void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
- SvtListener* pListener, ScBroadcastArea*& rpArea
- )
+ SvtListener* pListener, ScBroadcastArea*& rpArea )
{
DBG_ASSERT(pListener, "EndListeningArea: pListener Null");
if ( !rpArea )
@@ -186,6 +207,7 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange));
if (aIter == aBroadcastAreaTbl.end())
return;
+ DBG_ASSERT( *aIter == rpArea, "EndListeningArea: area pointer mismatch");
aBroadcastAreaTbl.erase( aIter);
if ( !rpArea->DecRef() )
{
@@ -211,9 +233,8 @@ BOOL ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const
return FALSE;
BOOL bIsBroadcasted = FALSE;
const ScAddress& rAddress = rHint.GetAddress();
- // Unfortunately we can't search for the first matching entry.
- ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
- while (aIter != aBroadcastAreaTbl.end())
+ for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
ScBroadcastArea* pArea = *aIter;
// A Notify() during broadcast may call EndListeningArea() and thus
@@ -229,8 +250,6 @@ BOOL ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const
bIsBroadcasted = TRUE;
}
}
- else if (rAddress < rAreaRange.aStart)
- break; // while loop, only ranges greater than rAddress follow
}
return bIsBroadcasted;
}
@@ -242,9 +261,8 @@ BOOL ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
if (aBroadcastAreaTbl.empty())
return FALSE;
BOOL bIsBroadcasted = FALSE;
- // Unfortunately we can't search for the first matching entry.
- ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
- while (aIter != aBroadcastAreaTbl.end())
+ for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
ScBroadcastArea* pArea = *aIter;
// A Notify() during broadcast may call EndListeningArea() and thus
@@ -260,8 +278,6 @@ BOOL ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
bIsBroadcasted = TRUE;
}
}
- else if (rRange.aEnd < rAreaRange.aStart)
- break; // while loop, only ranges greater than end address follow
}
return bIsBroadcasted;
}
@@ -271,14 +287,8 @@ void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
{
if (aBroadcastAreaTbl.empty())
return;
- // Searching for areas bound completely within rRange, so it's fine to
- // exclude all upper left corners smaller than the upper left corner of
- // rRange and get a lower bound.
- aTmpSeekBroadcastArea.UpdateRange( ScRange( rRange.aStart));
- // Search for lower bound, inclusive, not less than.
- ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.lower_bound(
- &aTmpSeekBroadcastArea));
- for ( ; aIter != aBroadcastAreaTbl.end(); )
+ for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
const ScRange& rAreaRange = (*aIter)->GetRange();
if (rRange.In( rAreaRange))
@@ -290,12 +300,8 @@ void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
pBASM->RemoveBulkArea( pArea);
delete pArea;
}
- ScBroadcastAreas::iterator aDel( aIter);
- ++aIter;
- aBroadcastAreaTbl.erase( aDel);
+ aBroadcastAreaTbl.erase( aIter++);
}
- else if (rRange.aEnd < rAreaRange.aStart)
- break; // for loop, only ranges greater than end address follow
else
++aIter;
}
@@ -303,8 +309,7 @@ void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
- const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz
- )
+ const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
if (aBroadcastAreaTbl.empty())
return;
@@ -312,39 +317,24 @@ void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
SCCOL nCol1, nCol2, theCol1, theCol2;
SCROW nRow1, nRow2, theRow1, theRow2;
SCTAB nTab1, nTab2, theTab1, theTab2;
- nCol1 = rRange.aStart.Col();
- nRow1 = rRange.aStart.Row();
- nTab1 = rRange.aStart.Tab();
- nCol2 = rRange.aEnd.Col();
- nRow2 = rRange.aEnd.Row();
- nTab2 = rRange.aEnd.Tab();
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
- aIter != aBroadcastAreaTbl.end(); )
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
{
ScBroadcastArea* pArea = *aIter;
- ScBroadcastAreas::iterator aDel( aIter);
- ++aIter;
if ( pArea->IsInUpdateChain() )
{
- aBroadcastAreaTbl.erase( aDel);
+ aBroadcastAreaTbl.erase( aIter++);
pArea->DecRef();
}
else
{
- const ScAddress& rAdr1 = pArea->GetStart();
- theCol1 = rAdr1.Col();
- theRow1 = rAdr1.Row();
- theTab1 = rAdr1.Tab();
- const ScAddress& rAdr2 = pArea->GetEnd();
- theCol2 = rAdr2.Col();
- theRow2 = rAdr2.Row();
- theTab2 = rAdr2.Tab();
+ 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 )
- )
+ theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
{
- aBroadcastAreaTbl.erase( aDel);
+ aBroadcastAreaTbl.erase( aIter++);
pArea->DecRef();
if (pBASM->IsInBulkBroadcast())
pBASM->RemoveBulkArea( pArea);
@@ -356,6 +346,8 @@ void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
pBASM->SetUpdateChain( pArea );
pBASM->SetEOUpdateChain( pArea );
}
+ else
+ ++aIter;
}
}
}
@@ -363,13 +355,48 @@ void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
{
- aBroadcastAreaTbl.insert( pArea );
- pArea->IncRef();
+ ::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* [ BCA_SLOTS ];
+ memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * BCA_SLOTS );
+}
+
+
+ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
+{
+ for ( ScBroadcastAreaSlot** pp = ppSlots + BCA_SLOTS; --pp >= ppSlots; /* nothing */ )
+ {
+ if (*pp)
+ delete *pp;
+ }
+ delete [] ppSlots;
+}
+
+
ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
ScDocument* pDocument ) :
pBCAlways( NULL ),
@@ -378,20 +405,16 @@ ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
pEOUpdateChain( NULL ),
nInBulkBroadcast( 0 )
{
- ppSlots = new ScBroadcastAreaSlot* [ BCA_SLOTS ];
- memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * BCA_SLOTS );
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin());
+ iTab != aTableSlotsMap.end(); ++iTab)
+ {
+ delete (*iTab).second;
+ }
}
ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
{
- for ( ScBroadcastAreaSlot** pp = ppSlots + BCA_SLOTS; --pp >= ppSlots; )
- {
- if ( *pp )
- delete *pp;
- }
- delete[] ppSlots;
-
delete pBCAlways;
}
@@ -414,8 +437,7 @@ inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
- SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak
- ) const
+ SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
{
rStart = ComputeSlotOffset( rRange.aStart );
rEnd = ComputeSlotOffset( rRange.aEnd );
@@ -426,8 +448,7 @@ void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
- SvtListener* pListener
- )
+ SvtListener* pListener )
{
if ( rRange == BCA_LISTEN_ALWAYS )
{
@@ -437,29 +458,48 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
}
else
{
- SCSIZE nStart, nEnd, nRowBreak;
- ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
- SCSIZE nOff = nStart;
- SCSIZE nBreak = nOff + nRowBreak;
- ScBroadcastAreaSlot** pp = ppSlots + nOff;
- ScBroadcastArea* pArea = NULL;
- while ( nOff <= nEnd )
+ bool bDone = false;
+ for (SCTAB nTab = rRange.aStart.Tab();
+ !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
{
- if ( !*pp )
- *pp = new ScBroadcastAreaSlot( pDoc, this );
- // the first call creates the ScBroadcastArea
- (*pp)->StartListeningArea( rRange, pListener, pArea );
- if ( nOff < nBreak )
+ 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 )
{
- ++nOff;
- ++pp;
- }
- else
- {
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ 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);
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
}
}
}
@@ -467,8 +507,7 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
- SvtListener* pListener
- )
+ SvtListener* pListener )
{
if ( rRange == BCA_LISTEN_ALWAYS )
{
@@ -485,27 +524,47 @@ void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
}
else
{
- SCSIZE nStart, nEnd, nRowBreak;
- ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
- SCSIZE nOff = nStart;
- SCSIZE nBreak = nOff + nRowBreak;
- ScBroadcastAreaSlot** pp = ppSlots + nOff;
- ScBroadcastArea* pArea = NULL;
- while ( nOff <= nEnd )
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
- if ( *pp )
- (*pp)->EndListeningArea( rRange, pListener, pArea );
- if ( nOff < nBreak )
+ 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 == BCA_SLOTS-1)
{
- ++nOff;
- ++pp;
+ // 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
{
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->EndListeningArea( rRange, pListener, pArea );
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
+ }
}
}
}
@@ -527,7 +586,11 @@ BOOL ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
}
else
{
- ScBroadcastAreaSlot* pSlot = ppSlots[ ComputeSlotOffset( rAddress ) ];
+ TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
+ if (iTab == aTableSlotsMap.end())
+ return FALSE;
+ ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot(
+ ComputeSlotOffset( rAddress));
if ( pSlot )
return pSlot->AreaBroadcast( rHint );
else
@@ -540,26 +603,32 @@ BOOL ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
const ScHint& rHint ) const
{
BOOL bBroadcasted = FALSE;
- SCSIZE nStart, nEnd, nRowBreak;
- ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
- SCSIZE nOff = nStart;
- SCSIZE nBreak = nOff + nRowBreak;
- ScBroadcastAreaSlot** pp = ppSlots + nOff;
- while ( nOff <= nEnd )
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
- if ( *pp )
- bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
- if ( nOff < nBreak )
- {
- ++nOff;
- ++pp;
- }
- else
+ 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 )
{
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ if ( *pp )
+ bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint );
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
}
}
return bBroadcasted;
@@ -567,126 +636,213 @@ BOOL ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange,
void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
- const ScRange& rRange
- )
+ const ScRange& rRange )
{
- SCSIZE nStart, nEnd, nRowBreak;
- ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
- SCSIZE nOff = nStart;
- SCSIZE nBreak = nOff + nRowBreak;
- ScBroadcastAreaSlot** pp = ppSlots + nOff;
- while ( nOff <= nEnd )
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
- if ( *pp )
- (*pp)->DelBroadcastAreasInRange( rRange );
- if ( nOff < nBreak )
+ 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 == BCA_SLOTS-1)
{
- ++nOff;
- ++pp;
+ // 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
{
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->DelBroadcastAreasInRange( rRange );
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
+ }
}
}
}
-// for all affected: remove, chain, update range, insert
+// 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
- )
+ const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
- SCSIZE nStart, nEnd, nRowBreak;
// remove affected and put in chain
- ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
- SCSIZE nOff = nStart;
- SCSIZE nBreak = nOff + nRowBreak;
- ScBroadcastAreaSlot** pp = ppSlots + nOff;
- while ( nOff <= nEnd )
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
{
- if ( *pp )
- (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
- if ( nOff < nBreak )
+ 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 == BCA_SLOTS-1)
{
- ++nOff;
- ++pp;
+ // 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
{
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + 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;
- nCol1 = rRange.aStart.Col();
- nRow1 = rRange.aStart.Row();
- nTab1 = rRange.aStart.Tab();
- nCol2 = rRange.aEnd.Col();
- nRow2 = rRange.aEnd.Row();
- nTab2 = rRange.aEnd.Tab();
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
while ( pUpdateChain )
{
- ScAddress aAdr;
- ScRange aRange;
ScBroadcastArea* pArea = pUpdateChain;
+ ScRange aRange( pArea->GetRange());
pUpdateChain = pArea->GetUpdateChainNext();
// update range
- aAdr = pArea->GetStart();
- theCol1 = aAdr.Col();
- theRow1 = aAdr.Row();
- theTab1 = aAdr.Tab();
- aAdr = pArea->GetEnd();
- theCol2 = aAdr.Col();
- theRow2 = aAdr.Row();
- theTab2 = aAdr.Tab();
+ 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 )
- )
+ theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
{
- aRange = ScRange( ScAddress( theCol1,theRow1,theTab1 ),
- ScAddress( theCol2,theRow2,theTab2 ) );
+ aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
pArea->UpdateRange( aRange );
pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE
}
- // insert in slot
- ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
- nOff = nStart;
- nBreak = nOff + nRowBreak;
- pp = ppSlots + nOff;
- while ( nOff <= nEnd )
+ // insert to slots
+ for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
{
- if ( *pp )
- (*pp)->UpdateInsert( pArea );
- if ( nOff < nBreak )
- {
- ++nOff;
- ++pp;
- }
- else
+ 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 )
{
- nStart += BCA_SLOTS_ROW;
- nOff = nStart;
- pp = ppSlots + nOff;
- nBreak = nOff + nRowBreak;
+ if (!*pp)
+ *pp = new ScBroadcastAreaSlot( pDoc, this );
+ (*pp)->UpdateInsert( pArea );
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += BCA_SLOTS_ROW;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
}
}
// unchain
pArea->SetUpdateChainNext( NULL );
pArea->SetInUpdateChain( FALSE );
+
+ // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
+ // already executed in UpdateRemove().
+ if (!pArea->GetRef())
+ delete pArea;
}
pEOUpdateChain = NULL;
}
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index c58e054fb62a..8353c0cee8c3 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -325,6 +325,10 @@ BOOL ScDocument::InsertTab( SCTAB nPos, const String& rName,
pTab[i] = pTab[i - 1];
pTab[nPos] = new ScTable(this, nPos, rName);
++nMaxTableNumber;
+ // UpdateBroadcastAreas must be called between UpdateInsertTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
for (i = 0; i <= MAXTAB; i++)
if (pTab[i])
pTab[i]->UpdateCompile();
@@ -419,6 +423,10 @@ BOOL ScDocument::DeleteTab( SCTAB nTab, ScDocument* pRefUndoDoc )
pTab[i - 1] = pTab[i];
pTab[nTabCount - 1] = NULL;
--nMaxTableNumber;
+ // UpdateBroadcastAreas must be called between UpdateDeleteTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
for (i = 0; i <= MAXTAB; i++)
if (pTab[i])
pTab[i]->UpdateCompile();
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
index 387535a770db..ef2f77b55684 100644
--- a/sc/source/core/inc/bcaslot.hxx
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -40,7 +40,10 @@
#include "global.hxx"
#include "brdcst.hxx"
-/// Used in a Unique Sorted Associative Container
+/**
+ Used in a Unique Associative Container.
+ */
+
class ScBroadcastArea
{
private:
@@ -62,32 +65,41 @@ public:
inline const ScAddress& GetStart() const { return aRange.aStart; }
inline const ScAddress& GetEnd() const { return aRange.aEnd; }
inline void IncRef() { ++nRefCount; }
- inline ULONG DecRef() { return --nRefCount; }
+ inline ULONG DecRef() { return nRefCount ? --nRefCount : 0; }
+ inline ULONG GetRef() { return nRefCount; }
inline ScBroadcastArea* GetUpdateChainNext() const { return pUpdateChainNext; }
inline void SetUpdateChainNext( ScBroadcastArea* p ) { pUpdateChainNext = p; }
inline BOOL IsInUpdateChain() const { return bInUpdateChain; }
inline void SetInUpdateChain( BOOL b ) { bInUpdateChain = b; }
- /** Strict weak sorting order, upper left corner and then lower right */
- inline bool operator<( const ScBroadcastArea& rArea ) const;
+ /** Equalness of this or range. */
+ inline bool operator==( const ScBroadcastArea & rArea ) const;
};
-inline bool ScBroadcastArea::operator<( const ScBroadcastArea& rArea ) const
+inline bool ScBroadcastArea::operator==( const ScBroadcastArea & rArea ) const
{
- return aRange < rArea.aRange;
+ return aRange == rArea.aRange;
}
//=============================================================================
-struct ScBroadcastAreaSort
+struct ScBroadcastAreaHash
+{
+ size_t operator()( const ScBroadcastArea* p ) const
+ {
+ return p->GetRange().hashArea();
+ }
+};
+
+struct ScBroadcastAreaEqual
{
bool operator()( const ScBroadcastArea* p1, const ScBroadcastArea* p2) const
{
- return *p1 < *p2;
+ return *p1 == *p2;
}
};
-typedef ::std::set< ScBroadcastArea*, ScBroadcastAreaSort > ScBroadcastAreas;
+typedef ::std::hash_set< ScBroadcastArea*, ScBroadcastAreaHash, ScBroadcastAreaEqual > ScBroadcastAreas;
//=============================================================================
@@ -125,20 +137,55 @@ private:
ScBroadcastAreas::iterator FindBroadcastArea( const ScRange& rRange ) const;
+ /**
+ More hypothetical (memory would probably be doomed anyway) check
+ whether there would be an overflow when adding an area, setting the
+ proper state if so.
+
+ @return TRUE if a HardRecalcState is effective and area is not to be
+ added.
+ */
+ bool CheckHardRecalcStateCondition() const;
+
public:
ScBroadcastAreaSlot( ScDocument* pDoc,
ScBroadcastAreaSlotMachine* pBASM );
~ScBroadcastAreaSlot();
const ScBroadcastAreas& GetBroadcastAreas() const
{ return aBroadcastAreaTbl; }
- void StartListeningArea( const ScRange& rRange,
+
+ /**
+ Only here new ScBroadcastArea objects are created, prevention of dupes.
+
+ @param rpArea
+ If NULL, a new ScBroadcastArea is created and assigned ton the
+ reference if a matching area wasn't found. If a matching area was
+ found, that is assigned. In any case, the SvtListener is added to
+ the broadcaster.
+
+ If not NULL then no listeners are startet, only the area is
+ inserted and the reference count incremented. Effectively the same
+ as InsertListeningArea(), so use that instead.
+
+ @return
+ TRUE if rpArea passed was NULL and ScBroadcastArea is newly
+ created.
+ */
+ bool StartListeningArea( const ScRange& rRange,
SvtListener* pListener,
ScBroadcastArea*& rpArea );
+
+ /**
+ Insert a ScBroadcastArea obtained via StartListeningArea() to
+ subsequent slots.
+ */
+ void InsertListeningArea( ScBroadcastArea* pArea );
+
void EndListeningArea( const ScRange& rRange,
SvtListener* pListener,
ScBroadcastArea*& rpArea );
BOOL AreaBroadcast( const ScHint& rHint ) const;
- // return: mindestens ein Broadcast gewesen
+ /// @return TRUE if at least one broadcast occurred.
BOOL AreaBroadcastInRange( const ScRange& rRange,
const ScHint& rHint ) const;
void DelBroadcastAreasInRange( const ScRange& rRange );
@@ -149,26 +196,61 @@ public:
};
-/*
- BroadcastAreaSlots und deren Verwaltung, einmal je Dokument
-
- +---+---+
- | 0 | 2 | Anordnung Cols/Rows
- +---+---+
- | 1 | 3 |
- +---+---+
+/**
+ BroadcastAreaSlots and their management, once per document.
*/
class ScBroadcastAreaSlotMachine
{
private:
- ScBroadcastAreasBulk aBulkBroadcastAreas;
- ScBroadcastAreaSlot** ppSlots;
- SvtBroadcaster* pBCAlways; // for the RC_ALWAYS special range
- ScDocument* pDoc;
- ScBroadcastArea* pUpdateChain;
- ScBroadcastArea* pEOUpdateChain;
- ULONG nInBulkBroadcast;
+
+ /**
+ Slot offset arrangement of columns and rows, once per sheet.
+
+ +---+---+
+ | 0 | 3 |
+ +---+---+
+ | 1 | 4 |
+ +---+---+
+ | 2 | 5 |
+ +---+---+
+ */
+
+ /* TODO: When going for 1M rows this will definitely need some change, or
+ * with lots of referred sheets even the reservation of NULL pointers would
+ * be a memory hog. */
+
+ class TableSlots
+ {
+ public:
+ TableSlots();
+ ~TableSlots();
+ inline ScBroadcastAreaSlot** getSlots() { return ppSlots; }
+
+ /**
+ Obtain slot pointer, no check on validity! It is assumed that
+ all calls are made with the result of ComputeSlotOfsset()
+ */
+ inline ScBroadcastAreaSlot* getAreaSlot( SCSIZE nOff ) { return *(ppSlots + nOff); }
+
+ private:
+ ScBroadcastAreaSlot** ppSlots;
+
+ // prevent usage
+ TableSlots( const TableSlots& );
+ TableSlots& operator=( const TableSlots& );
+ };
+
+ typedef ::std::map< SCTAB, TableSlots* > TableSlotsMap;
+
+private:
+ ScBroadcastAreasBulk aBulkBroadcastAreas;
+ TableSlotsMap aTableSlotsMap;
+ SvtBroadcaster *pBCAlways; // for the RC_ALWAYS special range
+ ScDocument *pDoc;
+ ScBroadcastArea *pUpdateChain;
+ ScBroadcastArea *pEOUpdateChain;
+ ULONG nInBulkBroadcast;
inline SCSIZE ComputeSlotOffset( const ScAddress& rAddress ) const;
void ComputeAreaPoints( const ScRange& rRange,
@@ -183,7 +265,7 @@ public:
void EndListeningArea( const ScRange& rRange,
SvtListener* pListener );
BOOL AreaBroadcast( const ScHint& rHint ) const;
- // return: mindestens ein Broadcast gewesen
+ // return: at least one broadcast occurred
BOOL AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) const;
void DelBroadcastAreasInRange( const ScRange& rRange );
void UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 90029b6ac0c1..e148f3ab8516 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -3443,6 +3443,23 @@ void ScCompiler::AutoCorrectParsedSymbol()
}
}
+inline bool lcl_UpperAsciiOrI18n( String& rUpper, const String& rOrg, FormulaGrammar::Grammar eGrammar )
+{
+ if (FormulaGrammar::isODFF( eGrammar ))
+ {
+ // ODFF has a defined set of English function names, avoid i18n
+ // overhead.
+ rUpper = rOrg;
+ rUpper.ToUpperAscii();
+ return true;
+ }
+ else
+ {
+ rUpper = ScGlobal::pCharClass->upper( rOrg );
+ return false;
+ }
+}
+
BOOL ScCompiler::NextNewToken( bool bInArray )
{
bool bAllowBooleans = bInArray;
@@ -3453,115 +3470,154 @@ BOOL ScCompiler::NextNewToken( bool bInArray )
rtl::OUStringToOString( cSymbol, RTL_TEXTENCODING_UTF8 ).getStr(), nSpaces );
#endif
- ScRawToken aToken;
- if( cSymbol[0] )
+ if (!cSymbol[0])
+ return false;
+
+ if( nSpaces )
{
- if( nSpaces )
+ ScRawToken aToken;
+ aToken.SetOpCode( ocSpaces );
+ aToken.sbyte.cByte = (BYTE) ( nSpaces > 255 ? 255 : nSpaces );
+ if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
{
- aToken.SetOpCode( ocSpaces );
- aToken.sbyte.cByte = (BYTE) ( nSpaces > 255 ? 255 : nSpaces );
- if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
- {
- SetError(errCodeOverflow); return FALSE;
- }
+ SetError(errCodeOverflow);
+ return false;
}
- // Short cut for references when reading ODF to speedup things.
- if (mnPredetectedReference)
+ }
+
+ // Short cut for references when reading ODF to speedup things.
+ if (mnPredetectedReference)
+ {
+ String aStr( cSymbol);
+ if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
{
- String aStr( cSymbol);
- if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
- {
- /* TODO: it would be nice to generate a #REF! error here, which
- * would need an ocBad token with additional error value.
- * FormulaErrorToken wouldn't do because we want to preserve the
- * original string containing partial valid address
- * information. */
- aToken.SetString( aStr.GetBuffer() );
- aToken.NewOpCode( ocBad );
- pRawToken = aToken.Clone();
- }
- return TRUE;
- }
- if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
- !bAutoCorrect )
- { // #101100# special case to speed up broken [$]#REF documents
- /* FIXME: ISERROR(#REF!) would be valid and TRUE and the formula to
- * be processed as usual. That would need some special treatment,
- * also in NextSymbol() because of possible combinations of
- * #REF!.#REF!#REF! parts. In case of reading ODF that is all
- * handled by IsPredetectedReference(), this case here remains for
- * manual/API input. */
- String aBad( aFormula.Copy( nSrcPos-1 ) );
- eLastOp = pArr->AddBad( aBad )->GetOpCode();
- return FALSE;
+ /* TODO: it would be nice to generate a #REF! error here, which
+ * would need an ocBad token with additional error value.
+ * FormulaErrorToken wouldn't do because we want to preserve the
+ * original string containing partial valid address
+ * information. */
+ ScRawToken aToken;
+ aToken.SetString( aStr.GetBuffer() );
+ aToken.NewOpCode( ocBad );
+ pRawToken = aToken.Clone();
}
- if( !IsString() )
- {
- BOOL bMayBeFuncName;
- if ( cSymbol[0] < 128 )
- bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] );
- else
- {
- String aTmpStr( cSymbol[0] );
- bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
- }
- if ( bMayBeFuncName )
- { // a function name must be followed by a parenthesis
- const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
- while( *p == ' ' )
- p++;
- bMayBeFuncName = ( *p == '(' );
- }
- else
- bMayBeFuncName = TRUE; // operators and other opcodes
+ return true;
+ }
+
+ if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
+ !bAutoCorrect )
+ { // #101100# special case to speed up broken [$]#REF documents
+ /* FIXME: ISERROR(#REF!) would be valid and TRUE and the formula to
+ * be processed as usual. That would need some special treatment,
+ * also in NextSymbol() because of possible combinations of
+ * #REF!.#REF!#REF! parts. In case of reading ODF that is all
+ * handled by IsPredetectedReference(), this case here remains for
+ * manual/API input. */
+ String aBad( aFormula.Copy( nSrcPos-1 ) );
+ eLastOp = pArr->AddBad( aBad )->GetOpCode();
+ return false;
+ }
+
+ if( IsString() )
+ return true;
+
+ bool bMayBeFuncName;
+ bool bAsciiNonAlnum; // operators, separators, ...
+ if ( cSymbol[0] < 128 )
+ {
+ bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] );
+ bAsciiNonAlnum = !bMayBeFuncName && !CharClass::isAsciiDigit( cSymbol[0] );
+ }
+ else
+ {
+ String aTmpStr( cSymbol[0] );
+ bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
+ bAsciiNonAlnum = false;
+ }
+ if ( bMayBeFuncName )
+ {
+ // a function name must be followed by a parenthesis
+ const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
+ while( *p == ' ' )
+ p++;
+ bMayBeFuncName = ( *p == '(' );
+ }
- String aOrg( cSymbol ); // preserve file names in IsReference()
- String aUpper( ScGlobal::pCharClass->upper( aOrg ) );
#if 0
- fprintf( stderr, "Token '%s'\n",
- rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() );
+ fprintf( stderr, "Token '%s'\n",
+ rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
- // Column 'DM' ("Deutsche Mark", German currency) couldn't be
- // referred to => IsReference() before IsValue().
- // #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
- // IsReference().
- // IsBoolean before isValue to catch inline bools without the kludge
- // for inline arrays.
- if ( !(bMayBeFuncName && IsOpCode( aUpper, bInArray ))
- && !IsReference( aOrg )
- && !(bAllowBooleans && IsBoolean( aUpper ))
- && !IsValue( aUpper )
- && !IsNamedRange( aUpper )
- && !IsExternalNamedRange(aOrg)
- && !IsDBRange( aUpper )
- && !IsColRowName( aUpper )
- && !(bMayBeFuncName && IsMacro( aUpper ))
- && !(bMayBeFuncName && IsOpCode2( aUpper )) )
- {
- if ( mbExtendedErrorDetection )
- {
- // set an error and end compilation
- SetError( errNoName );
- return FALSE;
- }
- else
- {
- // Provide single token information and continue. Do not set an
- // error, that would prematurely end compilation. Simple
- // unknown names are handled by the interpreter.
- ScGlobal::pCharClass->toLower( aUpper );
- aToken.SetString( aUpper.GetBuffer() );
- aToken.NewOpCode( ocBad );
- pRawToken = aToken.Clone();
- if ( bAutoCorrect )
- AutoCorrectParsedSymbol();
- }
- }
- }
- return TRUE;
+
+ // #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
+ // IsReference().
+
+ const String aOrg( cSymbol );
+
+ if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray ))
+ return true;
+
+ String aUpper;
+ bool bAsciiUpper = false;
+ if (bMayBeFuncName)
+ {
+ bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
+ if (IsOpCode( aUpper, bInArray ))
+ return true;
}
- else
- return FALSE;
+
+ // Column 'DM' ("Deutsche Mark", German currency) couldn't be
+ // referred => IsReference() before IsValue().
+ // Preserve case of file names in external references.
+ if (IsReference( aOrg ))
+ return true;
+
+ if (!aUpper.Len())
+ bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
+
+ // IsBoolean() before IsValue() to catch inline bools without the kludge
+ // for inline arrays.
+ if (bAllowBooleans && IsBoolean( aUpper ))
+ return true;
+
+ if (IsValue( aUpper ))
+ return true;
+
+ // User defined names and such do need i18n upper also in ODF.
+ if (bAsciiUpper)
+ aUpper = ScGlobal::pCharClass->upper( aOrg );
+
+ if (IsNamedRange( aUpper ))
+ return true;
+ // Preserve case of file names in external references.
+ if (IsExternalNamedRange( aOrg ))
+ return true;
+ if (IsDBRange( aUpper ))
+ return true;
+ if (IsColRowName( aUpper ))
+ return true;
+ if (bMayBeFuncName && IsMacro( aUpper ))
+ return true;
+ if (bMayBeFuncName && IsOpCode2( aUpper ))
+ return true;
+
+ if ( mbExtendedErrorDetection )
+ {
+ // set an error and end compilation
+ SetError( errNoName );
+ return false;
+ }
+
+ // Provide single token information and continue. Do not set an error, that
+ // would prematurely end compilation. Simple unknown names are handled by
+ // the interpreter.
+ ScGlobal::pCharClass->toLower( aUpper );
+ ScRawToken aToken;
+ aToken.SetString( aUpper.GetBuffer() );
+ aToken.NewOpCode( ocBad );
+ pRawToken = aToken.Clone();
+ if ( bAutoCorrect )
+ AutoCorrectParsedSymbol();
+ return true;
}
ScTokenArray* ScCompiler::CompileString( const String& rFormula )
diff --git a/sc/source/filter/xml/XMLExportIterator.cxx b/sc/source/filter/xml/XMLExportIterator.cxx
index c4d585740339..72194c8be11c 100644
--- a/sc/source/filter/xml/XMLExportIterator.cxx
+++ b/sc/source/filter/xml/XMLExportIterator.cxx
@@ -808,11 +808,11 @@ sal_Bool ScMyNotEmptyCellsIterator::GetNext(ScMyCell& aCell, ScFormatRangeStyles
HasAnnotation( aCell );
SetMatrixCellData( aCell );
sal_Bool bIsAutoStyle;
- sal_Bool bRemoveStyleRange((aLastAddress.Row == aCell.aCellAddress.Row) &&
- (aLastAddress.Column + 1 == aCell.aCellAddress.Column));
+ // Ranges before the previous cell are not needed by ExportFormatRanges anymore and can be removed
+ sal_Int32 nRemoveBeforeRow = aLastAddress.Row;
aCell.nStyleIndex = pCellStyles->GetStyleNameIndex(aCell.aCellAddress.Sheet,
aCell.aCellAddress.Column, aCell.aCellAddress.Row,
- bIsAutoStyle, aCell.nValidationIndex, aCell.nNumberFormat, bRemoveStyleRange);
+ bIsAutoStyle, aCell.nValidationIndex, aCell.nNumberFormat, nRemoveBeforeRow);
aLastAddress = aCell.aCellAddress;
aCell.bIsAutoStyle = bIsAutoStyle;
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.cxx b/sc/source/filter/xml/XMLStylesExportHelper.cxx
index 9abb3339d01d..6482fe571b62 100644
--- a/sc/source/filter/xml/XMLStylesExportHelper.cxx
+++ b/sc/source/filter/xml/XMLStylesExportHelper.cxx
@@ -944,7 +944,7 @@ sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable,
}
sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
- sal_Bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Bool bRemoveRange)
+ sal_Bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow)
{
DBG_ASSERT(static_cast<size_t>(nTable) < aTables.size(), "wrong table");
ScMyFormatRangeAddresses* pFormatRanges(aTables[nTable]);
@@ -977,7 +977,7 @@ sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable, const s
}
else
{
- if (bRemoveRange && (*aItr).aRangeAddress.EndRow < nRow)
+ if ((*aItr).aRangeAddress.EndRow < nRemoveBeforeRow)
aItr = pFormatRanges->erase(aItr);
else
++aItr;
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.hxx b/sc/source/filter/xml/XMLStylesExportHelper.hxx
index 25fc9e040995..a9e42afcd585 100644
--- a/sc/source/filter/xml/XMLStylesExportHelper.hxx
+++ b/sc/source/filter/xml/XMLStylesExportHelper.hxx
@@ -224,7 +224,7 @@ public:
sal_Bool& bIsAutoStyle) const;
// deletes not necessary ranges if wanted
sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
- sal_Bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Bool bRemoveRange = sal_True );
+ sal_Bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow);
void GetFormatRanges(const sal_Int32 nStartColumn, const sal_Int32 nEndColumn, const sal_Int32 nRow,
const sal_Int32 nTable, ScRowFormatRanges* pFormatRanges);
void AddRangeStyleName(const com::sun::star::table::CellRangeAddress aCellRangeAddress, const sal_Int32 nStringIndex,
diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx
index c84d4a905283..7d95e92c2891 100644
--- a/sc/source/filter/xml/xmlexprt.cxx
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -68,6 +68,7 @@
#include "convuno.hxx"
#include "postit.hxx"
#include "externalrefmgr.hxx"
+#include "editutil.hxx"
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlnmspe.hxx>
@@ -180,6 +181,35 @@ OUString lcl_RangeSequenceToString(
}
return aResult.makeStringAndClear();
}
+
+OUString lcl_GetRawString( ScDocument* pDoc, const ScAddress& rPos )
+{
+ // return text/edit cell string content, with line feeds in edit cells
+
+ String aVal; // document uses tools-strings
+ if (pDoc)
+ {
+ ScBaseCell* pCell = pDoc->GetCell( rPos );
+ if (pCell)
+ {
+ CellType eType = pCell->GetCellType();
+ if ( eType == CELLTYPE_STRING )
+ static_cast<ScStringCell*>(pCell)->GetString(aVal); // string cell: content
+ else if ( eType == CELLTYPE_EDIT )
+ {
+ // edit cell: text with line breaks
+ const EditTextObject* pData = static_cast<ScEditCell*>(pCell)->GetData();
+ if (pData)
+ {
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText( *pData );
+ aVal = rEngine.GetText( LINEEND_LF );
+ }
+ }
+ }
+ }
+ return aVal;
+}
} // anonymous namespace
//----------------------------------------------------------------------------
@@ -2335,9 +2365,7 @@ void ScXMLExport::WriteCell (ScMyCell& aCell)
{
if (GetCellText(aCell, aCellPos))
{
- rtl::OUString sFormula(ScCellObj::GetInputString_Impl(pDoc, aCellPos, TRUE));
- if (sFormula[0] == '\'')
- sFormula = sFormula.copy(1);
+ rtl::OUString sFormula(lcl_GetRawString(pDoc, aCellPos));
GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
sFormula, aCell.sStringValue, sal_True, sal_True);
}
@@ -3002,7 +3030,7 @@ sal_Bool ScXMLExport::IsCellEqual (ScMyCell& aCell1, ScMyCell& aCell2)
if (GetCellText(aCell1, aCellPos1) && GetCellText(aCell2, aCellPos2))
{
bIsEqual = (aCell1.sStringValue == aCell2.sStringValue) &&
- (ScCellObj::GetInputString_Impl(pDoc, aCellPos1, TRUE) == ScCellObj::GetInputString_Impl(pDoc, aCellPos2, TRUE));
+ (lcl_GetRawString(pDoc, aCellPos1) == lcl_GetRawString(pDoc, aCellPos2));
}
else
bIsEqual = sal_False;
diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx
index 9e6eb0cb0c39..c4f51d0eb65f 100644
--- a/sc/source/ui/unoobj/cellsuno.cxx
+++ b/sc/source/ui/unoobj/cellsuno.cxx
@@ -6218,11 +6218,6 @@ String ScCellObj::GetInputString_Impl(BOOL bEnglish) const // fuer getFormu
return String();
}
-String ScCellObj::GetInputString_Impl(ScDocument* pDoc, const ScAddress& aPos, BOOL bEnglish) // fuer getFormula / FormulaLocal
-{
- return lcl_GetInputString( pDoc, aPos, bEnglish );
-}
-
String ScCellObj::GetOutputString_Impl(ScDocument* pDoc, const ScAddress& aCellPos)
{
String aVal;
@@ -9608,90 +9603,124 @@ struct ScPatternHashCode
}
};
+// Hash map to find a range by its start row
+typedef ::std::hash_map< SCROW, ScRange > ScRowRangeHashMap;
+
+typedef ::std::vector<ScRange> ScRangeVector;
+
// Hash map entry.
-// Keeps track of column positions and calls Join only for ranges between empty columns.
+// The Join method depends on the column-wise order of ScAttrRectIterator
class ScUniqueFormatsEntry
{
- ScRangeListRef aCompletedRanges;
- ScRangeListRef aJoinedRanges;
- SCCOL nLastColumn;
- SCCOL nLastStart;
+ enum EntryState { STATE_EMPTY, STATE_SINGLE, STATE_COMPLEX };
- void MoveToCompleted();
+ EntryState eState;
+ ScRange aSingleRange;
+ ScRowRangeHashMap aJoinedRanges; // "active" ranges to be merged
+ ScRangeVector aCompletedRanges; // ranges that will no longer be touched
+ ScRangeListRef aReturnRanges; // result as ScRangeList for further use
public:
- ScUniqueFormatsEntry() : nLastColumn(0), nLastStart(0) {}
+ ScUniqueFormatsEntry() : eState( STATE_EMPTY ) {}
ScUniqueFormatsEntry( const ScUniqueFormatsEntry& r ) :
- aCompletedRanges( r.aCompletedRanges ),
+ eState( r.eState ),
+ aSingleRange( r.aSingleRange ),
aJoinedRanges( r.aJoinedRanges ),
- nLastColumn( r.nLastColumn ),
- nLastStart( r.nLastStart ) {}
+ aCompletedRanges( r.aCompletedRanges ),
+ aReturnRanges( r.aReturnRanges ) {}
~ScUniqueFormatsEntry() {}
- void Join( const ScRange& rRange );
+ void Join( const ScRange& rNewRange );
const ScRangeList& GetRanges();
- void Clear()
- {
- aCompletedRanges.Clear();
- aJoinedRanges.Clear();
- }
+ void Clear() { aReturnRanges.Clear(); } // aJoinedRanges and aCompletedRanges are cleared in GetRanges
};
-void ScUniqueFormatsEntry::MoveToCompleted()
+void ScUniqueFormatsEntry::Join( const ScRange& rNewRange )
{
- if ( !aCompletedRanges.Is() )
- aCompletedRanges = new ScRangeList;
+ // Special-case handling for single range
- if ( aJoinedRanges.Is() )
+ if ( eState == STATE_EMPTY )
{
- for ( const ScRange* pRange = aJoinedRanges->First(); pRange; pRange = aJoinedRanges->Next() )
- aCompletedRanges->Append( *pRange );
- aJoinedRanges->RemoveAll();
+ aSingleRange = rNewRange;
+ eState = STATE_SINGLE;
+ return;
}
-}
-
-void ScUniqueFormatsEntry::Join( const ScRange& rRange )
-{
- if ( !aJoinedRanges.Is() )
+ if ( eState == STATE_SINGLE )
{
- // first range - store and initialize columns
- aJoinedRanges = new ScRangeList;
- aJoinedRanges->Append( rRange );
- nLastColumn = rRange.aEnd.Col();
- nLastStart = rRange.aStart.Col();
+ if ( aSingleRange.aStart.Row() == rNewRange.aStart.Row() &&
+ aSingleRange.aEnd.Row() == rNewRange.aEnd.Row() &&
+ aSingleRange.aEnd.Col() + 1 == rNewRange.aStart.Col() )
+ {
+ aSingleRange.aEnd.SetCol( rNewRange.aEnd.Col() );
+ return; // still a single range
+ }
+
+ SCROW nSingleRow = aSingleRange.aStart.Row();
+ aJoinedRanges.insert( ScRowRangeHashMap::value_type( nSingleRow, aSingleRange ) );
+ eState = STATE_COMPLEX;
+ // continue normally
}
- else
- {
- // This works only if the start columns never go back
- DBG_ASSERT( rRange.aStart.Col() >= nLastStart, "wrong column order in ScUniqueFormatsEntry" );
- if ( rRange.aStart.Col() <= nLastColumn + 1 )
+ // This is called in the order of ScAttrRectIterator results.
+ // rNewRange can only be joined with an existing entry if it's the same rows, starting in the next column.
+ // If the old entry for the start row extends to a different end row, or ends in a different column, it
+ // can be moved to aCompletedRanges because it can't be joined with following iterator results.
+ // Everything happens within one sheet, so Tab can be ignored.
+
+ SCROW nStartRow = rNewRange.aStart.Row();
+ ScRowRangeHashMap::iterator aIter( aJoinedRanges.find( nStartRow ) ); // find the active entry for the start row
+ if ( aIter != aJoinedRanges.end() )
+ {
+ ScRange& rOldRange = aIter->second;
+ if ( rOldRange.aEnd.Row() == rNewRange.aEnd.Row() &&
+ rOldRange.aEnd.Col() + 1 == rNewRange.aStart.Col() )
{
- // The new range may touch one of the existing ranges, have to use Join.
- aJoinedRanges->Join( rRange );
+ // extend existing range
+ rOldRange.aEnd.SetCol( rNewRange.aEnd.Col() );
}
else
{
- // The new range starts right of all existing ranges.
- // The existing ranges can be ignored for all future Join calls.
-
- MoveToCompleted(); // aJoinedRanges is emptied
- aJoinedRanges->Append( rRange );
+ // move old range to aCompletedRanges, keep rNewRange for joining
+ aCompletedRanges.push_back( rOldRange );
+ rOldRange = rNewRange; // replace in hash map
}
-
- if ( rRange.aEnd.Col() > nLastColumn )
- nLastColumn = rRange.aEnd.Col();
- nLastStart = rRange.aStart.Col();
+ }
+ else
+ {
+ // keep rNewRange for joining
+ aJoinedRanges.insert( ScRowRangeHashMap::value_type( nStartRow, rNewRange ) );
}
}
const ScRangeList& ScUniqueFormatsEntry::GetRanges()
{
- if ( aJoinedRanges.Is() && !aCompletedRanges.Is() )
- return *aJoinedRanges;
+ if ( eState == STATE_SINGLE )
+ {
+ aReturnRanges = new ScRangeList;
+ aReturnRanges->Append( aSingleRange );
+ return *aReturnRanges;
+ }
+
+ // move remaining entries from aJoinedRanges to aCompletedRanges
+
+ ScRowRangeHashMap::const_iterator aJoinedEnd = aJoinedRanges.end();
+ for ( ScRowRangeHashMap::const_iterator aJoinedIter = aJoinedRanges.begin(); aJoinedIter != aJoinedEnd; ++aJoinedIter )
+ aCompletedRanges.push_back( aJoinedIter->second );
+ aJoinedRanges.clear();
+
+ // sort all ranges for a predictable API result
+
+ std::sort( aCompletedRanges.begin(), aCompletedRanges.end() );
+
+ // fill and return ScRangeList
+
+ aReturnRanges = new ScRangeList;
+ ScRangeVector::const_iterator aCompEnd( aCompletedRanges.end() );
+ for ( ScRangeVector::const_iterator aCompIter( aCompletedRanges.begin() ); aCompIter != aCompEnd; ++aCompIter )
+ aReturnRanges->Append( *aCompIter );
+ aCompletedRanges.clear();
- MoveToCompleted(); // aCompletedRanges is always set after this
- return *aCompletedRanges;
+ return *aReturnRanges;
}
typedef ::std::hash_map< const ScPatternAttr*, ScUniqueFormatsEntry, ScPatternHashCode > ScUniqueFormatsHashMap;