diff options
author | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-11-12 22:18:49 -0500 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-11-18 08:31:55 -0500 |
commit | 2030b9ac6c68ba6f15b0283e0b4e57ae49bd67b0 (patch) | |
tree | dc7ad2b2f890ea0ac614d7820c2e655c84327c3f /sc | |
parent | 192f6a41444b62feae03185975c120f770e2938f (diff) |
Dedicated listener type tailored for formula groups.
Right now, it's only used when loading an xlsx file. But eventually
this one should be used everywhere.
Change-Id: I216c3a9a33c4b8040e8284d59299e0637471fb50
Diffstat (limited to 'sc')
-rw-r--r-- | sc/Library_sc.mk | 2 | ||||
-rw-r--r-- | sc/inc/bulkdatahint.hxx | 43 | ||||
-rw-r--r-- | sc/inc/document.hxx | 7 | ||||
-rw-r--r-- | sc/inc/formulacell.hxx | 12 | ||||
-rw-r--r-- | sc/inc/grouparealistener.hxx | 73 | ||||
-rw-r--r-- | sc/inc/simplehintids.hxx | 9 | ||||
-rw-r--r-- | sc/source/core/data/bcaslot.cxx | 116 | ||||
-rw-r--r-- | sc/source/core/data/colorscale.cxx | 2 | ||||
-rw-r--r-- | sc/source/core/data/documen2.cxx | 4 | ||||
-rw-r--r-- | sc/source/core/data/documen7.cxx | 17 | ||||
-rw-r--r-- | sc/source/core/data/formulacell.cxx | 52 | ||||
-rw-r--r-- | sc/source/core/data/table3.cxx | 8 | ||||
-rw-r--r-- | sc/source/core/inc/bcaslot.hxx | 57 | ||||
-rw-r--r-- | sc/source/core/tool/bulkdatahint.cxx | 49 | ||||
-rw-r--r-- | sc/source/core/tool/chartlis.cxx | 4 | ||||
-rw-r--r-- | sc/source/core/tool/grouparealistener.cxx | 321 | ||||
-rw-r--r-- | sc/source/core/tool/sharedformula.cxx | 78 | ||||
-rw-r--r-- | sc/source/ui/docshell/macromgr.cxx | 2 | ||||
-rw-r--r-- | sc/source/ui/docshell/servobj.cxx | 6 | ||||
-rw-r--r-- | sc/source/ui/unoobj/cellsuno.cxx | 4 | ||||
-rw-r--r-- | sc/source/ui/unoobj/chart2uno.cxx | 4 |
21 files changed, 787 insertions, 83 deletions
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk index 3f79a124874d..898284e74221 100644 --- a/sc/Library_sc.mk +++ b/sc/Library_sc.mk @@ -196,6 +196,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/core/tool/adiasync \ sc/source/core/tool/appoptio \ sc/source/core/tool/autoform \ + sc/source/core/tool/bulkdatahint \ sc/source/core/tool/brdcst \ sc/source/core/tool/calcconfig \ sc/source/core/tool/callform \ @@ -226,6 +227,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/core/tool/formulaopt \ sc/source/core/tool/formulaparserpool \ sc/source/core/tool/formularesult \ + sc/source/core/tool/grouparealistener \ sc/source/core/tool/hints \ sc/source/core/tool/inputopt \ sc/source/core/tool/interpr1 \ diff --git a/sc/inc/bulkdatahint.hxx b/sc/inc/bulkdatahint.hxx new file mode 100644 index 000000000000..31c13fb9ea2c --- /dev/null +++ b/sc/inc/bulkdatahint.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SC_BULKDATAHINT_HXX +#define INCLUDED_SC_BULKDATAHINT_HXX + +#include <simplehintids.hxx> + +class ScDocument; + +namespace sc { + +class ColumnSpanSet; + +class BulkDataHint : public SfxSimpleHint +{ + struct Impl; + Impl* mpImpl; + + BulkDataHint( const BulkDataHint& ); + BulkDataHint& operator= ( const BulkDataHint& ); + +public: + BulkDataHint( ScDocument& rDoc, const ColumnSpanSet* pSpans ); + virtual ~BulkDataHint(); + + void setSpans( const ColumnSpanSet* pSpans ); + const ColumnSpanSet* getSpans() const; + + ScDocument& getDoc(); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 3124a4cc9826..f766f5497e4a 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1853,10 +1853,9 @@ private: static ScRecursionHelper* CreateRecursionHelperInstance(); public: - void StartListeningArea( const ScRange& rRange, - SvtListener* pListener ); - void EndListeningArea( const ScRange& rRange, - SvtListener* pListener ); + void StartListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener ); + + void EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener ); /** Broadcast wrapper, calls rHint.GetCell()->Broadcast() and AreaBroadcast() and TrackFormulas() and conditional format list diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index b65504e71fcc..67381a01a002 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -23,6 +23,7 @@ #include <set> #include <boost/noncopyable.hpp> +#include <boost/ptr_container/ptr_vector.hpp> #include <formula/tokenarray.hxx> #include <osl/conditn.hxx> @@ -47,16 +48,18 @@ struct RefUpdateInsertTabContext; struct RefUpdateDeleteTabContext; struct RefUpdateMoveTabContext; class CompileFormulaContext; +class FormulaGroupAreaListener; } class ScFormulaCell; class ScProgress; class ScTokenArray; -struct ScSimilarFormulaDelta; struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable { + typedef boost::ptr_vector<sc::FormulaGroupAreaListener> AreaListenersType; + mutable size_t mnRefCount; ScTokenArray* mpCode; @@ -70,6 +73,8 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable sal_uInt8 meCalcState; sal_uInt8 meKernelState; + AreaListenersType maAreaListeners; + ScFormulaCellGroup(); ~ScFormulaCellGroup(); @@ -81,6 +86,11 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable ScDocument& rDoc, const ScAddress& rPos, formula::FormulaGrammar::Grammar eGram ); void compileOpenCLKernel(); + sc::FormulaGroupAreaListener* getAreaListener( + ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed ); + + void endAllGroupListening( ScDocument& rDoc ); + #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION static int snCount; static rtl::Reference<sc::CLBuildKernelThread> sxCompilationThread; diff --git a/sc/inc/grouparealistener.hxx b/sc/inc/grouparealistener.hxx new file mode 100644 index 000000000000..6301023d8093 --- /dev/null +++ b/sc/inc/grouparealistener.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SC_GROUPAREALISTENER_HXX +#define INCLUDED_SC_GROUPAREALISTENER_HXX + +#include <address.hxx> + +#include <svl/listener.hxx> + +class ScFormulaCell; + +namespace sc { + +class BulkDataHint; + +class FormulaGroupAreaListener : public SvtListener +{ + ScRange maRange; + ScFormulaCell** mppTopCell; + SCROW mnGroupLen; + bool mbStartFixed; + bool mbEndFixed; + + FormulaGroupAreaListener(); // disabled + +public: + FormulaGroupAreaListener( + const ScRange& rRange, ScFormulaCell** ppTopCell, SCROW nGroupLen, bool bStartFixed, bool bEndFixed ); + + ScRange getListeningRange() const; + + virtual void Notify( const SfxHint& rHint ) SAL_OVERRIDE; + + /** + * Given the position of a changed cell, collect all formula cells that + * need to be notified of the change. + * + * @param rPos position of changed cell. + * @param rCells all formula cells that need to be notified are put into + * this container. + */ + void collectFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells ) const; + + /** + * Given the row span of changed cells within a single column, collect all + * formula cells that need to be notified of the change. + * + * @param nTab sheet position of the changed cell span. + * @param nCol column position of the changed cell span. + * @param nRow1 top row position of the changed cell span. + * @param nRow2 bottom row position of the changed cell span. + * @param rCells all formula cells that need to be notified are put into + * this container. + */ + void collectFormulaCells( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const; + +private: + void notifyCellChange( const SfxHint& rHint, const ScAddress& rPos ); + void notifyBulkChange( const BulkDataHint& rHint ); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/simplehintids.hxx b/sc/inc/simplehintids.hxx index 34900c07a51b..64d2b08c970b 100644 --- a/sc/inc/simplehintids.hxx +++ b/sc/inc/simplehintids.hxx @@ -12,10 +12,11 @@ #include <svl/smplhint.hxx> -#define SC_HINT_DATACHANGED SFX_HINT_DATACHANGED -#define SC_HINT_TABLEOPDIRTY SFX_HINT_USER00 -#define SC_HINT_CALCALL SFX_HINT_USER01 -#define SC_HINT_REFERENCE SFX_HINT_USER02 +#define SC_HINT_DATACHANGED SFX_HINT_DATACHANGED +#define SC_HINT_TABLEOPDIRTY SFX_HINT_USER00 +#define SC_HINT_CALCALL SFX_HINT_USER01 +#define SC_HINT_REFERENCE SFX_HINT_USER02 +#define SC_HINT_BULK_DATACHANGED SFX_HINT_USER03 #endif diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx index 8229d6846bdb..9af7c19e2304 100644 --- a/sc/source/core/data/bcaslot.cxx +++ b/sc/source/core/data/bcaslot.cxx @@ -27,6 +27,7 @@ #include "docoptio.hxx" #include "refupdat.hxx" #include "table.hxx" +#include <bulkdatahint.hxx> #if DEBUG_AREA_BROADCASTER #include <formulacell.hxx> @@ -107,6 +108,13 @@ static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow) // Ensure that all static variables are initialized with this one call. #endif +ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) : + pUpdateChainNext(NULL), + aRange(rRange), + nRefCount(0), + mbInUpdateChain(false), + mbGroupListening(false) {} + ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument, ScBroadcastAreaSlotMachine* pBASMa ) : aTmpSeekBroadcastArea( ScRange()), @@ -155,8 +163,8 @@ bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const return false; } -bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange, - SvtListener* pListener, ScBroadcastArea*& rpArea ) +bool ScBroadcastAreaSlot::StartListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea ) { bool bNewArea = false; OSL_ENSURE(pListener, "StartListeningArea: pListener Null"); @@ -168,12 +176,13 @@ bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange, // 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)); + ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening)); if (aIter != aBroadcastAreaTbl.end()) rpArea = (*aIter).mpArea; else { rpArea = new ScBroadcastArea( rRange); + rpArea->SetGroupListening(bGroupListening); if (aBroadcastAreaTbl.insert( rpArea).second) { rpArea->IncRef(); @@ -208,13 +217,13 @@ void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea ) // If rpArea != NULL then no listeners are stopped, only the area is removed // and the reference count decremented. -void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange, - SvtListener* pListener, ScBroadcastArea*& rpArea ) +void ScBroadcastAreaSlot::EndListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea ) { OSL_ENSURE(pListener, "EndListeningArea: pListener Null"); if ( !rpArea ) { - ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange)); + ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening)); if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter)) return; rpArea = (*aIter).mpArea; @@ -230,7 +239,7 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange, { if (rpArea && !rpArea->GetBroadcaster().HasListeners()) { - ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange)); + ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening)); if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter)) return; OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch"); @@ -242,9 +251,10 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange, } ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea( - const ScRange& rRange ) const + const ScRange& rRange, bool bGroupListening ) const { aTmpSeekBroadcastArea.UpdateRange( rRange); + aTmpSeekBroadcastArea.SetGroupListening(bGroupListening); return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea); } @@ -270,7 +280,19 @@ bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const ScRange& rAreaRange = pArea->GetRange(); if (rAreaRange.In( rAddress)) { - if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) + if (pArea->IsGroupListening()) + { + if (pBASM->IsInBulkBroadcast()) + { + pBASM->InsertBulkGroupArea(pArea, rAddress); + } + else + { + pArea->GetBroadcaster().Broadcast( rHint); + bIsBroadcasted = true; + } + } + else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) { pArea->GetBroadcaster().Broadcast( rHint); bIsBroadcasted = true; @@ -296,27 +318,46 @@ bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange, bool bInBroadcast = mbInBroadcastIteration; mbInBroadcastIteration = true; bool bIsBroadcasted = false; + + mbHasErasedArea = false; + for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()), aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter ) { - if (isMarkedErased( aIter)) + if (mbHasErasedArea && isMarkedErased( aIter)) continue; + ScBroadcastArea* pArea = (*aIter).mpArea; const ScRange& rAreaRange = pArea->GetRange(); if (rAreaRange.Intersects( rRange )) { - if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) + if (pArea->IsGroupListening()) + { + if (pBASM->IsInBulkBroadcast()) + { + pBASM->InsertBulkGroupArea(pArea, rRange); + } + else + { + pArea->GetBroadcaster().Broadcast( rHint); + bIsBroadcasted = true; + } + } + else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) { pArea->GetBroadcaster().Broadcast( rHint); bIsBroadcasted = true; } } } + mbInBroadcastIteration = bInBroadcast; + // A Notify() during broadcast may call EndListeningArea() and thus dispose // an area if it was the last listener, which would invalidate an iterator // pointing to it, hence the real erase is done afterwards. FinallyEraseAreas(); + return bIsBroadcasted; } @@ -487,6 +528,7 @@ void ScBroadcastAreaSlot::GetAllListeners( { sc::AreaListener aEntry; aEntry.maArea = rAreaRange; + aEntry.mbGroupListening = pArea->IsGroupListening(); aEntry.mpListener = *itLst; rListeners.push_back(aEntry); } @@ -617,8 +659,8 @@ inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot } } -void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, - SvtListener* pListener ) +void ScBroadcastAreaSlotMachine::StartListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener ) { if ( rRange == BCA_LISTEN_ALWAYS ) { @@ -653,7 +695,7 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, // 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)) + if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea)) bDone = true; } else @@ -664,8 +706,8 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, } } -void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, - SvtListener* pListener ) +void ScBroadcastAreaSlotMachine::EndListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener ) { if ( rRange == BCA_LISTEN_ALWAYS ) { @@ -700,7 +742,7 @@ void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, do { if ( *pp ) - (*pp)->EndListeningArea( rRange, pListener, pArea ); + (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea); } while (++pp < pStop); } else @@ -708,7 +750,7 @@ void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, while ( nOff <= nEnd ) { if ( *pp ) - (*pp)->EndListeningArea( rRange, pListener, pArea ); + (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea); ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak); } } @@ -988,7 +1030,10 @@ void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast() if (nInBulkBroadcast > 0) { if (--nInBulkBroadcast == 0) + { ScBroadcastAreasBulk().swap( aBulkBroadcastAreas); + BulkBroadcastGroupAreas(); + } } } @@ -997,6 +1042,41 @@ bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea ) return aBulkBroadcastAreas.insert( pArea ).second; } +void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange ) +{ + BulkGroupAreasType::iterator it = maBulkGroupAreas.lower_bound(pArea); + if (it == maBulkGroupAreas.end() || maBulkGroupAreas.key_comp()(pArea, it->first)) + { + // Insert a new one. + it = maBulkGroupAreas.insert(it, pArea, new sc::ColumnSpanSet(false)); + } + + sc::ColumnSpanSet* pSet = it->second; + assert(pSet); + pSet->set(rRange, true); +} + +void ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas() +{ + if (maBulkGroupAreas.empty()) + return; + + sc::BulkDataHint aHint(*pDoc, NULL); + + BulkGroupAreasType::iterator it = maBulkGroupAreas.begin(), itEnd = maBulkGroupAreas.end(); + for (; it != itEnd; ++it) + { + ScBroadcastArea* pArea = it->first; + const sc::ColumnSpanSet* pSpans = it->second; + assert(pArea); + assert(pSpans); + aHint.setSpans(pSpans); + pArea->GetBroadcaster().Broadcast(aHint); + } + + maBulkGroupAreas.clear(); +} + size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea ) { return aBulkBroadcastAreas.erase( pArea ); diff --git a/sc/source/core/data/colorscale.cxx b/sc/source/core/data/colorscale.cxx index b915e25b13af..9de8919294b2 100644 --- a/sc/source/core/data/colorscale.cxx +++ b/sc/source/core/data/colorscale.cxx @@ -81,7 +81,7 @@ void ScFormulaListener::startListening(ScTokenArray* pArr, const ScAddress& rPos aCell2.SetCol(MAXCOL); } } - mpDoc->StartListeningArea(ScRange(aCell1, aCell2), this); + mpDoc->StartListeningArea(ScRange(aCell1, aCell2), false, this); maCells.push_back(ScRange(aCell1, aCell2)); } } diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx index 1693ad7e54d3..89389251012e 100644 --- a/sc/source/core/data/documen2.cxx +++ b/sc/source/core/data/documen2.cxx @@ -1187,7 +1187,7 @@ void ScDocument::AddLookupCache( ScLookupCache & rCache ) OSL_FAIL( "ScDocument::AddLookupCache: couldn't add to hash map"); } else - StartListeningArea( rCache.getRange(), &rCache); + StartListeningArea( rCache.getRange(), false, &rCache); } void ScDocument::RemoveLookupCache( ScLookupCache & rCache ) @@ -1202,7 +1202,7 @@ void ScDocument::RemoveLookupCache( ScLookupCache & rCache ) { ScLookupCache* pCache = (*it).second; pLookupCacheMapImpl->aCacheMap.erase( it); - EndListeningArea( pCache->getRange(), &rCache); + EndListeningArea( pCache->getRange(), false, &rCache); } } diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx index af09c83ce084..5ea4ce59f338 100644 --- a/sc/source/core/data/documen7.cxx +++ b/sc/source/core/data/documen7.cxx @@ -45,20 +45,17 @@ extern const ScFormulaCell* pLastFormulaTreeTop; // cellform.cxx Err527 WorkA // STATIC DATA ----------------------------------------------------------- -void ScDocument::StartListeningArea( const ScRange& rRange, - SvtListener* pListener - ) +void ScDocument::StartListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener ) { if ( pBASM ) - pBASM->StartListeningArea( rRange, pListener ); + pBASM->StartListeningArea(rRange, bGroupListening, pListener); } -void ScDocument::EndListeningArea( const ScRange& rRange, - SvtListener* pListener - ) +void ScDocument::EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener ) { if ( pBASM ) - pBASM->EndListeningArea( rRange, pListener ); + pBASM->EndListeningArea(rRange, bGroupListening, pListener); } void ScDocument::Broadcast( const ScHint& rHint ) @@ -155,7 +152,7 @@ void ScDocument::BroadcastRefMoved( const sc::RefMovedHint& rHint ) std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end(); for (; it != itEnd; ++it) { - pBASM->EndListeningArea(it->maArea, it->mpListener); + pBASM->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener); it->mpListener->Notify(rHint); // Adjust the references. } } @@ -206,7 +203,7 @@ void ScDocument::BroadcastRefMoved( const sc::RefMovedHint& rHint ) { ScRange aNewRange = it->maArea; aNewRange.Move(rDelta.Col(), rDelta.Row(), rDelta.Tab()); - pBASM->StartListeningArea(aNewRange, it->mpListener); + pBASM->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener); } } } diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index 1d9b20fb0d9e..6b3cbe46ba28 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -58,6 +58,7 @@ #include <refhint.hxx> #include <listenerquery.hxx> #include <listenerqueryids.hxx> +#include <grouparealistener.hxx> #include <boost/scoped_ptr.hpp> @@ -508,6 +509,29 @@ void ScFormulaCellGroup::compileOpenCLKernel() meKernelState = sc::OpenCLKernelBinaryCreated; } +sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener( + ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed ) +{ + // TODO : Find existing one with the same criteria. + maAreaListeners.push_back(new sc::FormulaGroupAreaListener(rRange, ppTopCell, mnLength, bStartFixed, bEndFixed)); + return &maAreaListeners.back(); +} + +void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc ) +{ + AreaListenersType::iterator it = maAreaListeners.begin(), itEnd = maAreaListeners.end(); + for (; it != itEnd; ++it) + { + sc::FormulaGroupAreaListener* pListener = &(*it); + ScRange aListenRange = pListener->getListeningRange(); + // This "always listen" special range is never grouped. + bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS); + rDoc.EndListeningArea(aListenRange, bGroupListening, pListener); + } + + maAreaListeners.clear(); +} + ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) : eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT), pCode(new ScTokenArray), @@ -1854,14 +1878,14 @@ void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) if (pCode->IsRecalcModeAlways()) { // The formula was previously volatile, but no more. - pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this); + pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this); pCode->SetExclusiveRecalcModeNormal(); } else { // non-volatile formula. End listening to the area in case // it's listening due to macro module change. - pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this); + pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this); } pDocument->RemoveFromFormulaTree(this); break; @@ -3799,7 +3823,7 @@ void startListeningArea( aCell2.SetCol(MAXCOL); } } - rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell); + rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell); } } @@ -3807,6 +3831,9 @@ void startListeningArea( void ScFormulaCell::StartListeningTo( ScDocument* pDoc ) { + if (mxGroup) + mxGroup->endAllGroupListening(*pDoc); + if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack()) return; @@ -3815,7 +3842,7 @@ void ScFormulaCell::StartListeningTo( ScDocument* pDoc ) ScTokenArray* pArr = GetCode(); if( pArr->IsRecalcModeAlways() ) { - pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this); + pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, false, this); } pArr->Reset(); @@ -3845,6 +3872,9 @@ void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt ) { ScDocument& rDoc = rCxt.getDoc(); + if (mxGroup) + mxGroup->endAllGroupListening(rDoc); + if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack()) return; @@ -3853,7 +3883,7 @@ void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt ) ScTokenArray* pArr = GetCode(); if( pArr->IsRecalcModeAlways() ) { - rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, this); + rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this); } pArr->Reset(); @@ -3902,7 +3932,7 @@ void endListeningArea( } } - rDoc.EndListeningArea(ScRange(aCell1, aCell2), pCell); + rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell); } } @@ -3911,6 +3941,9 @@ void endListeningArea( void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, ScAddress aCellPos ) { + if (mxGroup) + mxGroup->endAllGroupListening(*pDoc); + if (pDoc->IsClipOrUndo() || IsInChangeTrack()) return; @@ -3918,7 +3951,7 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, if ( GetCode()->IsRecalcModeAlways() ) { - pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this ); + pDoc->EndListeningArea(BCA_LISTEN_ALWAYS, false, this); } if (!pArr) @@ -3950,6 +3983,9 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt ) { + if (mxGroup) + mxGroup->endAllGroupListening(rCxt.getDoc()); + if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack()) return; @@ -3963,7 +3999,7 @@ void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt ) if (pArr->IsRecalcModeAlways()) { - rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, this); + rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this); } pArr->Reset(); diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index f7c61184fecd..c42f777912af 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -739,7 +739,7 @@ void ScTable::SortReorderByColumn( std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end(); for (; it != itEnd; ++it) { - pDocument->EndListeningArea(it->maArea, it->mpListener); + pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener); aListeners.push_back( it->mpListener); } } @@ -770,7 +770,7 @@ void ScTable::SortReorderByColumn( aNewRange.aStart.SetCol( itCol->second); aNewRange.aEnd.SetCol( itCol->second); } - pDocument->StartListeningArea(aNewRange, it->mpListener); + pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener); } } @@ -1020,7 +1020,7 @@ void ScTable::SortReorderByRow( std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end(); for (; it != itEnd; ++it) { - pDocument->EndListeningArea(it->maArea, it->mpListener); + pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener); aListeners.push_back( it->mpListener); } } @@ -1082,7 +1082,7 @@ void ScTable::SortReorderByRow( aNewRange.aStart.SetRow( itRow->second); aNewRange.aEnd.SetRow( itRow->second); } - pDocument->StartListeningArea(aNewRange, it->mpListener); + pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener); } } diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx index 2f632071b46a..bf9890834cf7 100644 --- a/sc/source/core/inc/bcaslot.hxx +++ b/sc/source/core/inc/bcaslot.hxx @@ -22,17 +22,23 @@ #include <set> #include <boost/unordered_set.hpp> +#include <boost/ptr_container/ptr_map.hpp> #include <functional> + #include <svl/broadcast.hxx> #include "global.hxx" #include "brdcst.hxx" +#include <columnspanset.hxx> + +class ScBroadcastArea; namespace sc { struct AreaListener { ScRange maArea; + bool mbGroupListening; SvtListener* mpListener; }; @@ -42,19 +48,20 @@ struct AreaListener Used in a Unique Associative Container. */ -class ScBroadcastArea +class ScBroadcastArea : boost::noncopyable { private: ScBroadcastArea* pUpdateChainNext; SvtBroadcaster aBroadcaster; ScRange aRange; sal_uLong nRefCount; - bool bInUpdateChain; + + bool mbInUpdateChain:1; + bool mbGroupListening:1; public: - ScBroadcastArea( const ScRange& rRange ) - : pUpdateChainNext( NULL ), aRange( rRange ), - nRefCount( 0 ), bInUpdateChain( false ) {} + ScBroadcastArea( const ScRange& rRange ); + inline SvtBroadcaster& GetBroadcaster() { return aBroadcaster; } inline const SvtBroadcaster& GetBroadcaster() const { return aBroadcaster; } inline void UpdateRange( const ScRange& rNewRange ) @@ -67,8 +74,11 @@ public: inline sal_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; } + inline bool IsInUpdateChain() const { return mbInUpdateChain; } + inline void SetInUpdateChain( bool b ) { mbInUpdateChain = b; } + + inline bool IsGroupListening() const { return mbGroupListening; } + void SetGroupListening( bool b ) { mbGroupListening = b; } /** Equalness of this or range. */ inline bool operator==( const ScBroadcastArea & rArea ) const; @@ -76,7 +86,7 @@ public: inline bool ScBroadcastArea::operator==( const ScBroadcastArea & rArea ) const { - return aRange == rArea.aRange; + return aRange == rArea.aRange && mbGroupListening == rArea.mbGroupListening; } struct ScBroadcastAreaEntry @@ -91,7 +101,7 @@ struct ScBroadcastAreaHash { size_t operator()( const ScBroadcastAreaEntry& rEntry ) const { - return rEntry.mpArea->GetRange().hashArea(); + return rEntry.mpArea->GetRange().hashArea() + rEntry.mpArea->IsGroupListening(); } }; @@ -145,7 +155,7 @@ private: */ bool mbHasErasedArea; - ScBroadcastAreas::const_iterator FindBroadcastArea( const ScRange& rRange ) const; + ScBroadcastAreas::const_iterator FindBroadcastArea( const ScRange& rRange, bool bGroupListening ) const; /** More hypothetical (memory would probably be doomed anyway) check @@ -189,9 +199,8 @@ public: true if rpArea passed was NULL and ScBroadcastArea is newly created. */ - bool StartListeningArea( const ScRange& rRange, - SvtListener* pListener, - ScBroadcastArea*& rpArea ); + bool StartListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea ); /** Insert a ScBroadcastArea obtained via StartListeningArea() to @@ -199,9 +208,9 @@ public: */ void InsertListeningArea( ScBroadcastArea* pArea ); - void EndListeningArea( const ScRange& rRange, - SvtListener* pListener, - ScBroadcastArea*& rpArea ); + void EndListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea ); + bool AreaBroadcast( const ScHint& rHint ); /// @return true if at least one broadcast occurred. bool AreaBroadcastInRange( const ScRange& rRange, @@ -238,6 +247,7 @@ public: class ScBroadcastAreaSlotMachine { private: + typedef boost::ptr_map<ScBroadcastArea*, sc::ColumnSpanSet> BulkGroupAreasType; /** Slot offset arrangement of columns and rows, once per sheet. @@ -279,6 +289,7 @@ private: private: ScBroadcastAreasBulk aBulkBroadcastAreas; + BulkGroupAreasType maBulkGroupAreas; TableSlotsMap aTableSlotsMap; AreasToBeErased maAreasToBeErased; SvtBroadcaster *pBCAlways; // for the RC_ALWAYS special range @@ -295,10 +306,12 @@ private: public: ScBroadcastAreaSlotMachine( ScDocument* pDoc ); ~ScBroadcastAreaSlotMachine(); - void StartListeningArea( const ScRange& rRange, - SvtListener* pListener ); - void EndListeningArea( const ScRange& rRange, - SvtListener* pListener ); + void StartListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener ); + + void EndListeningArea( + const ScRange& rRange, bool bGroupListening, SvtListener* pListener ); + bool AreaBroadcast( const ScHint& rHint ) const; // return: at least one broadcast occurred bool AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) const; @@ -309,6 +322,10 @@ public: void EnterBulkBroadcast(); void LeaveBulkBroadcast(); bool InsertBulkArea( const ScBroadcastArea* p ); + + void InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange ); + void BulkBroadcastGroupAreas(); + /// @return: how many removed size_t RemoveBulkArea( const ScBroadcastArea* p ); inline ScBroadcastArea* GetUpdateChain() const { return pUpdateChain; } diff --git a/sc/source/core/tool/bulkdatahint.cxx b/sc/source/core/tool/bulkdatahint.cxx new file mode 100644 index 000000000000..78c238444c1f --- /dev/null +++ b/sc/source/core/tool/bulkdatahint.cxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <bulkdatahint.hxx> + +namespace sc { + +struct BulkDataHint::Impl +{ + ScDocument& mrDoc; + const ColumnSpanSet* mpSpans; + + Impl( ScDocument& rDoc, const ColumnSpanSet* pSpans ) : + mrDoc(rDoc), + mpSpans(pSpans) {} +}; + +BulkDataHint::BulkDataHint( ScDocument& rDoc, const ColumnSpanSet* pSpans ) : + SfxSimpleHint(SC_HINT_BULK_DATACHANGED), mpImpl(new Impl(rDoc, pSpans)) {} + +BulkDataHint::~BulkDataHint() +{ + delete mpImpl; +} + +void BulkDataHint::setSpans( const ColumnSpanSet* pSpans ) +{ + mpImpl->mpSpans = pSpans; +} + +const ColumnSpanSet* BulkDataHint::getSpans() const +{ + return mpImpl->mpSpans; +} + +ScDocument& BulkDataHint::getDoc() +{ + return mpImpl->mrDoc; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/tool/chartlis.cxx b/sc/source/core/tool/chartlis.cxx index 837dbfecd1ab..d68b3538a08c 100644 --- a/sc/source/core/tool/chartlis.cxx +++ b/sc/source/core/tool/chartlis.cxx @@ -285,7 +285,7 @@ private: if (rRange.aStart == rRange.aEnd) mpDoc->StartListeningCell(rRange.aStart, &mrParent); else - mpDoc->StartListeningArea(rRange, &mrParent); + mpDoc->StartListeningArea(rRange, false, &mrParent); } void endListening(const ScRange& rRange) @@ -293,7 +293,7 @@ private: if (rRange.aStart == rRange.aEnd) mpDoc->EndListeningCell(rRange.aStart, &mrParent); else - mpDoc->EndListeningArea(rRange, &mrParent); + mpDoc->EndListeningArea(rRange, false, &mrParent); } private: ScDocument* mpDoc; diff --git a/sc/source/core/tool/grouparealistener.cxx b/sc/source/core/tool/grouparealistener.cxx new file mode 100644 index 000000000000..e682d613d0ec --- /dev/null +++ b/sc/source/core/tool/grouparealistener.cxx @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <grouparealistener.hxx> +#include <brdcst.hxx> +#include <formulacell.hxx> +#include <bulkdatahint.hxx> +#include <columnspanset.hxx> +#include <column.hxx> + +namespace sc { + +namespace { + +class Notifier : std::unary_function<ScFormulaCell*, void> +{ + const SfxHint& mrHint; +public: + Notifier( const SfxHint& rHint ) : mrHint(rHint) {} + + void operator() ( ScFormulaCell* pCell ) + { + pCell->Notify(mrHint); + } +}; + +class CollectCellAction : public sc::ColumnSpanSet::ColumnAction +{ + const FormulaGroupAreaListener& mrAreaListener; + ScAddress maPos; + std::vector<ScFormulaCell*> maCells; + +public: + CollectCellAction( const FormulaGroupAreaListener& rAreaListener ) : + mrAreaListener(rAreaListener) {} + + virtual void startColumn( ScColumn* pCol ) + { + maPos.SetTab(pCol->GetTab()); + maPos.SetCol(pCol->GetCol()); + } + + virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) + { + if (!bVal) + return; + + mrAreaListener.collectFormulaCells(maPos.Tab(), maPos.Col(), nRow1, nRow2, maCells); + }; + + void swapCells( std::vector<ScFormulaCell*>& rCells ) + { + // Remove duplicate before the swap. + std::sort(maCells.begin(), maCells.end()); + std::vector<ScFormulaCell*>::iterator it = std::unique(maCells.begin(), maCells.end()); + maCells.erase(it, maCells.end()); + + rCells.swap(maCells); + } +}; + +} + +FormulaGroupAreaListener::FormulaGroupAreaListener( + const ScRange& rRange, ScFormulaCell** ppTopCell, SCROW nGroupLen, bool bStartFixed, bool bEndFixed ) : + maRange(rRange), + mppTopCell(ppTopCell), + mnGroupLen(nGroupLen), + mbStartFixed(bStartFixed), + mbEndFixed(bEndFixed) +{ + assert(mppTopCell); // This can't be NULL. +} + +ScRange FormulaGroupAreaListener::getListeningRange() const +{ + ScRange aRet = maRange; + if (!mbEndFixed) + aRet.aEnd.IncRow(mnGroupLen-1); + return aRet; +} + +void FormulaGroupAreaListener::Notify( const SfxHint& rHint ) +{ + const SfxSimpleHint* pSimpleHint = dynamic_cast<const SfxSimpleHint*>(&rHint); + if (!pSimpleHint) + return; + + switch (pSimpleHint->GetId()) + { + case SC_HINT_DATACHANGED: + notifyCellChange(rHint, static_cast<const ScHint*>(pSimpleHint)->GetAddress()); + break; + case SC_HINT_BULK_DATACHANGED: + { + const BulkDataHint& rBulkHint = static_cast<const BulkDataHint&>(*pSimpleHint); + notifyBulkChange(rBulkHint); + } + break; + default: + ; + } +} + +void FormulaGroupAreaListener::notifyBulkChange( const BulkDataHint& rHint ) +{ + const ColumnSpanSet* pSpans = rHint.getSpans(); + if (!pSpans) + return; + + ScDocument& rDoc = const_cast<BulkDataHint&>(rHint).getDoc(); + + CollectCellAction aAction(*this); + pSpans->executeColumnAction(rDoc, aAction); + + std::vector<ScFormulaCell*> aCells; + aAction.swapCells(aCells); + ScHint aHint(SC_HINT_DATACHANGED, ScAddress()); + std::for_each(aCells.begin(), aCells.end(), Notifier(aHint)); +} + +void FormulaGroupAreaListener::collectFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells ) const +{ + if (rPos.Tab() < maRange.aStart.Tab() || maRange.aEnd.Tab() < rPos.Tab()) + // Wrong sheet. + return; + + if (rPos.Col() < maRange.aStart.Col() || maRange.aEnd.Col() < rPos.Col()) + // Outside the column range. + return; + + ScFormulaCell** pp = mppTopCell; + ScFormulaCell** ppEnd = pp + mnGroupLen; + + if (mbStartFixed) + { + if (mbEndFixed) + { + // Both top and bottom row positions are absolute. Use the original range as-is. + SCROW nRow1 = maRange.aStart.Row(); + SCROW nRow2 = maRange.aEnd.Row(); + if (rPos.Row() < nRow1 || nRow2 < rPos.Row()) + return; + + for (; pp != ppEnd; ++pp) + rCells.push_back(*pp); + } + else + { + // Only the end row is relative. + SCROW nRow1 = maRange.aStart.Row(); + SCROW nRow2 = maRange.aEnd.Row(); + SCROW nMaxRow = nRow2 + mnGroupLen - 1; + if (rPos.Row() < nRow1 || nMaxRow < rPos.Row()) + return; + + if (nRow2 < rPos.Row()) + { + // Skip ahead to the first hit. + SCROW nSkip = rPos.Row() - nRow2; + pp += nSkip; + nRow2 += nSkip; + } + + // Notify the first hit cell and all subsequent ones. + for (; pp != ppEnd; ++pp) + rCells.push_back(*pp); + } + } + else if (mbEndFixed) + { + // Only the start row is relative. + SCROW nRow1 = maRange.aStart.Row(); + SCROW nRow2 = maRange.aEnd.Row(); + + if (rPos.Row() < nRow1 || nRow2 < rPos.Row()) + return; + + for (; pp != ppEnd && nRow1 <= nRow2; ++pp, ++nRow1) + rCells.push_back(*pp); + } + else + { + // Both top and bottom row positions are relative. + SCROW nRow1 = maRange.aStart.Row(); + SCROW nRow2 = maRange.aEnd.Row(); + SCROW nMaxRow = nRow2 + mnGroupLen - 1; + if (nMaxRow < rPos.Row()) + return; + + if (nRow2 < rPos.Row()) + { + // Skip ahead. + SCROW nSkip = rPos.Row() - nRow2; + pp += nSkip; + nRow1 += nSkip; + nRow2 += nSkip; + } + + for (; pp != ppEnd; ++pp, ++nRow1, ++nRow2) + { + if (rPos.Row() < nRow1 || nRow2 < rPos.Row()) + // Changed cell is outside the range. + continue; + + rCells.push_back(*pp); + } + } +} + +void FormulaGroupAreaListener::collectFormulaCells( + SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const +{ + PutInOrder(nRow1, nRow2); + + if (nTab < maRange.aStart.Tab() || maRange.aEnd.Tab() < nTab) + // Wrong sheet. + return; + + if (nCol < maRange.aStart.Col() || maRange.aEnd.Col() < nCol) + // Outside the column range. + return; + + ScFormulaCell** pp = mppTopCell; + ScFormulaCell** ppEnd = pp + mnGroupLen; + + if (mbStartFixed) + { + if (mbEndFixed) + { + // Both top and bottom row positions are absolute. Use the original range as-is. + SCROW nRefRow1 = maRange.aStart.Row(); + SCROW nRefRow2 = maRange.aEnd.Row(); + if (nRow2 < nRefRow1 || nRefRow2 < nRow1) + return; + + for (; pp != ppEnd; ++pp) + rCells.push_back(*pp); + } + else + { + // Only the end row is relative. + SCROW nRefRow1 = maRange.aStart.Row(); + SCROW nRefRow2 = maRange.aEnd.Row(); + SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1; + if (nRow2 < nRefRow1 || nMaxRefRow < nRow1) + return; + + if (nRefRow2 < nRow1) + { + // Skip ahead to the first hit. + SCROW nSkip = nRow1 - nRefRow2; + pp += nSkip; + nRefRow2 += nSkip; + } + + assert(nRow1 <= nRefRow2); + + // Notify the first hit cell and all subsequent ones. + for (; pp != ppEnd; ++pp) + rCells.push_back(*pp); + } + } + else if (mbEndFixed) + { + // Only the start row is relative. + SCROW nRefRow1 = maRange.aStart.Row(); + SCROW nRefRow2 = maRange.aEnd.Row(); + + if (nRow2 < nRefRow1 || nRefRow2 < nRow1) + return; + + for (; pp != ppEnd && nRefRow1 <= nRefRow2; ++pp, ++nRefRow1) + rCells.push_back(*pp); + } + else + { + // Both top and bottom row positions are relative. + SCROW nRefRow1 = maRange.aStart.Row(); + SCROW nRefRow2 = maRange.aEnd.Row(); + SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1; + if (nMaxRefRow < nRow1) + return; + + if (nRefRow2 < nRow1) + { + // The ref row range is above the changed row span. Skip ahead. + SCROW nSkip = nRow1 - nRefRow2; + pp += nSkip; + nRefRow1 += nSkip; + nRefRow2 += nSkip; + } + + // At this point the initial ref row range should be overlapping the + // dirty cell range. + assert(nRow1 <= nRefRow2); + + // Keep sliding down until the top ref row position is below the + // bottom row of the dirty cell range. + for (; pp != ppEnd && nRefRow1 <= nRow2; ++pp, ++nRefRow1, ++nRefRow2) + rCells.push_back(*pp); + } +} + +void FormulaGroupAreaListener::notifyCellChange( const SfxHint& rHint, const ScAddress& rPos ) +{ + // Determine which formula cells within the group need to be notified of this change. + std::vector<ScFormulaCell*> aCells; + collectFormulaCells(rPos, aCells); + std::for_each(aCells.begin(), aCells.end(), Notifier(rHint)); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx index 65d91a73d7dc..ce00fc31421e 100644 --- a/sc/source/core/tool/sharedformula.cxx +++ b/sc/source/core/tool/sharedformula.cxx @@ -10,6 +10,11 @@ #include "sharedformula.hxx" #include "calcmacros.hxx" #include "tokenarray.hxx" +#include <listenercontext.hxx> +#include <document.hxx> +#include <grouparealistener.hxx> + +#define USE_FORMULA_GROUP_LISTENER 1 namespace sc { @@ -328,15 +333,86 @@ void SharedFormulaUtil::unshareFormulaCells(CellStoreType& rCells, std::vector<S void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop ) { - assert((**ppSharedTop).IsSharedTop()); + ScFormulaCell& rTopCell = **ppSharedTop; + assert(rTopCell.IsSharedTop()); + +#if USE_FORMULA_GROUP_LISTENER + ScDocument& rDoc = rCxt.getDoc(); + rDoc.SetDetectiveDirty(true); + + ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup(); + const ScTokenArray* pCode = xGroup->mpCode; + assert(pCode == rTopCell.GetCode()); + if (pCode->IsRecalcModeAlways()) + { + rDoc.StartListeningArea( + BCA_LISTEN_ALWAYS, false, + xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true)); + } + + formula::FormulaToken** p = pCode->GetCode(); + formula::FormulaToken** pEnd = p + pCode->GetCodeLen(); + for (; p != pEnd; ++p) + { + const formula::FormulaToken* t = *p; + switch (t->GetType()) + { + case formula::svSingleRef: + { + ScAddress aPos = t->GetSingleRef()->toAbs(rTopCell.aPos); + ScFormulaCell** pp = ppSharedTop; + ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength; + for (; pp != ppEnd; ++pp, aPos.IncRow()) + { + if (!aPos.IsValid()) + break; + + rDoc.StartListeningCell(rCxt, aPos, **pp); + } + } + break; + case formula::svDoubleRef: + { + const ScSingleRefData& rRef1 = *t->GetSingleRef(); + const ScSingleRefData& rRef2 = *t->GetSingleRef2(); + ScAddress aPos1 = rRef1.toAbs(rTopCell.aPos); + ScAddress aPos2 = rRef2.toAbs(rTopCell.aPos); + + ScRange aOrigRange = ScRange(aPos1, aPos2); + ScRange aListenedRange = aOrigRange; + if (rRef2.IsRowRel()) + aListenedRange.aEnd.IncRow(xGroup->mnLength-1); + + if (aPos1.IsValid() && aPos2.IsValid()) + { + rDoc.StartListeningArea( + aListenedRange, true, + xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel())); + } + } + break; + default: + ; + } + } ScFormulaCell** pp = ppSharedTop; + ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength; + for (; pp != ppEnd; ++pp) + { + ScFormulaCell& rCell = **pp; + rCell.SetNeedsListening(false); + } + +#else + ScFormulaCell** pp = ppSharedTop; ScFormulaCell** ppEnd = ppSharedTop + (**ppSharedTop).GetSharedLength(); for (; pp != ppEnd; ++pp) { ScFormulaCell& rFC = **pp; rFC.StartListeningTo(rCxt); } +#endif } } diff --git a/sc/source/ui/docshell/macromgr.cxx b/sc/source/ui/docshell/macromgr.cxx index a5e5b9fbd796..1a6201e10319 100644 --- a/sc/source/ui/docshell/macromgr.cxx +++ b/sc/source/ui/docshell/macromgr.cxx @@ -193,7 +193,7 @@ void ScMacroManager::BroadcastModuleUpdate(const OUString& aModuleName) // for recalc on cell value change. If the cell is not volatile, the // cell stops listening right away after it gets re-interpreted. - mpDoc->StartListeningArea(BCA_LISTEN_ALWAYS, pCell); + mpDoc->StartListeningArea(BCA_LISTEN_ALWAYS, false, pCell); } } diff --git a/sc/source/ui/docshell/servobj.cxx b/sc/source/ui/docshell/servobj.cxx index 664887ee59af..26208b2771cf 100644 --- a/sc/source/ui/docshell/servobj.cxx +++ b/sc/source/ui/docshell/servobj.cxx @@ -100,7 +100,7 @@ ScServerObject::ScServerObject( ScDocShell* pShell, const OUString& rItem ) : } pDocSh->GetDocument().GetLinkManager()->InsertServer( this ); - pDocSh->GetDocument().StartListeningArea( aRange, &aForwarder ); + pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder ); StartListening(*pDocSh); // um mitzubekommen, wenn die DocShell geloescht wird StartListening(*SfxGetpApp()); // for SC_HINT_AREAS_CHANGED @@ -118,7 +118,7 @@ void ScServerObject::Clear() ScDocShell* pTemp = pDocSh; pDocSh = NULL; - pTemp->GetDocument().EndListeningArea( aRange, &aForwarder ); + pTemp->GetDocument().EndListeningArea(aRange, false, &aForwarder); pTemp->GetDocument().GetLinkManager()->RemoveServer( this ); EndListening(*pTemp); EndListening(*SfxGetpApp()); @@ -154,7 +154,7 @@ bool ScServerObject::GetData( // refresh the listeners now (this is called from a timer) EndListeningAll(); - pDocSh->GetDocument().StartListeningArea( aRange, &aForwarder ); + pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder ); StartListening(*pDocSh); StartListening(*SfxGetpApp()); bRefreshListener = false; diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx index 7a50745eea01..a2b58a87904a 100644 --- a/sc/source/ui/unoobj/cellsuno.cxx +++ b/sc/source/ui/unoobj/cellsuno.cxx @@ -1674,7 +1674,7 @@ void ScCellRangesBase::RefChanged() ScDocument& rDoc = pDocShell->GetDocument(); for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i ) - rDoc.StartListeningArea( *aRanges[ i ], pValueListener ); + rDoc.StartListeningArea( *aRanges[ i ], false, pValueListener ); } ForgetCurrentAttrs(); @@ -3395,7 +3395,7 @@ void SAL_CALL ScCellRangesBase::addModifyListener(const uno::Reference<util::XMo ScDocument& rDoc = pDocShell->GetDocument(); for ( size_t i = 0, nCount = aRanges.size(); i < nCount; i++) - rDoc.StartListeningArea( *aRanges[ i ], pValueListener ); + rDoc.StartListeningArea( *aRanges[ i ], false, pValueListener ); acquire(); // don't lose this object (one ref for all listeners) } diff --git a/sc/source/ui/unoobj/chart2uno.cxx b/sc/source/ui/unoobj/chart2uno.cxx index 54103ebc3707..cb944ee802b3 100644 --- a/sc/source/ui/unoobj/chart2uno.cxx +++ b/sc/source/ui/unoobj/chart2uno.cxx @@ -2548,7 +2548,7 @@ void ScChart2DataSequence::RefChanged() if (!ScRefTokenHelper::getRangeFromToken(aRange, *itr, ScAddress())) continue; - m_pDocument->StartListeningArea(aRange, m_pValueListener); + m_pDocument->StartListeningArea(aRange, false, m_pValueListener); if (pCLC) pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); } @@ -3400,7 +3400,7 @@ void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< uti if (!ScRefTokenHelper::getRangeFromToken(aRange, *itr, ScAddress())) continue; - m_pDocument->StartListeningArea( aRange, m_pValueListener ); + m_pDocument->StartListeningArea( aRange, false, m_pValueListener ); if (pCLC) pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); } |