/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include 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 DIST_POISSON = 9; 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(PRECISION); double parameter2 = parameterInteger2 / static_cast(PRECISION); std::optional aDecimalPlaces; if (mxEnableRounding->get_active()) { aDecimalPlaces = static_cast(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 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_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; } 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_POISSON: { std::poisson_distribution<> distribution(parameter1); auto rng = std::bind(distribution, seed); GenerateNumbers(rng, STR_DISTRIBUTION_POISSON, aDecimalPlaces); break; } } } template void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, std::optional aDecimalPlaces) { OUString aUndo = ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE); OUString aDistributionName = ScResId(pDistributionStringId); aUndo = aUndo.replaceAll("%1", 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 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; } case DIST_POISSON: { mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MEAN)); mxParameter1Value->set_value(PRECISION); mxParameter1Value->set_increments(1000, 10000); mxParameter1Value->set_min(1000); mxParameter2Text->hide(); mxParameter2Value->hide(); break; } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */