tdf#99969: make sure to copy the chart source ranges to clipboard

... even when they are outside of the copied cell range. Otherwise,
it is  impossible to transfer the missing data when switching to
own data on paste.

The copy of the missing ranges avoids copying cell attributes, for
performance reasons, but also to avoid overwriting the attributes
of already copied cells. Otherwise, ScDrawLayer::CopyToClip would
need the bKeepScenarioFlags, or the CopyToClipContext used in the
caller ScDocument::CopyToClip, for consistent copy; or a method to
avoid overwriting already copied cells (this change simply copies
all chart ranges, withiut checking if they were copied already).

Change-Id: Id02e0c20517e7e8a17bb0a31d1b230196cda1a58
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164294
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164397
diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index b3ce874..d93e6ac 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -161,12 +161,15 @@ public:
class CopyToClipContext final : public ClipContextBase
{
    bool mbKeepScenarioFlags:1;
    bool mbCopyChartRanges : 1 = false; // Copying ranges not included in selection:
                                        // only copy data, not cell attributes

public:
    CopyToClipContext(ScDocument& rDoc, bool bKeepScenarioFlags);
    CopyToClipContext(ScDocument& rDoc, bool bKeepScenarioFlags, bool bCopyChartRanges = false);
    virtual ~CopyToClipContext() override;

    bool isKeepScenarioFlags() const;
    bool isCopyChartRanges() const { return mbCopyChartRanges; }
};

class CopyToDocContext final : public ClipContextBase
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
index ce6974d..be145f9 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -403,8 +403,8 @@ bool CopyFromClipContext::isDateCell( const ScColumn& rCol, SCROW nRow ) const
}

CopyToClipContext::CopyToClipContext(
    ScDocument& rDoc, bool bKeepScenarioFlags) :
    ClipContextBase(rDoc), mbKeepScenarioFlags(bKeepScenarioFlags) {}
    ScDocument& rDoc, bool bKeepScenarioFlags, bool bCopyChartRanges) :
    ClipContextBase(rDoc), mbKeepScenarioFlags(bKeepScenarioFlags), mbCopyChartRanges(bCopyChartRanges) {}

CopyToClipContext::~CopyToClipContext() {}

diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 5bae9b7..ceadfc2 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -880,14 +880,16 @@ public:
void ScColumn::CopyToClip(
    sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const
{
    pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
                          rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );
    if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
        pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
                              rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );

    {
        CopyToClipHandler aFunc(GetDoc(), *this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol));
        sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    }

    if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
    {
        CopyTextAttrToClipHandler aFunc(rColumn.maCellTextAttrs);
        sc::ParseBlock(maCellTextAttrs.begin(), maCellTextAttrs, aFunc, nRow1, nRow2);
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 15bb28f..2bca3a7 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2218,6 +2218,7 @@ void ScDocument::CopyToClip(const ScClipParam& rClipParam,
    sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
    CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);

    // 1. Copy selected cells
    for (SCTAB i = 0; i < nEndTab; ++i)
    {
        if (!maTabs[i] || i >= pClipDoc->GetTableCount() || !pClipDoc->maTabs[i])
@@ -2227,12 +2228,17 @@ void ScDocument::CopyToClip(const ScClipParam& rClipParam,
            continue;

        maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
    }

        if (mpDrawLayer && bIncludeObjects)
    // 2. Copy drawing objects in the selection. Do in after the first "copy cells" pass, because
    // the embedded objects (charts) coud reference cells from tabs not (yet) copied; doing it now
    // allows to know what is already copied, to not owerwrite attributes of already copied data.
    if (mpDrawLayer && bIncludeObjects)
    {
        for (SCTAB i = 0; i < nEndTab; ++i)
        {
            //  also copy drawing objects
            tools::Rectangle aObjRect = GetMMRect(
                aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
            tools::Rectangle aObjRect = GetMMRect(aClipRange.aStart.Col(), aClipRange.aStart.Row(),
                                                  aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
            mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
        }
    }
diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx
index b4fed97..d5f2cc0 100644
--- a/sc/source/core/data/drwlayer.cxx
+++ b/sc/source/core/data/drwlayer.cxx
@@ -88,6 +88,7 @@
#include <docpool.hxx>
#include <detfunc.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <clipcontext.hxx>
#include <clipparam.hxx>

#include <memory>
@@ -1853,6 +1854,34 @@ void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rec

            pDestPage->InsertObject(pNewObject.get());

            // Store the chart's source data to the clipboad document, even when it's out of the
            // copied range. It will be ignored when pasted to the same document; when pasted to
            // another document, ScDocument::mpClipParam will provide the actually copied ranges,
            // and the data copied here will be used to break connection and switch to own data
            // in ScDrawLayer::CopyFromClip.
            if (xOldChart && !xOldChart->hasInternalDataProvider())
            {
                sc::CopyToClipContext aCxt(*pClipDoc, false, true);
                OUString aChartName = static_cast<SdrOle2Obj*>(pOldObject)->GetPersistName();
                std::vector<ScRangeList> aRangesVector;
                pDoc->GetChartRanges(aChartName, aRangesVector, *pDoc);
                for (const ScRangeList& ranges : aRangesVector)
                {
                    for (const ScRange& r : ranges)
                    {
                        for (SCTAB i = r.aStart.Tab(); i <= r.aEnd.Tab(); ++i)
                        {
                            ScTable* pTab = pDoc->FetchTable(i);
                            ScTable* pClipTab = pClipDoc->FetchTable(i);
                            if (!pTab || !pClipTab)
                                continue;
                            pTab->CopyToClip(aCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(),
                                             r.aEnd.Row(), pClipTab);
                        }
                    }
                }
            }

            //  no undo needed in clipboard document
            //  charts are not updated
        }