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/.
11 #include <svl/undo.hxx>
12 #include <rtl/math.hxx>
15 #include <rangelst.hxx>
17 #include <document.hxx>
18 #include <reffact.hxx>
19 #include <docfunc.hxx>
20 #include <scresid.hxx>
21 #include <strings.hrc>
25 #include <RandomNumberGeneratorDialog.hxx>
30 const sal_Int64 DIST_UNIFORM
= 0;
31 const sal_Int64 DIST_NORMAL
= 1;
32 const sal_Int64 DIST_CAUCHY
= 2;
33 const sal_Int64 DIST_BERNOULLI
= 3;
34 const sal_Int64 DIST_BINOMIAL
= 4;
35 const sal_Int64 DIST_CHI_SQUARED
= 5;
36 const sal_Int64 DIST_GEOMETRIC
= 6;
37 const sal_Int64 DIST_NEGATIVE_BINOMIAL
= 7;
38 const sal_Int64 DIST_UNIFORM_INTEGER
= 8;
40 const sal_Int64 PRECISION
= 10000;
41 const sal_Int64 DIGITS
= 4;
45 ScRandomNumberGeneratorDialog::ScRandomNumberGeneratorDialog(
46 SfxBindings
* pSfxBindings
, SfxChildWindow
* pChildWindow
,
47 weld::Window
* pParent
, ScViewData
& rViewData
)
48 : ScAnyRefDlgController(pSfxBindings
, pChildWindow
, pParent
,
49 "modules/scalc/ui/randomnumbergenerator.ui",
50 "RandomNumberGeneratorDialog")
51 , mrViewData(rViewData
)
52 , mrDoc(rViewData
.GetDocument())
53 , mbDialogLostFocus(false)
54 , mxInputRangeText(m_xBuilder
->weld_label("cell-range-label"))
55 , mxInputRangeEdit(new formula::RefEdit(m_xBuilder
->weld_entry("cell-range-edit")))
56 , mxInputRangeButton(new formula::RefButton(m_xBuilder
->weld_button("cell-range-button")))
57 , mxDistributionCombo(m_xBuilder
->weld_combo_box("distribution-combo"))
58 , mxParameter1Text(m_xBuilder
->weld_label("parameter1-label"))
59 , mxParameter1Value(m_xBuilder
->weld_spin_button("parameter1-spin"))
60 , mxParameter2Text(m_xBuilder
->weld_label("parameter2-label"))
61 , mxParameter2Value(m_xBuilder
->weld_spin_button("parameter2-spin"))
62 , mxSeed(m_xBuilder
->weld_spin_button("seed-spin"))
63 , mxEnableSeed(m_xBuilder
->weld_check_button("enable-seed-check"))
64 , mxDecimalPlaces(m_xBuilder
->weld_spin_button("decimal-places-spin"))
65 , mxEnableRounding(m_xBuilder
->weld_check_button("enable-rounding-check"))
66 , mxButtonApply(m_xBuilder
->weld_button("apply"))
67 , mxButtonOk(m_xBuilder
->weld_button("ok"))
68 , mxButtonClose(m_xBuilder
->weld_button("close"))
70 mxInputRangeEdit
->SetReferences(this, mxInputRangeText
.get());
71 mxInputRangeButton
->SetReferences(this, mxInputRangeEdit
.get());
74 GetRangeFromSelection();
77 ScRandomNumberGeneratorDialog::~ScRandomNumberGeneratorDialog()
81 void ScRandomNumberGeneratorDialog::Init()
83 mxButtonOk
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, OkClicked
) );
84 mxButtonClose
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, CloseClicked
) );
85 mxButtonApply
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, ApplyClicked
) );
87 mxInputRangeEdit
->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog
, GetEditFocusHandler
));
88 mxInputRangeButton
->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog
, GetButtonFocusHandler
));
90 mxInputRangeEdit
->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog
, LoseEditFocusHandler
));
91 mxInputRangeButton
->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog
, LoseButtonFocusHandler
));
93 mxInputRangeEdit
->SetModifyHdl( LINK( this, ScRandomNumberGeneratorDialog
, InputRangeModified
));
94 mxParameter1Value
->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog
, Parameter1ValueModified
));
95 mxParameter2Value
->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog
, Parameter2ValueModified
));
97 mxDistributionCombo
->connect_changed( LINK( this, ScRandomNumberGeneratorDialog
, DistributionChanged
));
99 mxEnableSeed
->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog
, CheckChanged
));
100 mxEnableRounding
->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog
, CheckChanged
));
102 DistributionChanged(*mxDistributionCombo
);
103 CheckChanged(*mxEnableSeed
);
106 void ScRandomNumberGeneratorDialog::GetRangeFromSelection()
108 mrViewData
.GetSimpleArea(maInputRange
);
109 OUString
aCurrentString(maInputRange
.Format(mrDoc
, ScRefFlags::RANGE_ABS_3D
, mrDoc
.GetAddressConvention()));
110 mxInputRangeEdit
->SetText( aCurrentString
);
113 void ScRandomNumberGeneratorDialog::SetActive()
115 if ( mbDialogLostFocus
)
117 mbDialogLostFocus
= false;
118 if( mxInputRangeEdit
)
119 mxInputRangeEdit
->GrabFocus();
123 m_xDialog
->grab_focus();
128 void ScRandomNumberGeneratorDialog::Close()
130 DoClose( ScRandomNumberGeneratorDialogWrapper::GetChildWindowId() );
133 void ScRandomNumberGeneratorDialog::SetReference( const ScRange
& rReferenceRange
, ScDocument
& rDoc
)
135 if (!mxInputRangeEdit
->GetWidget()->get_sensitive())
138 if ( rReferenceRange
.aStart
!= rReferenceRange
.aEnd
)
139 RefInputStart(mxInputRangeEdit
.get());
141 maInputRange
= rReferenceRange
;
143 OUString
aReferenceString(maInputRange
.Format(rDoc
, ScRefFlags::RANGE_ABS_3D
, rDoc
.GetAddressConvention()));
144 mxInputRangeEdit
->SetRefString( aReferenceString
);
146 mxButtonApply
->set_sensitive(true);
147 mxButtonOk
->set_sensitive(true);
150 void ScRandomNumberGeneratorDialog::SelectGeneratorAndGenerateNumbers()
152 if (!maInputRange
.IsValid())
155 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
157 sal_uInt32 seedValue
;
159 if( mxEnableSeed
->get_active() )
161 seedValue
= mxSeed
->get_value();
166 osl_getSystemTime(&now
);
167 seedValue
= now
.Nanosec
;
170 std::mt19937
seed(seedValue
);
172 sal_Int64 parameterInteger1
= mxParameter1Value
->get_value();
173 sal_Int64 parameterInteger2
= mxParameter2Value
->get_value();
175 double parameter1
= parameterInteger1
/ static_cast<double>(PRECISION
);
176 double parameter2
= parameterInteger2
/ static_cast<double>(PRECISION
);
178 std::optional
<sal_Int8
> aDecimalPlaces
;
179 if (mxEnableRounding
->get_active())
181 aDecimalPlaces
= static_cast<sal_Int8
>(mxDecimalPlaces
->get_value());
188 std::uniform_real_distribution
<> distribution(parameter1
, parameter2
);
189 auto rng
= std::bind(distribution
, seed
);
190 GenerateNumbers(rng
, STR_DISTRIBUTION_UNIFORM_REAL
, aDecimalPlaces
);
193 case DIST_UNIFORM_INTEGER
:
195 std::uniform_int_distribution
<sal_Int64
> distribution(parameterInteger1
, parameterInteger2
);
196 auto rng
= std::bind(distribution
, seed
);
197 GenerateNumbers(rng
, STR_DISTRIBUTION_UNIFORM_INTEGER
, aDecimalPlaces
);
202 std::normal_distribution
<> distribution(parameter1
, parameter2
);
203 auto rng
= std::bind(distribution
, seed
);
204 GenerateNumbers(rng
, STR_DISTRIBUTION_NORMAL
, aDecimalPlaces
);
209 std::cauchy_distribution
<> distribution(parameter1
);
210 auto rng
= std::bind(distribution
, seed
);
211 GenerateNumbers(rng
, STR_DISTRIBUTION_CAUCHY
, aDecimalPlaces
);
216 std::bernoulli_distribution
distribution(parameter1
);
217 auto rng
= std::bind(distribution
, seed
);
218 GenerateNumbers(rng
, STR_DISTRIBUTION_BERNOULLI
, aDecimalPlaces
);
223 std::binomial_distribution
<> distribution(parameterInteger2
, parameter1
);
224 auto rng
= std::bind(distribution
, seed
);
225 GenerateNumbers(rng
, STR_DISTRIBUTION_BINOMIAL
, aDecimalPlaces
);
228 case DIST_NEGATIVE_BINOMIAL
:
230 std::negative_binomial_distribution
<> distribution(parameterInteger2
, parameter1
);
231 auto rng
= std::bind(distribution
, seed
);
232 GenerateNumbers(rng
, STR_DISTRIBUTION_NEGATIVE_BINOMIAL
, aDecimalPlaces
);
235 case DIST_CHI_SQUARED
:
237 std::chi_squared_distribution
<> distribution(parameter1
);
238 auto rng
= std::bind(distribution
, seed
);
239 GenerateNumbers(rng
, STR_DISTRIBUTION_CHI_SQUARED
, aDecimalPlaces
);
244 std::geometric_distribution
<> distribution(parameter1
);
245 auto rng
= std::bind(distribution
, seed
);
246 GenerateNumbers(rng
, STR_DISTRIBUTION_GEOMETRIC
, aDecimalPlaces
);
253 void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG
& randomGenerator
, const char* pDistributionStringId
, std::optional
<sal_Int8
> aDecimalPlaces
)
255 OUString aUndo
= ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE
);
256 OUString aDistributionName
= ScResId(pDistributionStringId
);
257 aUndo
= aUndo
.replaceAll("$(DISTRIBUTION)", aDistributionName
);
259 ScDocShell
* pDocShell
= mrViewData
.GetDocShell();
260 SfxUndoManager
* pUndoManager
= pDocShell
->GetUndoManager();
261 pUndoManager
->EnterListAction( aUndo
, aUndo
, 0, mrViewData
.GetViewShell()->GetViewShellId() );
263 SCROW nRowStart
= maInputRange
.aStart
.Row();
264 SCROW nRowEnd
= maInputRange
.aEnd
.Row();
265 SCCOL nColStart
= maInputRange
.aStart
.Col();
266 SCCOL nColEnd
= maInputRange
.aEnd
.Col();
267 SCTAB nTabStart
= maInputRange
.aStart
.Tab();
268 SCTAB nTabEnd
= maInputRange
.aEnd
.Tab();
270 std::vector
<double> aVals
;
271 aVals
.reserve(nRowEnd
- nRowStart
+ 1);
273 for (SCROW nTab
= nTabStart
; nTab
<= nTabEnd
; ++nTab
)
275 for (SCCOL nCol
= nColStart
; nCol
<= nColEnd
; ++nCol
)
279 ScAddress
aPos(nCol
, nRowStart
, nTab
);
280 for (SCROW nRow
= nRowStart
; nRow
<= nRowEnd
; ++nRow
)
284 aVals
.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces
));
286 aVals
.push_back(randomGenerator());
289 pDocShell
->GetDocFunc().SetValueCells(aPos
, aVals
, true);
293 pUndoManager
->LeaveListAction();
295 pDocShell
->PostPaint( maInputRange
, PaintPartFlags::Grid
);
298 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, OkClicked
, weld::Button
&, void )
300 ApplyClicked(*mxButtonApply
);
301 CloseClicked(*mxButtonClose
);
304 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, ApplyClicked
, weld::Button
&, void )
306 SelectGeneratorAndGenerateNumbers();
309 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, CloseClicked
, weld::Button
&, void )
314 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, GetEditFocusHandler
, formula::RefEdit
&, void)
316 mxInputRangeEdit
->SelectAll();
319 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, GetButtonFocusHandler
, formula::RefButton
&, void)
321 mxInputRangeEdit
->SelectAll();
324 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, LoseEditFocusHandler
, formula::RefEdit
&, void)
326 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
329 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, LoseButtonFocusHandler
, formula::RefButton
&, void)
331 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
334 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, InputRangeModified
, formula::RefEdit
&, void)
336 ScRangeList aRangeList
;
337 bool bValid
= ParseWithNames( aRangeList
, mxInputRangeEdit
->GetText(), mrDoc
);
338 const ScRange
* pRange
= (bValid
&& aRangeList
.size() == 1) ? &aRangeList
[0] : nullptr;
341 maInputRange
= *pRange
;
342 mxButtonApply
->set_sensitive(true);
343 mxButtonOk
->set_sensitive(true);
344 // Highlight the resulting range.
345 mxInputRangeEdit
->StartUpdateData();
349 maInputRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
350 mxButtonApply
->set_sensitive(false);
351 mxButtonOk
->set_sensitive(false);
355 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, Parameter1ValueModified
, weld::SpinButton
&, void)
357 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
358 if (aSelectedId
== DIST_UNIFORM
||
359 aSelectedId
== DIST_UNIFORM_INTEGER
)
361 sal_Int64 min
= mxParameter1Value
->get_value();
362 sal_Int64 max
= mxParameter2Value
->get_value();
365 mxParameter2Value
->set_value(min
);
370 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, Parameter2ValueModified
, weld::SpinButton
&, void)
372 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
373 if (aSelectedId
== DIST_UNIFORM
||
374 aSelectedId
== DIST_UNIFORM_INTEGER
)
376 sal_Int64 min
= mxParameter1Value
->get_value();
377 sal_Int64 max
= mxParameter2Value
->get_value();
380 mxParameter1Value
->set_value(max
);
385 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, CheckChanged
, weld::ToggleButton
&, void)
387 mxSeed
->set_sensitive(mxEnableSeed
->get_active());
388 mxDecimalPlaces
->set_sensitive(mxEnableRounding
->get_active());
391 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, DistributionChanged
, weld::ComboBox
&, void)
393 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
395 mxParameter1Value
->set_range(SAL_MIN_INT32
, SAL_MAX_INT32
);
396 mxParameter2Value
->set_range(SAL_MIN_INT32
, SAL_MAX_INT32
);
398 mxParameter1Value
->set_digits(DIGITS
);
399 mxParameter1Value
->set_increments(PRECISION
, PRECISION
* 10);
401 mxParameter2Value
->set_digits(DIGITS
);
402 mxParameter2Value
->set_increments(PRECISION
, PRECISION
* 10);
408 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM
));
409 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM
));
410 mxParameter2Text
->show();
411 mxParameter2Value
->show();
414 case DIST_UNIFORM_INTEGER
:
416 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM
));
417 mxParameter1Value
->set_digits(0);
418 mxParameter1Value
->set_increments(1, 10);
420 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM
));
421 mxParameter2Value
->set_digits(0);
422 mxParameter2Value
->set_increments(1, 10);
424 mxParameter2Text
->show();
425 mxParameter2Value
->show();
430 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MEAN
));
431 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_DEVIATION
));
432 mxParameter2Text
->show();
433 mxParameter2Value
->show();
438 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_MEDIAN
));
439 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_SIGMA
));
440 mxParameter2Text
->show();
441 mxParameter2Value
->show();
447 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY
));
448 mxParameter1Value
->set_range(0, PRECISION
);
449 mxParameter1Value
->set_increments(1000, 10000);
451 mxParameter2Text
->hide();
452 mxParameter2Value
->hide();
456 case DIST_NEGATIVE_BINOMIAL
:
458 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY
));
459 mxParameter1Value
->set_range(0, PRECISION
);
460 mxParameter1Value
->set_increments(1000, 10000);
462 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NUMBER_OF_TRIALS
));
463 mxParameter2Value
->set_digits(0);
464 mxParameter2Value
->set_increments(1, 10);
465 mxParameter2Value
->set_min(0);
467 mxParameter2Text
->show();
468 mxParameter2Value
->show();
471 case DIST_CHI_SQUARED
:
473 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE
));
475 mxParameter2Text
->hide();
476 mxParameter2Value
->hide();
482 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */