tdf#99938 : Allow batch of formula-cells to be written...

using a single undo document from ScMovingAverageDialog
rather than write tons of formula-cells to the document
one by one thus creating that many number of undo docs
unnecessarily.

Change-Id: I2528e0ab47f83e0c5ea40c73d00db5af14f656e0
Reviewed-on: https://gerrit.libreoffice.org/71823
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
diff --git a/sc/inc/cellvalues.hxx b/sc/inc/cellvalues.hxx
index 4312398..4b877c6 100644
--- a/sc/inc/cellvalues.hxx
+++ b/sc/inc/cellvalues.hxx
@@ -15,6 +15,7 @@
#include <vector>

class ScColumn;
class ScFormulaCell;

namespace svl {

@@ -63,6 +64,7 @@ public:
    void swapNonEmpty( ScColumn& rCol );

    void assign( const std::vector<double>& rVals );
    void assign( const std::vector<ScFormulaCell*>& rVals );

    size_t size() const;

diff --git a/sc/source/core/data/cellvalues.cxx b/sc/source/core/data/cellvalues.cxx
index 83c01a9..290dc0d 100644
--- a/sc/source/core/data/cellvalues.cxx
+++ b/sc/source/core/data/cellvalues.cxx
@@ -10,6 +10,7 @@
#include <memory>
#include <cellvalues.hxx>
#include <column.hxx>
#include <formulacell.hxx>

#include <cassert>

@@ -98,6 +99,25 @@ void CellValues::assign( const std::vector<double>& rVals )
    mpImpl->maCellTextAttrs.set(0, aDefaults.begin(), aDefaults.end());
}

void CellValues::assign( const std::vector<ScFormulaCell*>& rVals )
{
    std::vector<ScFormulaCell*> aCopyVals(rVals.size());
    size_t nIdx = 0;
    for (const auto* pCell : rVals)
    {
        aCopyVals[nIdx] = pCell->Clone();
        ++nIdx;
    }

    mpImpl->maCells.resize(aCopyVals.size());
    mpImpl->maCells.set(0, aCopyVals.begin(), aCopyVals.end());

    // Set default text attributes.
    std::vector<CellTextAttr> aDefaults(rVals.size(), CellTextAttr());
    mpImpl->maCellTextAttrs.resize(rVals.size());
    mpImpl->maCellTextAttrs.set(0, aDefaults.begin(), aDefaults.end());
}

size_t CellValues::size() const
{
    assert(mpImpl->maCells.size() == mpImpl->maCellTextAttrs.size());
@@ -154,7 +174,7 @@ void CellValues::copyCellsTo( ScColumn& rCol, SCROW nRow ) const
    const CellStoreType& rSrc = mpImpl->maCells;

    // Caller must ensure the destination is long enough.
    assert(rSrc.size() + static_cast<size_t>(nRow) < rDest.size());
    assert(rSrc.size() + static_cast<size_t>(nRow) <= rDest.size());

    SCROW nCurRow = nRow;
    CellStoreType::iterator itPos = rDest.begin();
@@ -219,7 +239,7 @@ void CellValues::copyCellTextAttrsTo( ScColumn& rCol, SCROW nRow ) const
    const CellTextAttrStoreType& rSrc = mpImpl->maCellTextAttrs;

    // Caller must ensure the destination is long enough.
    assert(rSrc.size() + static_cast<size_t>(nRow) < rDest.size());
    assert(rSrc.size() + static_cast<size_t>(nRow) <= rDest.size());

    SCROW nCurRow = nRow;
    CellTextAttrStoreType::iterator itPos = rDest.begin();
diff --git a/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx b/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx
index 4f4d596..4d59e28 100644
--- a/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx
+++ b/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx
@@ -75,6 +75,7 @@ ScRange ScMovingAverageDialog::ApplyOutput(ScDocShell* pDocShell)
        output.nextRow();

        DataCellIterator aDataCellIterator = pIterator->iterateCells();
        std::vector<OUString> aFormulas;

        for (; aDataCellIterator.hasNext(); aDataCellIterator.next())
        {
@@ -98,14 +99,15 @@ ScRange ScMovingAverageDialog::ApplyOutput(ScDocShell* pDocShell)
            {
                aTemplate.setTemplate("=AVERAGE(%RANGE%)");
                aTemplate.applyRange("%RANGE%", ScRange(aIntervalStart, aIntervalEnd));
                output.writeFormula(aTemplate.getTemplate());
                aFormulas.push_back(aTemplate.getTemplate());
            }
            else
            {
                output.writeFormula("=#N/A");
                aFormulas.push_back("=#N/A");
            }
            output.nextRow();
        }

        output.writeFormulas(aFormulas);
        output.nextColumn();
    }
    return ScRange(output.mMinimumAddress, output.mMaximumAddress);
diff --git a/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx b/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
index 15936bc..f793417 100644
--- a/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
+++ b/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
@@ -163,6 +163,28 @@ void AddressWalkerWriter::writeFormula(const OUString& aFormula)
            new ScFormulaCell(mpDocument, mCurrentAddress, aFormula, meGrammar), true);
}

void AddressWalkerWriter::writeFormulas(const std::vector<OUString>& rFormulas)
{
    size_t nLength = rFormulas.size();
    if (!nLength)
        return;

    const size_t nMaxLen = MAXROW - mCurrentAddress.Row() + 1;
    // If not done already, trim the length to fit.
    if (nLength > nMaxLen)
        nLength = nMaxLen;

    std::vector<ScFormulaCell*> aFormulaCells(nLength);
    ScAddress aAddr(mCurrentAddress);
    for (size_t nIdx = 0; nIdx < nLength; ++nIdx)
    {
        aFormulaCells[nIdx] = new ScFormulaCell(mpDocument, aAddr, rFormulas[nIdx], meGrammar);
        aAddr.IncRow(1);
    }

    mpDocShell->GetDocFunc().SetFormulaCells(mCurrentAddress, aFormulaCells, true);
}

void AddressWalkerWriter::writeMatrixFormula(const OUString& aFormula, SCCOL nCols, SCROW nRows)
{
    ScRange aRange;
diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
index 1df41fa..a5a6183 100644
--- a/sc/source/ui/docshell/docfunc.cxx
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -1023,6 +1023,61 @@ bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, boo
    return true;
}

bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction )
{
    const size_t nLength = rCells.size();
    if (rPos.Row() + nLength - 1 > MAXROW)
        // out of bound
        return false;

    ScRange aRange(rPos);
    aRange.aEnd.IncRow(nLength - 1);

    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo = rDoc.IsUndoEnabled();

    std::unique_ptr<sc::UndoSetCells> pUndoObj;
    if (bUndo)
    {
        pUndoObj.reset(new sc::UndoSetCells(&rDocShell, rPos));
        rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues());
    }

    rDoc.SetFormulaCells(rPos, rCells);

    // For performance reasons API calls may disable calculation while
    // operating and recalculate once when done. If through user interaction
    // and AutoCalc is disabled, calculate the formula (without its
    // dependencies) once so the result matches the current document's content.
    if (bInteraction && !rDoc.GetAutoCalc())
    {
        for (auto* pCell : rCells)
        {
            // calculate just the cell once and set Dirty again
            pCell->Interpret();
            pCell->SetDirtyVar();
            rDoc.PutInFormulaTree( pCell);
        }
    }

    if (bUndo)
    {
        pUndoObj->SetNewValues(rCells);
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        pUndoMgr->AddUndoAction(std::move(pUndoObj));
    }

    rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
{
    ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
diff --git a/sc/source/ui/inc/TableFillingAndNavigationTools.hxx b/sc/source/ui/inc/TableFillingAndNavigationTools.hxx
index dd5cdca..d1d75d2 100644
--- a/sc/source/ui/inc/TableFillingAndNavigationTools.hxx
+++ b/sc/source/ui/inc/TableFillingAndNavigationTools.hxx
@@ -80,6 +80,7 @@ public:
            formula::FormulaGrammar::Grammar eGrammar );

    void writeFormula(const OUString& aFormula);
    void writeFormulas(const std::vector<OUString>& rFormulas);
    void writeMatrixFormula(const OUString& aFormula, SCCOL nCols = 1, SCROW nRows = 1);
    void writeString(const OUString& aString);
    void writeString(const char* aCharArray);
diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx
index 28da723..bf07e23 100644
--- a/sc/source/ui/inc/docfunc.hxx
+++ b/sc/source/ui/inc/docfunc.hxx
@@ -104,10 +104,11 @@ public:
    bool SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction );

    /**
     * This method takes ownership of the formula cell instance. The caller
     * Below two methods take ownership of the formula cell instance(s). The caller
     * must not delete it after passing it to this call.
     */
    bool SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction );
    bool SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction );
    void PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi );
    bool SetCellText(
        const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
diff --git a/sc/source/ui/inc/undocell.hxx b/sc/source/ui/inc/undocell.hxx
index 6eb30ee..c62ea72 100644
--- a/sc/source/ui/inc/undocell.hxx
+++ b/sc/source/ui/inc/undocell.hxx
@@ -32,6 +32,7 @@
class ScDocShell;
class ScPatternAttr;
class ScRangeName;
class ScFormulaCell;

class ScUndoCursorAttr: public ScSimpleUndo
{
@@ -361,6 +362,7 @@ public:

    CellValues& GetOldValues() { return maOldValues;}
    void SetNewValues( const std::vector<double>& rVals );
    void SetNewValues( const std::vector<ScFormulaCell*>& rVals );
};

} // namespace sc
diff --git a/sc/source/ui/undo/undocell2.cxx b/sc/source/ui/undo/undocell2.cxx
index da7c2dd..2222afa 100644
--- a/sc/source/ui/undo/undocell2.cxx
+++ b/sc/source/ui/undo/undocell2.cxx
@@ -11,6 +11,7 @@
#include <globstr.hrc>
#include <scresid.hxx>
#include <cellvalues.hxx>
#include <formulacell.hxx>

namespace sc {

@@ -60,6 +61,11 @@ void UndoSetCells::SetNewValues( const std::vector<double>& rVals )
    maNewValues.assign(rVals);
}

void UndoSetCells::SetNewValues( const std::vector<ScFormulaCell*>& rVals )
{
    maNewValues.assign(rVals);
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */