summaryrefslogtreecommitdiffstats
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-14 16:42:24 -0500
commit12899ce686d302352645605f6e262784df77f0b2 (patch)
tree79cce67c69262fa7238b0093f66c7ddb12136120
parentfix jpeg windows build (diff)
downloadcore-12899ce686d302352645605f6e262784df77f0b2.tar.gz
core-12899ce686d302352645605f6e262784df77f0b2.zip
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 364872083a0f..440426399d68 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -197,6 +197,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 \
@@ -227,6 +228,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 9f6ab2cf25e4..72b49a830c86 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1852,10 +1852,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 5a6af4ba40e2..6837a549e83c 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 96b433932f45..0a2226f52ba0 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;
@@ -3793,7 +3817,7 @@ void startListeningArea(
aCell2.SetCol(MAXCOL);
}
}
- rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell);
+ rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
}
}
@@ -3801,6 +3825,9 @@ void startListeningArea(
void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
{
+ if (mxGroup)
+ mxGroup->endAllGroupListening(*pDoc);
+
if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
return;
@@ -3809,7 +3836,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();
@@ -3839,6 +3866,9 @@ void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
{
ScDocument& rDoc = rCxt.getDoc();
+ if (mxGroup)
+ mxGroup->endAllGroupListening(rDoc);
+
if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
return;
@@ -3847,7 +3877,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();
@@ -3896,7 +3926,7 @@ void endListeningArea(
}
}
- rDoc.EndListeningArea(ScRange(aCell1, aCell2), pCell);
+ rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
}
}
@@ -3905,6 +3935,9 @@ void endListeningArea(
void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
ScAddress aCellPos )
{
+ if (mxGroup)
+ mxGroup->endAllGroupListening(*pDoc);
+
if (pDoc->IsClipOrUndo() || IsInChangeTrack())
return;
@@ -3912,7 +3945,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)
@@ -3944,6 +3977,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;
@@ -3957,7 +3993,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 be5632533959..b9f5a3e5fd3f 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 c17c0b7563f9..542e04bcd474 100644
--- a/sc/source/ui/docshell/macromgr.cxx
+++ b/sc/source/ui/docshell/macromgr.cxx
@@ -192,7 +192,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 cad19eeaa308..bc8f175076f9 100644
--- a/sc/source/ui/unoobj/chart2uno.cxx
+++ b/sc/source/ui/unoobj/chart2uno.cxx
@@ -2547,7 +2547,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());
}
@@ -3399,7 +3399,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());
}