sc/inc/SparklineGroup.hxx           |   31 +++++++++-
 sc/inc/clipcontext.hxx              |    7 +-
 sc/inc/column.hxx                   |   11 +++
 sc/inc/mtvcellfunc.hxx              |    8 ++
 sc/qa/unit/SparklineTest.cxx        |  110 ++++++++++++++++++++++++++++++++++++
 sc/source/core/data/clipcontext.cxx |   21 ++++++
 sc/source/core/data/column.cxx      |    1 
 sc/source/core/data/column2.cxx     |   87 +++++++++++++++++++++++++---
 sc/source/core/data/column3.cxx     |   14 ++++
 sc/source/core/data/column4.cxx     |   50 ++++++++++++++--
 sc/source/core/data/document10.cxx  |    3 
 sc/source/ui/inc/cliputil.hxx       |    6 +
 12 files changed, 327 insertions(+), 22 deletions(-)

New commits:
commit b8cf500ed8ac7bd01a351e2815ce8251e506d79c
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Sat Mar 19 12:52:21 2022 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Mon Apr 4 09:51:36 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>

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 36ea217a481a..3afda2acd885 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -229,6 +229,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). */
@@ -254,6 +257,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 );
@@ -668,6 +673,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 );
@@ -696,7 +705,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 19fb532ee74f..d9133e3ad55c 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -919,6 +919,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 b010cdb7aa73..787059376f44 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>
@@ -1971,6 +1972,28 @@ void ScColumn::PrepareBroadcastersForDestruction()
     }
 }
 
+namespace
+{
+struct BroadcasterNoListenersPredicate
+{
+    bool operator()( size_t, const SvtBroadcaster* broadcaster )
+    {
+        return !broadcaster->HasListeners();
+    }
+};
+
+}
+
+void ScColumn::DeleteEmptyBroadcasters()
+{
+    if(!mbEmptyBroadcastersPending)
+        return;
+    // Clean up after ScDocument::EnableDelayDeletingBroadcasters().
+    BroadcasterNoListenersPredicate predicate;
+    sc::SetElementsToEmpty1<sc::broadcaster_block>( maBroadcasters, predicate 
);
+    mbEmptyBroadcastersPending = false;
+}
+
 // Sparklines
 
 sc::SparklineCell* ScColumn::GetSparklineCell(SCROW nRow)
@@ -1997,26 +2020,72 @@ 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
 {
-struct BroadcasterNoListenersPredicate
+
+class CopySparklinesHandler
 {
-    bool operator()( size_t, const SvtBroadcaster* broadcaster )
+    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)
     {
-        return !broadcaster->HasListeners();
+        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::DeleteEmptyBroadcasters()
+void ScColumn::CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, 
ScColumn& rDestCol, SCROW nRowOffsetDest) const
 {
-    if(!mbEmptyBroadcastersPending)
+    if (IsSparklinesEmptyBlock(nRow1, nRow2))
+        // The column has no cell sparklines to copy between specified rows.
         return;
-    // Clean up after ScDocument::EnableDelayDeletingBroadcasters().
-    BroadcasterNoListenersPredicate predicate;
-    sc::SetElementsToEmpty1<sc::broadcaster_block>( maBroadcasters, predicate 
);
-    mbEmptyBroadcastersPending = false;
+
+    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
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 19d731c8b0c3..56aff45386b5 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1075,6 +1075,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),
@@ -1114,6 +1119,7 @@ public:
     {
         SCROW nSrcRow1 = node.position + nOffset;
         bool bCopyCellNotes = mrCxt.isCloneNotes();
+        bool bCopySparklines = mrCxt.isCloneSparklines();
 
         InsertDeleteFlags nFlags = mrCxt.getInsertFlag();
 
@@ -1124,6 +1130,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;
         }
 
@@ -1314,6 +1324,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 e109448c4fce..e6bb2c09db47 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 1d76747b5d3d..4a9efa170285 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);
 }

Reply via email to