fdo#58988, fdo#58562: Populate draw clip document with data for charts.
Without populating the clip document, copying a chart (or charts) into
clipboard makes the charts lose all their referenced data, which causes
the pasted chart to appear empty.
Change-Id: I3675f76baed19b48cec403964c19df00725a044b
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 96df4d9..f4370a9 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -168,6 +168,7 @@ public:
void DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex, sal_uInt16 nDelFlag );
void DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag );
void CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, bool bKeepScenarioFlags) const;
void CopyStaticToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol);
void CopyFromClip(SCROW nRow1, SCROW nRow2, long nDy,
sal_uInt16 nInsFlag, bool bAsLink, bool bSkipAttrForEmpty, ScColumn& rColumn);
void StartListeningInArea( SCROW nRow1, SCROW nRow2 );
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 7573a80..3874916 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1029,6 +1029,16 @@ public:
const ScMarkData* pMarks = NULL, bool bAllTabs = false, bool bKeepScenarioFlags = false,
bool bIncludeObjects = false, bool bCloneNoteCaptions = true, bool bUseRangeForVBA = false );
/**
* Copy only raw cell values to another document. Formula cells are
* converted to raw cells. No formatting info are copied.
*
* @param rSrcRange source range in the source document
* @param nDestTab table in the clip document to copy to.
* @param pDestDoc document to copy to
*/
void CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument* pDestDoc);
void CopyTabToClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
SCTAB nTab, ScDocument* pClipDoc = NULL);
void CopyBlockFromClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index fa03a7f..30eb97a 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -351,6 +351,7 @@ public:
bool bKeepScenarioFlags, bool bCloneNoteCaptions);
void CopyToClip(const ScRangeList& rRanges, ScTable* pTable,
bool bKeepScenarioFlags, bool bCloneNoteCaptions);
void CopyStaticToDocument(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab);
void CopyFromClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, SCsCOL nDx, SCsROW nDy,
sal_uInt16 nInsFlag, bool bAsLink, bool bSkipAttrForEmpty, ScTable* pTable);
void StartListeningInArea( SCCOL nCol1, SCROW nRow1,
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 9e116c3..6a2ac0f 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -117,7 +117,7 @@ public:
void testRangeList();
void testInput();
void testCellFunctions();
void testCopyToDocument();
/**
* Make sure the SHEETS function gets properly updated during sheet
* insertion and removal.
@@ -268,6 +268,7 @@ public:
CPPUNIT_TEST(testRangeList);
CPPUNIT_TEST(testInput);
CPPUNIT_TEST(testCellFunctions);
CPPUNIT_TEST(testCopyToDocument);
CPPUNIT_TEST(testSheetsFunc);
CPPUNIT_TEST(testVolatileFunc);
CPPUNIT_TEST(testFormulaDepTracking);
@@ -1153,6 +1154,34 @@ void Test::testCellFunctions()
m_pDoc->DeleteTab(0);
}
void Test::testCopyToDocument()
{
CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
m_pDoc->SetString(0, 0, 0, "Header");
m_pDoc->SetString(0, 1, 0, "1");
m_pDoc->SetString(0, 2, 0, "2");
m_pDoc->SetString(0, 3, 0, "3");
m_pDoc->SetString(0, 4, 0, "=4/2");
m_pDoc->CalcAll();
// Copy statically to another document.
ScDocument aDestDoc(SCDOCMODE_DOCUMENT);
aDestDoc.InsertTab(0, "src");
m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, &aDestDoc); // Copy A2:A4
m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, &aDestDoc); // Copy A1
m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, &aDestDoc); // Copy A5:A8
CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), aDestDoc.GetString(0,0,0));
CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), aDestDoc.GetString(0,1,0));
CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), aDestDoc.GetString(0,2,0));
CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), aDestDoc.GetString(0,3,0));
CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), aDestDoc.GetString(0,4,0));
m_pDoc->DeleteTab(0);
}
void Test::testSheetsFunc()
{
rtl::OUString aTabName1("test1");
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index aefe043..8520dc3 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -1191,6 +1191,111 @@ void ScColumn::CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, bool bKee
}
}
namespace {
class FindInRows : std::unary_function<ColEntry, bool>
{
SCROW mnRow1;
SCROW mnRow2;
public:
FindInRows(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {}
bool operator() (const ColEntry& rEntry)
{
return mnRow1 <= rEntry.nRow && rEntry.nRow <= mnRow2;
}
};
class FindAboveRow : std::unary_function<ColEntry, bool>
{
SCROW mnRow;
public:
FindAboveRow(SCROW nRow) : mnRow(nRow) {}
bool operator() (const ColEntry& rEntry)
{
return mnRow < rEntry.nRow;
}
};
}
void ScColumn::CopyStaticToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol)
{
if (nRow1 > nRow2)
return;
// First, clear the destination column for the row range specified.
std::vector<ColEntry>::iterator it, itEnd;
it = std::find_if(rDestCol.maItems.begin(), rDestCol.maItems.end(), FindInRows(nRow1, nRow2));
if (it != rDestCol.maItems.end())
{
itEnd = std::find_if(it, rDestCol.maItems.end(), FindAboveRow(nRow2));
rDestCol.maItems.erase(it, itEnd);
}
// Determine the range of cells in the original column that need to be copied.
it = std::find_if(maItems.begin(), maItems.end(), FindInRows(nRow1, nRow2));
if (it != maItems.end())
itEnd = std::find_if(it, maItems.end(), FindAboveRow(nRow2));
// Clone and staticize all cells that need to be copied.
std::vector<ColEntry> aCopied;
aCopied.reserve(std::distance(it, itEnd));
for (; it != itEnd; ++it)
{
ColEntry aEntry;
aEntry.nRow = it->nRow;
switch (it->pCell->GetCellType())
{
case CELLTYPE_VALUE:
{
const ScValueCell& rCell = static_cast<const ScValueCell&>(*it->pCell);
aEntry.pCell = new ScValueCell(rCell.GetValue());
aCopied.push_back(aEntry);
}
break;
case CELLTYPE_STRING:
{
const ScStringCell& rCell = static_cast<const ScStringCell&>(*it->pCell);
aEntry.pCell = new ScStringCell(rCell.GetString());
aCopied.push_back(aEntry);
}
break;
case CELLTYPE_EDIT:
{
// Convert to a simple string cell.
const ScEditCell& rCell = static_cast<const ScEditCell&>(*it->pCell);
aEntry.pCell = new ScStringCell(rCell.GetString());
aCopied.push_back(aEntry);
}
break;
case CELLTYPE_FORMULA:
{
ScFormulaCell& rCell = static_cast<ScFormulaCell&>(*it->pCell);
if (rCell.GetDirty() && pDocument->GetAutoCalc())
rCell.Interpret();
if (rCell.GetErrorCode())
// Skip cells with error.
break;
if (rCell.IsValue())
aEntry.pCell = new ScValueCell(rCell.GetValue());
else
aEntry.pCell = new ScStringCell(rCell.GetString());
aCopied.push_back(aEntry);
}
break;
default:
; // ignore the rest.
}
}
// Insert the cells into destination column. At this point the
// destination column shouldn't have any cells within the specified range.
it = std::find_if(rDestCol.maItems.begin(), rDestCol.maItems.end(), FindAboveRow(nRow2));
rDestCol.maItems.insert(it, aCopied.begin(), aCopied.end());
}
void ScColumn::CopyToColumn(
SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, bool bMarked, ScColumn& rColumn,
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index a3e5cfa..d2ff1ee 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -1956,6 +1956,21 @@ void ScDocument::CopyToClip(const ScClipParam& rClipParam,
pClipDoc->ExtendMerge(aClipRange, true);
}
void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument* pDestDoc)
{
if (!pDestDoc)
return;
ScTable* pSrcTab = rSrcRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()] : NULL;
ScTable* pDestTab = nDestTab < static_cast<SCTAB>(pDestDoc->maTabs.size()) ? pDestDoc->maTabs[nDestTab] : NULL;
if (!pSrcTab || !pDestTab)
return;
pSrcTab->CopyStaticToDocument(
rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(), pDestTab);
}
void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1,
SCCOL nCol2, SCROW nRow2,
SCTAB nTab, ScDocument* pClipDoc)
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 6087841c..2d909c0 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -653,6 +653,19 @@ void ScTable::CopyToClip(const ScRangeList& rRanges, ScTable* pTable,
}
}
void ScTable::CopyStaticToDocument(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab)
{
if (nCol1 > nCol2)
return;
for (SCCOL i = nCol1; i <= nCol2; ++i)
{
ScColumn& rSrcCol = aCol[i];
ScColumn& rDestCol = pDestTab->aCol[i];
rSrcCol.CopyStaticToDocument(nRow1, nRow2, rDestCol);
}
}
void ScTable::CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
SCsCOL nDx, SCsROW nDy, ScTable* pTable)
{
diff --git a/sc/source/ui/view/drawvie4.cxx b/sc/source/ui/view/drawvie4.cxx
index c49820b..a6289d0 100644
--- a/sc/source/ui/view/drawvie4.cxx
+++ b/sc/source/ui/view/drawvie4.cxx
@@ -42,6 +42,8 @@
#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/embed/XComponentSupplier.hpp>
using namespace com::sun::star;
@@ -137,14 +139,183 @@ sal_Bool ScDrawView::BeginDrag( Window* pWindow, const Point& rStartPos )
return bReturn;
}
namespace {
void getRangeFromOle2Object(const SdrOle2Obj& rObj, std::vector<OUString>& rRangeRep)
{
if (!rObj.IsChart())
// not a chart object.
return;
uno::Reference<embed::XEmbeddedObject> xObj = rObj.GetObjRef();
if (!xObj.is())
return;
uno::Reference<embed::XComponentSupplier> xCompSupp(xObj, uno::UNO_QUERY);
if (!xCompSupp.is())
return;
uno::Reference<chart2::XChartDocument> xChartDoc(xCompSupp->getComponent(), uno::UNO_QUERY);
if (!xChartDoc.is())
return;
uno::Reference<chart2::data::XDataSource> xDataSource(xChartDoc, uno::UNO_QUERY);
if (!xDataSource.is())
return;
// Get all data sources used in this chart.
uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence> > xSeqs = xDataSource->getDataSequences();
for (sal_Int32 i = 0, n = xSeqs.getLength(); i < n; ++i)
{
uno::Reference<chart2::data::XLabeledDataSequence> xLS = xSeqs[i];
uno::Reference<chart2::data::XDataSequence> xSeq = xLS->getValues();
if (xSeq.is())
{
OUString aRep = xSeq->getSourceRangeRepresentation();
rRangeRep.push_back(aRep);
}
xSeq = xLS->getLabel();
if (xSeq.is())
{
OUString aRep = xSeq->getSourceRangeRepresentation();
rRangeRep.push_back(aRep);
}
}
}
/**
* Get all cell ranges that are referenced by the selected chart objects.
*/
void getChartSourceRanges(ScDocument* pDoc, const SdrMarkList& rObjs, std::vector<ScRange>& rRanges)
{
std::vector<OUString> aRangeReps;
for (size_t i = 0, n = rObjs.GetMarkCount(); i < n; ++i)
{
const SdrMark* pMark = rObjs.GetMark(i);
if (!pMark)
continue;
const SdrObject* pObj = pMark->GetMarkedSdrObj();
if (!pObj)
continue;
switch (pObj->GetObjIdentifier())
{
case OBJ_OLE2:
getRangeFromOle2Object(static_cast<const SdrOle2Obj&>(*pObj), aRangeReps);
break;
case OBJ_GRUP:
{
SdrObjListIter aIter(*pObj, IM_DEEPNOGROUPS);
for (SdrObject* pSubObj = aIter.Next(); pSubObj; pSubObj = aIter.Next())
{
if (pSubObj->GetObjIdentifier() != OBJ_OLE2)
continue;
getRangeFromOle2Object(static_cast<const SdrOle2Obj&>(*pSubObj), aRangeReps);
}
}
break;
default:
;
}
}
// Compile all range representation strings into ranges.
std::vector<OUString>::const_iterator it = aRangeReps.begin(), itEnd = aRangeReps.end();
for (; it != itEnd; ++it)
{
ScRange aRange;
ScAddress aAddr;
if (aRange.Parse(*it, pDoc, pDoc->GetAddressConvention()) & SCA_VALID)
rRanges.push_back(aRange);
else if (aAddr.Parse(*it, pDoc, pDoc->GetAddressConvention()) & SCA_VALID)
rRanges.push_back(aAddr);
}
}
class InsertTabIndex : std::unary_function<ScRange, void>
{
std::vector<SCTAB>& mrTabs;
public:
InsertTabIndex(std::vector<SCTAB>& rTabs) : mrTabs(rTabs) {}
void operator() (const ScRange& rRange)
{
mrTabs.push_back(rRange.aStart.Tab());
}
};
class CopyRangeData : std::unary_function<ScRange, void>
{
ScDocument* mpSrc;
ScDocument* mpDest;
public:
CopyRangeData(ScDocument* pSrc, ScDocument* pDest) : mpSrc(pSrc), mpDest(pDest) {}
void operator() (const ScRange& rRange)
{
OUString aTabName;
mpSrc->GetName(rRange.aStart.Tab(), aTabName);
SCTAB nTab;
if (!mpDest->GetTable(aTabName, nTab))
// Sheet by this name doesn't exist.
return;
mpSrc->CopyStaticToDocument(rRange, nTab, mpDest);
}
};
void copyChartRefDataToClipDoc(ScDocument* pSrcDoc, ScDocument* pClipDoc, const std::vector<ScRange>& rRanges)
{
// Get a list of referenced table indices.
std::vector<SCTAB> aTabs;
std::for_each(rRanges.begin(), rRanges.end(), InsertTabIndex(aTabs));
std::sort(aTabs.begin(), aTabs.end());
aTabs.erase(std::unique(aTabs.begin(), aTabs.end()), aTabs.end());
// Get table names.
if (aTabs.empty())
return;
// Create sheets only for referenced source sheets.
OUString aName;
std::vector<SCTAB>::const_iterator it = aTabs.begin(), itEnd = aTabs.end();
if (!pSrcDoc->GetName(*it, aName))
return;
pClipDoc->SetTabNameOnLoad(0, aName); // document initially has one sheet.
for (++it; it != itEnd; ++it)
{
if (!pSrcDoc->GetName(*it, aName))
return;
pClipDoc->AppendTabOnLoad(aName);
}
std::for_each(rRanges.begin(), rRanges.end(), CopyRangeData(pSrcDoc, pClipDoc));
}
}
void ScDrawView::DoCopy()
{
sal_Bool bAnyOle, bOneOle;
const SdrMarkList& rMarkList = GetMarkedObjectList();
CheckOle( rMarkList, bAnyOle, bOneOle );
std::vector<ScRange> aRanges;
getChartSourceRanges(pDoc, rMarkList, aRanges);
// update ScGlobal::pDrawClipDocShellRef
ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc(!aRanges.empty()) );
if (ScGlobal::pDrawClipDocShellRef)
{
// Copy data referenced by the chart objects to the draw clip
// document. We need to do this before GetMarkedObjModel() below.
ScDocShellRef xDocSh = *ScGlobal::pDrawClipDocShellRef;
ScDocument* pClipDoc = xDocSh->GetDocument();
copyChartRefDataToClipDoc(pDoc, pClipDoc, aRanges);
}
SdrModel* pModel = GetMarkedObjModel();
ScDrawLayer::SetGlobalDrawPersist(NULL);
@@ -152,7 +323,6 @@ void ScDrawView::DoCopy()
// there's no need to call SchDLL::Update for the charts in the clipboard doc.
// Update with the data (including NumberFormatter) from the live document would
// also store the NumberFormatter in the clipboard chart (#88749#)
// lcl_RefreshChartData( pModel, pViewData->GetDocument() );
ScDocShell* pDocSh = pViewData->GetDocShell();