summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKohei Yoshida <kohei.yoshida@collabora.com>2014-11-12 22:18:49 -0500
committerKohei Yoshida <kohei.yoshida@collabora.com>2014-11-18 08:31:55 -0500
commit2030b9ac6c68ba6f15b0283e0b4e57ae49bd67b0 (patch)
treedc7ad2b2f890ea0ac614d7820c2e655c84327c3f
parent192f6a41444b62feae03185975c120f770e2938f (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
-rw-r--r--sc/Library_sc.mk2
-rw-r--r--sc/inc/bulkdatahint.hxx43
-rw-r--r--sc/inc/document.hxx7
-rw-r--r--sc/inc/formulacell.hxx12
-rw-r--r--sc/inc/grouparealistener.hxx73
-rw-r--r--sc/inc/simplehintids.hxx9
-rw-r--r--sc/source/core/data/bcaslot.cxx116
-rw-r--r--sc/source/core/data/colorscale.cxx2
-rw-r--r--sc/source/core/data/documen2.cxx4
-rw-r--r--sc/source/core/data/documen7.cxx17
-rw-r--r--sc/source/core/data/formulacell.cxx52
-rw-r--r--sc/source/core/data/table3.cxx8
-rw-r--r--sc/source/core/inc/bcaslot.hxx57
-rw-r--r--sc/source/core/tool/bulkdatahint.cxx49
-rw-r--r--sc/source/core/tool/chartlis.cxx4
-rw-r--r--sc/source/core/tool/grouparealistener.cxx321
-rw-r--r--sc/source/core/tool/sharedformula.cxx78
-rw-r--r--sc/source/ui/docshell/macromgr.cxx2
-rw-r--r--sc/source/ui/docshell/servobj.cxx6
-rw-r--r--sc/source/ui/unoobj/cellsuno.cxx4
-rw-r--r--sc/source/ui/unoobj/chart2uno.cxx4
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());
}