1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <SparklineDialog.hxx>
11 #include <SparklineData.hxx>
12 #include <SparklineGroup.hxx>
13 #include <Sparkline.hxx>
14 #include <reffact.hxx>
16 #include <docfunc.hxx>
18 #include <svx/colorbox.hxx>
19 #include <vcl/formatter.hxx>
23 SparklineDialog::SparklineDialog(SfxBindings
* pBindings
, SfxChildWindow
* pChildWindow
,
24 weld::Window
* pWindow
, ScViewData
& rViewData
)
25 : ScAnyRefDlgController(pBindings
, pChildWindow
, pWindow
,
26 u
"modules/scalc/ui/sparklinedialog.ui"_ustr
, u
"SparklineDialog"_ustr
)
27 , mrViewData(rViewData
)
28 , mrDocument(rViewData
.GetDocument())
29 , mpActiveEdit(nullptr)
30 , mbDialogLostFocus(false)
31 , mxButtonOk(m_xBuilder
->weld_button(u
"ok"_ustr
))
32 , mxButtonCancel(m_xBuilder
->weld_button(u
"cancel"_ustr
))
33 , mxFrameData(m_xBuilder
->weld_frame(u
"frmData"_ustr
))
34 , mxInputRangeLabel(m_xBuilder
->weld_label(u
"lbInputRange"_ustr
))
35 , mxInputRangeEdit(new formula::RefEdit(m_xBuilder
->weld_entry(u
"edInputRange"_ustr
)))
36 , mxInputRangeButton(new formula::RefButton(m_xBuilder
->weld_button(u
"btnInputRange"_ustr
)))
37 , mxOutputRangeLabel(m_xBuilder
->weld_label(u
"lbOutputRange"_ustr
))
38 , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder
->weld_entry(u
"edOutputRange"_ustr
)))
39 , mxOutputRangeButton(new formula::RefButton(m_xBuilder
->weld_button(u
"btnOutputRange"_ustr
)))
40 , mxColorSeries(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colSeries"_ustr
),
41 [pWindow
] { return pWindow
; }))
42 , mxColorNegative(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colNegative"_ustr
),
43 [pWindow
] { return pWindow
; }))
44 , mxColorMarker(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colMarker"_ustr
),
45 [pWindow
] { return pWindow
; }))
46 , mxColorHigh(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colHigh"_ustr
),
47 [pWindow
] { return pWindow
; }))
48 , mxColorLow(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colLow"_ustr
),
49 [pWindow
] { return pWindow
; }))
50 , mxColorFirst(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colFirst"_ustr
),
51 [pWindow
] { return pWindow
; }))
52 , mxColorLast(new ColorListBox(m_xBuilder
->weld_menu_button(u
"colLast"_ustr
),
53 [pWindow
] { return pWindow
; }))
54 , mxCheckButtonNegative(m_xBuilder
->weld_check_button(u
"cbNegative"_ustr
))
55 , mxCheckButtonMarker(m_xBuilder
->weld_check_button(u
"cbMarker"_ustr
))
56 , mxCheckButtonHigh(m_xBuilder
->weld_check_button(u
"cbHigh"_ustr
))
57 , mxCheckButtonLow(m_xBuilder
->weld_check_button(u
"cbLow"_ustr
))
58 , mxCheckButtonFirst(m_xBuilder
->weld_check_button(u
"cbFirst"_ustr
))
59 , mxCheckButtonLast(m_xBuilder
->weld_check_button(u
"cbLast"_ustr
))
60 , mxSpinLineWidth(m_xBuilder
->weld_spin_button(u
"seLineWidth"_ustr
))
61 , mxType(m_xBuilder
->weld_combo_box(u
"cbType"_ustr
))
62 , mxCheckDisplayXAxis(m_xBuilder
->weld_check_button(u
"cbDisplayXAxis"_ustr
))
63 , mxCheckDisplayHidden(m_xBuilder
->weld_check_button(u
"cbHidden"_ustr
))
64 , mxCheckRightToLeft(m_xBuilder
->weld_check_button(u
"cbRTL"_ustr
))
65 , mxDisplayEmptyGap(m_xBuilder
->weld_combo_box(u
"cbEmptyCells"_ustr
))
66 , mxComboMinAxisType(m_xBuilder
->weld_combo_box(u
"cbMinAxisType"_ustr
))
67 , mxComboMaxAxisType(m_xBuilder
->weld_combo_box(u
"cbMaxAxisType"_ustr
))
68 , mxSpinCustomMin(m_xBuilder
->weld_formatted_spin_button(u
"seMinAxis"_ustr
))
69 , mxSpinCustomMax(m_xBuilder
->weld_formatted_spin_button(u
"seMaxAxis"_ustr
))
72 mxInputRangeEdit
->SetReferences(this, mxInputRangeLabel
.get());
73 mxInputRangeButton
->SetReferences(this, mxInputRangeEdit
.get());
75 mxOutputRangeEdit
->SetReferences(this, mxOutputRangeLabel
.get());
76 mxOutputRangeButton
->SetReferences(this, mxOutputRangeEdit
.get());
78 mxButtonCancel
->connect_clicked(LINK(this, SparklineDialog
, ButtonClicked
));
79 mxButtonOk
->connect_clicked(LINK(this, SparklineDialog
, ButtonClicked
));
80 mxButtonOk
->set_sensitive(false);
82 Link
<formula::RefEdit
&, void> aEditLink
= LINK(this, SparklineDialog
, EditFocusHandler
);
83 mxInputRangeEdit
->SetGetFocusHdl(aEditLink
);
84 mxOutputRangeEdit
->SetGetFocusHdl(aEditLink
);
85 aEditLink
= LINK(this, SparklineDialog
, LoseEditFocusHandler
);
86 mxInputRangeEdit
->SetLoseFocusHdl(aEditLink
);
87 mxOutputRangeEdit
->SetLoseFocusHdl(aEditLink
);
89 Link
<formula::RefButton
&, void> aButtonLink
= LINK(this, SparklineDialog
, ButtonFocusHandler
);
90 mxInputRangeButton
->SetGetFocusHdl(aButtonLink
);
91 mxOutputRangeButton
->SetGetFocusHdl(aButtonLink
);
92 aButtonLink
= LINK(this, SparklineDialog
, LoseButtonFocusHandler
);
93 mxInputRangeButton
->SetLoseFocusHdl(aButtonLink
);
94 mxOutputRangeButton
->SetLoseFocusHdl(aButtonLink
);
96 Link
<formula::RefEdit
&, void> aModifyLink
= LINK(this, SparklineDialog
, RefInputModifyHandler
);
97 mxInputRangeEdit
->SetModifyHdl(aModifyLink
);
98 mxOutputRangeEdit
->SetModifyHdl(aModifyLink
);
100 mxType
->connect_changed(LINK(this, SparklineDialog
, SelectSparklineType
));
101 mxDisplayEmptyGap
->connect_changed(LINK(this, SparklineDialog
, SelectSparklineType
));
103 Link
<weld::Toggleable
&, void> aLink
= LINK(this, SparklineDialog
, ToggleHandler
);
104 mxCheckButtonNegative
->connect_toggled(aLink
);
105 mxCheckButtonMarker
->connect_toggled(aLink
);
106 mxCheckButtonHigh
->connect_toggled(aLink
);
107 mxCheckButtonLow
->connect_toggled(aLink
);
108 mxCheckButtonFirst
->connect_toggled(aLink
);
109 mxCheckButtonLast
->connect_toggled(aLink
);
110 mxCheckDisplayXAxis
->connect_toggled(aLink
);
111 mxCheckDisplayHidden
->connect_toggled(aLink
);
112 mxCheckRightToLeft
->connect_toggled(aLink
);
114 mxSpinLineWidth
->connect_value_changed(LINK(this, SparklineDialog
, SpinLineWidthChanged
));
116 mxComboMinAxisType
->connect_changed(LINK(this, SparklineDialog
, ComboValueChanged
));
117 mxComboMaxAxisType
->connect_changed(LINK(this, SparklineDialog
, ComboValueChanged
));
119 mxSpinCustomMin
->connect_value_changed(LINK(this, SparklineDialog
, SpinCustomChanged
));
120 Formatter
& rSpinCustomMinFormatter
= mxSpinCustomMin
->GetFormatter();
121 rSpinCustomMinFormatter
.ClearMinValue();
122 rSpinCustomMinFormatter
.ClearMaxValue();
123 rSpinCustomMinFormatter
.UseInputStringForFormatting();
125 mxSpinCustomMax
->connect_value_changed(LINK(this, SparklineDialog
, SpinCustomChanged
));
126 Formatter
& rSpinCustomMaxFormatter
= mxSpinCustomMax
->GetFormatter();
127 rSpinCustomMaxFormatter
.ClearMinValue();
128 rSpinCustomMaxFormatter
.ClearMaxValue();
129 rSpinCustomMaxFormatter
.UseInputStringForFormatting();
133 mxOutputRangeEdit
->GrabFocus();
134 mxButtonOk
->set_sensitive(checkValidInputOutput());
137 SparklineDialog::~SparklineDialog() = default;
139 void SparklineDialog::setInputSelection()
141 mrViewData
.GetSimpleArea(maInputRange
);
142 OUString aString
= maInputRange
.Format(mrDocument
, ScRefFlags::VALID
| ScRefFlags::TAB_3D
,
143 mrDocument
.GetAddressConvention());
144 mxInputRangeEdit
->SetRefString(aString
);
147 void SparklineDialog::setupValues()
149 ScRange aSelectionRange
;
150 mrViewData
.GetSimpleArea(aSelectionRange
);
152 if (mrDocument
.HasOneSparklineGroup(aSelectionRange
))
154 if (auto pSparkline
= mrDocument
.GetSparkline(aSelectionRange
.aStart
))
156 mpSparklineGroup
= pSparkline
->getSparklineGroup();
157 maAttributes
= mpSparklineGroup
->getAttributes();
158 mxFrameData
->set_visible(false);
164 maInputRange
= aSelectionRange
;
169 switch (maAttributes
.getType())
171 case sc::SparklineType::Line
:
172 mxType
->set_active(0);
174 case sc::SparklineType::Column
:
175 mxType
->set_active(1);
177 case sc::SparklineType::Stacked
:
178 mxType
->set_active(2);
182 switch (maAttributes
.getDisplayEmptyCellsAs())
184 case sc::DisplayEmptyCellsAs::Gap
:
185 mxDisplayEmptyGap
->set_active(0);
187 case sc::DisplayEmptyCellsAs::Zero
:
188 mxDisplayEmptyGap
->set_active(1);
190 case sc::DisplayEmptyCellsAs::Span
:
191 mxDisplayEmptyGap
->set_active(2);
195 mxColorSeries
->SelectEntry(maAttributes
.getColorSeries().getFinalColor());
196 mxColorNegative
->SelectEntry(maAttributes
.getColorNegative().getFinalColor());
197 mxColorMarker
->SelectEntry(maAttributes
.getColorMarkers().getFinalColor());
198 mxColorHigh
->SelectEntry(maAttributes
.getColorHigh().getFinalColor());
199 mxColorLow
->SelectEntry(maAttributes
.getColorLow().getFinalColor());
200 mxColorFirst
->SelectEntry(maAttributes
.getColorFirst().getFinalColor());
201 mxColorLast
->SelectEntry(maAttributes
.getColorLast().getFinalColor());
203 mxCheckButtonNegative
->set_active(maAttributes
.isNegative());
204 mxCheckButtonMarker
->set_active(maAttributes
.isMarkers());
205 mxCheckButtonHigh
->set_active(maAttributes
.isHigh());
206 mxCheckButtonLow
->set_active(maAttributes
.isLow());
207 mxCheckButtonFirst
->set_active(maAttributes
.isFirst());
208 mxCheckButtonLast
->set_active(maAttributes
.isLast());
210 mxSpinLineWidth
->set_value(sal_Int64(maAttributes
.getLineWeight() * 100.0));
212 mxCheckDisplayXAxis
->set_active(maAttributes
.shouldDisplayXAxis());
213 mxCheckDisplayHidden
->set_active(maAttributes
.shouldDisplayHidden());
214 mxCheckRightToLeft
->set_active(maAttributes
.isRightToLeft());
216 switch (maAttributes
.getMinAxisType())
218 case sc::AxisType::Individual
:
219 mxComboMinAxisType
->set_active(0);
220 mxSpinCustomMin
->GetFormatter().SetValue(0.0);
222 case sc::AxisType::Group
:
223 mxComboMinAxisType
->set_active(1);
224 mxSpinCustomMin
->GetFormatter().SetValue(0.0);
226 case sc::AxisType::Custom
:
227 mxComboMinAxisType
->set_active(2);
228 if (maAttributes
.getManualMin())
229 mxSpinCustomMin
->GetFormatter().SetValue(*maAttributes
.getManualMin());
232 ComboValueChanged(*mxComboMinAxisType
);
234 switch (maAttributes
.getMaxAxisType())
236 case sc::AxisType::Individual
:
237 mxComboMaxAxisType
->set_active(0);
238 mxSpinCustomMax
->GetFormatter().SetValue(0.0);
240 case sc::AxisType::Group
:
241 mxComboMaxAxisType
->set_active(1);
242 mxSpinCustomMax
->GetFormatter().SetValue(0.0);
244 case sc::AxisType::Custom
:
245 mxComboMaxAxisType
->set_active(2);
246 if (maAttributes
.getManualMax())
247 mxSpinCustomMax
->GetFormatter().SetValue(*maAttributes
.getManualMax());
250 ComboValueChanged(*mxComboMaxAxisType
);
253 void SparklineDialog::Close() { DoClose(sc::SparklineDialogWrapper::GetChildWindowId()); }
255 void SparklineDialog::SetActive()
257 if (mbDialogLostFocus
)
259 mbDialogLostFocus
= false;
261 mpActiveEdit
->GrabFocus();
265 m_xDialog
->grab_focus();
270 void SparklineDialog::SetReference(const ScRange
& rReferenceRange
, ScDocument
& rDocument
)
274 if (rReferenceRange
.aStart
!= rReferenceRange
.aEnd
)
275 RefInputStart(mpActiveEdit
);
278 const ScRefFlags eFlags
= ScRefFlags::VALID
| ScRefFlags::TAB_3D
;
279 auto eAddressConvention
= rDocument
.GetAddressConvention();
281 if (mpActiveEdit
== mxInputRangeEdit
.get())
283 maInputRange
= rReferenceRange
;
284 aString
= maInputRange
.Format(rDocument
, eFlags
, eAddressConvention
);
285 mxInputRangeEdit
->SetRefString(aString
);
287 else if (mpActiveEdit
== mxOutputRangeEdit
.get())
289 maOutputRange
= rReferenceRange
;
290 aString
= maOutputRange
.Format(rDocument
, eFlags
, eAddressConvention
);
291 mxOutputRangeEdit
->SetRefString(aString
);
295 mxButtonOk
->set_sensitive(checkValidInputOutput());
298 IMPL_LINK(SparklineDialog
, EditFocusHandler
, formula::RefEdit
&, rEdit
, void)
300 auto* pEdit
= &rEdit
;
302 if (mxInputRangeEdit
.get() == pEdit
)
303 mpActiveEdit
= mxInputRangeEdit
.get();
304 else if (mxOutputRangeEdit
.get() == pEdit
)
305 mpActiveEdit
= mxOutputRangeEdit
.get();
307 mpActiveEdit
= nullptr;
310 mpActiveEdit
->SelectAll();
313 IMPL_LINK(SparklineDialog
, ButtonFocusHandler
, formula::RefButton
&, rButton
, void)
315 auto* pButton
= &rButton
;
317 if (mxInputRangeButton
.get() == pButton
)
318 mpActiveEdit
= mxInputRangeEdit
.get();
319 else if (mxOutputRangeButton
.get() == pButton
)
320 mpActiveEdit
= mxOutputRangeEdit
.get();
322 mpActiveEdit
= nullptr;
325 mpActiveEdit
->SelectAll();
328 IMPL_LINK_NOARG(SparklineDialog
, LoseEditFocusHandler
, formula::RefEdit
&, void)
330 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
333 IMPL_LINK_NOARG(SparklineDialog
, LoseButtonFocusHandler
, formula::RefButton
&, void)
335 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
338 IMPL_LINK_NOARG(SparklineDialog
, RefInputModifyHandler
, formula::RefEdit
&, void)
342 if (mpActiveEdit
== mxInputRangeEdit
.get())
344 ScRangeList aRangeList
;
345 bool bValid
= ParseWithNames(aRangeList
, mxInputRangeEdit
->GetText(), mrDocument
);
346 const ScRange
* pRange
= (bValid
&& aRangeList
.size() == 1) ? &aRangeList
[0] : nullptr;
349 maInputRange
= *pRange
;
350 mxInputRangeEdit
->StartUpdateData();
354 maInputRange
= ScRange(ScAddress::INITIALIZE_INVALID
);
357 else if (mpActiveEdit
== mxOutputRangeEdit
.get())
359 ScRangeList aRangeList
;
360 bool bValid
= ParseWithNames(aRangeList
, mxOutputRangeEdit
->GetText(), mrDocument
);
361 const ScRange
* pRange
= (bValid
&& aRangeList
.size() == 1) ? &aRangeList
[0] : nullptr;
364 maOutputRange
= *pRange
;
365 mxOutputRangeEdit
->StartUpdateData();
369 maOutputRange
= ScRange(ScAddress::INITIALIZE_INVALID
);
374 mxButtonOk
->set_sensitive(checkValidInputOutput());
377 IMPL_LINK(SparklineDialog
, ButtonClicked
, weld::Button
&, rButton
, void)
379 if (mxButtonOk
.get() == &rButton
)
386 response(RET_CANCEL
);
390 IMPL_LINK(SparklineDialog
, ToggleHandler
, weld::Toggleable
&, rToggle
, void)
392 if (mxCheckButtonNegative
.get() == &rToggle
)
393 maAttributes
.setNegative(mxCheckButtonNegative
->get_active());
394 if (mxCheckButtonMarker
.get() == &rToggle
)
395 maAttributes
.setMarkers(mxCheckButtonMarker
->get_active());
396 if (mxCheckButtonHigh
.get() == &rToggle
)
397 maAttributes
.setHigh(mxCheckButtonHigh
->get_active());
398 if (mxCheckButtonLow
.get() == &rToggle
)
399 maAttributes
.setLow(mxCheckButtonLow
->get_active());
400 if (mxCheckButtonFirst
.get() == &rToggle
)
401 maAttributes
.setFirst(mxCheckButtonFirst
->get_active());
402 if (mxCheckButtonLast
.get() == &rToggle
)
403 maAttributes
.setLast(mxCheckButtonLast
->get_active());
404 if (mxCheckDisplayXAxis
.get() == &rToggle
)
405 maAttributes
.setDisplayXAxis(mxCheckDisplayXAxis
->get_active());
406 if (mxCheckDisplayHidden
.get() == &rToggle
)
407 maAttributes
.setDisplayHidden(mxCheckDisplayHidden
->get_active());
408 if (mxCheckRightToLeft
.get() == &rToggle
)
409 maAttributes
.setRightToLeft(mxCheckRightToLeft
->get_active());
412 IMPL_LINK_NOARG(SparklineDialog
, SelectSparklineType
, weld::ComboBox
&, void)
414 switch (mxType
->get_active())
417 maAttributes
.setType(sc::SparklineType::Line
);
420 maAttributes
.setType(sc::SparklineType::Column
);
423 maAttributes
.setType(sc::SparklineType::Stacked
);
426 switch (mxDisplayEmptyGap
->get_active())
429 maAttributes
.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Gap
);
432 maAttributes
.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Zero
);
435 maAttributes
.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Span
);
440 IMPL_LINK_NOARG(SparklineDialog
, SpinLineWidthChanged
, weld::SpinButton
&, void)
442 double value
= mxSpinLineWidth
->get_value() / 100.0;
443 maAttributes
.setLineWeight(value
);
446 IMPL_LINK(SparklineDialog
, SpinCustomChanged
, weld::FormattedSpinButton
&, rFormatted
, void)
448 if (mxSpinCustomMin
.get() == &rFormatted
)
450 maAttributes
.setManualMin(rFormatted
.GetFormatter().GetValue());
452 else if (mxSpinCustomMax
.get() == &rFormatted
)
454 maAttributes
.setManualMax(rFormatted
.GetFormatter().GetValue());
458 IMPL_LINK(SparklineDialog
, ComboValueChanged
, weld::ComboBox
&, rComboBox
, void)
460 int nActive
= rComboBox
.get_active();
462 if (mxComboMinAxisType
.get() == &rComboBox
)
467 maAttributes
.setMinAxisType(sc::AxisType::Individual
);
468 mxSpinCustomMin
->set_sensitive(false);
471 maAttributes
.setMinAxisType(sc::AxisType::Group
);
472 mxSpinCustomMin
->set_sensitive(false);
475 maAttributes
.setMinAxisType(sc::AxisType::Custom
);
476 mxSpinCustomMin
->set_sensitive(true);
482 else if (mxComboMaxAxisType
.get() == &rComboBox
)
487 maAttributes
.setMaxAxisType(sc::AxisType::Individual
);
488 mxSpinCustomMax
->set_sensitive(false);
491 maAttributes
.setMaxAxisType(sc::AxisType::Group
);
492 mxSpinCustomMax
->set_sensitive(false);
495 maAttributes
.setMaxAxisType(sc::AxisType::Custom
);
496 mxSpinCustomMax
->set_sensitive(true);
504 bool SparklineDialog::checkValidInputOutput()
509 if (!maInputRange
.IsValid() || !maOutputRange
.IsValid())
512 sc::RangeOrientation eInputOrientation
= sc::RangeOrientation::Unknown
;
513 if (maOutputRange
.aStart
.Col() == maOutputRange
.aEnd
.Col())
515 sal_Int32 nOutputRowSize
= maOutputRange
.aEnd
.Row() - maOutputRange
.aStart
.Row();
516 eInputOrientation
= sc::calculateOrientation(nOutputRowSize
, maInputRange
);
518 else if (maOutputRange
.aStart
.Row() == maOutputRange
.aEnd
.Row())
520 sal_Int32 nOutputColSize
= maOutputRange
.aEnd
.Col() - maOutputRange
.aStart
.Col();
521 eInputOrientation
= sc::calculateOrientation(nOutputColSize
, maInputRange
);
524 return eInputOrientation
!= sc::RangeOrientation::Unknown
;
527 void SparklineDialog::perform()
529 maAttributes
.setColorSeries(mxColorSeries
->GetSelectedEntry().getComplexColor());
530 maAttributes
.setColorNegative(mxColorNegative
->GetSelectedEntry().getComplexColor());
531 maAttributes
.setColorMarkers(mxColorMarker
->GetSelectedEntry().getComplexColor());
532 maAttributes
.setColorHigh(mxColorHigh
->GetSelectedEntry().getComplexColor());
533 maAttributes
.setColorLow(mxColorLow
->GetSelectedEntry().getComplexColor());
534 maAttributes
.setColorFirst(mxColorFirst
->GetSelectedEntry().getComplexColor());
535 maAttributes
.setColorLast(mxColorLast
->GetSelectedEntry().getComplexColor());
537 auto& rDocFunc
= mrViewData
.GetDocShell()->GetDocFunc();
539 if (mpSparklineGroup
)
541 rDocFunc
.ChangeSparklineGroupAttributes(mpSparklineGroup
, maAttributes
);
545 auto pNewSparklineGroup
= std::make_shared
<sc::SparklineGroup
>(maAttributes
);
546 rDocFunc
.InsertSparklines(maInputRange
, maOutputRange
, pNewSparklineGroup
);
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */