summaryrefslogtreecommitdiff
path: root/sc/source/core/tool
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 /sc/source/core/tool
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
Diffstat (limited to 'sc/source/core/tool')
-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
4 files changed, 449 insertions, 3 deletions
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
}
}