diff options
author | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-11-12 22:18:49 -0500 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-11-18 08:31:55 -0500 |
commit | 2030b9ac6c68ba6f15b0283e0b4e57ae49bd67b0 (patch) | |
tree | dc7ad2b2f890ea0ac614d7820c2e655c84327c3f /sc/source/core/tool | |
parent | 192f6a41444b62feae03185975c120f770e2938f (diff) |
Dedicated listener type tailored for formula groups.
Right now, it's only used when loading an xlsx file. But eventually
this one should be used everywhere.
Change-Id: I216c3a9a33c4b8040e8284d59299e0637471fb50
Diffstat (limited to 'sc/source/core/tool')
-rw-r--r-- | sc/source/core/tool/bulkdatahint.cxx | 49 | ||||
-rw-r--r-- | sc/source/core/tool/chartlis.cxx | 4 | ||||
-rw-r--r-- | sc/source/core/tool/grouparealistener.cxx | 321 | ||||
-rw-r--r-- | sc/source/core/tool/sharedformula.cxx | 78 |
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 } } |