nss: upgrade to release 3.73
[LibreOffice.git] / sc / source / ui / StatisticsDialogs / RandomNumberGeneratorDialog.cxx
blob68f04630de29d3beaa98987b3573ab0f57f5fb1a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 */
11 #include <svl/undo.hxx>
12 #include <rtl/math.hxx>
13 #include <osl/time.h>
15 #include <rangelst.hxx>
16 #include <docsh.hxx>
17 #include <document.hxx>
18 #include <reffact.hxx>
19 #include <docfunc.hxx>
20 #include <scresid.hxx>
21 #include <strings.hrc>
23 #include <random>
25 #include <RandomNumberGeneratorDialog.hxx>
27 namespace
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());
73 Init();
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();
121 else
123 m_xDialog->grab_focus();
125 RefInputDone();
128 void ScRandomNumberGeneratorDialog::Close()
130 DoClose( ScRandomNumberGeneratorDialogWrapper::GetChildWindowId() );
133 void ScRandomNumberGeneratorDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDoc )
135 if (!mxInputRangeEdit->GetWidget()->get_sensitive())
136 return;
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())
153 return;
155 sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
157 sal_uInt32 seedValue;
159 if( mxEnableSeed->get_active() )
161 seedValue = mxSeed->get_value();
163 else
165 TimeValue now;
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());
184 switch(aSelectedId)
186 case DIST_UNIFORM:
188 std::uniform_real_distribution<> distribution(parameter1, parameter2);
189 auto rng = std::bind(distribution, seed);
190 GenerateNumbers(rng, STR_DISTRIBUTION_UNIFORM_REAL, aDecimalPlaces);
191 break;
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);
198 break;
200 case DIST_NORMAL:
202 std::normal_distribution<> distribution(parameter1, parameter2);
203 auto rng = std::bind(distribution, seed);
204 GenerateNumbers(rng, STR_DISTRIBUTION_NORMAL, aDecimalPlaces);
205 break;
207 case DIST_CAUCHY:
209 std::cauchy_distribution<> distribution(parameter1);
210 auto rng = std::bind(distribution, seed);
211 GenerateNumbers(rng, STR_DISTRIBUTION_CAUCHY, aDecimalPlaces);
212 break;
214 case DIST_BERNOULLI:
216 std::bernoulli_distribution distribution(parameter1);
217 auto rng = std::bind(distribution, seed);
218 GenerateNumbers(rng, STR_DISTRIBUTION_BERNOULLI, aDecimalPlaces);
219 break;
221 case DIST_BINOMIAL:
223 std::binomial_distribution<> distribution(parameterInteger2, parameter1);
224 auto rng = std::bind(distribution, seed);
225 GenerateNumbers(rng, STR_DISTRIBUTION_BINOMIAL, aDecimalPlaces);
226 break;
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);
233 break;
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);
240 break;
242 case DIST_GEOMETRIC:
244 std::geometric_distribution<> distribution(parameter1);
245 auto rng = std::bind(distribution, seed);
246 GenerateNumbers(rng, STR_DISTRIBUTION_GEOMETRIC, aDecimalPlaces);
247 break;
252 template<class RNG>
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)
277 aVals.clear();
279 ScAddress aPos(nCol, nRowStart, nTab);
280 for (SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
283 if (aDecimalPlaces)
284 aVals.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces));
285 else
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 )
311 response(RET_CLOSE);
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;
339 if (pRange)
341 maInputRange = *pRange;
342 mxButtonApply->set_sensitive(true);
343 mxButtonOk->set_sensitive(true);
344 // Highlight the resulting range.
345 mxInputRangeEdit->StartUpdateData();
347 else
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();
363 if(min > max)
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();
378 if(min > max)
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);
404 switch(aSelectedId)
406 case DIST_UNIFORM:
408 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
409 mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
410 mxParameter2Text->show();
411 mxParameter2Value->show();
412 break;
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();
426 break;
428 case DIST_NORMAL:
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();
434 break;
436 case DIST_CAUCHY:
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();
442 break;
444 case DIST_BERNOULLI:
445 case DIST_GEOMETRIC:
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();
453 break;
455 case DIST_BINOMIAL:
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();
469 break;
471 case DIST_CHI_SQUARED:
473 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE));
475 mxParameter2Text->hide();
476 mxParameter2Value->hide();
477 break;
482 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */