summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx')
-rw-r--r--sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx482
1 files changed, 482 insertions, 0 deletions
diff --git a/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx b/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx
new file mode 100644
index 000000000..91b43cbe0
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx
@@ -0,0 +1,482 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <svl/undo.hxx>
+#include <rtl/math.hxx>
+#include <osl/time.h>
+
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <reffact.hxx>
+#include <docfunc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <random>
+
+#include <RandomNumberGeneratorDialog.hxx>
+
+namespace
+{
+
+const sal_Int64 DIST_UNIFORM = 0;
+const sal_Int64 DIST_NORMAL = 1;
+const sal_Int64 DIST_CAUCHY = 2;
+const sal_Int64 DIST_BERNOULLI = 3;
+const sal_Int64 DIST_BINOMIAL = 4;
+const sal_Int64 DIST_CHI_SQUARED = 5;
+const sal_Int64 DIST_GEOMETRIC = 6;
+const sal_Int64 DIST_NEGATIVE_BINOMIAL = 7;
+const sal_Int64 DIST_UNIFORM_INTEGER = 8;
+
+const sal_Int64 PRECISION = 10000;
+const sal_Int64 DIGITS = 4;
+
+}
+
+ScRandomNumberGeneratorDialog::ScRandomNumberGeneratorDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent,
+ "modules/scalc/ui/randomnumbergenerator.ui",
+ "RandomNumberGeneratorDialog")
+ , mrViewData(rViewData)
+ , mrDoc(rViewData.GetDocument())
+ , mbDialogLostFocus(false)
+ , mxInputRangeText(m_xBuilder->weld_label("cell-range-label"))
+ , mxInputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("cell-range-edit")))
+ , mxInputRangeButton(new formula::RefButton(m_xBuilder->weld_button("cell-range-button")))
+ , mxDistributionCombo(m_xBuilder->weld_combo_box("distribution-combo"))
+ , mxParameter1Text(m_xBuilder->weld_label("parameter1-label"))
+ , mxParameter1Value(m_xBuilder->weld_spin_button("parameter1-spin"))
+ , mxParameter2Text(m_xBuilder->weld_label("parameter2-label"))
+ , mxParameter2Value(m_xBuilder->weld_spin_button("parameter2-spin"))
+ , mxSeed(m_xBuilder->weld_spin_button("seed-spin"))
+ , mxEnableSeed(m_xBuilder->weld_check_button("enable-seed-check"))
+ , mxDecimalPlaces(m_xBuilder->weld_spin_button("decimal-places-spin"))
+ , mxEnableRounding(m_xBuilder->weld_check_button("enable-rounding-check"))
+ , mxButtonApply(m_xBuilder->weld_button("apply"))
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonClose(m_xBuilder->weld_button("close"))
+{
+ mxInputRangeEdit->SetReferences(this, mxInputRangeText.get());
+ mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+ Init();
+ GetRangeFromSelection();
+}
+
+ScRandomNumberGeneratorDialog::~ScRandomNumberGeneratorDialog()
+{
+}
+
+void ScRandomNumberGeneratorDialog::Init()
+{
+ mxButtonOk->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, OkClicked ) );
+ mxButtonClose->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, CloseClicked ) );
+ mxButtonApply->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, ApplyClicked ) );
+
+ mxInputRangeEdit->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetEditFocusHandler ));
+ mxInputRangeButton->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetButtonFocusHandler ));
+
+ mxInputRangeEdit->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseEditFocusHandler ));
+ mxInputRangeButton->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseButtonFocusHandler ));
+
+ mxInputRangeEdit->SetModifyHdl( LINK( this, ScRandomNumberGeneratorDialog, InputRangeModified ));
+ mxParameter1Value->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog, Parameter1ValueModified ));
+ mxParameter2Value->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog, Parameter2ValueModified ));
+
+ mxDistributionCombo->connect_changed( LINK( this, ScRandomNumberGeneratorDialog, DistributionChanged ));
+
+ mxEnableSeed->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
+ mxEnableRounding->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
+
+ DistributionChanged(*mxDistributionCombo);
+ CheckChanged(*mxEnableSeed);
+}
+
+void ScRandomNumberGeneratorDialog::GetRangeFromSelection()
+{
+ mrViewData.GetSimpleArea(maInputRange);
+ OUString aCurrentString(maInputRange.Format(mrDoc, ScRefFlags::RANGE_ABS_3D, mrDoc.GetAddressConvention()));
+ mxInputRangeEdit->SetText( aCurrentString );
+}
+
+void ScRandomNumberGeneratorDialog::SetActive()
+{
+ if ( mbDialogLostFocus )
+ {
+ mbDialogLostFocus = false;
+ if( mxInputRangeEdit )
+ mxInputRangeEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScRandomNumberGeneratorDialog::Close()
+{
+ DoClose( ScRandomNumberGeneratorDialogWrapper::GetChildWindowId() );
+}
+
+void ScRandomNumberGeneratorDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDoc )
+{
+ if (!mxInputRangeEdit->GetWidget()->get_sensitive())
+ return;
+
+ if ( rReferenceRange.aStart != rReferenceRange.aEnd )
+ RefInputStart(mxInputRangeEdit.get());
+
+ maInputRange = rReferenceRange;
+
+ OUString aReferenceString(maInputRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ mxInputRangeEdit->SetRefString( aReferenceString );
+
+ mxButtonApply->set_sensitive(true);
+ mxButtonOk->set_sensitive(true);
+}
+
+void ScRandomNumberGeneratorDialog::SelectGeneratorAndGenerateNumbers()
+{
+ if (!maInputRange.IsValid())
+ return;
+
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+
+ sal_uInt32 seedValue;
+
+ if( mxEnableSeed->get_active() )
+ {
+ seedValue = mxSeed->get_value();
+ }
+ else
+ {
+ TimeValue now;
+ osl_getSystemTime(&now);
+ seedValue = now.Nanosec;
+ }
+
+ std::mt19937 seed(seedValue);
+
+ sal_Int64 parameterInteger1 = mxParameter1Value->get_value();
+ sal_Int64 parameterInteger2 = mxParameter2Value->get_value();
+
+ double parameter1 = parameterInteger1 / static_cast<double>(PRECISION);
+ double parameter2 = parameterInteger2 / static_cast<double>(PRECISION);
+
+ std::optional<sal_Int8> aDecimalPlaces;
+ if (mxEnableRounding->get_active())
+ {
+ aDecimalPlaces = static_cast<sal_Int8>(mxDecimalPlaces->get_value());
+ }
+
+ switch(aSelectedId)
+ {
+ case DIST_UNIFORM:
+ {
+ std::uniform_real_distribution<> distribution(parameter1, parameter2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_UNIFORM_REAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_UNIFORM_INTEGER:
+ {
+ std::uniform_int_distribution<sal_Int64> distribution(parameterInteger1, parameterInteger2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_UNIFORM_INTEGER, aDecimalPlaces);
+ break;
+ }
+ case DIST_NORMAL:
+ {
+ std::normal_distribution<> distribution(parameter1, parameter2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_NORMAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_CAUCHY:
+ {
+ std::cauchy_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_CAUCHY, aDecimalPlaces);
+ break;
+ }
+ case DIST_BERNOULLI:
+ {
+ std::bernoulli_distribution distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_BERNOULLI, aDecimalPlaces);
+ break;
+ }
+ case DIST_BINOMIAL:
+ {
+ std::binomial_distribution<> distribution(parameterInteger2, parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_BINOMIAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_NEGATIVE_BINOMIAL:
+ {
+ std::negative_binomial_distribution<> distribution(parameterInteger2, parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_NEGATIVE_BINOMIAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_CHI_SQUARED:
+ {
+ std::chi_squared_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_CHI_SQUARED, aDecimalPlaces);
+ break;
+ }
+ case DIST_GEOMETRIC:
+ {
+ std::geometric_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_GEOMETRIC, aDecimalPlaces);
+ break;
+ }
+ }
+}
+
+template<class RNG>
+void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, std::optional<sal_Int8> aDecimalPlaces)
+{
+ OUString aUndo = ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE);
+ OUString aDistributionName = ScResId(pDistributionStringId);
+ aUndo = aUndo.replaceAll("$(DISTRIBUTION)", aDistributionName);
+
+ ScDocShell* pDocShell = mrViewData.GetDocShell();
+ SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ SCROW nRowStart = maInputRange.aStart.Row();
+ SCROW nRowEnd = maInputRange.aEnd.Row();
+ SCCOL nColStart = maInputRange.aStart.Col();
+ SCCOL nColEnd = maInputRange.aEnd.Col();
+ SCTAB nTabStart = maInputRange.aStart.Tab();
+ SCTAB nTabEnd = maInputRange.aEnd.Tab();
+
+ std::vector<double> aVals;
+ aVals.reserve(nRowEnd - nRowStart + 1);
+
+ for (SCROW nTab = nTabStart; nTab <= nTabEnd; ++nTab)
+ {
+ for (SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ {
+ aVals.clear();
+
+ ScAddress aPos(nCol, nRowStart, nTab);
+ for (SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
+ {
+
+ if (aDecimalPlaces)
+ aVals.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces));
+ else
+ aVals.push_back(randomGenerator());
+ }
+
+ pDocShell->GetDocFunc().SetValueCells(aPos, aVals, true);
+ }
+ }
+
+ pUndoManager->LeaveListAction();
+
+ pDocShell->PostPaint( maInputRange, PaintPartFlags::Grid );
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, OkClicked, weld::Button&, void )
+{
+ ApplyClicked(*mxButtonApply);
+ CloseClicked(*mxButtonClose);
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, ApplyClicked, weld::Button&, void )
+{
+ SelectGeneratorAndGenerateNumbers();
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, CloseClicked, weld::Button&, void )
+{
+ response(RET_CLOSE);
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, GetEditFocusHandler, formula::RefEdit&, void)
+{
+ mxInputRangeEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, GetButtonFocusHandler, formula::RefButton&, void)
+{
+ mxInputRangeEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, InputRangeModified, formula::RefEdit&, void)
+{
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxInputRangeEdit->GetText(), mrDoc);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ maInputRange = *pRange;
+ mxButtonApply->set_sensitive(true);
+ mxButtonOk->set_sensitive(true);
+ // Highlight the resulting range.
+ mxInputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ maInputRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ mxButtonApply->set_sensitive(false);
+ mxButtonOk->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, Parameter1ValueModified, weld::SpinButton&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+ if (aSelectedId == DIST_UNIFORM ||
+ aSelectedId == DIST_UNIFORM_INTEGER)
+ {
+ sal_Int64 min = mxParameter1Value->get_value();
+ sal_Int64 max = mxParameter2Value->get_value();
+ if(min > max)
+ {
+ mxParameter2Value->set_value(min);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, Parameter2ValueModified, weld::SpinButton&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+ if (aSelectedId == DIST_UNIFORM ||
+ aSelectedId == DIST_UNIFORM_INTEGER)
+ {
+ sal_Int64 min = mxParameter1Value->get_value();
+ sal_Int64 max = mxParameter2Value->get_value();
+ if(min > max)
+ {
+ mxParameter1Value->set_value(max);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, CheckChanged, weld::Toggleable&, void)
+{
+ mxSeed->set_sensitive(mxEnableSeed->get_active());
+ mxDecimalPlaces->set_sensitive(mxEnableRounding->get_active());
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, DistributionChanged, weld::ComboBox&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+
+ mxParameter1Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
+ mxParameter2Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
+
+ mxParameter1Value->set_digits(DIGITS);
+ mxParameter1Value->set_increments(PRECISION, PRECISION * 10);
+
+ mxParameter2Value->set_digits(DIGITS);
+ mxParameter2Value->set_increments(PRECISION, PRECISION * 10);
+
+ switch(aSelectedId)
+ {
+ case DIST_UNIFORM:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_UNIFORM_INTEGER:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
+ mxParameter1Value->set_digits(0);
+ mxParameter1Value->set_increments(1, 10);
+
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
+ mxParameter2Value->set_digits(0);
+ mxParameter2Value->set_increments(1, 10);
+
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_NORMAL:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MEAN));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_DEVIATION));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_CAUCHY:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_MEDIAN));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_SIGMA));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_BERNOULLI:
+ case DIST_GEOMETRIC:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
+ mxParameter1Value->set_range(0, PRECISION);
+ mxParameter1Value->set_increments(1000, 10000);
+
+ mxParameter2Text->hide();
+ mxParameter2Value->hide();
+ break;
+ }
+ case DIST_BINOMIAL:
+ case DIST_NEGATIVE_BINOMIAL:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
+ mxParameter1Value->set_range(0, PRECISION);
+ mxParameter1Value->set_increments(1000, 10000);
+
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NUMBER_OF_TRIALS));
+ mxParameter2Value->set_digits(0);
+ mxParameter2Value->set_increments(1, 10);
+ mxParameter2Value->set_min(0);
+
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_CHI_SQUARED:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE));
+
+ mxParameter2Text->hide();
+ mxParameter2Value->hide();
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */