Related: tdf#123736 re-establish listeners also for vector unsharing

... via DetachFormulaCells()

Change-Id: Ia57308495a06e0df612eb1610b5f387d6b60ce08
Reviewed-on: https://gerrit.libreoffice.org/69320
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 8664c81..da63528 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -647,10 +647,12 @@ public:
    /** Re-establish listeners on unshared formula groups */
    void StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows );

    void DetachFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength );
    void DetachFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength,
                             std::vector<SCROW>* pNewSharedRows );

    void AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 );
    void DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 );
    void DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
                             std::vector<SCROW>* pNewSharedRows );

    /**
     * Regroup formula cells for the entire column.
@@ -714,7 +716,9 @@ private:
        const std::vector<SCROW>& rNewSharedRows,
        bool bJoin = true, sc::StartListeningType eListenType = sc::SingleCellListening );

    void AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength );
    void AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength,
                                const std::vector<SCROW>& rNewSharedRows );

    void BroadcastNewCell( SCROW nRow );
    bool UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow, sc::CellStoreType::iterator& itr );

diff --git a/sc/inc/sharedformula.hxx b/sc/inc/sharedformula.hxx
index 97e7fc0..b7c9577b 100644
--- a/sc/inc/sharedformula.hxx
+++ b/sc/inc/sharedformula.hxx
@@ -57,6 +57,9 @@ public:
        }
    }

    /** Get shared formula top cell from position, if any, else nullptr. */
    static const ScFormulaCell* getSharedTopFormulaCell(const CellStoreType::position_type& aPos);

    /**
     * Split existing shared formula range at specified position. The cell at
     * specified position becomes the top cell of the lower shared formula
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 1d69e35..8af8464 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -387,14 +387,59 @@ public:
}

void ScColumn::DetachFormulaCells(
    const sc::CellStoreType::position_type& aPos, size_t nLength )
    const sc::CellStoreType::position_type& aPos, size_t nLength, std::vector<SCROW>* pNewSharedRows )
{
    const size_t nRow = aPos.first->position + aPos.second;
    const size_t nNextTopRow = nRow + nLength; // start row of next formula group.

    bool bLowerSplitOff = false;
    if (pNewSharedRows && !GetDoc()->IsClipOrUndo())
    {
        const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos);
        if (pFC)
        {
            const SCROW nTopRow = pFC->GetSharedTopRow();
            const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
            // nTopRow <= nRow <= nBotRow, because otherwise pFC would not exist.
            if (nTopRow < static_cast<SCROW>(nRow))
            {
                // Upper part will be split off.
                pNewSharedRows->push_back(nTopRow);
                pNewSharedRows->push_back(nRow - 1);
            }
            if (static_cast<SCROW>(nNextTopRow) <= nBotRow)
            {
                // Lower part will be split off.
                pNewSharedRows->push_back(nNextTopRow);
                pNewSharedRows->push_back(nBotRow);
                bLowerSplitOff = true;
            }
        }
    }

    // Split formula grouping at the top and bottom boundaries.
    sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
    size_t nRow = aPos.first->position + aPos.second;
    size_t nNextTopRow = nRow + nLength; // start row of next formula group.
    if (ValidRow(nNextTopRow))

    if (nLength > 0 && ValidRow(nNextTopRow))
    {
        if (pNewSharedRows && !bLowerSplitOff && !GetDoc()->IsClipOrUndo())
        {
            sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow-1);
            const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2);
            if (pFC)
            {
                const SCROW nTopRow = pFC->GetSharedTopRow();
                const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
                // nRow < nTopRow < nNextTopRow <= nBotRow
                if (static_cast<SCROW>(nNextTopRow) <= nBotRow)
                {
                    // Lower part will be split off.
                    pNewSharedRows->push_back(nNextTopRow);
                    pNewSharedRows->push_back(nBotRow);
                }
            }
        }

        sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow);
        sc::SharedFormulaUtil::splitFormulaCellGroup(aPos2, nullptr);
    }
@@ -425,15 +470,59 @@ void ScColumn::AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1,
    sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc);
}

void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
        std::vector<SCROW>* pNewSharedRows )
{
    sc::CellStoreType::position_type aPos = maCells.position(nRow1);
    sc::CellStoreType::iterator it = aPos.first;

    bool bLowerSplitOff = false;
    if (pNewSharedRows && !GetDoc()->IsClipOrUndo())
    {
        const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos);
        if (pFC)
        {
            const SCROW nTopRow = pFC->GetSharedTopRow();
            const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
            // nTopRow <= nRow1 <= nBotRow, because otherwise pFC would not exist.
            if (nTopRow < nRow1)
            {
                // Upper part will be split off.
                pNewSharedRows->push_back(nTopRow);
                pNewSharedRows->push_back(nRow1 - 1);
            }
            if (nRow2 < nBotRow)
            {
                // Lower part will be split off.
                pNewSharedRows->push_back(nRow2 + 1);
                pNewSharedRows->push_back(nBotRow);
                bLowerSplitOff = true;
            }
        }
    }

    // Split formula grouping at the top and bottom boundaries.
    sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt);
    if (ValidRow(nRow2+1))
    {
        if (pNewSharedRows && !bLowerSplitOff && !GetDoc()->IsClipOrUndo())
        {
            sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nRow2);
            const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2);
            if (pFC)
            {
                const SCROW nTopRow = pFC->GetSharedTopRow();
                const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
                // nRow1 < nTopRow <= nRow2 < nBotRow
                if (nRow2 < nBotRow)
                {
                    // Lower part will be split off.
                    pNewSharedRows->push_back(nRow2 + 1);
                    pNewSharedRows->push_back(nBotRow);
                }
            }
        }

        aPos = maCells.position(it, nRow2+1);
        sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt);
    }
@@ -535,7 +624,8 @@ void ScColumn::AttachNewFormulaCell(
        rCell.SetDirty();
}

void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength )
void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength,
        const std::vector<SCROW>& rNewSharedRows )
{
    // Make sure the whole length consists of formula cells.
    if (aPos.first->type != sc::element_type_formula)
@@ -557,6 +647,8 @@ void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aP
    ScDocument* pDocument = GetDoc();
    if (!pDocument->IsClipOrUndo() && !pDocument->IsInsertingFromOtherDoc())
    {
        StartListeningUnshared( rNewSharedRows);

        sc::StartListeningContext aCxt(*pDocument);
        ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
        ScFormulaCell** ppEnd = pp + nLength;
@@ -1601,7 +1693,7 @@ public:

        // Stop all formula cells in the destination range first.
        sc::CellStoreType::position_type aPos = rDestCells.position(mrBlockPos.miCellPos, mnRowOffset);
        mrDestColumn.DetachFormulaCells(aPos, maNewCells.size());
        mrDestColumn.DetachFormulaCells(aPos, maNewCells.size(), nullptr);

        // Move the new cells to the destination range.
        sc::CellStoreType::iterator& itDestPos = mrBlockPos.miCellPos;
@@ -2104,7 +2196,8 @@ bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells 
    sc::CellStoreType::position_type aPos = maCells.position(nRow);

    // Detach all formula cells that will be overwritten.
    DetachFormulaCells(aPos, rCells.size());
    std::vector<SCROW> aNewSharedRows;
    DetachFormulaCells(aPos, rCells.size(), &aNewSharedRows);

    if (!GetDoc()->IsClipOrUndo())
    {
@@ -2124,7 +2217,7 @@ bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells 

    CellStorageModified();

    AttachNewFormulaCells(aPos, rCells.size());
    AttachNewFormulaCells(aPos, rCells.size(), aNewSharedRows);

    return true;
}
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 11a0609..bdefbe8 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -317,7 +317,8 @@ void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals )
        return;

    sc::CellStoreType::position_type aPos = maCells.position(nRow);
    DetachFormulaCells(aPos, rVals.size());
    std::vector<SCROW> aNewSharedRows;
    DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows);

    maCells.set(nRow, rVals.begin(), rVals.end());
    std::vector<sc::CellTextAttr> aDefaults(rVals.size());
@@ -325,6 +326,8 @@ void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals )

    CellStorageModified();

    StartListeningUnshared( aNewSharedRows);

    std::vector<SCROW> aRows;
    aRows.reserve(rVals.size());
    for (SCROW i = nRow; i <= nLastRow; ++i)
@@ -344,7 +347,7 @@ void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rD
        return;

    sc::CellStoreType::position_type aPos = maCells.position(nRow);
    DetachFormulaCells(aPos, nLen);
    DetachFormulaCells(aPos, nLen, nullptr);

    rDest.transferFrom(*this, nRow, nLen);

@@ -369,7 +372,7 @@ void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
        return;

    sc::CellStoreType::position_type aPos = maCells.position(nRow);
    DetachFormulaCells(aPos, rSrc.size());
    DetachFormulaCells(aPos, rSrc.size(), nullptr);

    rSrc.copyTo(*this, nRow);

@@ -445,7 +448,7 @@ void ScColumn::ConvertFormulaToValue(
        // No formula cells encountered.
        return;

    DetachFormulaCells(rCxt, nRow1, nRow2);
    DetachFormulaCells(rCxt, nRow1, nRow2, nullptr);

    // Undo storage to hold static values which will get swapped to the cell storage later.
    sc::CellValues aUndoCells;
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 65d87fb..bc2293f 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1085,7 +1085,7 @@ void ScTable::DetachFormulaCells(
    sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
        aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2);
        aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2, nullptr);
}

void ScTable::SetDirtyFromClip(
diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx
index bcfa045..ea815b1 100644
--- a/sc/source/core/tool/sharedformula.cxx
+++ b/sc/source/core/tool/sharedformula.cxx
@@ -17,6 +17,22 @@

namespace sc {

const ScFormulaCell* SharedFormulaUtil::getSharedTopFormulaCell(const CellStoreType::position_type& aPos)
{
    if (aPos.first->type != sc::element_type_formula)
        // Not a formula cell block.
        return nullptr;

    sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
    std::advance(it, aPos.second);
    const ScFormulaCell* pCell = *it;
    if (!pCell->IsShared())
        // Not a shared formula.
        return nullptr;

    return pCell->GetCellGroup()->mpTopCell;
}

bool SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt)
{
    SCROW nRow = aPos.first->position + aPos.second;