Speed up pasting of single row onto multiple destination rows.

This is an extension of the earlier paste optimization of a single cell
across multiple destination cells.

Change-Id: I3a60300d3d0e11420d997bea8f7f540e948f56cc
diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index 5f558cd..ccaca67 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -54,10 +54,12 @@ class CopyFromClipContext : public ClipContextBase
    ScDocument* mpClipDoc;
    InsertDeleteFlags mnInsertFlag;
    InsertDeleteFlags mnDeleteFlag;
    ScCellValue maSingleCell;

    std::vector<ScCellValue> maSingleCells;
    std::vector<const ScPatternAttr*> maSinglePatterns;
    std::vector<const ScPostIt*> maSingleNotes;

    ScConditionalFormatList* mpCondFormatList;
    const ScPatternAttr* mpSinglePattern;
    const ScPostIt* mpSingleNote;
    bool mbAsLink:1;
    bool mbSkipAttrForEmptyCells:1;
    bool mbCloneNotes:1;
@@ -96,17 +98,24 @@ public:
    void setDeleteFlag( InsertDeleteFlags nFlag );
    InsertDeleteFlags getDeleteFlag() const;

    ScCellValue& getSingleCell();
    /**
     * Set the column size of a "single cell" row, which is used when copying
     * a single row of cells in a clip doc and pasting it into multiple
     * rows by replicating it.
     */
    void setSingleCellColumnSize( size_t nSize );

    ScCellValue& getSingleCell( size_t nColOffset );

    const ScPatternAttr* getSingleCellPattern( size_t nColOffset ) const;
    void setSingleCellPattern( size_t nColOffset, const ScPatternAttr* pAttr );

    const ScPostIt* getSingleCellNote( size_t nColOffset ) const;
    void setSingleCellNote( size_t nColOffset, const ScPostIt* pNote );

    void setCondFormatList( ScConditionalFormatList* pCondFormatList );
    ScConditionalFormatList* getCondFormatList();

    const ScPatternAttr* getSingleCellPattern() const;
    void setSingleCellPattern( const ScPatternAttr* pAttr );

    const ScPostIt* getSingleCellNote() const;
    void setSingleCellNote( const ScPostIt* pNote );

    void setTableProtected( bool b );
    bool isTableProtected() const;

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 1877a93..d9b208c 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -245,7 +245,7 @@ public:
    bool InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const;

    void DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol );
    void CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2 );
    void CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset );

    void CopyFromClip(
        sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn );
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
index d60a2e2..a414cfd4 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -36,7 +36,7 @@ CopyFromClipContext::CopyFromClipContext(ScDocument& rDoc,
    mnTabStart(-1), mnTabEnd(-1),
    mpRefUndoDoc(pRefUndoDoc), mpClipDoc(pClipDoc),
    mnInsertFlag(nInsertFlag), mnDeleteFlag(IDF_NONE),
    mpCondFormatList(NULL), mpSinglePattern(NULL), mpSingleNote(NULL),
    mpCondFormatList(NULL),
    mbAsLink(bAsLink), mbSkipAttrForEmptyCells(bSkipAttrForEmptyCells),
    mbCloneNotes (mnInsertFlag & (IDF_NOTE|IDF_ADDNOTES)),
    mbTableProtected(false)
@@ -106,9 +106,41 @@ InsertDeleteFlags CopyFromClipContext::getDeleteFlag() const
    return mnDeleteFlag;
}

ScCellValue& CopyFromClipContext::getSingleCell()
void CopyFromClipContext::setSingleCellColumnSize( size_t nSize )
{
    return maSingleCell;
    maSingleCells.resize(nSize);
    maSinglePatterns.resize(nSize, NULL);
    maSingleNotes.resize(nSize, NULL);
}

ScCellValue& CopyFromClipContext::getSingleCell( size_t nColOffset )
{
    assert(nColOffset < maSingleCells.size());
    return maSingleCells[nColOffset];
}

const ScPatternAttr* CopyFromClipContext::getSingleCellPattern( size_t nColOffset ) const
{
    assert(nColOffset < maSinglePatterns.size());
    return maSinglePatterns[nColOffset];
}

void CopyFromClipContext::setSingleCellPattern( size_t nColOffset, const ScPatternAttr* pAttr )
{
    assert(nColOffset < maSinglePatterns.size());
    maSinglePatterns[nColOffset] = pAttr;
}

const ScPostIt* CopyFromClipContext::getSingleCellNote( size_t nColOffset ) const
{
    assert(nColOffset < maSingleNotes.size());
    return maSingleNotes[nColOffset];
}

void CopyFromClipContext::setSingleCellNote( size_t nColOffset, const ScPostIt* pNote )
{
    assert(nColOffset < maSingleNotes.size());
    maSingleNotes[nColOffset] = pNote;
}

void CopyFromClipContext::setCondFormatList( ScConditionalFormatList* pCondFormatList )
@@ -121,26 +153,6 @@ ScConditionalFormatList* CopyFromClipContext::getCondFormatList()
    return mpCondFormatList;
}

const ScPatternAttr* CopyFromClipContext::getSingleCellPattern() const
{
    return mpSinglePattern;
}

void CopyFromClipContext::setSingleCellPattern( const ScPatternAttr* pAttr )
{
    mpSinglePattern = pAttr;
}

const ScPostIt* CopyFromClipContext::getSingleCellNote() const
{
    return mpSingleNote;
}

void CopyFromClipContext::setSingleCellNote( const ScPostIt* pNote )
{
    mpSingleNote = pNote;
}

void CopyFromClipContext::setTableProtected( bool b )
{
    mbTableProtected = b;
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 3e93a2d..94634dd 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -138,7 +138,7 @@ void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const Sc
    BroadcastCells(aDeletedRows, SC_HINT_DATACHANGED);
}

void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2 )
void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
{
    assert(nRow1 <= nRow2);

@@ -147,7 +147,7 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, 
    if (!pBlockPos)
        return;

    ScCellValue& rSrcCell = rCxt.getSingleCell();
    ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);

    InsertDeleteFlags nFlags = rCxt.getInsertFlag();

@@ -155,7 +155,7 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, 
    {
        if (!rCxt.isSkipAttrForEmptyCells() || rSrcCell.meType != CELLTYPE_NONE)
        {
            const ScPatternAttr* pAttr = rCxt.getSingleCellPattern();
            const ScPatternAttr* pAttr = rCxt.getSingleCellPattern(nColOffset);
            pAttrArray->SetPatternArea(nRow1, nRow2, pAttr, true);
        }
    }
