diff options
author | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-01-18 14:36:33 -0500 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-01-18 14:41:42 -0500 |
commit | c55d52262ea1d5f869a9528fd051ee19e687f1cc (patch) | |
tree | a6e1006b20330584d62dd611ddd9580fb4a28e37 | |
parent | c881b1b50e3be023efd4dfdebddd002545ed71b3 (diff) |
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
-rw-r--r-- | sc/inc/column.hxx | 1 | ||||
-rw-r--r-- | sc/inc/document.hxx | 10 | ||||
-rw-r--r-- | sc/inc/table.hxx | 1 | ||||
-rw-r--r-- | sc/qa/unit/ucalc.cxx | 31 | ||||
-rw-r--r-- | sc/source/core/data/column.cxx | 105 | ||||
-rw-r--r-- | sc/source/core/data/document.cxx | 15 | ||||
-rw-r--r-- | sc/source/core/data/table2.cxx | 13 | ||||
-rw-r--r-- | sc/source/ui/view/drawvie4.cxx | 178 |
8 files changed, 349 insertions, 5 deletions
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index 96df4d99403f..f4370a98356a 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 7573a80c0b24..3874916a4793 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 fa03a7f1e292..30eb97abb66d 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 9e116c3aeb60..6a2ac0fbfcfe 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 aefe0437e923..8520dc3953fa 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 a3e5cfa6286e..d2ff1eecafee 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 6087841ce86d..2d909c020fb8 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 c49820b937ed..a6289d0b9aa7 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(); |