[ooo-build-commit] Branch 'ooo/master' - sc/inc sc/source

Jan Holesovsky kendy at kemper.freedesktop.org
Tue Jun 2 17:09:56 PDT 2009


 sc/inc/address.hxx                             |   39 +
 sc/inc/cellsuno.hxx                            |    1 
 sc/source/core/data/bcaslot.cxx                |  596 +++++++++++++++----------
 sc/source/core/data/document.cxx               |    8 
 sc/source/core/inc/bcaslot.hxx                 |  136 ++++-
 sc/source/core/tool/compiler.cxx               |  256 ++++++----
 sc/source/filter/xml/XMLExportIterator.cxx     |    6 
 sc/source/filter/xml/XMLStylesExportHelper.cxx |    4 
 sc/source/filter/xml/XMLStylesExportHelper.hxx |    2 
 sc/source/filter/xml/xmlexprt.cxx              |   36 +
 sc/source/ui/unoobj/cellsuno.cxx               |  145 +++---
 11 files changed, 811 insertions(+), 418 deletions(-)

New commits:
commit 7c7377dc78e2b722c069d46d01f192aed5606723
Author: Kurt Zenker <kz at openoffice.org>
Date:   Tue Jun 2 11:23:01 2009 +0000

    CWS-TOOLING: integrate CWS calcperf04
    2009-04-23 12:47:25 +0200 er  r271155 : CWS-TOOLING: rebase CWS calcperf04 to trunk at 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

diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx
index 2e81edd..c623c4f 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 377e790..ababb5a 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 8c46299..e0c877d 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 )
@@ -170,7 +191,7 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
         rpArea = *aIter;
         pListener->EndListening( rpArea->GetBroadcaster() );
         if ( !rpArea->GetBroadcaster().HasListeners() )
-        {	// if nobody is listening we can dispose it
+        {   // if nobody is listening we can dispose it
             aBroadcastAreaTbl.erase( aIter);
             if ( !rpArea->DecRef() )
             {
@@ -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
+            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 6baaf30..094bad7 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 f697799..5c24873 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 8af7c1d..d07787f 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 f11ce68..79f88f9 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 6d7ea02..e6f52cd 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 1bc7084..9af6de0 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 f0b631a..172e0ae 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 d1631ad..617b383 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 getFormula /
     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;


More information about the ooo-build-commit mailing list