diff options
-rw-r--r-- | sc/inc/address.hxx | 39 | ||||
-rw-r--r-- | sc/inc/cellsuno.hxx | 1 | ||||
-rw-r--r-- | sc/source/core/data/bcaslot.cxx | 592 | ||||
-rw-r--r-- | sc/source/core/data/document.cxx | 8 | ||||
-rw-r--r-- | sc/source/core/inc/bcaslot.hxx | 136 | ||||
-rw-r--r-- | sc/source/core/tool/compiler.cxx | 256 | ||||
-rw-r--r-- | sc/source/filter/xml/XMLExportIterator.cxx | 6 | ||||
-rw-r--r-- | sc/source/filter/xml/XMLStylesExportHelper.cxx | 4 | ||||
-rw-r--r-- | sc/source/filter/xml/XMLStylesExportHelper.hxx | 2 | ||||
-rw-r--r-- | sc/source/filter/xml/xmlexprt.cxx | 36 | ||||
-rw-r--r-- | sc/source/ui/unoobj/cellsuno.cxx | 145 |
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; |