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;
39 const sal_Int64 DIST_POISSON
= 9;
41 const sal_Int64 PRECISION
= 10000;
42 const sal_Int64 DIGITS
= 4;
46 ScRandomNumberGeneratorDialog::ScRandomNumberGeneratorDialog(
47 SfxBindings
* pSfxBindings
, SfxChildWindow
* pChildWindow
,
48 weld::Window
* pParent
, ScViewData
& rViewData
)
49 : ScAnyRefDlgController(pSfxBindings
, pChildWindow
, pParent
,
50 u
"modules/scalc/ui/randomnumbergenerator.ui"_ustr
,
51 u
"RandomNumberGeneratorDialog"_ustr
)
52 , mrViewData(rViewData
)
53 , mrDoc(rViewData
.GetDocument())
54 , mbDialogLostFocus(false)
55 , mxInputRangeText(m_xBuilder
->weld_label(u
"cell-range-label"_ustr
))
56 , mxInputRangeEdit(new formula::RefEdit(m_xBuilder
->weld_entry(u
"cell-range-edit"_ustr
)))
57 , mxInputRangeButton(new formula::RefButton(m_xBuilder
->weld_button(u
"cell-range-button"_ustr
)))
58 , mxDistributionCombo(m_xBuilder
->weld_combo_box(u
"distribution-combo"_ustr
))
59 , mxParameter1Text(m_xBuilder
->weld_label(u
"parameter1-label"_ustr
))
60 , mxParameter1Value(m_xBuilder
->weld_spin_button(u
"parameter1-spin"_ustr
))
61 , mxParameter2Text(m_xBuilder
->weld_label(u
"parameter2-label"_ustr
))
62 , mxParameter2Value(m_xBuilder
->weld_spin_button(u
"parameter2-spin"_ustr
))
63 , mxSeed(m_xBuilder
->weld_spin_button(u
"seed-spin"_ustr
))
64 , mxEnableSeed(m_xBuilder
->weld_check_button(u
"enable-seed-check"_ustr
))
65 , mxDecimalPlaces(m_xBuilder
->weld_spin_button(u
"decimal-places-spin"_ustr
))
66 , mxEnableRounding(m_xBuilder
->weld_check_button(u
"enable-rounding-check"_ustr
))
67 , mxButtonApply(m_xBuilder
->weld_button(u
"apply"_ustr
))
68 , mxButtonOk(m_xBuilder
->weld_button(u
"ok"_ustr
))
69 , mxButtonClose(m_xBuilder
->weld_button(u
"close"_ustr
))
71 mxInputRangeEdit
->SetReferences(this, mxInputRangeText
.get());
72 mxInputRangeButton
->SetReferences(this, mxInputRangeEdit
.get());
75 GetRangeFromSelection();
78 ScRandomNumberGeneratorDialog::~ScRandomNumberGeneratorDialog()
82 void ScRandomNumberGeneratorDialog::Init()
84 mxButtonOk
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, OkClicked
) );
85 mxButtonClose
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, CloseClicked
) );
86 mxButtonApply
->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog
, ApplyClicked
) );
88 mxInputRangeEdit
->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog
, GetEditFocusHandler
));
89 mxInputRangeButton
->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog
, GetButtonFocusHandler
));
91 mxInputRangeEdit
->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog
, LoseEditFocusHandler
));
92 mxInputRangeButton
->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog
, LoseButtonFocusHandler
));
94 mxInputRangeEdit
->SetModifyHdl( LINK( this, ScRandomNumberGeneratorDialog
, InputRangeModified
));
95 mxParameter1Value
->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog
, Parameter1ValueModified
));
96 mxParameter2Value
->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog
, Parameter2ValueModified
));
98 mxDistributionCombo
->connect_changed( LINK( this, ScRandomNumberGeneratorDialog
, DistributionChanged
));
100 mxEnableSeed
->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog
, CheckChanged
));
101 mxEnableRounding
->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog
, CheckChanged
));
103 DistributionChanged(*mxDistributionCombo
);
104 CheckChanged(*mxEnableSeed
);
107 void ScRandomNumberGeneratorDialog::GetRangeFromSelection()
109 mrViewData
.GetSimpleArea(maInputRange
);
110 OUString
aCurrentString(maInputRange
.Format(mrDoc
, ScRefFlags::RANGE_ABS_3D
, mrDoc
.GetAddressConvention()));
111 mxInputRangeEdit
->SetText( aCurrentString
);
114 void ScRandomNumberGeneratorDialog::SetActive()
116 if ( mbDialogLostFocus
)
118 mbDialogLostFocus
= false;
119 if( mxInputRangeEdit
)
120 mxInputRangeEdit
->GrabFocus();
124 m_xDialog
->grab_focus();
129 void ScRandomNumberGeneratorDialog::Close()
131 DoClose( ScRandomNumberGeneratorDialogWrapper::GetChildWindowId() );
134 void ScRandomNumberGeneratorDialog::SetReference( const ScRange
& rReferenceRange
, ScDocument
& rDoc
)
136 if (!mxInputRangeEdit
->GetWidget()->get_sensitive())
139 if ( rReferenceRange
.aStart
!= rReferenceRange
.aEnd
)
140 RefInputStart(mxInputRangeEdit
.get());
142 maInputRange
= rReferenceRange
;
144 OUString
aReferenceString(maInputRange
.Format(rDoc
, ScRefFlags::RANGE_ABS_3D
, rDoc
.GetAddressConvention()));
145 mxInputRangeEdit
->SetRefString( aReferenceString
);
147 mxButtonApply
->set_sensitive(true);
148 mxButtonOk
->set_sensitive(true);
151 void ScRandomNumberGeneratorDialog::SelectGeneratorAndGenerateNumbers()
153 if (!maInputRange
.IsValid())
156 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
158 sal_uInt32 seedValue
;
160 if( mxEnableSeed
->get_active() )
162 seedValue
= mxSeed
->get_value();
167 osl_getSystemTime(&now
);
168 seedValue
= now
.Nanosec
;
171 std::mt19937
seed(seedValue
);
173 sal_Int64 parameterInteger1
= mxParameter1Value
->get_value();
174 sal_Int64 parameterInteger2
= mxParameter2Value
->get_value();
176 double parameter1
= parameterInteger1
/ static_cast<double>(PRECISION
);
177 double parameter2
= parameterInteger2
/ static_cast<double>(PRECISION
);
179 std::optional
<sal_Int8
> aDecimalPlaces
;
180 if (mxEnableRounding
->get_active())
182 aDecimalPlaces
= static_cast<sal_Int8
>(mxDecimalPlaces
->get_value());
189 std::uniform_real_distribution
<> distribution(parameter1
, parameter2
);
190 auto rng
= std::bind(distribution
, seed
);
191 GenerateNumbers(rng
, STR_DISTRIBUTION_UNIFORM_REAL
, aDecimalPlaces
);
194 case DIST_UNIFORM_INTEGER
:
196 std::uniform_int_distribution
<sal_Int64
> distribution(parameterInteger1
, parameterInteger2
);
197 auto rng
= std::bind(distribution
, seed
);
198 GenerateNumbers(rng
, STR_DISTRIBUTION_UNIFORM_INTEGER
, aDecimalPlaces
);
203 std::normal_distribution
<> distribution(parameter1
, parameter2
);
204 auto rng
= std::bind(distribution
, seed
);
205 GenerateNumbers(rng
, STR_DISTRIBUTION_NORMAL
, aDecimalPlaces
);
210 std::cauchy_distribution
<> distribution(parameter1
);
211 auto rng
= std::bind(distribution
, seed
);
212 GenerateNumbers(rng
, STR_DISTRIBUTION_CAUCHY
, aDecimalPlaces
);
217 std::bernoulli_distribution
distribution(parameter1
);
218 auto rng
= std::bind(distribution
, seed
);
219 GenerateNumbers(rng
, STR_DISTRIBUTION_BERNOULLI
, aDecimalPlaces
);
224 std::binomial_distribution
<> distribution(parameterInteger2
, parameter1
);
225 auto rng
= std::bind(distribution
, seed
);
226 GenerateNumbers(rng
, STR_DISTRIBUTION_BINOMIAL
, aDecimalPlaces
);
229 case DIST_CHI_SQUARED
:
231 std::chi_squared_distribution
<> distribution(parameter1
);
232 auto rng
= std::bind(distribution
, seed
);
233 GenerateNumbers(rng
, STR_DISTRIBUTION_CHI_SQUARED
, aDecimalPlaces
);
238 std::geometric_distribution
<> distribution(parameter1
);
239 auto rng
= std::bind(distribution
, seed
);
240 GenerateNumbers(rng
, STR_DISTRIBUTION_GEOMETRIC
, aDecimalPlaces
);
243 case DIST_NEGATIVE_BINOMIAL
:
245 std::negative_binomial_distribution
<> distribution(parameterInteger2
, parameter1
);
246 auto rng
= std::bind(distribution
, seed
);
247 GenerateNumbers(rng
, STR_DISTRIBUTION_NEGATIVE_BINOMIAL
, aDecimalPlaces
);
252 std::poisson_distribution
<> distribution(parameter1
);
253 auto rng
= std::bind(distribution
, seed
);
254 GenerateNumbers(rng
, STR_DISTRIBUTION_POISSON
, aDecimalPlaces
);
261 void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG
& randomGenerator
, TranslateId pDistributionStringId
, std::optional
<sal_Int8
> aDecimalPlaces
)
263 OUString aUndo
= ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE
);
264 OUString aDistributionName
= ScResId(pDistributionStringId
);
265 aUndo
= aUndo
.replaceAll("%1", aDistributionName
);
267 ScDocShell
* pDocShell
= mrViewData
.GetDocShell();
268 SfxUndoManager
* pUndoManager
= pDocShell
->GetUndoManager();
269 pUndoManager
->EnterListAction( aUndo
, aUndo
, 0, mrViewData
.GetViewShell()->GetViewShellId() );
271 SCROW nRowStart
= maInputRange
.aStart
.Row();
272 SCROW nRowEnd
= maInputRange
.aEnd
.Row();
273 SCCOL nColStart
= maInputRange
.aStart
.Col();
274 SCCOL nColEnd
= maInputRange
.aEnd
.Col();
275 SCTAB nTabStart
= maInputRange
.aStart
.Tab();
276 SCTAB nTabEnd
= maInputRange
.aEnd
.Tab();
278 std::vector
<double> aVals
;
279 aVals
.reserve(nRowEnd
- nRowStart
+ 1);
281 for (SCROW nTab
= nTabStart
; nTab
<= nTabEnd
; ++nTab
)
283 for (SCCOL nCol
= nColStart
; nCol
<= nColEnd
; ++nCol
)
287 ScAddress
aPos(nCol
, nRowStart
, nTab
);
288 for (SCROW nRow
= nRowStart
; nRow
<= nRowEnd
; ++nRow
)
292 aVals
.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces
));
294 aVals
.push_back(randomGenerator());
297 pDocShell
->GetDocFunc().SetValueCells(aPos
, aVals
, true);
301 pUndoManager
->LeaveListAction();
303 pDocShell
->PostPaint( maInputRange
, PaintPartFlags::Grid
);
306 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, OkClicked
, weld::Button
&, void )
308 ApplyClicked(*mxButtonApply
);
309 CloseClicked(*mxButtonClose
);
312 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, ApplyClicked
, weld::Button
&, void )
314 SelectGeneratorAndGenerateNumbers();
317 IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog
, CloseClicked
, weld::Button
&, void )
322 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, GetEditFocusHandler
, formula::RefEdit
&, void)
324 mxInputRangeEdit
->SelectAll();
327 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, GetButtonFocusHandler
, formula::RefButton
&, void)
329 mxInputRangeEdit
->SelectAll();
332 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, LoseEditFocusHandler
, formula::RefEdit
&, void)
334 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
337 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, LoseButtonFocusHandler
, formula::RefButton
&, void)
339 mbDialogLostFocus
= !m_xDialog
->has_toplevel_focus();
342 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, InputRangeModified
, formula::RefEdit
&, void)
344 ScRangeList aRangeList
;
345 bool bValid
= ParseWithNames( aRangeList
, mxInputRangeEdit
->GetText(), mrDoc
);
346 const ScRange
* pRange
= (bValid
&& aRangeList
.size() == 1) ? &aRangeList
[0] : nullptr;
349 maInputRange
= *pRange
;
350 mxButtonApply
->set_sensitive(true);
351 mxButtonOk
->set_sensitive(true);
352 // Highlight the resulting range.
353 mxInputRangeEdit
->StartUpdateData();
357 maInputRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
358 mxButtonApply
->set_sensitive(false);
359 mxButtonOk
->set_sensitive(false);
363 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, Parameter1ValueModified
, weld::SpinButton
&, void)
365 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
366 if (aSelectedId
== DIST_UNIFORM
||
367 aSelectedId
== DIST_UNIFORM_INTEGER
)
369 sal_Int64 min
= mxParameter1Value
->get_value();
370 sal_Int64 max
= mxParameter2Value
->get_value();
373 mxParameter2Value
->set_value(min
);
378 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, Parameter2ValueModified
, weld::SpinButton
&, void)
380 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
381 if (aSelectedId
== DIST_UNIFORM
||
382 aSelectedId
== DIST_UNIFORM_INTEGER
)
384 sal_Int64 min
= mxParameter1Value
->get_value();
385 sal_Int64 max
= mxParameter2Value
->get_value();
388 mxParameter1Value
->set_value(max
);
393 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, CheckChanged
, weld::Toggleable
&, void)
395 mxSeed
->set_sensitive(mxEnableSeed
->get_active());
396 mxDecimalPlaces
->set_sensitive(mxEnableRounding
->get_active());
399 IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog
, DistributionChanged
, weld::ComboBox
&, void)
401 sal_Int64 aSelectedId
= mxDistributionCombo
->get_active_id().toInt64();
403 mxParameter1Value
->set_range(SAL_MIN_INT32
, SAL_MAX_INT32
);
404 mxParameter2Value
->set_range(SAL_MIN_INT32
, SAL_MAX_INT32
);
406 mxParameter1Value
->set_digits(DIGITS
);
407 mxParameter1Value
->set_increments(PRECISION
, PRECISION
* 10);
409 mxParameter2Value
->set_digits(DIGITS
);
410 mxParameter2Value
->set_increments(PRECISION
, PRECISION
* 10);
416 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM
));
417 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM
));
418 mxParameter2Text
->show();
419 mxParameter2Value
->show();
422 case DIST_UNIFORM_INTEGER
:
424 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM
));
425 mxParameter1Value
->set_digits(0);
426 mxParameter1Value
->set_increments(1, 10);
428 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM
));
429 mxParameter2Value
->set_digits(0);
430 mxParameter2Value
->set_increments(1, 10);
432 mxParameter2Text
->show();
433 mxParameter2Value
->show();
438 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MEAN
));
439 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_DEVIATION
));
440 mxParameter2Text
->show();
441 mxParameter2Value
->show();
446 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_MEDIAN
));
447 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_SIGMA
));
448 mxParameter2Text
->show();
449 mxParameter2Value
->show();
455 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY
));
456 mxParameter1Value
->set_range(0, PRECISION
);
457 mxParameter1Value
->set_increments(1000, 10000);
459 mxParameter2Text
->hide();
460 mxParameter2Value
->hide();
464 case DIST_NEGATIVE_BINOMIAL
:
466 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY
));
467 mxParameter1Value
->set_range(0, PRECISION
);
468 mxParameter1Value
->set_increments(1000, 10000);
470 mxParameter2Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NUMBER_OF_TRIALS
));
471 mxParameter2Value
->set_digits(0);
472 mxParameter2Value
->set_increments(1, 10);
473 mxParameter2Value
->set_min(0);
475 mxParameter2Text
->show();
476 mxParameter2Value
->show();
479 case DIST_CHI_SQUARED
:
481 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE
));
483 mxParameter2Text
->hide();
484 mxParameter2Value
->hide();
489 mxParameter1Text
->set_label(ScResId(STR_RNG_PARAMETER_MEAN
));
490 mxParameter1Value
->set_value(PRECISION
);
491 mxParameter1Value
->set_increments(1000, 10000);
492 mxParameter1Value
->set_min(1000);
493 mxParameter2Text
->hide();
494 mxParameter2Value
->hide();
500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */