LibreOffice Module sc (master) 1
RandomNumberGeneratorDialog.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 */
10
11#include <svl/undo.hxx>
12#include <rtl/math.hxx>
13#include <osl/time.h>
14
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>
22
23#include <random>
24
26
27namespace
28{
29
30const sal_Int64 DIST_UNIFORM = 0;
31const sal_Int64 DIST_NORMAL = 1;
32const sal_Int64 DIST_CAUCHY = 2;
33const sal_Int64 DIST_BERNOULLI = 3;
34const sal_Int64 DIST_BINOMIAL = 4;
35const sal_Int64 DIST_CHI_SQUARED = 5;
36const sal_Int64 DIST_GEOMETRIC = 6;
37const sal_Int64 DIST_NEGATIVE_BINOMIAL = 7;
38const sal_Int64 DIST_UNIFORM_INTEGER = 8;
39
40const sal_Int64 PRECISION = 10000;
41const sal_Int64 DIGITS = 4;
42
43}
44
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"))
69{
70 mxInputRangeEdit->SetReferences(this, mxInputRangeText.get());
71 mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
72
73 Init();
75}
76
78{
79}
80
82{
83 mxButtonOk->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, OkClicked ) );
84 mxButtonClose->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, CloseClicked ) );
85 mxButtonApply->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, ApplyClicked ) );
86
87 mxInputRangeEdit->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetEditFocusHandler ));
88 mxInputRangeButton->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetButtonFocusHandler ));
89
90 mxInputRangeEdit->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseEditFocusHandler ));
91 mxInputRangeButton->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseButtonFocusHandler ));
92
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 ));
96
97 mxDistributionCombo->connect_changed( LINK( this, ScRandomNumberGeneratorDialog, DistributionChanged ));
98
99 mxEnableSeed->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
100 mxEnableRounding->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
101
102 DistributionChanged(*mxDistributionCombo);
103 CheckChanged(*mxEnableSeed);
104}
105
107{
110 mxInputRangeEdit->SetText( aCurrentString );
111}
112
114{
115 if ( mbDialogLostFocus )
116 {
117 mbDialogLostFocus = false;
118 if( mxInputRangeEdit )
119 mxInputRangeEdit->GrabFocus();
120 }
121 else
122 {
123 m_xDialog->grab_focus();
124 }
125 RefInputDone();
126}
127
129{
131}
132
134{
135 if (!mxInputRangeEdit->GetWidget()->get_sensitive())
136 return;
137
138 if ( rReferenceRange.aStart != rReferenceRange.aEnd )
140
141 maInputRange = rReferenceRange;
142
143 OUString aReferenceString(maInputRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
144 mxInputRangeEdit->SetRefString( aReferenceString );
145
146 mxButtonApply->set_sensitive(true);
147 mxButtonOk->set_sensitive(true);
148}
149
151{
152 if (!maInputRange.IsValid())
153 return;
154
155 sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
156
157 sal_uInt32 seedValue;
158
159 if( mxEnableSeed->get_active() )
160 {
161 seedValue = mxSeed->get_value();
162 }
163 else
164 {
165 TimeValue now;
166 osl_getSystemTime(&now);
167 seedValue = now.Nanosec;
168 }
169
170 std::mt19937 seed(seedValue);
171
172 sal_Int64 parameterInteger1 = mxParameter1Value->get_value();
173 sal_Int64 parameterInteger2 = mxParameter2Value->get_value();
174
175 double parameter1 = parameterInteger1 / static_cast<double>(PRECISION);
176 double parameter2 = parameterInteger2 / static_cast<double>(PRECISION);
177
178 std::optional<sal_Int8> aDecimalPlaces;
179 if (mxEnableRounding->get_active())
180 {
181 aDecimalPlaces = static_cast<sal_Int8>(mxDecimalPlaces->get_value());
182 }
183
184 switch(aSelectedId)
185 {
186 case DIST_UNIFORM:
187 {
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;
192 }
193 case DIST_UNIFORM_INTEGER:
194 {
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;
199 }
200 case DIST_NORMAL:
201 {
202 std::normal_distribution<> distribution(parameter1, parameter2);
203 auto rng = std::bind(distribution, seed);
204 GenerateNumbers(rng, STR_DISTRIBUTION_NORMAL, aDecimalPlaces);
205 break;
206 }
207 case DIST_CAUCHY:
208 {
209 std::cauchy_distribution<> distribution(parameter1);
210 auto rng = std::bind(distribution, seed);
211 GenerateNumbers(rng, STR_DISTRIBUTION_CAUCHY, aDecimalPlaces);
212 break;
213 }
214 case DIST_BERNOULLI:
215 {
216 std::bernoulli_distribution distribution(parameter1);
217 auto rng = std::bind(distribution, seed);
218 GenerateNumbers(rng, STR_DISTRIBUTION_BERNOULLI, aDecimalPlaces);
219 break;
220 }
221 case DIST_BINOMIAL:
222 {
223 std::binomial_distribution<> distribution(parameterInteger2, parameter1);
224 auto rng = std::bind(distribution, seed);
225 GenerateNumbers(rng, STR_DISTRIBUTION_BINOMIAL, aDecimalPlaces);
226 break;
227 }
228 case DIST_NEGATIVE_BINOMIAL:
229 {
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;
234 }
235 case DIST_CHI_SQUARED:
236 {
237 std::chi_squared_distribution<> distribution(parameter1);
238 auto rng = std::bind(distribution, seed);
239 GenerateNumbers(rng, STR_DISTRIBUTION_CHI_SQUARED, aDecimalPlaces);
240 break;
241 }
242 case DIST_GEOMETRIC:
243 {
244 std::geometric_distribution<> distribution(parameter1);
245 auto rng = std::bind(distribution, seed);
246 GenerateNumbers(rng, STR_DISTRIBUTION_GEOMETRIC, aDecimalPlaces);
247 break;
248 }
249 }
250}
251
252template<class RNG>
253void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, std::optional<sal_Int8> aDecimalPlaces)
254{
255 OUString aUndo = ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE);
256 OUString aDistributionName = ScResId(pDistributionStringId);
257 aUndo = aUndo.replaceAll("$(DISTRIBUTION)", aDistributionName);
258
259 ScDocShell* pDocShell = mrViewData.GetDocShell();
260 SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
261 pUndoManager->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
262
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();
269
270 std::vector<double> aVals;
271 aVals.reserve(nRowEnd - nRowStart + 1);
272
273 for (SCROW nTab = nTabStart; nTab <= nTabEnd; ++nTab)
274 {
275 for (SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
276 {
277 aVals.clear();
278
279 ScAddress aPos(nCol, nRowStart, nTab);
280 for (SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
281 {
282
283 if (aDecimalPlaces)
284 aVals.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces));
285 else
286 aVals.push_back(randomGenerator());
287 }
288
289 pDocShell->GetDocFunc().SetValueCells(aPos, aVals, true);
290 }
291 }
292
293 pUndoManager->LeaveListAction();
294
296}
297
299{
300 ApplyClicked(*mxButtonApply);
301 CloseClicked(*mxButtonClose);
302}
303
305{
306 SelectGeneratorAndGenerateNumbers();
307}
308
310{
311 response(RET_CLOSE);
312}
313
315{
316 mxInputRangeEdit->SelectAll();
317}
318
320{
321 mxInputRangeEdit->SelectAll();
322}
323
325{
326 mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
327}
328
330{
331 mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
332}
333
335{
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)
340 {
341 maInputRange = *pRange;
342 mxButtonApply->set_sensitive(true);
343 mxButtonOk->set_sensitive(true);
344 // Highlight the resulting range.
345 mxInputRangeEdit->StartUpdateData();
346 }
347 else
348 {
349 maInputRange = ScRange( ScAddress::INITIALIZE_INVALID);
350 mxButtonApply->set_sensitive(false);
351 mxButtonOk->set_sensitive(false);
352 }
353}
354
356{
357 sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
358 if (aSelectedId == DIST_UNIFORM ||
359 aSelectedId == DIST_UNIFORM_INTEGER)
360 {
361 sal_Int64 min = mxParameter1Value->get_value();
362 sal_Int64 max = mxParameter2Value->get_value();
363 if(min > max)
364 {
365 mxParameter2Value->set_value(min);
366 }
367 }
368}
369
371{
372 sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
373 if (aSelectedId == DIST_UNIFORM ||
374 aSelectedId == DIST_UNIFORM_INTEGER)
375 {
376 sal_Int64 min = mxParameter1Value->get_value();
377 sal_Int64 max = mxParameter2Value->get_value();
378 if(min > max)
379 {
380 mxParameter1Value->set_value(max);
381 }
382 }
383}
384
386{
387 mxSeed->set_sensitive(mxEnableSeed->get_active());
388 mxDecimalPlaces->set_sensitive(mxEnableRounding->get_active());
389}
390
392{
393 sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
394
395 mxParameter1Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
396 mxParameter2Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
397
398 mxParameter1Value->set_digits(DIGITS);
399 mxParameter1Value->set_increments(PRECISION, PRECISION * 10);
400
401 mxParameter2Value->set_digits(DIGITS);
402 mxParameter2Value->set_increments(PRECISION, PRECISION * 10);
403
404 switch(aSelectedId)
405 {
406 case DIST_UNIFORM:
407 {
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;
413 }
414 case DIST_UNIFORM_INTEGER:
415 {
416 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
417 mxParameter1Value->set_digits(0);
418 mxParameter1Value->set_increments(1, 10);
419
420 mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
421 mxParameter2Value->set_digits(0);
422 mxParameter2Value->set_increments(1, 10);
423
424 mxParameter2Text->show();
425 mxParameter2Value->show();
426 break;
427 }
428 case DIST_NORMAL:
429 {
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;
435 }
436 case DIST_CAUCHY:
437 {
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;
443 }
444 case DIST_BERNOULLI:
445 case DIST_GEOMETRIC:
446 {
447 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
448 mxParameter1Value->set_range(0, PRECISION);
449 mxParameter1Value->set_increments(1000, 10000);
450
451 mxParameter2Text->hide();
452 mxParameter2Value->hide();
453 break;
454 }
455 case DIST_BINOMIAL:
456 case DIST_NEGATIVE_BINOMIAL:
457 {
458 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
459 mxParameter1Value->set_range(0, PRECISION);
460 mxParameter1Value->set_increments(1000, 10000);
461
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);
466
467 mxParameter2Text->show();
468 mxParameter2Value->show();
469 break;
470 }
471 case DIST_CHI_SQUARED:
472 {
473 mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE));
474
475 mxParameter2Text->hide();
476 mxParameter2Value->hide();
477 break;
478 }
479 }
480}
481
482/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, OkClicked, weld::Button &, void)
Reference< XExecutableDialog > m_xDialog
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
@ INITIALIZE_INVALID
Definition: address.hxx:221
SCCOL Col() const
Definition: address.hxx:279
void SetValueCells(const ScAddress &rPos, const std::vector< double > &aVals, bool bInteraction)
Definition: docfunc.cxx:902
void PostPaint(SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab, SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, PaintPartFlags nPart, sal_uInt16 nExtFlags=0)
Definition: docsh3.cxx:101
virtual SfxUndoManager * GetUndoManager() override
Definition: docsh.cxx:2963
ScDocFunc & GetDocFunc()
Definition: docsh.hxx:222
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:500
std::unique_ptr< weld::CheckButton > mxEnableSeed
std::unique_ptr< weld::Button > mxButtonApply
virtual void SetReference(const ScRange &rRef, ScDocument &rDoc) override
std::unique_ptr< weld::CheckButton > mxEnableRounding
std::unique_ptr< weld::ComboBox > mxDistributionCombo
std::unique_ptr< weld::Label > mxInputRangeText
std::unique_ptr< weld::Button > mxButtonOk
std::unique_ptr< weld::SpinButton > mxParameter2Value
std::unique_ptr< weld::Button > mxButtonClose
std::unique_ptr< weld::SpinButton > mxDecimalPlaces
std::unique_ptr< weld::SpinButton > mxParameter1Value
std::unique_ptr< formula::RefButton > mxInputRangeButton
std::unique_ptr< formula::RefEdit > mxInputRangeEdit
ScRandomNumberGeneratorDialog(SfxBindings *pB, SfxChildWindow *pCW, weld::Window *pParent, ScViewData &rViewData)
void GenerateNumbers(RNG &randomGenerator, TranslateId pDistributionStringId, const std::optional< sal_Int8 > aDecimalPlaces)
std::unique_ptr< weld::SpinButton > mxSeed
size_t size() const
Definition: rangelst.hxx:89
OUString Format(const ScDocument &rDocument, ScRefFlags nFlags=ScRefFlags::ZERO, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, bool bFullAddressNotation=false) const
Returns string with formatted cell range from aStart to aEnd, according to provided address conventio...
Definition: address.cxx:2170
ScAddress aEnd
Definition: address.hxx:498
bool IsValid() const
Definition: address.hxx:544
ScAddress aStart
Definition: address.hxx:497
virtual void RefInputStart(formula::RefEdit *pEdit, formula::RefButton *pButton=nullptr) override
Definition: anyrefdg.cxx:749
virtual void RefInputDone(bool bForced=false) override
Definition: anyrefdg.cxx:779
bool DoClose(sal_uInt16 nId)
Definition: anyrefdg.cxx:718
ScDocShell * GetDocShell() const
Definition: viewdata.hxx:354
ScTabViewShell * GetViewShell() const
Definition: viewdata.hxx:357
ScMarkType GetSimpleArea(SCCOL &rStartCol, SCROW &rStartRow, SCTAB &rStartTab, SCCOL &rEndCol, SCROW &rEndRow, SCTAB &rEndTab) const
Definition: viewdata.cxx:1181
size_t LeaveListAction()
virtual void EnterListAction(const OUString &rComment, const OUString &rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId)
ViewShellId GetViewShellId() const override
#define max(a, b)
DateTime now
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
OUString ScResId(TranslateId aId)
Definition: scdll.cxx:90
#define SAL_MAX_INT32
#define SAL_MIN_INT32
signed char sal_Int8
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
RET_CLOSE