sc/inc/SparklineGroup.hxx                |   31 +++++++-
 sc/inc/clipcontext.hxx                   |    7 +
 sc/inc/column.hxx                        |   11 ++
 sc/inc/document.hxx                      |    2 
 sc/inc/mtvcellfunc.hxx                   |    8 ++
 sc/inc/table.hxx                         |    2 
 sc/qa/unit/SparklineImportExportTest.cxx |   12 +--
 sc/qa/unit/SparklineTest.cxx             |  120 +++++++++++++++++++++++++++++--
 sc/source/core/data/clipcontext.cxx      |   21 ++++-
 sc/source/core/data/column.cxx           |    1 
 sc/source/core/data/column2.cxx          |   69 +++++++++++++++++
 sc/source/core/data/column3.cxx          |   14 +++
 sc/source/core/data/column4.cxx          |   50 +++++++++++-
 sc/source/core/data/document.cxx         |    4 -
 sc/source/core/data/document10.cxx       |    3 
 sc/source/core/data/table2.cxx           |   11 +-
 sc/source/ui/inc/cliputil.hxx            |    6 +
 sc/source/ui/view/cellsh.cxx             |    4 -
 sc/source/ui/view/output.cxx             |    4 -
 19 files changed, 341 insertions(+), 39 deletions(-)

New commits:
commit 7edd5f552c76bbe05c196cfdcdc5f94292a38a30
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Sat Mar 19 12:52:21 2022 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Tue Apr 12 01:23:55 2022 +0200

    sc: add support for copy/cut and paste of Sparklines
    
    Currently cut,copy and paste will copy the Sparkline and create
    a new SparklineGroup for each cell in the new cell range. This
    probably need to be adjusted so the SparklineGroup is shared.
    
    Change-Id: I6f86bb026753b2b4b5bfa46aca4ca9794721f311
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132473
    Tested-by: Tomaž Vajngerl <qui...@gmail.com>
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    (cherry picked from commit b8cf500ed8ac7bd01a351e2815ce8251e506d79c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132828
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sc/inc/SparklineGroup.hxx b/sc/inc/SparklineGroup.hxx
index 0d3935492d04..9f00985e9f61 100644
--- a/sc/inc/SparklineGroup.hxx
+++ b/sc/inc/SparklineGroup.hxx
@@ -105,7 +105,36 @@ public:
     {
     }
 
-    SparklineGroup(const SparklineGroup&) = delete;
+    SparklineGroup(SparklineGroup const& pOtherSparkline)
+        : m_aColorSeries(pOtherSparkline.m_aColorSeries)
+        , m_aColorNegative(pOtherSparkline.m_aColorNegative)
+        , m_aColorAxis(pOtherSparkline.m_aColorAxis)
+        , m_aColorMarkers(pOtherSparkline.m_aColorMarkers)
+        , m_aColorFirst(pOtherSparkline.m_aColorFirst)
+        , m_aColorLast(pOtherSparkline.m_aColorLast)
+        , m_aColorHigh(pOtherSparkline.m_aColorHigh)
+        , m_aColorLow(pOtherSparkline.m_aColorLow)
+        , m_eMinAxisType(pOtherSparkline.m_eMinAxisType)
+        , m_eMaxAxisType(pOtherSparkline.m_eMaxAxisType)
+        , m_fLineWeight(pOtherSparkline.m_fLineWeight)
+        , m_eType(pOtherSparkline.m_eType)
+        , m_bDateAxis(pOtherSparkline.m_bDateAxis)
+        , m_eDisplayEmptyCellsAs(pOtherSparkline.m_eDisplayEmptyCellsAs)
+        , m_bMarkers(pOtherSparkline.m_bMarkers)
+        , m_bHigh(pOtherSparkline.m_bHigh)
+        , m_bLow(pOtherSparkline.m_bLow)
+        , m_bFirst(pOtherSparkline.m_bFirst)
+        , m_bLast(pOtherSparkline.m_bLast)
+        , m_bNegative(pOtherSparkline.m_bNegative)
+        , m_bDisplayXAxis(pOtherSparkline.m_bDisplayXAxis)
+        , m_bDisplayHidden(pOtherSparkline.m_bDisplayHidden)
+        , m_bRightToLeft(pOtherSparkline.m_bRightToLeft)
+        , m_aManualMax(pOtherSparkline.m_aManualMax)
+        , m_aManualMin(pOtherSparkline.m_aManualMin)
+        , m_sUID(pOtherSparkline.m_sUID)
+    {
+    }
+
     SparklineGroup& operator=(const SparklineGroup&) = delete;
 };
 
diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index 32e2dd97767a..b09e1be78761 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -12,6 +12,7 @@
 #include "address.hxx"
 #include "cellvalue.hxx"
 #include "celltextattr.hxx"
+#include "Sparkline.hxx"
 
 #include <memory>
 #include <vector>
@@ -60,11 +61,11 @@ class SC_DLLPUBLIC CopyFromClipContext final : public 
ClipContextBase
     std::vector<sc::CellTextAttr> maSingleCellAttrs;
     std::vector<const ScPatternAttr*> maSinglePatterns;
     std::vector<const ScPostIt*> maSingleNotes;
+    std::vector<std::shared_ptr<sc::Sparkline>> maSingleSparkline;
 
     ScConditionalFormatList* mpCondFormatList;
     bool mbAsLink:1;
     bool mbSkipEmptyCells:1;
-    bool mbCloneNotes:1;
     bool mbTableProtected:1;
 
 public:
@@ -119,6 +120,9 @@ public:
     const ScPostIt* getSingleCellNote( size_t nColOffset ) const;
     void setSingleCellNote( size_t nColOffset, const ScPostIt* pNote );
 
+    std::shared_ptr<sc::Sparkline> const& getSingleSparkline(size_t 
nColOffset) const;
+    void setSingleSparkline(size_t nColOffset, std::shared_ptr<sc::Sparkline> 
const& pSparkline);
+
     void setCondFormatList( ScConditionalFormatList* pCondFormatList );
     ScConditionalFormatList* getCondFormatList();
 
@@ -135,6 +139,7 @@ public:
      */
     bool isSkipEmptyCells() const;
     bool isCloneNotes() const;
+    bool isCloneSparklines() const;
     bool isDateCell( const ScColumn& rCol, SCROW nRow ) const;
 };
 
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 6e2039a27456..962e6cc1f2ea 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -222,6 +222,9 @@ friend class sc::CellStoreEvent;
         SCROW nRow, SCTAB nTab, const OUString& rString, 
formula::FormulaGrammar::AddressConvention eConv,
         const ScSetStringParam* pParam );
 
+    void duplicateSparkline(sc::CopyFromClipContext& rContext, 
sc::ColumnBlockPosition* pBlockPos,
+                            size_t nColOffset, size_t nDestSize, ScAddress 
aDestPosition);
+
 public:
 
     /** Broadcast mode for SetDirty(SCROW,SCROW,BroadcastMode). */
@@ -247,6 +250,8 @@ public:
     const sc::CellTextAttrStoreType& GetCellAttrStore() const { return 
maCellTextAttrs; }
     sc::CellNoteStoreType& GetCellNoteStore() { return maCellNotes; }
     const sc::CellNoteStoreType& GetCellNoteStore() const { return 
maCellNotes; }
+    sc::SparklineStoreType& GetSparklineStore() { return maSparklines; }
+    const sc::SparklineStoreType& GetSparklineStore() const { return 
maSparklines; }
 
     ScRefCellValue GetCellValue( SCROW nRow ) const;
     ScRefCellValue GetCellValue( sc::ColumnBlockPosition& rBlockPos, SCROW 
nRow );
@@ -660,6 +665,10 @@ public:
     void CreateSparklineCell(SCROW nRow, std::shared_ptr<sc::Sparkline> const& 
pSparkline);
     void DeleteSparklineCells(sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, 
SCROW nRow2);
     bool DeleteSparkline(SCROW nRow);
+    bool IsSparklinesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const;
+    void CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, ScColumn& 
rDestCol, SCROW nRowOffsetDest) const;
+    void DuplicateSparklines(SCROW nStartRow, size_t nDataSize, ScColumn& 
rDestCol,
+                             sc::ColumnBlockPosition& rDestBlockPos, SCROW 
nRowOffsetDest = 0) const;
 
     // cell notes
     ScPostIt* GetCellNote( SCROW nRow );
@@ -688,7 +697,7 @@ public:
         SCROW nRowOffsetDest = 0) const;
 
     void DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol,
-                            sc::ColumnBlockPosition& maDestBlockPos, bool 
bCloneCaption, SCROW nRowOffsetDest=0 ) const;
+                            sc::ColumnBlockPosition& rDestBlockPos, bool 
bCloneCaption, SCROW nRowOffsetDest = 0) const;
 
     void UpdateNoteCaptions( SCROW nRow1, SCROW nRow2 );
 
diff --git a/sc/inc/mtvcellfunc.hxx b/sc/inc/mtvcellfunc.hxx
index a2a708d5f8fc..89e41fb915fd 100644
--- a/sc/inc/mtvcellfunc.hxx
+++ b/sc/inc/mtvcellfunc.hxx
@@ -178,6 +178,14 @@ ProcessBroadcaster(
         BroadcasterStoreType, broadcaster_block, FuncElem, 
FuncElseNoOp<size_t> >(it, rStore, nRow1, nRow2, rFuncElem, aElse);
 }
 
+template<typename Functor>
+typename SparklineStoreType::const_iterator
+ParseSparkline(const SparklineStoreType::const_iterator& itPos, const 
SparklineStoreType& rStore, SCROW nStart, SCROW nEnd, Functor& rFunctor)
+{
+    FuncElseNoOp<size_t> aElse;
+    return ParseElements1<SparklineStoreType, sparkline_block, Functor, 
FuncElseNoOp<size_t> >(itPos, rStore, nStart, nEnd, rFunctor, aElse);
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/SparklineTest.cxx b/sc/qa/unit/SparklineTest.cxx
index 2a2dfde71b5b..167c4e4d9e3d 100644
--- a/sc/qa/unit/SparklineTest.cxx
+++ b/sc/qa/unit/SparklineTest.cxx
@@ -9,6 +9,8 @@
 
 #include "helper/qahelper.hxx"
 #include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <cliputil.hxx>
 #include <Sparkline.hxx>
 #include <SparklineGroup.hxx>
 
@@ -46,10 +48,14 @@ public:
 
     void testAddSparkline();
     void testDeleteSprkline();
+    void testCopyPasteSparkline();
+    void testCutPasteSparkline();
 
     CPPUNIT_TEST_SUITE(SparklineTest);
     CPPUNIT_TEST(testAddSparkline);
     CPPUNIT_TEST(testDeleteSprkline);
+    CPPUNIT_TEST(testCopyPasteSparkline);
+    CPPUNIT_TEST(testCutPasteSparkline);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -118,6 +124,110 @@ void SparklineTest::testDeleteSprkline()
     xDocSh->DoClose();
 }
 
+void SparklineTest::testCopyPasteSparkline()
+{
+    ScDocShellRef xDocSh = loadEmptyDocument();
+    CPPUNIT_ASSERT(xDocSh);
+
+    ScDocument& rDocument = xDocSh->GetDocument();
+    ScTabViewShell* pViewShell = xDocSh->GetBestViewShell(false);
+    CPPUNIT_ASSERT(pViewShell);
+
+    auto* pCreatedSparkline = createTestSparkline(rDocument);
+    CPPUNIT_ASSERT(pCreatedSparkline);
+
+    ScRange aSourceRange(0, 6, 0, 0, 6, 0);
+    auto pSparkline = rDocument.GetSparkline(aSourceRange.aStart);
+
+    CPPUNIT_ASSERT(pSparkline);
+    CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparkline->getColumn());
+    CPPUNIT_ASSERT_EQUAL(SCROW(6), pSparkline->getRow());
+
+    // CopyToClip / CopyFromClip with a aClipDoc
+    {
+        ScDocument aClipDoc(SCDOCMODE_CLIP);
+        copyToClip(&rDocument, aSourceRange, &aClipDoc);
+
+        auto pClipSparkline = aClipDoc.GetSparkline(aSourceRange.aStart);
+        CPPUNIT_ASSERT(pClipSparkline);
+
+        ScRange aPasteRange(0, 7, 0, 0, 7, 0);
+
+        ScMarkData aMark(rDocument.GetSheetLimits());
+        aMark.SetMarkArea(aPasteRange);
+        rDocument.CopyFromClip(aPasteRange, aMark, InsertDeleteFlags::ALL, 
nullptr, &aClipDoc);
+
+        auto pSparklineCopy = rDocument.GetSparkline(aPasteRange.aStart);
+        CPPUNIT_ASSERT(pSparklineCopy);
+
+        CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparklineCopy->getColumn());
+        CPPUNIT_ASSERT_EQUAL(SCROW(7), pSparklineCopy->getRow());
+    }
+
+    // Copy / Paste with a ClipDoc
+    {
+        pViewShell->GetViewData().GetMarkData().SetMarkArea(aSourceRange);
+
+        // Copy
+        ScDocument aClipDoc(SCDOCMODE_CLIP);
+        pViewShell->GetViewData().GetView()->CopyToClip(&aClipDoc, false, 
false, false, false);
+
+        // Paste
+        ScRange aPasteRange(0, 8, 0, 0, 8, 0);
+
+        pViewShell->GetViewData().GetMarkData().SetMarkArea(aPasteRange);
+        
pViewShell->GetViewData().GetView()->PasteFromClip(InsertDeleteFlags::ALL, 
&aClipDoc);
+
+        auto pSparklineCopy = rDocument.GetSparkline(aPasteRange.aStart);
+        CPPUNIT_ASSERT(pSparklineCopy);
+
+        CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparklineCopy->getColumn());
+        CPPUNIT_ASSERT_EQUAL(SCROW(8), pSparklineCopy->getRow());
+    }
+
+    xDocSh->DoClose();
+}
+
+void SparklineTest::testCutPasteSparkline()
+{
+    ScDocShellRef xDocSh = loadEmptyDocument();
+    CPPUNIT_ASSERT(xDocSh);
+
+    ScDocument& rDocument = xDocSh->GetDocument();
+    ScTabViewShell* pViewShell = xDocSh->GetBestViewShell(false);
+    CPPUNIT_ASSERT(pViewShell);
+
+    auto* pCreatedSparkline = createTestSparkline(rDocument);
+    CPPUNIT_ASSERT(pCreatedSparkline);
+
+    ScRange aSourceRange(0, 6, 0, 0, 6, 0);
+    auto pSparkline = rDocument.GetSparkline(aSourceRange.aStart);
+
+    CPPUNIT_ASSERT(pSparkline);
+    CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparkline->getColumn());
+    CPPUNIT_ASSERT_EQUAL(SCROW(6), pSparkline->getRow());
+
+    // Mark source range
+    pViewShell->GetViewData().GetMarkData().SetMarkArea(aSourceRange);
+
+    // Cut
+    pViewShell->GetViewData().GetView()->CopyToClip(nullptr, true /*bCut*/, 
false, false, true);
+
+    // Paste
+    ScRange aPasteRange(0, 7, 0, 0, 7, 0);
+    pViewShell->GetViewData().GetMarkData().SetMarkArea(aPasteRange);
+    ScClipUtil::PasteFromClipboard(pViewShell->GetViewData(), pViewShell, 
false);
+
+    // Check
+    auto pSparklineCopy = rDocument.GetSparkline(aPasteRange.aStart);
+    CPPUNIT_ASSERT(pSparklineCopy);
+
+    CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparklineCopy->getColumn());
+    CPPUNIT_ASSERT_EQUAL(SCROW(7), pSparklineCopy->getRow());
+
+    xDocSh->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SparklineTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/clipcontext.cxx 
b/sc/source/core/data/clipcontext.cxx
index 02e2bcc86652..70f2319a185f 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -46,7 +46,6 @@ CopyFromClipContext::CopyFromClipContext(ScDocument& rDoc,
     mnInsertFlag(nInsertFlag), mnDeleteFlag(InsertDeleteFlags::NONE),
     mpCondFormatList(nullptr),
     mbAsLink(bAsLink), mbSkipEmptyCells(bSkipEmptyCells),
-    mbCloneNotes (mnInsertFlag & 
(InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES)),
     mbTableProtected(false)
 {
 }
@@ -120,6 +119,7 @@ void CopyFromClipContext::setSingleCellColumnSize( size_t 
nSize )
     maSingleCellAttrs.resize(nSize);
     maSinglePatterns.resize(nSize, nullptr);
     maSingleNotes.resize(nSize, nullptr);
+    maSingleSparkline.resize(nSize);
 }
 
 ScCellValue& CopyFromClipContext::getSingleCell( size_t nColOffset )
@@ -300,6 +300,18 @@ void CopyFromClipContext::setSingleCellNote( size_t 
nColOffset, const ScPostIt*
     maSingleNotes[nColOffset] = pNote;
 }
 
+std::shared_ptr<sc::Sparkline> const& 
CopyFromClipContext::getSingleSparkline(size_t nColOffset) const
+{
+    assert(nColOffset < maSingleSparkline.size());
+    return maSingleSparkline[nColOffset];
+}
+
+void CopyFromClipContext::setSingleSparkline(size_t nColOffset, 
std::shared_ptr<sc::Sparkline> const& pSparkline)
+{
+    assert(nColOffset < maSingleSparkline.size());
+    maSingleSparkline[nColOffset] = pSparkline;
+}
+
 void CopyFromClipContext::setCondFormatList( ScConditionalFormatList* 
pCondFormatList )
 {
     mpCondFormatList = pCondFormatList;
@@ -332,7 +344,12 @@ bool CopyFromClipContext::isSkipEmptyCells() const
 
 bool CopyFromClipContext::isCloneNotes() const
 {
-    return mbCloneNotes;
+    return bool(mnInsertFlag & (InsertDeleteFlags::NOTE | 
InsertDeleteFlags::ADDNOTES));
+}
+
+bool CopyFromClipContext::isCloneSparklines() const
+{
+    return bool(mnInsertFlag & InsertDeleteFlags::SPARKLINES);
 }
 
 bool CopyFromClipContext::isDateCell( const ScColumn& rCol, SCROW nRow ) const
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index da2763064c31..94c83f002dfe 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -918,6 +918,7 @@ public:
             setDefaultAttrsToDest(nTopRow, nDataSize);
 
         mrSrcCol.DuplicateNotes(nTopRow, nDataSize, mrDestCol, maDestPos, 
false);
+        mrSrcCol.DuplicateSparklines(nTopRow, nDataSize, mrDestCol, maDestPos);
     }
 };
 
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 5a6d4c435e68..fc262bc78627 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -42,6 +42,7 @@
 #include <rowheightcontext.hxx>
 #include <tokenstringcontext.hxx>
 #include <sortparam.hxx>
+#include <SparklineGroup.hxx>
 
 #include <editeng/eeitem.hxx>
 #include <o3tl/safeint.hxx>
@@ -1996,6 +1997,74 @@ bool ScColumn::DeleteSparkline(SCROW nRow)
     return true;
 }
 
+bool ScColumn::IsSparklinesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
+{
+    std::pair<sc::SparklineStoreType::const_iterator,size_t> aPos = 
maSparklines.position(nStartRow);
+    sc::SparklineStoreType::const_iterator it = aPos.first;
+    if (it == maSparklines.end())
+        return false;
+
+    if (it->type != sc::element_type_empty)
+        return false;
+
+    // start position of next block which is not empty.
+    SCROW nNextRow = nStartRow + it->size - aPos.second;
+    return nEndRow < nNextRow;
+}
+
+namespace
+{
+
+class CopySparklinesHandler
+{
+    ScColumn& mrDestColumn;
+    sc::SparklineStoreType& mrDestSparkline;
+    sc::SparklineStoreType::iterator miDestPosition;
+    SCROW mnDestOffset;
+
+public:
+    CopySparklinesHandler(ScColumn& rDestColumn, SCROW nDestOffset)
+        : mrDestColumn(rDestColumn)
+        , mrDestSparkline(mrDestColumn.GetSparklineStore())
+        , miDestPosition(mrDestSparkline.begin())
+        , mnDestOffset(nDestOffset)
+    {}
+
+    void operator() (size_t nRow, const sc::SparklineCell* pCell)
+    {
+        SCROW nDestRow = nRow + mnDestOffset;
+
+        auto const& pSparkline = pCell->getSparkline();
+        auto const& pGroup = pCell->getSparklineGroup();
+
+        auto pNewSparklineGroup = 
std::make_shared<sc::SparklineGroup>(*pGroup); // Copy the group
+        auto pNewSparkline = 
std::make_shared<sc::Sparkline>(mrDestColumn.GetCol(), nDestRow, 
pNewSparklineGroup);
+
+        pNewSparkline->setInputRange(pSparkline->getInputRange());
+
+        miDestPosition = mrDestSparkline.set(miDestPosition, nDestRow, new 
sc::SparklineCell(pNewSparkline));
+    }
+};
+
+}
+
+void ScColumn::CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, 
ScColumn& rDestCol, SCROW nRowOffsetDest) const
+{
+    if (IsSparklinesEmptyBlock(nRow1, nRow2))
+        // The column has no cell sparklines to copy between specified rows.
+        return;
+
+    CopySparklinesHandler aFunctor(rDestCol, nRowOffsetDest);
+    sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow1, nRow2, 
aFunctor);
+}
+
+void ScColumn::DuplicateSparklines(SCROW nStartRow, size_t nDataSize, 
ScColumn& rDestCol,
+                             sc::ColumnBlockPosition& rDestBlockPos, SCROW 
nRowOffsetDest) const
+{
+    CopyCellSparklinesToDocument(nStartRow, nStartRow + nDataSize - 1, 
rDestCol, nRowOffsetDest);
+    rDestBlockPos.miSparklinePos = rDestCol.maSparklines.begin();
+}
+
 // Notes
 
 ScPostIt* ScColumn::GetCellNote(SCROW nRow)
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 798ac2579ce3..966a182cbf7c 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1120,6 +1120,11 @@ class CopyCellsFromClipHandler
         mrSrcCol.DuplicateNotes(nStartRow, nDataSize, mrDestCol, 
maDestBlockPos, bCloneCaption, mnRowOffset);
     }
 
+    void duplicateSparklines(SCROW nStartRow, size_t nDataSize)
+    {
+        mrSrcCol.DuplicateSparklines(nStartRow, nDataSize, mrDestCol, 
maDestBlockPos, mnRowOffset);
+    }
+
 public:
     CopyCellsFromClipHandler(sc::CopyFromClipContext& rCxt, ScColumn& rSrcCol, 
ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, tools::Long nRowOffset, 
svl::SharedStringPool* pSharedStringPool) :
         mrCxt(rCxt),
@@ -1159,6 +1164,7 @@ public:
     {
         SCROW nSrcRow1 = node.position + nOffset;
         bool bCopyCellNotes = mrCxt.isCloneNotes();
+        bool bCopySparklines = mrCxt.isCloneSparklines();
 
         InsertDeleteFlags nFlags = mrCxt.getInsertFlag();
 
@@ -1169,6 +1175,10 @@ public:
                 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) 
== InsertDeleteFlags::NONE;
                 duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
             }
+            if (bCopySparklines) // If there is a sparkline is it empty?
+            {
+                duplicateSparklines(nSrcRow1, nDataSize);
+            }
             return;
         }
 
@@ -1359,6 +1369,10 @@ public:
             bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == 
InsertDeleteFlags::NONE;
             duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
         }
+        if (bCopySparklines)
+        {
+            duplicateSparklines(nSrcRow1, nDataSize);
+        }
     }
 };
 
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index c2fc0e54d33e..c2dc4a63085b 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -31,6 +31,8 @@
 #include <recursionhelper.hxx>
 #include <docsh.hxx>
 
+#include <SparklineGroup.hxx>
+
 #include <o3tl/safeint.hxx>
 #include <svl/sharedstringpool.hxx>
 #include <sal/log.hxx>
@@ -116,6 +118,9 @@ void ScColumn::DeleteBeforeCopyFromClip(
         if (nDelFlag & InsertDeleteFlags::NOTE)
             DeleteCellNotes(*pBlockPos, aRange.mnRow1, aRange.mnRow2, false);
 
+        if (nDelFlag & InsertDeleteFlags::SPARKLINES)
+            DeleteSparklineCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
+
         if (nDelFlag & InsertDeleteFlags::EDITATTR)
             RemoveEditAttribs(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
 
@@ -204,6 +209,9 @@ void ScColumn::DeleteBeforeCopyFromClip(
         if (nDelFlag & InsertDeleteFlags::NOTE)
             DeleteCellNotes(*pBlockPos, nRow1, nRow2, false);
 
+        if (nDelFlag & InsertDeleteFlags::SPARKLINES)
+            DeleteSparklineCells(*pBlockPos, nRow1, nRow2);
+
         if (nDelFlag & InsertDeleteFlags::EDITATTR)
             RemoveEditAttribs(*pBlockPos, nRow1, nRow2);
 
@@ -321,6 +329,11 @@ void ScColumn::CopyOneCellFromClip( 
sc::CopyFromClipContext& rCxt, SCROW nRow1,
         }
     }
 
+    ScAddress aDestPosition(nCol, nRow1, nTab);
+
+    duplicateSparkline(rCxt, pBlockPos, nColOffset, nDestSize, aDestPosition);
+
+    // Notes
     const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
     if (!(pNote && (nFlags & (InsertDeleteFlags::NOTE | 
InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE))
         return;
@@ -330,13 +343,12 @@ void ScColumn::CopyOneCellFromClip( 
sc::CopyFromClipContext& rCxt, SCROW nRow1,
     ScDocument* pClipDoc = rCxt.getClipDoc();
     const ScAddress aSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
     std::vector<ScPostIt*> aNotes;
-    ScAddress aDestPos(nCol, nRow1, nTab);
     aNotes.reserve(nDestSize);
     for (size_t i = 0; i < nDestSize; ++i)
     {
         bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == 
InsertDeleteFlags::NONE;
-        aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPos, 
bCloneCaption).release());
-        aDestPos.IncRow();
+        aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPosition, 
bCloneCaption).release());
+        aDestPosition.IncRow();
     }
 
     pBlockPos->miCellNotePos =
@@ -344,11 +356,37 @@ void ScColumn::CopyOneCellFromClip( 
sc::CopyFromClipContext& rCxt, SCROW nRow1,
             pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
 
     // Notify our LOK clients.
-    aDestPos.SetRow(nRow1);
+    aDestPosition.SetRow(nRow1);
     for (size_t i = 0; i < nDestSize; ++i)
     {
-        ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, 
&rDocument, aDestPos, aNotes[i]);
-        aDestPos.IncRow();
+        ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, 
&rDocument, aDestPosition, aNotes[i]);
+        aDestPosition.IncRow();
+    }
+}
+
+void ScColumn::duplicateSparkline(sc::CopyFromClipContext& rContext, 
sc::ColumnBlockPosition* pBlockPos,
+                                  size_t nColOffset, size_t nDestSize, 
ScAddress aDestPosition)
+{
+    if ((rContext.getInsertFlag() & InsertDeleteFlags::SPARKLINES) == 
InsertDeleteFlags::NONE)
+        return;
+
+    auto pSparkline = rContext.getSingleSparkline(nColOffset);
+    if (pSparkline)
+    {
+        auto const& pSparklineGroup = pSparkline->getSparklineGroup();
+
+        std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr);
+        ScAddress aCurrentPosition = aDestPosition;
+        for (size_t i = 0; i < nDestSize; ++i)
+        {
+            auto pNewSparklineGroup = 
std::make_shared<sc::SparklineGroup>(*pSparklineGroup);
+            auto pNewSparkline = 
std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), 
pNewSparklineGroup);
+            pNewSparkline->setInputRange(pSparkline->getInputRange());
+            aSparklines[i] = new sc::SparklineCell(pNewSparkline);
+            aCurrentPosition.IncRow();
+        }
+
+        pBlockPos->miSparklinePos = 
maSparklines.set(pBlockPos->miSparklinePos, aDestPosition.Row(), 
aSparklines.begin(), aSparklines.end());
     }
 }
 
diff --git a/sc/source/core/data/document10.cxx 
b/sc/source/core/data/document10.cxx
index ada5aff2d625..2ceeb4954fd5 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -125,6 +125,9 @@ bool ScDocument::CopyOneCellFromClip(
         if ((rCxt.getInsertFlag() & (InsertDeleteFlags::NOTE | 
InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE)
             rCxt.setSingleCellNote(nColOffset, pClipDoc->GetNote(aSrcPos));
 
+        if ((rCxt.getInsertFlag() & InsertDeleteFlags::SPARKLINES) != 
InsertDeleteFlags::NONE)
+            rCxt.setSingleSparkline(nColOffset, 
pClipDoc->GetSparkline(aSrcPos));
+
         ScColumn* pSrcCol = pSrcTab->FetchColumn(aSrcPos.Col());
         assert(pSrcCol);
         // Determine the script type of the copied single cell.
diff --git a/sc/source/ui/inc/cliputil.hxx b/sc/source/ui/inc/cliputil.hxx
index 022b3586d241..dc0ee5b9b8d4 100644
--- a/sc/source/ui/inc/cliputil.hxx
+++ b/sc/source/ui/inc/cliputil.hxx
@@ -10,6 +10,7 @@
 #pragma once
 
 #include <types.hxx>
+#include <scdllapi.h>
 
 class ScViewData;
 class ScTabViewShell;
@@ -19,9 +20,10 @@ class ScRangeList;
 
 namespace ScClipUtil
 {
-    void PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* 
pTabViewShell, bool bShowDialog );
 
-    bool CheckDestRanges(
+SC_DLLPUBLIC void PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* 
pTabViewShell, bool bShowDialog );
+
+bool CheckDestRanges(
         const ScDocument& rDoc, SCCOL nSrcCols, SCROW nSrcRows, const 
ScMarkData& rMark,
         const ScRangeList& rDest);
 }
commit ff13e71e72271736116632662b63f8f8dd3de54a
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Sat Mar 19 10:56:27 2022 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Tue Apr 12 01:23:41 2022 +0200

    sc: change GetSparkline to return a shared_ptr instead of raw ptr
    
    Change-Id: If3d7b3ad4b96eb7d3b126ee8b130f8d5e684cd3c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132472
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    (cherry picked from commit 413f144e84629fe8f3bae5d984b40228fdeec5c1)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132795
    Tested-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 02e8a8b26e4d..a79584f3e7a6 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1241,7 +1241,7 @@ public:
     sc::MultiDataCellState HasMultipleDataCells( const ScRange& rRange ) const;
 
     /** Spaklines */
-    SC_DLLPUBLIC sc::Sparkline* GetSparkline(ScAddress const & rPosition);
+    SC_DLLPUBLIC std::shared_ptr<sc::Sparkline> GetSparkline(ScAddress const & 
rPosition);
     SC_DLLPUBLIC sc::Sparkline* CreateSparkline(ScAddress const & rPosition, 
std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup);
     SC_DLLPUBLIC sc::SparklineList* GetSparklineList(SCTAB nTab);
     SC_DLLPUBLIC bool DeleteSparkline(ScAddress const& rPosition);
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 4ae72c22418a..3fde00a7b414 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -475,7 +475,7 @@ public:
 
     // Sparklines
 
-    sc::Sparkline* GetSparkline(SCCOL nCol, SCROW nRow);
+    std::shared_ptr<sc::Sparkline> GetSparkline(SCCOL nCol, SCROW nRow);
     sc::Sparkline* CreateSparkline(SCCOL nCol, SCROW nRow, 
std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup);
     bool DeleteSparkline(SCCOL nCol, SCROW nRow);
 
diff --git a/sc/qa/unit/SparklineImportExportTest.cxx 
b/sc/qa/unit/SparklineImportExportTest.cxx
index fe15d783b58e..25af95c8770e 100644
--- a/sc/qa/unit/SparklineImportExportTest.cxx
+++ b/sc/qa/unit/SparklineImportExportTest.cxx
@@ -57,7 +57,7 @@ void checkSparklines(ScDocument& rDocument)
 {
     // Sparkline at Sheet1:A2
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(0, 1, 
0)); // A2
+        auto pSparkline = rDocument.GetSparkline(ScAddress(0, 1, 0)); // A2
         CPPUNIT_ASSERT(pSparkline);
         auto pSparklineGroup = pSparkline->getSparklineGroup();
         CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Line, 
pSparklineGroup->m_eType);
@@ -90,7 +90,7 @@ void checkSparklines(ScDocument& rDocument)
     }
     // Sparkline at Sheet1:A3
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(0, 2, 
0)); // A3
+        auto pSparkline = rDocument.GetSparkline(ScAddress(0, 2, 0)); // A3
         CPPUNIT_ASSERT(pSparkline);
         auto pSparklineGroup = pSparkline->getSparklineGroup();
         CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Column, 
pSparklineGroup->m_eType);
@@ -123,28 +123,28 @@ void checkSparklines(ScDocument& rDocument)
     }
     // Sparkline at Sheet2:B1
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(1, 0, 
1)); //B1
+        auto pSparkline = rDocument.GetSparkline(ScAddress(1, 0, 1)); //B1
         CPPUNIT_ASSERT(pSparkline);
         auto pSparklineGroup = pSparkline->getSparklineGroup();
         CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Column, 
pSparklineGroup->m_eType);
     }
     // Sparkline at Sheet2:B2
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(1, 1, 
1)); //B2
+        auto pSparkline = rDocument.GetSparkline(ScAddress(1, 1, 1)); //B2
         CPPUNIT_ASSERT(pSparkline);
         auto pSparklineGroup = pSparkline->getSparklineGroup();
         CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Line, 
pSparklineGroup->m_eType);
     }
     // Sparkline at Sheet2:B2
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(1, 1, 
1)); //B2
+        auto pSparkline = rDocument.GetSparkline(ScAddress(1, 1, 1)); //B2
         CPPUNIT_ASSERT(pSparkline);
         auto pSparklineGroup = pSparkline->getSparklineGroup();
         CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Line, 
pSparklineGroup->m_eType);
     }
     // Sparkline doesn't exists at A4
     {
-        sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(0, 3, 
0)); //A4
+        auto pSparkline = rDocument.GetSparkline(ScAddress(0, 3, 0)); //A4
         CPPUNIT_ASSERT(!pSparkline);
     }
 }
diff --git a/sc/qa/unit/SparklineTest.cxx b/sc/qa/unit/SparklineTest.cxx
index 74f40579d99f..2a2dfde71b5b 100644
--- a/sc/qa/unit/SparklineTest.cxx
+++ b/sc/qa/unit/SparklineTest.cxx
@@ -82,13 +82,13 @@ void SparklineTest::testAddSparkline()
 
     ScDocument& rDocument = xDocSh->GetDocument();
 
-    sc::Sparkline* pSparkline = createTestSparkline(rDocument);
+    auto pSparkline = createTestSparkline(rDocument);
     CPPUNIT_ASSERT(pSparkline);
 
-    sc::Sparkline* pGetSparkline = rDocument.GetSparkline(ScAddress(0, 6, 0));
+    auto pGetSparkline = rDocument.GetSparkline(ScAddress(0, 6, 0));
     CPPUNIT_ASSERT(pGetSparkline);
 
-    CPPUNIT_ASSERT_EQUAL(pGetSparkline, pSparkline);
+    CPPUNIT_ASSERT_EQUAL(pGetSparkline.get(), pSparkline);
 
     sc::SparklineList* pList = rDocument.GetSparklineList(0);
     CPPUNIT_ASSERT(pList);
@@ -107,12 +107,12 @@ void SparklineTest::testDeleteSprkline()
 
     ScDocument& rDocument = xDocSh->GetDocument();
 
-    sc::Sparkline* pSparkline = createTestSparkline(rDocument);
+    auto pSparkline = createTestSparkline(rDocument);
     CPPUNIT_ASSERT(pSparkline);
 
     clearRange(&rDocument, ScRange(0, 6, 0, 0, 6, 0));
 
-    sc::Sparkline* pGetSparkline = rDocument.GetSparkline(ScAddress(0, 6, 0));
+    auto pGetSparkline = rDocument.GetSparkline(ScAddress(0, 6, 0));
     CPPUNIT_ASSERT(!pGetSparkline);
 
     xDocSh->DoClose();
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index db13642e060d..5f4e6c466c07 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -6557,7 +6557,7 @@ bool ScDocument::IsInVBAMode() const
 }
 
 // Sparklines
-sc::Sparkline* ScDocument::GetSparkline(ScAddress const& rPosition)
+std::shared_ptr<sc::Sparkline> ScDocument::GetSparkline(ScAddress const& 
rPosition)
 {
     SCTAB nTab = rPosition.Tab();
 
@@ -6565,7 +6565,7 @@ sc::Sparkline* ScDocument::GetSparkline(ScAddress const& 
rPosition)
     {
         return maTabs[nTab]->GetSparkline(rPosition.Col(), rPosition.Row());
     }
-    return nullptr;
+    return std::shared_ptr<sc::Sparkline>();
 }
 
 sc::Sparkline* ScDocument::CreateSparkline(ScAddress const& rPosition, 
std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup)
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index fc7689ebe4a4..3f12b3cd3579 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1811,19 +1811,16 @@ ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, 
SCROW nRow )
 
 // Sparklines
 
-sc::Sparkline* ScTable::GetSparkline(SCCOL nCol, SCROW nRow)
+std::shared_ptr<sc::Sparkline> ScTable::GetSparkline(SCCOL nCol, SCROW nRow)
 {
     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
-        return nullptr;
+        return std::shared_ptr<sc::Sparkline>();
 
     sc::SparklineCell* pSparklineCell = aCol[nCol].GetSparklineCell(nRow);
     if (!pSparklineCell)
-        return nullptr;
+        return std::shared_ptr<sc::Sparkline>();
 
-    std::shared_ptr<sc::Sparkline> pSparkline(pSparklineCell->getSparkline());
-    assert(pSparkline);
-
-    return pSparkline.get();
+    return pSparklineCell->getSparkline();
 }
 
 sc::Sparkline* ScTable::CreateSparkline(SCCOL nCol, SCROW nRow, 
std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup)
diff --git a/sc/source/ui/view/cellsh.cxx b/sc/source/ui/view/cellsh.cxx
index 72e4b3c74e1d..f1df448a1288 100644
--- a/sc/source/ui/view/cellsh.cxx
+++ b/sc/source/ui/view/cellsh.cxx
@@ -110,8 +110,8 @@ bool canShowDeleteSparkline(ScDocument& rDocument, ScRange 
const& rRange)
     {
         for (SCROW nY = rRange.aStart.Row(); nY <= rRange.aEnd.Row(); nY++)
         {
-            auto* pSparkline = rDocument.GetSparkline(ScAddress(nX, nY, nTab));
-            if (pSparkline == nullptr)
+            auto pSparkline = rDocument.GetSparkline(ScAddress(nX, nY, nTab));
+            if (!pSparkline)
             {
                 return false;
             }
diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
index 29073a3aac81..1e6a6c018f29 100644
--- a/sc/source/ui/view/output.cxx
+++ b/sc/source/ui/view/output.cxx
@@ -2466,7 +2466,7 @@ void drawColumn(vcl::RenderContext& rRenderContext, 
tools::Rectangle const & rRe
     }
 }
 
-void drawSparkline(sc::Sparkline* pSparkline, vcl::RenderContext& 
rRenderContext, ScDocument* pDocument,
+void drawSparkline(std::shared_ptr<sc::Sparkline> const& pSparkline, 
vcl::RenderContext& rRenderContext, ScDocument* pDocument,
                                  tools::Rectangle const & rRectangle)
 {
     auto const & rRangeList = pSparkline->getInputRange();
@@ -2566,7 +2566,7 @@ void ScOutputData::DrawSparklines(vcl::RenderContext& 
rRenderContext)
                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
                 }
 
-                sc::Sparkline* pSparkline = nullptr;
+                std::shared_ptr<sc::Sparkline> pSparkline;
                 ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
 
                 if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = 
mpDoc->GetSparkline(aCurrentAddress))

Reply via email to