From 1ecb30cede30fea5a6a239d3d8da8ebbf8b79bf9 Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Fri, 18 Jan 2013 14:36:33 -0500 Subject: 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. Conflicts: sc/source/ui/view/drawvie4.cxx Change-Id: I0e7ce7cfbcdb3c9f120c6f0c72c58ab320901e6b Reviewed-on: https://gerrit.libreoffice.org/1757 Reviewed-by: Eike Rathke Tested-by: Eike Rathke --- sc/inc/column.hxx | 1 + sc/inc/document.hxx | 10 +++ sc/inc/table.hxx | 1 + sc/qa/unit/ucalc.cxx | 31 ++++++- sc/source/core/data/column.cxx | 114 ++++++++++++++++++++++++ sc/source/core/data/document.cxx | 15 ++++ sc/source/core/data/table2.cxx | 13 +++ sc/source/ui/view/drawvie4.cxx | 183 +++++++++++++++++++++++++++++++++++++-- 8 files changed, 362 insertions(+), 6 deletions(-) diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index 7abce092966a..3e927c84a6ca 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 76df28486a89..c7c09d8bee84 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1027,6 +1027,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 512dee2b87cc..26d5a692e7ae 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -350,6 +350,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 14d0da128da7..11ca7fe11696 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. @@ -263,6 +263,7 @@ public: CPPUNIT_TEST(testRangeList); CPPUNIT_TEST(testInput); CPPUNIT_TEST(testCellFunctions); + CPPUNIT_TEST(testCopyToDocument); CPPUNIT_TEST(testSheetsFunc); CPPUNIT_TEST(testVolatileFunc); CPPUNIT_TEST(testFormulaDepTracking); @@ -1146,6 +1147,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 d40e359c5a67..462abe7c5561 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -1205,6 +1205,120 @@ void ScColumn::CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, bool bKee } } +namespace { + +class FindInRows : std::unary_function +{ + 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 +{ + SCROW mnRow; +public: + FindAboveRow(SCROW nRow) : mnRow(nRow) {} + bool operator() (const ColEntry& rEntry) + { + return mnRow < rEntry.nRow; + } +}; + +struct DeleteCell : std::unary_function +{ + void operator() (ColEntry& rEntry) + { + rEntry.pCell->Delete(); + } +}; + +} + +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::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)); + std::for_each(it, itEnd, DeleteCell()); + 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 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(*it->pCell); + aEntry.pCell = new ScValueCell(rCell.GetValue()); + aCopied.push_back(aEntry); + } + break; + case CELLTYPE_STRING: + { + const ScStringCell& rCell = static_cast(*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(*it->pCell); + aEntry.pCell = new ScStringCell(rCell.GetString()); + aCopied.push_back(aEntry); + } + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell& rCell = static_cast(*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 65e904f1c923..95018d741a2f 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(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()] : NULL; + ScTable* pDestTab = nDestTab < static_cast(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 07ab79e8652c..f0bd119a0cc7 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 52837817cbe9..651aea2c45d5 100644 --- a/sc/source/ui/view/drawvie4.cxx +++ b/sc/source/ui/view/drawvie4.cxx @@ -42,6 +42,11 @@ #include "globstr.hrc" #include "chartarr.hxx" +#include +#include +#include +#include + using namespace com::sun::star; // STATIC DATA ----------------------------------------------------------- @@ -144,22 +149,190 @@ sal_Bool ScDrawView::BeginDrag( Window* pWindow, const Point& rStartPos ) return bReturn; } +namespace { + +void getRangeFromOle2Object(const SdrOle2Obj& rObj, std::vector& rRangeRep) +{ + if (!rObj.IsChart()) + // not a chart object. + return; + + uno::Reference xObj = rObj.GetObjRef(); + if (!xObj.is()) + return; + + uno::Reference xCompSupp(xObj, uno::UNO_QUERY); + if (!xCompSupp.is()) + return; + + uno::Reference xChartDoc(xCompSupp->getComponent(), uno::UNO_QUERY); + if (!xChartDoc.is()) + return; + + uno::Reference xDataSource(xChartDoc, uno::UNO_QUERY); + if (!xDataSource.is()) + return; + + // Get all data sources used in this chart. + uno::Sequence > xSeqs = xDataSource->getDataSequences(); + for (sal_Int32 i = 0, n = xSeqs.getLength(); i < n; ++i) + { + uno::Reference xLS = xSeqs[i]; + uno::Reference 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& rRanges) +{ + std::vector 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(*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(*pSubObj), aRangeReps); + } + + } + break; + default: + ; + } + } + + // Compile all range representation strings into ranges. + std::vector::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 +{ + std::vector& mrTabs; +public: + InsertTabIndex(std::vector& rTabs) : mrTabs(rTabs) {} + void operator() (const ScRange& rRange) + { + mrTabs.push_back(rRange.aStart.Tab()); + } +}; + +class CopyRangeData : std::unary_function +{ + 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& rRanges) +{ + // Get a list of referenced table indices. + std::vector 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::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 aRanges; + getChartSourceRanges(pDoc, rMarkList, aRanges); // update ScGlobal::pDrawClipDocShellRef - ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) ); - SdrModel* pModel = GetAllMarkedModel(); + 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); // Charts now always copy their data in addition to the source reference, so // 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(); -- cgit v1.2.3