@@ -221,7 +221,7 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, 
        }
    }

    const ScPostIt* pNote = rCxt.getSingleCellNote();
    const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
    if (pNote && (nFlags & (IDF_NOTE | IDF_ADDNOTES)) != IDF_NONE)
    {
        // Duplicate the cell note over the whole pasted range.
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index 7fcd4da..714bc23 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -70,142 +70,159 @@ bool ScDocument::CopyOneCellFromClip(
        return false;

    ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
    if (aClipRange.aStart != aClipRange.aEnd)
        // The source is not really a single cell. Bail out.
    if (aClipRange.aStart.Row() != aClipRange.aEnd.Row())
        // The source is not really a single row. Bail out.
        return false;

    SCCOL nSrcColSize = aClipRange.aEnd.Col() - aClipRange.aStart.Col() + 1;
    SCCOL nDestColSize = nCol2 - nCol1 + 1;
    if (nDestColSize < nSrcColSize)
        return false;

    ScAddress aSrcPos = aClipRange.aStart;
    if (pClipDoc->IsMerged(aSrcPos))
        // We don't handle merged source cell for this.
        return false;

    for (SCCOL nCol = aClipRange.aStart.Col(); nCol <= aClipRange.aEnd.Col(); ++nCol)
    {
        ScAddress aTestPos = aSrcPos;
        aTestPos.SetCol(nCol);
        if (pClipDoc->IsMerged(aTestPos))
            // We don't handle merged source cell for this.
            return false;
    }

    ScTable* pSrcTab = pClipDoc->FetchTable(aSrcPos.Tab());
    if (!pSrcTab)
        return false;

    ScCellValue& rSrcCell = rCxt.getSingleCell();
    const ScPatternAttr* pAttr = pClipDoc->GetPattern(aSrcPos);
    rCxt.setSingleCellPattern(pAttr);
    if (rCxt.isAsLink())
    rCxt.setSingleCellColumnSize(nSrcColSize);

    for (SCCOL nColOffset = 0; nColOffset < nSrcColSize; ++nColOffset, aSrcPos.IncCol())
    {
        ScSingleRefData aRef;
        aRef.InitAddress(aSrcPos);
        aRef.SetFlag3D(true);
        const ScPatternAttr* pAttr = pClipDoc->GetPattern(aSrcPos);
        rCxt.setSingleCellPattern(nColOffset, pAttr);

        ScTokenArray aArr;
        aArr.AddSingleReference(aRef);
        rSrcCell.set(new ScFormulaCell(pClipDoc, aSrcPos, aArr));
    }
    else
    {
        rSrcCell.set(pClipDoc->GetRefCellValue(aSrcPos));
        if ((rCxt.getInsertFlag() & (IDF_NOTE | IDF_ADDNOTES)) != IDF_NONE)
            rCxt.setSingleCellNote(nColOffset, pClipDoc->GetNote(aSrcPos));

        // Check the paste flag to see whether we want to paste this cell.  If the
        // flag says we don't want to paste this cell, we'll return with true.
        InsertDeleteFlags nFlags = rCxt.getInsertFlag();
        bool bNumeric  = (nFlags & IDF_VALUE) != IDF_NONE;
        bool bDateTime = (nFlags & IDF_DATETIME) != IDF_NONE;
        bool bString   = (nFlags & IDF_STRING) != IDF_NONE;
        bool bBoolean  = (nFlags & IDF_SPECIAL_BOOLEAN) != IDF_NONE;
        bool bFormula  = (nFlags & IDF_FORMULA) != IDF_NONE;

        switch (rSrcCell.meType)
        ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);
        if (rCxt.isAsLink())
        {
            case CELLTYPE_VALUE:
            {
                bool bPaste = rCxt.isDateCell(pSrcTab->aCol[aSrcPos.Col()], aSrcPos.Row()) ? bDateTime : bNumeric;
                if (!bPaste)
                    // Don't paste this.
                    rSrcCell.clear();
            }
            break;
            case CELLTYPE_STRING:
            case CELLTYPE_EDIT:
            {
                if (!bString)
                    // Skip pasting.
                    rSrcCell.clear();
            }
            break;
            case CELLTYPE_FORMULA:
            {
                if (bBoolean)
                {
                    // Check if this formula cell is a boolean cell, and if so, go ahead and paste it.
                    ScTokenArray* pCode = rSrcCell.mpFormula->GetCode();
                    if (pCode && pCode->GetLen() == 1)
                    {
                        const formula::FormulaToken* p = pCode->First();
                        if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
                            // This is a boolean formula. Good.
                            break;
                    }
                }
            ScSingleRefData aRef;
            aRef.InitAddress(aSrcPos);
            aRef.SetFlag3D(true);

                if (bFormula)
                    // Good.
                    break;
            ScTokenArray aArr;
            aArr.AddSingleReference(aRef);
            rSrcCell.set(new ScFormulaCell(pClipDoc, aSrcPos, aArr));
        }
        else
        {
            rSrcCell.assign(*pClipDoc, aSrcPos);

                sal_uInt16 nErr = rSrcCell.mpFormula->GetErrCode();
                if (nErr)
                {
                    // error codes are cloned with values
                    if (!bNumeric)
                        // Error code is treated as numeric value. Don't paste it.
                        rSrcCell.clear();
                }
                else if (rSrcCell.mpFormula->IsValue())
            // Check the paste flag to see whether we want to paste this cell.  If the
            // flag says we don't want to paste this cell, we'll return with true.
            InsertDeleteFlags nFlags = rCxt.getInsertFlag();
            bool bNumeric  = (nFlags & IDF_VALUE) != IDF_NONE;
            bool bDateTime = (nFlags & IDF_DATETIME) != IDF_NONE;
            bool bString   = (nFlags & IDF_STRING) != IDF_NONE;
            bool bBoolean  = (nFlags & IDF_SPECIAL_BOOLEAN) != IDF_NONE;
            bool bFormula  = (nFlags & IDF_FORMULA) != IDF_NONE;

            switch (rSrcCell.meType)
            {
                case CELLTYPE_VALUE:
                {
                    bool bPaste = rCxt.isDateCell(pSrcTab->aCol[aSrcPos.Col()], aSrcPos.Row()) ? bDateTime : bNumeric;
                    if (!bPaste)
                    {
                        // Don't paste this.
                        rSrcCell.clear();
                        break;
                    }

                    // Turn this into a numeric cell.
                    rSrcCell.set(rSrcCell.mpFormula->GetValue());
                }
                else if (bString)
                break;
                case CELLTYPE_STRING:
                case CELLTYPE_EDIT:
                {
                    svl::SharedString aStr = rSrcCell.mpFormula->GetString();
                    if (aStr.isEmpty())
                    {
                        // do not clone empty string
                    if (!bString)
                        // Skip pasting.
                        rSrcCell.clear();
                        break;
                }
                break;
                case CELLTYPE_FORMULA:
                {
                    if (bBoolean)
                    {
                        // Check if this formula cell is a boolean cell, and if so, go ahead and paste it.
                        ScTokenArray* pCode = rSrcCell.mpFormula->GetCode();
                        if (pCode && pCode->GetLen() == 1)
                        {
                            const formula::FormulaToken* p = pCode->First();
                            if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
                                // This is a boolean formula. Good.
                                break;
                        }
                    }

                    // Turn this into a string or edit cell.
                    if (rSrcCell.mpFormula->IsMultilineResult())
                    if (bFormula)
                        // Good.
                        break;

                    sal_uInt16 nErr = rSrcCell.mpFormula->GetErrCode();
                    if (nErr)
                    {
                        // TODO : Add shared string support to the edit engine to
                        // make this process simpler.
                        ScFieldEditEngine& rEngine = GetEditEngine();
                        rEngine.SetText(rSrcCell.mpFormula->GetString().getString());
                        boost::scoped_ptr<EditTextObject> pObj(rEngine.CreateTextObject());
                        pObj->NormalizeString(GetSharedStringPool());
                        rSrcCell.set(*pObj);
                        // error codes are cloned with values
                        if (!bNumeric)
                            // Error code is treated as numeric value. Don't paste it.
                            rSrcCell.clear();
                    }
                    else if (rSrcCell.mpFormula->IsValue())
                    {
                        bool bPaste = rCxt.isDateCell(pSrcTab->aCol[aSrcPos.Col()], aSrcPos.Row()) ? bDateTime : bNumeric;
                        if (!bPaste)
                        {
                            // Don't paste this.
                            rSrcCell.clear();
                            break;
                        }

                        // Turn this into a numeric cell.
                        rSrcCell.set(rSrcCell.mpFormula->GetValue());
                    }
                    else if (bString)
                    {
                        svl::SharedString aStr = rSrcCell.mpFormula->GetString();
                        if (aStr.isEmpty())
                        {
                            // do not clone empty string
                            rSrcCell.clear();
                            break;
                        }

                        // Turn this into a string or edit cell.
                        if (rSrcCell.mpFormula->IsMultilineResult())
                        {
                            // TODO : Add shared string support to the edit engine to
                            // make this process simpler.
                            ScFieldEditEngine& rEngine = GetEditEngine();
                            rEngine.SetText(rSrcCell.mpFormula->GetString().getString());
                            boost::scoped_ptr<EditTextObject> pObj(rEngine.CreateTextObject());
                            pObj->NormalizeString(GetSharedStringPool());
                            rSrcCell.set(*pObj);
                        }
                        else
                            rSrcCell.set(rSrcCell.mpFormula->GetString());
                    }
                    else
                        rSrcCell.set(rSrcCell.mpFormula->GetString());
                        // We don't want to paste this.
                        rSrcCell.clear();
                }
                else
                    // We don't want to paste this.
                break;
                case CELLTYPE_NONE:
                default:
                    // There is nothing to paste.
                    rSrcCell.clear();
            }
            break;
            case CELLTYPE_NONE:
            default:
                // There is nothing to paste.
                rSrcCell.clear();
        }
    }

    if ((rCxt.getInsertFlag() & (IDF_NOTE | IDF_ADDNOTES)) != IDF_NONE)
        rCxt.setSingleCellNote(pClipDoc->GetNote(aSrcPos));

    // All good. Proceed with the pasting.

    SCTAB nTabEnd = rCxt.getTabEnd();
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index 36a22c6..5686921 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -55,8 +55,16 @@ void ScTable::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScT
void ScTable::CopyOneCellFromClip(
    sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    ScRange aSrcRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
    SCCOL nSrcColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;

    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
        aCol[nCol].CopyOneCellFromClip(rCxt, nRow1, nRow2);
    {
        SCCOL nColOffset = nCol - nCol1;
        nColOffset = nColOffset % nSrcColSize;
        assert(nColOffset >= 0);
        aCol[nCol].CopyOneCellFromClip(rCxt, nRow1, nRow2, nColOffset);
    }
}

void ScTable::SetValues( SCCOL nCol, SCROW nRow, const std::vector<double>& rVals )