sc/Library_sc.mk | 2 sc/inc/Sparkline.hxx | 8 - sc/inc/SparklineCell.hxx | 4 sc/inc/SparklineData.hxx | 42 +++++ sc/inc/SparklineGroup.hxx | 31 ++++ sc/inc/clipcontext.hxx | 7 sc/inc/column.hxx | 11 + sc/inc/document.hxx | 2 sc/inc/globstr.hrc | 2 sc/inc/mtvcellfunc.hxx | 8 + sc/inc/table.hxx | 2 sc/qa/unit/SparklineImportExportTest.cxx | 12 - sc/qa/unit/SparklineTest.cxx | 194 ++++++++++++++++++++++++-- sc/source/core/data/clipcontext.cxx | 21 ++ sc/source/core/data/column.cxx | 1 sc/source/core/data/column2.cxx | 77 ++++++++++ sc/source/core/data/column3.cxx | 14 + sc/source/core/data/column4.cxx | 41 +++++ sc/source/core/data/document.cxx | 4 sc/source/core/data/document10.cxx | 3 sc/source/core/data/table2.cxx | 11 - sc/source/ui/dialogs/SparklineDialog.cxx | 95 +----------- sc/source/ui/docshell/docfunc.cxx | 82 ++++++++++ sc/source/ui/inc/cliputil.hxx | 6 sc/source/ui/inc/docfunc.hxx | 7 sc/source/ui/inc/undo/UndoInsertSparkline.hxx | 45 ++++++ sc/source/ui/sparklines/SparklineData.cxx | 30 ++++ sc/source/ui/undo/UndoInsertSparkline.cxx | 78 ++++++++++ sc/source/ui/view/cellsh.cxx | 4 sc/source/ui/view/output.cxx | 4 30 files changed, 711 insertions(+), 137 deletions(-)
New commits: commit 9a701051b5c81486e40c500ea9777700e734b421 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sun Mar 20 20:49:18 2022 +0900 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sun Mar 20 20:49:18 2022 +0900 sc: add Undo/Redo for inserting Sparklines Move the code to insert a sparkline from the SparklineDialog to DocFunc and inside the UndoInsertSparkline, so there is no code duplication and the code can be tested. Change-Id: I85f4020190ae835b33e706ec9cb2cda9fd6fc752 diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk index f145d3a7fbf2..2a9c6f659a0a 100644 --- a/sc/Library_sc.mk +++ b/sc/Library_sc.mk @@ -512,6 +512,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/ui/sidebar/NumberFormatControl \ sc/source/ui/sidebar/NumberFormatPropertyPanel \ sc/source/ui/sidebar/ScPanelFactory \ + sc/source/ui/sparklines/SparklineData \ sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog \ sc/source/ui/StatisticsDialogs/CorrelationDialog \ sc/source/ui/StatisticsDialogs/CovarianceDialog \ @@ -549,6 +550,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/ui/undo/undostyl \ sc/source/ui/undo/undotab \ sc/source/ui/undo/undoutil \ + sc/source/ui/undo/UndoInsertSparkline \ sc/source/ui/unoobj/ChartRangeSelectionListener \ sc/source/ui/unoobj/addruno \ sc/source/ui/unoobj/afmtuno \ diff --git a/sc/inc/SparklineData.hxx b/sc/inc/SparklineData.hxx new file mode 100644 index 000000000000..80cc8a0329c2 --- /dev/null +++ b/sc/inc/SparklineData.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#pragma once + +#include "address.hxx" +#include "scdllapi.h" + +namespace sc +{ +struct SC_DLLPUBLIC SparklineData +{ + ScAddress maPosition; + ScRange maData; + + SparklineData(ScAddress const& rPosition, ScRange const& rData) + : maPosition(rPosition) + , maData(rData) + { + } +}; + +enum class RangeOrientation +{ + Unknown, + Row, + Col +}; + +SC_DLLPUBLIC RangeOrientation calculateOrientation(sal_Int32 nOutputSize, + ScRange const& rInputRange); + +} // end sc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc index 793d3cb95d55..6ae26c9d4a0f 100644 --- a/sc/inc/globstr.hrc +++ b/sc/inc/globstr.hrc @@ -539,7 +539,7 @@ #define STR_HYPHENATECELL_ON NC_("STR_HYPHENATECELL_ON", "Hyphenate: On") #define STR_HYPHENATECELL_OFF NC_("STR_HYPHENATECELL_OFF", "Hyphenate: Off") #define STR_INDENTCELL NC_("STR_INDENTCELL", "Indent: ") - +#define STR_UNDO_INSERT_SPARKLINE_GROUP NC_("STR_UNDO_INSERT_SPARKLINE", "Insert Sparkline Group") #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/SparklineTest.cxx b/sc/qa/unit/SparklineTest.cxx index 826638feb17c..987cc0bb452a 100644 --- a/sc/qa/unit/SparklineTest.cxx +++ b/sc/qa/unit/SparklineTest.cxx @@ -11,6 +11,8 @@ #include <docsh.hxx> #include <tabvwsh.hxx> #include <cliputil.hxx> +#include <docfunc.hxx> + #include <Sparkline.hxx> #include <SparklineGroup.hxx> @@ -21,8 +23,6 @@ class SparklineTest : public ScBootstrapFixture private: uno::Reference<uno::XInterface> m_xCalcComponent; - sc::Sparkline* createTestSparkline(ScDocument& rDocument); - public: SparklineTest() : ScBootstrapFixture("sc/qa/unit/data") @@ -50,29 +50,38 @@ public: void testDeleteSprkline(); void testCopyPasteSparkline(); void testCutPasteSparkline(); + void testUndoRedoSparkline(); CPPUNIT_TEST_SUITE(SparklineTest); CPPUNIT_TEST(testAddSparkline); CPPUNIT_TEST(testDeleteSprkline); CPPUNIT_TEST(testCopyPasteSparkline); CPPUNIT_TEST(testCutPasteSparkline); + CPPUNIT_TEST(testUndoRedoSparkline); CPPUNIT_TEST_SUITE_END(); }; -sc::Sparkline* SparklineTest::createTestSparkline(ScDocument& rDocument) +namespace +{ +void insertTestData(ScDocument& rDocument) { - auto pSparklineGroup = std::make_shared<sc::SparklineGroup>(); - - sc::Sparkline* pSparkline = rDocument.CreateSparkline(ScAddress(0, 6, 0), pSparklineGroup); - if (!pSparkline) - return nullptr; - rDocument.SetValue(0, 0, 0, 4); rDocument.SetValue(0, 1, 0, -2); rDocument.SetValue(0, 2, 0, 1); rDocument.SetValue(0, 3, 0, -3); rDocument.SetValue(0, 4, 0, 5); rDocument.SetValue(0, 5, 0, 3); +} + +sc::Sparkline* createTestSparkline(ScDocument& rDocument) +{ + auto pSparklineGroup = std::make_shared<sc::SparklineGroup>(); + + sc::Sparkline* pSparkline = rDocument.CreateSparkline(ScAddress(0, 6, 0), pSparklineGroup); + if (!pSparkline) + return nullptr; + + insertTestData(rDocument); ScRangeList aList; aList.push_back(ScRange(0, 0, 0, 0, 5, 0)); @@ -81,6 +90,8 @@ sc::Sparkline* SparklineTest::createTestSparkline(ScDocument& rDocument) return pSparkline; } +} // end anonymous namespace + void SparklineTest::testAddSparkline() { ScDocShellRef xDocSh = loadEmptyDocument(); @@ -228,6 +239,55 @@ void SparklineTest::testCutPasteSparkline() xDocSh->DoClose(); } +void SparklineTest::testUndoRedoSparkline() +{ + ScDocShellRef xDocSh = loadEmptyDocument(); + CPPUNIT_ASSERT(xDocSh); + + ScDocument& rDocument = xDocSh->GetDocument(); + ScTabViewShell* pViewShell = xDocSh->GetBestViewShell(false); + CPPUNIT_ASSERT(pViewShell); + + auto& rDocFunc = xDocSh->GetDocFunc(); + + // insert test data - A1:A6 + insertTestData(rDocument); + + // Sparkline range + ScRange aRange(0, 6, 0, 0, 6, 0); + + // Check Sparkline at cell A7 doesn't exists + auto pSparkline = rDocument.GetSparkline(aRange.aStart); + CPPUNIT_ASSERT(!pSparkline); + + auto pSparklineGroup = std::make_shared<sc::SparklineGroup>(); + rDocFunc.InsertSparklines(ScRange(0, 0, 0, 0, 5, 0), aRange, pSparklineGroup); + + // Check Sparkline at cell A7 exists + pSparkline = rDocument.GetSparkline(aRange.aStart); + CPPUNIT_ASSERT(pSparkline); + CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparkline->getColumn()); + CPPUNIT_ASSERT_EQUAL(SCROW(6), pSparkline->getRow()); + + // Undo + rDocument.GetUndoManager()->Undo(); + + // Check Sparkline at cell A7 doesn't exists + pSparkline = rDocument.GetSparkline(aRange.aStart); + CPPUNIT_ASSERT(!pSparkline); + + // Redo + rDocument.GetUndoManager()->Redo(); + + // Check Sparkline at cell A7 exists + pSparkline = rDocument.GetSparkline(aRange.aStart); + CPPUNIT_ASSERT(pSparkline); + CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSparkline->getColumn()); + CPPUNIT_ASSERT_EQUAL(SCROW(6), pSparkline->getRow()); + + xDocSh->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SparklineTest); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/dialogs/SparklineDialog.cxx b/sc/source/ui/dialogs/SparklineDialog.cxx index fac1d8274be5..103582c25a6e 100644 --- a/sc/source/ui/dialogs/SparklineDialog.cxx +++ b/sc/source/ui/dialogs/SparklineDialog.cxx @@ -8,10 +8,13 @@ */ #include <SparklineDialog.hxx> +#include <SparklineData.hxx> #include <SparklineGroup.hxx> #include <Sparkline.hxx> #include <reffact.hxx> +#include <docfunc.hxx> + #include <svx/colorbox.hxx> namespace sc @@ -310,48 +313,24 @@ IMPL_LINK_NOARG(SparklineDialog, SelectSparklineType, weld::Toggleable&, void) mpLocalSparklineGroup->m_eType = sc::SparklineType::Stacked; } -namespace -{ -enum class RangeOrientation -{ - Unknown, - Row, - Col -}; - -RangeOrientation calculateOrientation(sal_Int32 nOutputSize, ScRange const& rInputRange) -{ - sal_Int32 nRowSize = rInputRange.aEnd.Row() - rInputRange.aStart.Row(); - sal_Int32 nColSize = rInputRange.aEnd.Col() - rInputRange.aStart.Col(); - - auto eInputOrientation = RangeOrientation::Unknown; - if (nOutputSize == nRowSize) - eInputOrientation = RangeOrientation::Row; - else if (nOutputSize == nColSize) - eInputOrientation = RangeOrientation::Col; - return eInputOrientation; -} - -} // end anonymous namespace - bool SparklineDialog::checkValidInputOutput() { if (!maInputRange.IsValid() || !maOutputRange.IsValid()) return false; - RangeOrientation eInputOrientation = RangeOrientation::Unknown; + sc::RangeOrientation eInputOrientation = sc::RangeOrientation::Unknown; if (maOutputRange.aStart.Col() == maOutputRange.aEnd.Col()) { sal_Int32 nOutputRowSize = maOutputRange.aEnd.Row() - maOutputRange.aStart.Row(); - eInputOrientation = calculateOrientation(nOutputRowSize, maInputRange); + eInputOrientation = sc::calculateOrientation(nOutputRowSize, maInputRange); } else if (maOutputRange.aStart.Row() == maOutputRange.aEnd.Row()) { sal_Int32 nOutputColSize = maOutputRange.aEnd.Col() - maOutputRange.aStart.Col(); - eInputOrientation = calculateOrientation(nOutputColSize, maInputRange); + eInputOrientation = sc::calculateOrientation(nOutputColSize, maInputRange); } - return eInputOrientation != RangeOrientation::Unknown; + return eInputOrientation != sc::RangeOrientation::Unknown; } void SparklineDialog::perform() @@ -364,65 +343,9 @@ void SparklineDialog::perform() mpLocalSparklineGroup->m_aColorFirst = mxColorFirst->GetSelectEntryColor(); mpLocalSparklineGroup->m_aColorLast = mxColorLast->GetSelectEntryColor(); - if (maOutputRange.aStart.Col() == maOutputRange.aEnd.Col()) - { - sal_Int32 nOutputRowSize = maOutputRange.aEnd.Row() - maOutputRange.aStart.Row(); - - auto eInputOrientation = calculateOrientation(nOutputRowSize, maInputRange); - - if (eInputOrientation == RangeOrientation::Unknown) - return; - - sal_Int32 nIndex = 0; - for (ScAddress aAddress = maOutputRange.aStart; aAddress.Row() <= maOutputRange.aEnd.Row(); - aAddress.IncRow()) - { - ScRange aInputRangeSlice = maInputRange; - if (eInputOrientation == RangeOrientation::Row) - { - aInputRangeSlice.aStart.SetRow(maInputRange.aStart.Row() + nIndex); - aInputRangeSlice.aEnd.SetRow(maInputRange.aStart.Row() + nIndex); - } - else - { - aInputRangeSlice.aStart.SetCol(maInputRange.aStart.Col() + nIndex); - aInputRangeSlice.aEnd.SetCol(maInputRange.aStart.Col() + nIndex); - } - auto* pCreated = mrDocument.CreateSparkline(aAddress, mpLocalSparklineGroup); - pCreated->setInputRange(aInputRangeSlice); - nIndex++; - } - } - else if (maOutputRange.aStart.Row() == maOutputRange.aEnd.Row()) - { - sal_Int32 nOutputColSize = maOutputRange.aEnd.Col() - maOutputRange.aStart.Col(); - - auto eInputOrientation = calculateOrientation(nOutputColSize, maInputRange); - - if (eInputOrientation == RangeOrientation::Unknown) - return; + auto& rDocFunc = mrViewData.GetDocShell()->GetDocFunc(); - sal_Int32 nIndex = 0; - - for (ScAddress aAddress = maOutputRange.aStart; aAddress.Col() <= maOutputRange.aEnd.Col(); - aAddress.IncCol()) - { - ScRange aInputRangeSlice = maInputRange; - if (eInputOrientation == RangeOrientation::Row) - { - aInputRangeSlice.aStart.SetRow(maInputRange.aStart.Row() + nIndex); - aInputRangeSlice.aEnd.SetRow(maInputRange.aStart.Row() + nIndex); - } - else - { - aInputRangeSlice.aStart.SetCol(maInputRange.aStart.Col() + nIndex); - aInputRangeSlice.aEnd.SetCol(maInputRange.aStart.Col() + nIndex); - } - auto* pCreated = mrDocument.CreateSparkline(aAddress, mpLocalSparklineGroup); - pCreated->setInputRange(aInputRangeSlice); - nIndex++; - } - } + rDocFunc.InsertSparklines(maInputRange, maOutputRange, mpLocalSparklineGroup); } } diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx index b6c93a86cfc0..f28ca7e74344 100644 --- a/sc/source/ui/docshell/docfunc.cxx +++ b/sc/source/ui/docshell/docfunc.cxx @@ -94,6 +94,9 @@ #include <conditio.hxx> #include <columnspanset.hxx> #include <validat.hxx> +#include <SparklineGroup.hxx> +#include <SparklineData.hxx> +#include <undo/UndoInsertSparkline.hxx> #include <config_features.h> #include <memory> @@ -5759,4 +5762,83 @@ void ScDocFunc::EndListAction() rDocShell.GetUndoManager()->LeaveListAction(); } +bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange, + std::shared_ptr<sc::SparklineGroup> pSparklineGroup) +{ + std::vector<sc::SparklineData> aSparklineDataVector; + + if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col()) + { + sal_Int32 nOutputRowSize = rSparklineRange.aEnd.Row() - rSparklineRange.aStart.Row(); + + auto eInputOrientation = sc::calculateOrientation(nOutputRowSize, rDataRange); + + if (eInputOrientation == sc::RangeOrientation::Unknown) + return false; + + sal_Int32 nIndex = 0; + + for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Row() <= rSparklineRange.aEnd.Row(); + aAddress.IncRow()) + { + ScRange aInputRangeSlice = rDataRange; + if (eInputOrientation == sc::RangeOrientation::Row) + { + aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex); + aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex); + } + else + { + aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex); + aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex); + } + + aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice); + + nIndex++; + } + } + else if (rSparklineRange.aStart.Row() == rSparklineRange.aEnd.Row()) + { + sal_Int32 nOutputColSize = rSparklineRange.aEnd.Col() - rSparklineRange.aStart.Col(); + + auto eInputOrientation = sc::calculateOrientation(nOutputColSize, rDataRange); + + if (eInputOrientation == sc::RangeOrientation::Unknown) + return false; + + sal_Int32 nIndex = 0; + + for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Col() <= rSparklineRange.aEnd.Col(); + aAddress.IncCol()) + { + ScRange aInputRangeSlice = rDataRange; + if (eInputOrientation == sc::RangeOrientation::Row) + { + aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex); + aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex); + } + else + { + aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex); + aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex); + } + + aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice); + + nIndex++; + } + } + + if (aSparklineDataVector.empty()) + return false; + + auto pUndoInsertSparkline = std::make_unique<sc::UndoInsertSparkline>(rDocShell, aSparklineDataVector, pSparklineGroup); + // insert the sparkline by "redoing" + pUndoInsertSparkline->Redo(); + rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoInsertSparkline)); + + return true; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx index 8b0ea7d7c16a..52d61cb2e86e 100644 --- a/sc/source/ui/inc/docfunc.hxx +++ b/sc/source/ui/inc/docfunc.hxx @@ -51,8 +51,10 @@ class ScPostIt; enum class TransliterationFlags; enum class CreateNameFlags; -namespace sc { +namespace sc +{ struct ColRowSpan; + class SparklineGroup; } class ScDocFunc @@ -234,6 +236,9 @@ public: void ConvertFormulaToValue( const ScRange& rRange, bool bInteraction ); + SC_DLLPUBLIC bool InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange, + std::shared_ptr<sc::SparklineGroup> pSparklineGroup); + private: void ProtectDocument(const ScDocProtection& rProtect); }; diff --git a/sc/source/ui/inc/undo/UndoInsertSparkline.hxx b/sc/source/ui/inc/undo/UndoInsertSparkline.hxx new file mode 100644 index 000000000000..3c2fa6d17977 --- /dev/null +++ b/sc/source/ui/inc/undo/UndoInsertSparkline.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#pragma once + +#include "undobase.hxx" +#include <address.hxx> +#include <memory> + +namespace sc +{ +class SparklineGroup; +class SparklineData; + +/** Undo action for inserting a Sparkline */ +class UndoInsertSparkline : public ScSimpleUndo +{ +private: + std::vector<sc::SparklineData> maSparklineDataVector; + std::shared_ptr<sc::SparklineGroup> mpSparklineGroup; + +public: + UndoInsertSparkline(ScDocShell& rDocShell, + std::vector<SparklineData> const& rSparklineDataVector, + std::shared_ptr<sc::SparklineGroup> pSparklineGroup); + + virtual ~UndoInsertSparkline() override; + + void Undo() override; + void Redo() override; + bool CanRepeat(SfxRepeatTarget& rTarget) const override; + void Repeat(SfxRepeatTarget& rTarget) override; + OUString GetComment() const override; +}; + +} // namespace sc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/sparklines/SparklineData.cxx b/sc/source/ui/sparklines/SparklineData.cxx new file mode 100644 index 000000000000..a126acc10b0a --- /dev/null +++ b/sc/source/ui/sparklines/SparklineData.cxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <SparklineData.hxx> + +namespace sc +{ +RangeOrientation calculateOrientation(sal_Int32 nOutputSize, ScRange const& rInputRange) +{ + sal_Int32 nRowSize = rInputRange.aEnd.Row() - rInputRange.aStart.Row(); + sal_Int32 nColSize = rInputRange.aEnd.Col() - rInputRange.aStart.Col(); + + auto eInputOrientation = RangeOrientation::Unknown; + if (nOutputSize == nRowSize) + eInputOrientation = RangeOrientation::Row; + else if (nOutputSize == nColSize) + eInputOrientation = RangeOrientation::Col; + return eInputOrientation; +} + +} // end sc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/undo/UndoInsertSparkline.cxx b/sc/source/ui/undo/UndoInsertSparkline.cxx new file mode 100644 index 000000000000..c35cc3f6dc03 --- /dev/null +++ b/sc/source/ui/undo/UndoInsertSparkline.cxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <undo/UndoInsertSparkline.hxx> +#include <globstr.hrc> +#include <scresid.hxx> + +#include <Sparkline.hxx> +#include <SparklineGroup.hxx> +#include <SparklineData.hxx> + +namespace sc +{ +UndoInsertSparkline::UndoInsertSparkline(ScDocShell& rDocShell, + std::vector<SparklineData> const& rSparklineDataVector, + std::shared_ptr<sc::SparklineGroup> pSparklineGroup) + : ScSimpleUndo(&rDocShell) + , maSparklineDataVector(rSparklineDataVector) + , mpSparklineGroup(pSparklineGroup) +{ +} + +UndoInsertSparkline::~UndoInsertSparkline() {} + +void UndoInsertSparkline::Undo() +{ + BeginUndo(); + + ScDocument& rDocument = pDocShell->GetDocument(); + ScRangeList aRanges; + for (auto const& rSparklineData : maSparklineDataVector) + { + rDocument.DeleteSparkline(rSparklineData.maPosition); + aRanges.push_back(ScRange(rSparklineData.maPosition)); + } + + pDocShell->PostPaint(aRanges, PaintPartFlags::All); + + EndUndo(); +} + +void UndoInsertSparkline::Redo() +{ + BeginRedo(); + + ScDocument& rDocument = pDocShell->GetDocument(); + ScRangeList aRanges; + for (auto const& rSparklineData : maSparklineDataVector) + { + auto* pCreated = rDocument.CreateSparkline(rSparklineData.maPosition, mpSparklineGroup); + pCreated->setInputRange(rSparklineData.maData); + aRanges.push_back(ScRange(rSparklineData.maPosition)); + } + + pDocShell->PostPaint(aRanges, PaintPartFlags::All); + + EndRedo(); +} + +void UndoInsertSparkline::Repeat(SfxRepeatTarget& /*rTarget*/) {} + +bool UndoInsertSparkline::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; } + +OUString UndoInsertSparkline::GetComment() const +{ + return ScResId(STR_UNDO_INSERT_SPARKLINE_GROUP); +} + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 62055d98dd2e6676aa20855a6dfe15234cde65b8 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat Mar 19 12:52:21 2022 +0900 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat Mar 19 12:52:21 2022 +0900 sc: add support for copy/cut and paster 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 diff --git a/sc/inc/SparklineGroup.hxx b/sc/inc/SparklineGroup.hxx index ec3a8c8a971f..a045f8d659d1 100644 --- a/sc/inc/SparklineGroup.hxx +++ b/sc/inc/SparklineGroup.hxx @@ -101,7 +101,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 91bb18b4813b..3da4a7bac798 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -168,6 +168,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). */ @@ -193,6 +196,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 ); @@ -621,6 +626,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 ); @@ -649,7 +658,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 74f40579d99f..826638feb17c 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(); }; @@ -85,10 +91,10 @@ void SparklineTest::testAddSparkline() sc::Sparkline* 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); @@ -112,12 +118,116 @@ void SparklineTest::testDeleteSprkline() 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(); } +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 b66638336a11..12bfca27adcf 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -1019,6 +1019,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 c7e2dd5d8006..24702e11d5a3 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> @@ -2005,6 +2006,82 @@ 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& mrDestCol; + sc::SparklineStoreType& mrDestSparkline; + sc::SparklineStoreType::iterator miDestPosition; + SCTAB mnSrcTab; + SCCOL mnSrcCol; + SCTAB mnDestTab; + SCCOL mnDestCol; + SCROW mnDestOffset; + +public: + CopySparklinesHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, SCROW nDestOffset) + : mrDestCol(rDestCol) + , mrDestSparkline(mrDestCol.GetSparklineStore()) + , miDestPosition(mrDestSparkline.begin()) + , mnSrcTab(rSrcCol.GetTab()) + , mnSrcCol(rSrcCol.GetCol()) + , mnDestTab(rDestCol.GetTab()) + , mnDestCol(rDestCol.GetCol()) + , 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>(mnDestCol, 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(*this, 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 dc6a68420a6f..a4c1e7ff57af 100644 --- a/sc/source/core/data/column3.cxx +++ b/sc/source/core/data/column3.cxx @@ -1117,6 +1117,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), @@ -1156,6 +1161,7 @@ public: { SCROW nSrcRow1 = node.position + nOffset; bool bCopyCellNotes = mrCxt.isCloneNotes(); + bool bCopySparklines = mrCxt.isCloneSparklines(); InsertDeleteFlags nFlags = mrCxt.getInsertFlag(); @@ -1166,6 +1172,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; } @@ -1356,6 +1366,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 e9b7d1d189dc..334652d86987 100644 --- a/sc/source/core/data/column4.cxx +++ b/sc/source/core/data/column4.cxx @@ -30,6 +30,8 @@ #include <compiler.hxx> #include <recursionhelper.hxx> +#include <SparklineGroup.hxx> + #include <o3tl/safeint.hxx> #include <svl/sharedstringpool.hxx> #include <sal/log.hxx> @@ -120,6 +122,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); @@ -208,6 +213,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); @@ -325,6 +333,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; @@ -334,13 +347,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 = @@ -348,6 +360,29 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end()); } +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); + 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()); +} + void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals ) { if (!GetDoc().ValidRow(nRow)) diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx index 2e839d80dba4..036e96bbc2fb 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..e57f5281d922 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 4032294a03dfa0432dae7ebcd392ce87f8dee680 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat Mar 19 10:56:27 2022 +0900 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat Mar 19 10:56:27 2022 +0900 sc: change GetSparkline to return a shared_ptr instead of raw ptr Change-Id: If3d7b3ad4b96eb7d3b126ee8b130f8d5e684cd3c diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index c101612ab2dc..b5274c3533e0 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1243,7 +1243,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> & 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 2393f8ace994..6c7053905f3a 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> & pSparklineGroup); bool DeleteSparkline(SCCOL nCol, SCROW nRow); diff --git a/sc/qa/unit/SparklineImportExportTest.cxx b/sc/qa/unit/SparklineImportExportTest.cxx index 650a45b89c6f..cd0578e91418 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/source/core/data/document.cxx b/sc/source/core/data/document.cxx index 03f468520a03..3a11c7a3d950 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -6506,7 +6506,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(); @@ -6514,7 +6514,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> & pSparklineGroup) diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx index 88f9a4e8ee97..d92e651c03b6 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>& 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 17903c3a23fd..a73989d848a8 100644 --- a/sc/source/ui/view/output.cxx +++ b/sc/source/ui/view/output.cxx @@ -2474,7 +2474,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(); @@ -2574,7 +2574,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)) commit bc4f48bd7e34fce2ab6c9338ce30c32c6ec7e716 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat Mar 19 10:39:20 2022 +0900 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat Mar 19 10:39:20 2022 +0900 sc: make getters const in SparklineCell and Sparkline classes Change-Id: Ia0bc1d4bd7da834da3640f34bfdb744dd2ddeba2 diff --git a/sc/inc/Sparkline.hxx b/sc/inc/Sparkline.hxx index 7b7785f3169a..5cc079f8530e 100644 --- a/sc/inc/Sparkline.hxx +++ b/sc/inc/Sparkline.hxx @@ -39,13 +39,13 @@ public: void setInputRange(ScRangeList const& rInputRange) { m_aInputRange = rInputRange; } - ScRangeList const& getInputRange() { return m_aInputRange; } + ScRangeList const& getInputRange() const { return m_aInputRange; } - std::shared_ptr<SparklineGroup> const& getSparklineGroup() { return m_pSparklineGroup; } + std::shared_ptr<SparklineGroup> const& getSparklineGroup() const { return m_pSparklineGroup; } - SCCOL getColumn() { return m_nColumn; } + SCCOL getColumn() const { return m_nColumn; } - SCROW getRow() { return m_nRow; } + SCROW getRow() const { return m_nRow; } }; class SC_DLLPUBLIC SparklineList diff --git a/sc/inc/SparklineCell.hxx b/sc/inc/SparklineCell.hxx index 0aca857170c9..0588646866c4 100644 --- a/sc/inc/SparklineCell.hxx +++ b/sc/inc/SparklineCell.hxx @@ -34,12 +34,12 @@ public: ScRangeList const& getInputRange() { return m_pSparkline->getInputRange(); } - std::shared_ptr<SparklineGroup> const& getSparklineGroup() + std::shared_ptr<SparklineGroup> const& getSparklineGroup() const { return m_pSparkline->getSparklineGroup(); } - std::shared_ptr<Sparkline> const& getSparkline() { return m_pSparkline; } + std::shared_ptr<Sparkline> const& getSparkline() const { return m_pSparkline; } }; } // end sc