Use group-area listeners during copy & paste if we can.

This should reduce the number of area listeners generated esp. when
replicating tons of formula cells down the column.

Change-Id: I1ea8f51f667e6b0e1a646f84d79f5e8430b478d5
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index adcd17a..6187d4e 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -255,7 +255,8 @@
    void CopyFromClip(
        sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn );

    void StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 );
    void StartListeningInArea(
        sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 );

    void        RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );

diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index ea1e2a3..b1e06e1 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -437,7 +437,8 @@
        SCsCOL nDx, SCsROW nDy, ScTable* pTable );

    void StartListeningInArea(
        sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
        sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
        SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );

    void SetDirtyFromClip(
        SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans );
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 6f31cf5..67c51be 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1535,28 +1535,6 @@

namespace {

class StartListeningInAreaHandler
{
    sc::StartListeningContext& mrCxt;
public:
    StartListeningInAreaHandler(sc::StartListeningContext& rCxt) : mrCxt(rCxt) {}

    void operator() (size_t /*nRow*/, ScFormulaCell* p)
    {
        p->StartListeningTo(mrCxt);
    }
};

}

void ScColumn::StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
{
    StartListeningInAreaHandler aFunc(rCxt);
    sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
}

namespace {

void applyTextNumFormat( ScColumn& rCol, SCROW nRow, SvNumberFormatter* pFormatter )
{
    sal_uInt32 nFormat = pFormatter->GetStandardFormat(NUMBERFORMAT_TEXT);
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index f0d28eb..925bb4c 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -1135,4 +1135,97 @@
    return aRet.first != maCells.end();
}

namespace {

class StartListeningInAreaHandler
{
    sc::StartListeningContext& mrStartCxt;
    sc::EndListeningContext& mrEndCxt;

public:
    StartListeningInAreaHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
        mrStartCxt(rStartCxt), mrEndCxt(rEndCxt) {}

    void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
    {
        if (node.type != sc::element_type_formula)
            // We are only interested in formulas.
            return;

        ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
        ScFormulaCell** ppEnd = ppBeg + nDataSize;

        ScFormulaCell** pp = ppBeg;

        // If the first formula cell belongs to a group and it's not the top
        // cell, move up to the top cell of the group, and have all the extra
        // formula cells stop listening.

        ScFormulaCell* pFC = *pp;
        if (pFC->IsShared() && !pFC->IsSharedTop())
        {
            SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
            if (nBackTrackSize > 0)
            {
                assert(static_cast<size_t>(nBackTrackSize) <= nOffset);
                for (SCROW i = 0; i < nBackTrackSize; ++i)
                    --pp;
                endListening(pp, ppBeg);
            }
        }

        for (; pp != ppEnd; ++pp)
        {
            pFC = *pp;

            if (!pFC->IsSharedTop())
            {
                pFC->StartListeningTo(mrStartCxt);
                continue;
            }

            // If This is the last group in the range, see if the group
            // extends beyond the range, in which case have the excess
            // formula cells stop listening.
            size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
            if (nEndGroupPos > nDataSize)
            {
                size_t nExcessSize = nEndGroupPos - nDataSize;
                ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
                ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
                endListening(ppGrp, ppGrpEnd);

                // Register formula cells as a group.
                sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
                pp = ppEnd - 1; // Move to the one before the end position.
            }
            else
            {
                // Register formula cells as a group.
                sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
                pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
            }
        }
    }

private:
    void endListening( ScFormulaCell** pp, ScFormulaCell** ppEnd )
    {
        for (; pp != ppEnd; ++pp)
        {
            ScFormulaCell& rFC = **pp;
            rFC.EndListeningTo(mrEndCxt);
        }
    }
};

}

void ScColumn::StartListeningInArea(
    sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 )
{
    StartListeningInAreaHandler aFunc(rStartCxt, rEndCxt);
    sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 8440e2b..8e3871f 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2420,12 +2420,17 @@
{
    if (nInsFlag & IDF_CONTENTS)
    {
        sc::StartListeningContext aCxt(*this);
        boost::shared_ptr<sc::ColumnBlockPositionSet> pSet(
            new sc::ColumnBlockPositionSet(*this));

        sc::StartListeningContext aStartCxt(*this, pSet);
        sc::EndListeningContext aEndCxt(*this, pSet, NULL);

        SCTAB nMax = static_cast<SCTAB>(maTabs.size());
        ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
        for (; itr != itrEnd && *itr < nMax; ++itr)
            if (maTabs[*itr])
                maTabs[*itr]->StartListeningInArea(aCxt, nCol1, nRow1, nCol2, nRow2);
                maTabs[*itr]->StartListeningInArea(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
    }
}

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 6b3cbe4..788cb73 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3947,6 +3947,9 @@
    if (pDoc->IsClipOrUndo() || IsInChangeTrack())
        return;

    if (!HasBroadcaster())
        return;

    pDoc->SetDetectiveDirty(true);  // It has changed something

    if ( GetCode()->IsRecalcModeAlways() )
@@ -3989,6 +3992,9 @@
    if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
        return;

    if (!HasBroadcaster())
        return;

    ScDocument& rDoc = rCxt.getDoc();
    rDoc.SetDetectiveDirty(true);  // It has changed something

diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 66db45d..8d7ecc6 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1060,13 +1060,14 @@
}

void ScTable::StartListeningInArea(
    sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
    sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    if (nCol2 > MAXCOL) nCol2 = MAXCOL;
    if (nRow2 > MAXROW) nRow2 = MAXROW;
    if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
        for (SCCOL i = nCol1; i <= nCol2; i++)
            aCol[i].StartListeningInArea(rCxt, nRow1, nRow2);
            aCol[i].StartListeningInArea(rStartCxt, rEndCxt, nRow1, nRow2);
}

void ScTable::CopyToTable(