officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu |    8 
 sc/Library_sc.mk                                                  |    1 
 sc/UIConfig_scalc.mk                                              |    1 
 sc/inc/sc.hrc                                                     |    5 
 sc/sdi/cellsh.sdi                                                 |    1 
 sc/sdi/scalc.sdi                                                  |   17 
 sc/source/ui/app/scdll.cxx                                        |    1 
 sc/source/ui/dialogs/SparklineDialog.cxx                          |  324 
++++++++++
 sc/source/ui/inc/SparklineDialog.hxx                              |   64 +
 sc/source/ui/inc/reffact.hxx                                      |   12 
 sc/source/ui/view/cellsh.cxx                                      |    1 
 sc/source/ui/view/cellsh1.cxx                                     |    9 
 sc/source/ui/view/tabvwsh.cxx                                     |    1 
 sc/source/ui/view/tabvwshc.cxx                                    |    6 
 sc/uiconfig/scalc/popupmenu/cell.xml                              |    2 
 sc/uiconfig/scalc/ui/sparklinedialog.ui                           |  218 ++++++
 16 files changed, 670 insertions(+), 1 deletion(-)

New commits:
commit c435ac74a727f4de14b06c87930127c709d4199d
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Fri Mar 11 20:49:09 2022 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Mon Apr 11 16:52:44 2022 +0200

    sc: SparklineDialog and "Insert Sparkline" to context menu
    
    This adds a SparklineDialog, which is used to add/edit the
    Sparkline input/output ranges. The command for the context menu
    "Insert Sparkline" calls the SparklineDialog for inserting a new
    sparkline into cells. Currently the SparklineDialog include the
    properties for the SparklineGroup, which will be added in a later
    commit.
    
    Change-Id: I9036d788fdf2a035f1ce10fc7413327a92144137
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132465
    Tested-by: Tomaž Vajngerl <qui...@gmail.com>
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    (cherry picked from commit c61aa2dea120cc083f3cd51f0347284f47a9d566)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132780

diff --git a/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu 
b/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
index 12e39a72ed0f..d59794e893b8 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
@@ -1066,6 +1066,14 @@
           <value>1</value>
         </prop>
       </node>
+      <node oor:name=".uno:InsertSparkline" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Insert Sparkline...</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
       <node oor:name=".uno:EditHeaderAndFooter" oor:op="replace">
         <prop oor:name="Label" oor:type="xs:string">
           <value xml:lang="en-US">~Headers and Footers...</value>
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 3c8dcb3e5085..8f301a537f85 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -416,6 +416,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/ui/dbgui/sfiltdlg \
     sc/source/ui/dbgui/validate \
     sc/source/ui/dialogs/searchresults \
+    sc/source/ui/dialogs/SparklineDialog \
     sc/source/ui/docshell/arealink \
     sc/source/ui/docshell/autostyl \
     sc/source/ui/docshell/datastream \
diff --git a/sc/UIConfig_scalc.mk b/sc/UIConfig_scalc.mk
index bc320cc44fd7..302f08ff3fb3 100644
--- a/sc/UIConfig_scalc.mk
+++ b/sc/UIConfig_scalc.mk
@@ -244,6 +244,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/scalc,\
        sc/uiconfig/scalc/ui/sortkey \
        sc/uiconfig/scalc/ui/sortoptionspage \
        sc/uiconfig/scalc/ui/sortwarning \
+       sc/uiconfig/scalc/ui/sparklinedialog \
        sc/uiconfig/scalc/ui/splitcolumnentry \
        sc/uiconfig/scalc/ui/subtotaldialog \
        sc/uiconfig/scalc/ui/subtotaloptionspage \
diff --git a/sc/inc/sc.hrc b/sc/inc/sc.hrc
index e958d421c18e..eebf39d321e3 100644
--- a/sc/inc/sc.hrc
+++ b/sc/inc/sc.hrc
@@ -235,6 +235,8 @@
 #define SID_COLUMN_OPERATIONS               (SC_MESSAGE_START + 86)
 #define SID_ROW_OPERATIONS                  (SC_MESSAGE_START + 87)
 #define SID_FOURIER_ANALYSIS_DIALOG         (SC_MESSAGE_START + 88)
+#define SID_SPARKLINE_DIALOG                (SC_MESSAGE_START + 89)
+
 
 // functions
 
@@ -303,7 +305,8 @@
 #define FID_INS_ROWS_BEFORE     (INSERT_MENU_START + 22)
 #define FID_INS_COLUMNS_BEFORE  (INSERT_MENU_START + 23)
 #define FID_DEFINE_CURRENT_NAME (INSERT_MENU_START + 24)
-#define INSERT_MENU_END         (INSERT_MENU_START + 25)
+#define SID_INSERT_SPARKLINE    (INSERT_MENU_START + 25)
+#define INSERT_MENU_END         (INSERT_MENU_START + 26)
 
 #define FORMAT_MENU_START       (INSERT_MENU_END)
 #define FID_CELL_FORMAT         (FORMAT_MENU_START)
diff --git a/sc/sdi/cellsh.sdi b/sc/sdi/cellsh.sdi
index c9eed43d665b..8feaf0c590e0 100644
--- a/sc/sdi/cellsh.sdi
+++ b/sc/sdi/cellsh.sdi
@@ -235,6 +235,7 @@ interface CellSelection
     SID_SELECT_VISIBLE_ROWS             [ ExecMethod = ExecuteEdit;]
     SID_SELECT_VISIBLE_COLUMNS          [ ExecMethod = ExecuteEdit;]
     SID_CURRENT_FORMULA_RANGE           [ ExecMethod = ExecuteEdit;]
+    SID_INSERT_SPARKLINE                [ ExecMethod = ExecuteEdit; 
StateMethod = GetBlockState; ]
 
     SID_THESAURUS   [ ExecMethod = ExecuteEdit; StateMethod = GetCellState; ]
     SID_SPELL_DIALOG [ ExecMethod = ExecuteEdit; StateMethod = GetState; ]
diff --git a/sc/sdi/scalc.sdi b/sc/sdi/scalc.sdi
index fb52b40bac8c..c779a47bc98d 100644
--- a/sc/sdi/scalc.sdi
+++ b/sc/sdi/scalc.sdi
@@ -2287,6 +2287,23 @@ SfxVoidItem SolverDialog SID_OPENDLG_OPTSOLVER
     GroupId = SfxGroupId::Options;
 ]
 
+SfxVoidItem InsertSparkline SID_INSERT_SPARKLINE
+()
+[
+    AutoUpdate = FALSE,
+    FastCall = FALSE,
+    ReadOnlyDoc = TRUE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+
+    AccelConfig = TRUE,
+    MenuConfig = TRUE,
+    ToolBoxConfig = TRUE,
+    GroupId = SfxGroupId::Insert;
+]
+
 SfxVoidItem SearchResultsDialog SID_SEARCH_RESULTS_DIALOG
 (SfxBoolItem Visible SID_SEARCH_RESULTS_DIALOG)
 [
diff --git a/sc/source/ui/app/scdll.cxx b/sc/source/ui/app/scdll.cxx
index b7f58002f71b..a0bdf08b16c0 100644
--- a/sc/source/ui/app/scdll.cxx
+++ b/sc/source/ui/app/scdll.cxx
@@ -206,6 +206,7 @@ void ScDLL::Init()
     ScZTestDialogWrapper                ::RegisterChildWindow(false, pMod);
     ScChiSquareTestDialogWrapper        ::RegisterChildWindow(false, pMod);
     ScFourierAnalysisDialogWrapper      ::RegisterChildWindow(false, pMod);
+    sc::SparklineDialogWrapper          ::RegisterChildWindow(false, pMod);
 
     // Redlining Window
     ScAcceptChgDlgWrapper       ::RegisterChildWindow(false, pMod);
diff --git a/sc/source/ui/dialogs/SparklineDialog.cxx 
b/sc/source/ui/dialogs/SparklineDialog.cxx
new file mode 100644
index 000000000000..5a167fe8b7c1
--- /dev/null
+++ b/sc/source/ui/dialogs/SparklineDialog.cxx
@@ -0,0 +1,324 @@
+/* -*- 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 <SparklineDialog.hxx>
+#include <SparklineGroup.hxx>
+#include <Sparkline.hxx>
+#include <reffact.hxx>
+
+namespace sc
+{
+SparklineDialog::SparklineDialog(SfxBindings* pBindings, SfxChildWindow* 
pChildWindow,
+                                 weld::Window* pWindow, ScViewData& rViewData)
+    : ScAnyRefDlgController(pBindings, pChildWindow, pWindow,
+                            u"modules/scalc/ui/sparklinedialog.ui", 
"SparklineDialog")
+    , mrViewData(rViewData)
+    , mrDocument(rViewData.GetDocument())
+    , mpActiveEdit(nullptr)
+    , mbDialogLostFocus(false)
+    , mxButtonOk(m_xBuilder->weld_button("ok"))
+    , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+    , mxInputRangeText(m_xBuilder->weld_label("cell-range-label"))
+    , mxInputRangeEdit(new 
formula::RefEdit(m_xBuilder->weld_entry("cell-range-edit")))
+    , mxInputRangeButton(new 
formula::RefButton(m_xBuilder->weld_button("cell-range-button")))
+    , mxOutputRangeLabel(m_xBuilder->weld_label("output-range-label"))
+    , mxOutputRangeEdit(new 
formula::RefEdit(m_xBuilder->weld_entry("output-range-edit")))
+    , mxOutputRangeButton(new 
formula::RefButton(m_xBuilder->weld_button("output-range-button")))
+{
+    mxInputRangeEdit->SetReferences(this, mxInputRangeText.get());
+    mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+    mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
+    mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());
+
+    mxButtonCancel->connect_clicked(LINK(this, SparklineDialog, 
ButtonClicked));
+    mxButtonOk->connect_clicked(LINK(this, SparklineDialog, ButtonClicked));
+    mxButtonOk->set_sensitive(false);
+
+    Link<formula::RefEdit&, void> aEditLink = LINK(this, SparklineDialog, 
EditFocusHandler);
+    mxInputRangeEdit->SetGetFocusHdl(aEditLink);
+    mxOutputRangeEdit->SetGetFocusHdl(aEditLink);
+    aEditLink = LINK(this, SparklineDialog, LoseEditFocusHandler);
+    mxInputRangeEdit->SetLoseFocusHdl(aEditLink);
+    mxOutputRangeEdit->SetLoseFocusHdl(aEditLink);
+
+    Link<formula::RefButton&, void> aButtonLink = LINK(this, SparklineDialog, 
ButtonFocusHandler);
+    mxInputRangeButton->SetGetFocusHdl(aButtonLink);
+    mxOutputRangeButton->SetGetFocusHdl(aButtonLink);
+    aButtonLink = LINK(this, SparklineDialog, LoseButtonFocusHandler);
+    mxInputRangeButton->SetLoseFocusHdl(aButtonLink);
+    mxOutputRangeButton->SetLoseFocusHdl(aButtonLink);
+
+    Link<formula::RefEdit&, void> aModifyLink = LINK(this, SparklineDialog, 
RefInputModifyHandler);
+    mxInputRangeEdit->SetModifyHdl(aModifyLink);
+    mxOutputRangeEdit->SetModifyHdl(aModifyLink);
+
+    mxOutputRangeEdit->GrabFocus();
+
+    GetRangeFromSelection();
+}
+
+SparklineDialog::~SparklineDialog() {}
+
+void SparklineDialog::Close() { 
DoClose(sc::SparklineDialogWrapper::GetChildWindowId()); }
+
+void SparklineDialog::SetActive()
+{
+    if (mbDialogLostFocus)
+    {
+        mbDialogLostFocus = false;
+        if (mpActiveEdit)
+            mpActiveEdit->GrabFocus();
+    }
+    else
+    {
+        m_xDialog->grab_focus();
+    }
+    RefInputDone();
+}
+
+void SparklineDialog::GetRangeFromSelection()
+{
+    mrViewData.GetSimpleArea(maInputRange);
+    OUString aString = maInputRange.Format(mrDocument, ScRefFlags::VALID | 
ScRefFlags::TAB_3D,
+                                           mrDocument.GetAddressConvention());
+    mxInputRangeEdit->SetRefString(aString);
+}
+
+void SparklineDialog::SetReference(const ScRange& rReferenceRange, ScDocument& 
rDocument)
+{
+    if (mpActiveEdit)
+    {
+        if (rReferenceRange.aStart != rReferenceRange.aEnd)
+            RefInputStart(mpActiveEdit);
+
+        OUString aString;
+        const ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+        auto eAddressConvention = rDocument.GetAddressConvention();
+
+        if (mpActiveEdit == mxInputRangeEdit.get())
+        {
+            maInputRange = rReferenceRange;
+            aString = maInputRange.Format(rDocument, eFlags, 
eAddressConvention);
+            mxInputRangeEdit->SetRefString(aString);
+        }
+        else if (mpActiveEdit == mxOutputRangeEdit.get())
+        {
+            maOutputRange = rReferenceRange;
+            aString = maOutputRange.Format(rDocument, eFlags, 
eAddressConvention);
+            mxOutputRangeEdit->SetRefString(aString);
+        }
+    }
+
+    mxButtonOk->set_sensitive(checkValidInputOutput());
+}
+
+IMPL_LINK(SparklineDialog, EditFocusHandler, formula::RefEdit&, rEdit, void)
+{
+    auto* pEdit = &rEdit;
+
+    if (mxInputRangeEdit.get() == pEdit)
+        mpActiveEdit = mxInputRangeEdit.get();
+    else if (mxOutputRangeEdit.get() == pEdit)
+        mpActiveEdit = mxOutputRangeEdit.get();
+    else
+        mpActiveEdit = nullptr;
+
+    if (mpActiveEdit)
+        mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(SparklineDialog, ButtonFocusHandler, formula::RefButton&, rButton, 
void)
+{
+    auto* pButton = &rButton;
+
+    if (mxInputRangeButton.get() == pButton)
+        mpActiveEdit = mxInputRangeEdit.get();
+    else if (mxOutputRangeButton.get() == pButton)
+        mpActiveEdit = mxOutputRangeEdit.get();
+    else
+        mpActiveEdit = nullptr;
+
+    if (mpActiveEdit)
+        mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+    mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, LoseButtonFocusHandler, formula::RefButton&, 
void)
+{
+    mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, RefInputModifyHandler, formula::RefEdit&, 
void)
+{
+    if (mpActiveEdit)
+    {
+        if (mpActiveEdit == mxInputRangeEdit.get())
+        {
+            ScRangeList aRangeList;
+            bool bValid = ParseWithNames(aRangeList, 
mxInputRangeEdit->GetText(), mrDocument);
+            const ScRange* pRange = (bValid && aRangeList.size() == 1) ? 
&aRangeList[0] : nullptr;
+            if (pRange)
+            {
+                maInputRange = *pRange;
+                mxInputRangeEdit->StartUpdateData();
+            }
+            else
+            {
+                maInputRange = ScRange(ScAddress::INITIALIZE_INVALID);
+            }
+        }
+        else if (mpActiveEdit == mxOutputRangeEdit.get())
+        {
+            ScRangeList aRangeList;
+            bool bValid = ParseWithNames(aRangeList, 
mxOutputRangeEdit->GetText(), mrDocument);
+            const ScRange* pRange = (bValid && aRangeList.size() == 1) ? 
&aRangeList[0] : nullptr;
+            if (pRange)
+            {
+                maOutputRange = *pRange;
+                mxOutputRangeEdit->StartUpdateData();
+            }
+            else
+            {
+                maOutputRange = ScRange(ScAddress::INITIALIZE_INVALID);
+            }
+        }
+    }
+
+    mxButtonOk->set_sensitive(checkValidInputOutput());
+}
+
+IMPL_LINK(SparklineDialog, ButtonClicked, weld::Button&, rButton, void)
+{
+    if (mxButtonOk.get() == &rButton)
+    {
+        perform();
+        response(RET_OK);
+    }
+    else
+    {
+        response(RET_CANCEL);
+    }
+}
+
+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;
+    if (maOutputRange.aStart.Col() == maOutputRange.aEnd.Col())
+    {
+        sal_Int32 nOutputRowSize = maOutputRange.aEnd.Row() - 
maOutputRange.aStart.Row();
+        eInputOrientation = calculateOrientation(nOutputRowSize, maInputRange);
+    }
+    else if (maOutputRange.aStart.Row() == maOutputRange.aEnd.Row())
+    {
+        sal_Int32 nOutputColSize = maOutputRange.aEnd.Col() - 
maOutputRange.aStart.Col();
+        eInputOrientation = calculateOrientation(nOutputColSize, maInputRange);
+    }
+
+    return eInputOrientation != RangeOrientation::Unknown;
+}
+
+void SparklineDialog::perform()
+{
+    auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
+
+    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, 
pSparklineGroup);
+            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;
+
+        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, 
pSparklineGroup);
+            pCreated->setInputRange(aInputRangeSlice);
+            nIndex++;
+        }
+    }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SparklineDialog.hxx 
b/sc/source/ui/inc/SparklineDialog.hxx
new file mode 100644
index 000000000000..a4a03f33491d
--- /dev/null
+++ b/sc/source/ui/inc/SparklineDialog.hxx
@@ -0,0 +1,64 @@
+/* -*- 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 "anyrefdg.hxx"
+#include <viewdata.hxx>
+
+namespace sc
+{
+class SparklineDialog : public ScAnyRefDlgController
+{
+private:
+    ScViewData& mrViewData;
+    ScDocument& mrDocument;
+
+    ScRange maInputRange;
+    ScRange maOutputRange;
+
+    formula::RefEdit* mpActiveEdit;
+    bool mbDialogLostFocus;
+
+    std::unique_ptr<weld::Button> mxButtonOk;
+    std::unique_ptr<weld::Button> mxButtonCancel;
+
+    std::unique_ptr<weld::Label> mxInputRangeText;
+    std::unique_ptr<formula::RefEdit> mxInputRangeEdit;
+    std::unique_ptr<formula::RefButton> mxInputRangeButton;
+
+    std::unique_ptr<weld::Label> mxOutputRangeLabel;
+    std::unique_ptr<formula::RefEdit> mxOutputRangeEdit;
+    std::unique_ptr<formula::RefButton> mxOutputRangeButton;
+
+    void GetRangeFromSelection();
+
+    DECL_LINK(ButtonClicked, weld::Button&, void);
+    DECL_LINK(EditFocusHandler, formula::RefEdit&, void);
+    DECL_LINK(ButtonFocusHandler, formula::RefButton&, void);
+    DECL_LINK(LoseEditFocusHandler, formula::RefEdit&, void);
+    DECL_LINK(LoseButtonFocusHandler, formula::RefButton&, void);
+    DECL_LINK(RefInputModifyHandler, formula::RefEdit&, void);
+
+    void perform();
+    bool checkValidInputOutput();
+
+public:
+    SparklineDialog(SfxBindings* pBindings, SfxChildWindow* pChildWindow, 
weld::Window* pWindow,
+                    ScViewData& rViewData);
+    virtual ~SparklineDialog() override;
+
+    virtual void SetReference(const ScRange& rRef, ScDocument& rDocument) 
override;
+    virtual void SetActive() override;
+    virtual void Close() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/reffact.hxx b/sc/source/ui/inc/reffact.hxx
index c1b73606eb7f..4bf1254b4556 100644
--- a/sc/source/ui/inc/reffact.hxx
+++ b/sc/source/ui/inc/reffact.hxx
@@ -147,6 +147,18 @@ private:
     ScFourierAnalysisDialogWrapper() = delete;
 };
 
+namespace sc
+{
+
+class SparklineDialogWrapper :
+    public ChildControllerWrapper<SID_SPARKLINE_DIALOG>
+{
+private:
+    SparklineDialogWrapper() = delete;
+};
+
+}
+
 class ScAcceptChgDlgWrapper : public SfxChildWindow
 {
 public:
diff --git a/sc/source/ui/view/cellsh.cxx b/sc/source/ui/view/cellsh.cxx
index 1a5f697a0a99..13d4430a956a 100644
--- a/sc/source/ui/view/cellsh.cxx
+++ b/sc/source/ui/view/cellsh.cxx
@@ -179,6 +179,7 @@ void ScCellShell::GetBlockState( SfxItemSet& rSet )
             case SID_ANALYSIS_OF_VARIANCE_DIALOG:
             case SID_CORRELATION_DIALOG:
             case SID_COVARIANCE_DIALOG:
+            case SID_INSERT_SPARKLINE:
             {
                 bDisable = !bSimpleArea;
             }
diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx
index 2b0e68827a90..795e8dad06ac 100644
--- a/sc/source/ui/view/cellsh1.cxx
+++ b/sc/source/ui/view/cellsh1.cxx
@@ -1053,6 +1053,15 @@ void ScCellShell::ExecuteEdit( SfxRequest& rReq )
         }
         break;
 
+        case SID_INSERT_SPARKLINE:
+        {
+            sal_uInt16 nId  = sc::SparklineDialogWrapper::GetChildWindowId();
+            SfxViewFrame* pViewFrame = pTabViewShell->GetViewFrame();
+            SfxChildWindow* pWindow = pViewFrame->GetChildWindow(nId);
+            pScMod->SetRefDialog(nId, pWindow == nullptr);
+        }
+        break;
+
         //  disposal (Outlines)
         //  SID_AUTO_OUTLINE, SID_OUTLINE_DELETEALL in Execute (in docsh.idl)
 
diff --git a/sc/source/ui/view/tabvwsh.cxx b/sc/source/ui/view/tabvwsh.cxx
index d8e48596f3f6..7337b5cc2f0d 100644
--- a/sc/source/ui/view/tabvwsh.cxx
+++ b/sc/source/ui/view/tabvwsh.cxx
@@ -97,6 +97,7 @@ void ScTabViewShell::InitInterface_Impl()
     
GetStaticInterface()->RegisterChildWindow(ScChiSquareTestDialogWrapper::GetChildWindowId());
     
GetStaticInterface()->RegisterChildWindow(ScFourierAnalysisDialogWrapper::GetChildWindowId());
     
GetStaticInterface()->RegisterChildWindow(ScCondFormatDlgWrapper::GetChildWindowId());
+    
GetStaticInterface()->RegisterChildWindow(sc::SparklineDialogWrapper::GetChildWindowId());
 }
 
 SFX_IMPL_NAMED_VIEWFACTORY( ScTabViewShell, "Default" )
diff --git a/sc/source/ui/view/tabvwshc.cxx b/sc/source/ui/view/tabvwshc.cxx
index e43cb2ff31bc..667992e57da9 100644
--- a/sc/source/ui/view/tabvwshc.cxx
+++ b/sc/source/ui/view/tabvwshc.cxx
@@ -69,6 +69,7 @@
 #include <FourierAnalysisDialog.hxx>
 
 #include <PivotLayoutDialog.hxx>
+#include <SparklineDialog.hxx>
 
 #include <comphelper/lok.hxx>
 #include <o3tl/make_shared.hxx>
@@ -236,6 +237,11 @@ std::shared_ptr<SfxModelessDialogController> 
ScTabViewShell::CreateRefDialogCont
         case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
             xResult = std::make_shared<ScRandomNumberGeneratorDialog>(pB, pCW, 
pParent, GetViewData());
             break;
+        case SID_SPARKLINE_DIALOG:
+        {
+            xResult = std::make_shared<sc::SparklineDialog>(pB, pCW, pParent, 
GetViewData());
+            break;
+        }
         case SID_DEFINE_DBNAME:
         {
             // when called for an existing range, then mark
diff --git a/sc/uiconfig/scalc/popupmenu/cell.xml 
b/sc/uiconfig/scalc/popupmenu/cell.xml
index fd086d0af975..135cfb2e742e 100644
--- a/sc/uiconfig/scalc/popupmenu/cell.xml
+++ b/sc/uiconfig/scalc/popupmenu/cell.xml
@@ -63,6 +63,8 @@
   <menu:menuitem menu:id=".uno:ShowNote"/>
   <menu:menuitem menu:id=".uno:HideNote"/>
   <menu:menuseparator/>
+  <menu:menuitem menu:id=".uno:InsertSparkline"/>
+  <menu:menuseparator/>
   <menu:menuitem menu:id=".uno:CurrentConditionalFormatDialog"/>
   <menu:menuitem menu:id=".uno:CurrentConditionalFormatManagerDialog"/>
   <menu:menuitem menu:id=".uno:FormatCellDialog"/>
diff --git a/sc/uiconfig/scalc/ui/sparklinedialog.ui 
b/sc/uiconfig/scalc/ui/sparklinedialog.ui
new file mode 100644
index 000000000000..85515462c323
--- /dev/null
+++ b/sc/uiconfig/scalc/ui/sparklinedialog.ui
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sc">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkDialog" id="SparklineDialog">
+    <property name="can-focus">False</property>
+    <property name="border-width">6</property>
+    <property name="title" translatable="yes" 
context="SparklineDialog">Sparkline Dialog</property>
+    <property name="resizable">False</property>
+    <property name="default-width">0</property>
+    <property name="default-height">0</property>
+    <property name="type-hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="ok">
+                <property name="label" translatable="yes" 
context="stock">_OK</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="can-default">True</property>
+                <property name="has-default">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel">
+                <property name="label" translatable="yes" 
context="stock">_Close</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="can-default">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help">
+                <property name="label" translatable="yes" 
context="stock">_Help</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack-type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkFrame" id="frame-data">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="label-xalign">0</property>
+            <property name="shadow-type">none</property>
+            <child>
+              <!-- n-columns=3 n-rows=2 -->
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="margin-start">6</property>
+                <property name="margin-end">6</property>
+                <property name="margin-top">6</property>
+                <property name="margin-bottom">7</property>
+                <property name="row-spacing">6</property>
+                <property name="column-spacing">6</property>
+                <child>
+                  <object class="GtkButton" id="cell-range-button">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">True</property>
+                    <accessibility>
+                      <relation type="labelled-by" target="cell-range-label"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="cell-range-edit">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="valign">center</property>
+                    <property name="hexpand">True</property>
+                    <property name="activates-default">True</property>
+                    <property name="width-chars">30</property>
+                    <property name="truncate-multiline">True</property>
+                    <accessibility>
+                      <relation type="labelled-by" target="cell-range-label"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="output-range-button">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">True</property>
+                    <accessibility>
+                      <relation type="labelled-by" 
target="output-range-label"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="output-range-edit">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="valign">center</property>
+                    <property name="hexpand">True</property>
+                    <property name="activates-default">True</property>
+                    <property name="width-chars">30</property>
+                    <property name="truncate-multiline">True</property>
+                    <accessibility>
+                      <relation type="labelled-by" 
target="output-range-label"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="cell-range-label">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="SparklineDialog|cell-range-label">Input range:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                    <accessibility>
+                      <relation type="label-for" target="cell-range-button"/>
+                      <relation type="label-for" target="cell-range-edit"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="output-range-label">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="SparklineDialog|cell-range-label">Output range:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                    <accessibility>
+                      <relation type="label-for" target="output-range-button"/>
+                      <relation type="label-for" target="output-range-edit"/>
+                    </accessibility>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="SparklineDialog|label1">Data</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-5">ok</action-widget>
+      <action-widget response="-6">cancel</action-widget>
+      <action-widget response="-11">help</action-widget>
+    </action-widgets>
+  </object>
+</interface>

Reply via email to