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 
27 namespace
28 {
29 
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 
40 const sal_Int64 PRECISION = 10000;
41 const 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 
252 template<class RNG>
253 void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, const char* 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: */
ScDocShell * GetDocShell() const
Definition: viewdata.hxx:354
ScAddress aStart
Definition: address.hxx:500
SCROW Row() const
Definition: address.hxx:262
signed char sal_Int8
ScRandomNumberGeneratorDialog(SfxBindings *pB, SfxChildWindow *pCW, weld::Window *pParent, ScViewData &rViewData)
#define max(a, b)
ScAddress aEnd
Definition: address.hxx:501
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:489
std::unique_ptr< weld::Button > mxButtonOk
void SetValueCells(const ScAddress &rPos, const std::vector< double > &aVals, bool bInteraction)
Definition: docfunc.cxx:890
virtual SfxUndoManager * GetUndoManager() override
Definition: docsh.cxx:2805
std::unique_ptr< weld::CheckButton > mxEnableSeed
std::unique_ptr< weld::Button > mxButtonClose
std::unique_ptr< formula::RefEdit > mxInputRangeEdit
#define min(a, b)
SCTAB Tab() const
Definition: address.hxx:271
std::unique_ptr< weld::SpinButton > mxParameter2Value
ScTabViewShell * GetViewShell() const
Definition: viewdata.hxx:357
virtual void SetReference(const ScRange &rRef, ScDocument &rDoc) override
#define SAL_MAX_INT32
std::unique_ptr< weld::SpinButton > mxParameter1Value
sal_Int16 SCCOL
Definition: types.hxx:22
IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, OkClicked, weld::Button &, void)
SC_DLLPUBLIC 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:2203
#define SAL_MIN_INT32
size_t size() const
Definition: rangelst.hxx:90
OUString ScResId(const char *pId)
Definition: scdll.cxx:95
ScMarkType GetSimpleArea(SCCOL &rStartCol, SCROW &rStartRow, SCTAB &rStartTab, SCCOL &rEndCol, SCROW &rEndRow, SCTAB &rEndTab) const
Definition: viewdata.cxx:1170
virtual void RefInputDone(bool bForced=false) override
Definition: anyrefdg.cxx:755
std::unique_ptr< weld::ComboBox > mxDistributionCombo
std::unique_ptr< weld::SpinButton > mxSeed
bool IsValid() const
Definition: address.hxx:547
void PostPaint(SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab, SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, PaintPartFlags nPart, sal_uInt16 nExtFlags=0)
Definition: docsh3.cxx:99
std::unique_ptr< weld::Label > mxInputRangeText
SCCOL Col() const
Definition: address.hxx:267
size_t LeaveListAction()
void GenerateNumbers(RNG &randomGenerator, const char *pDistributionStringId, const std::optional< sal_Int8 > aDecimalPlaces)
sal_Int32 SCROW
Definition: types.hxx:18
RET_CLOSE
std::unique_ptr< weld::SpinButton > mxDecimalPlaces
std::unique_ptr< formula::RefButton > mxInputRangeButton
std::unique_ptr< weld::CheckButton > mxEnableRounding
Reference< XExecutableDialog > m_xDialog
virtual void EnterListAction(const OUString &rComment, const OUString &rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId)
ScDocFunc & GetDocFunc()
Definition: docsh.hxx:218
std::unique_ptr< weld::Button > mxButtonApply
bool DoClose(sal_uInt16 nId)
Definition: anyrefdg.cxx:694
sal_Int16 SCTAB
Definition: types.hxx:23
virtual void RefInputStart(formula::RefEdit *pEdit, formula::RefButton *pButton=nullptr) override
Definition: anyrefdg.cxx:725
ViewShellId GetViewShellId() const override