/* -*- 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
ScCursorRefEdit::ScCursorRefEdit(std::unique_ptr<weld::Entry> xControl)
: formula::RefEdit(std::move(xControl))
{
xEntry->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
xEntry->connect_key_press(LINK(this, ScCursorRefEdit, KeyInputHdl));
}
m_xScrollBar->vadjustment_set_page_increment( EDIT_ROW_COUNT );
m_xScrollBar->vadjustment_set_page_size( EDIT_ROW_COUNT ); // Range is set in ShowConditions
// get available solver implementations //! sort by descriptions?
ScSolverUtil::GetImplementations( maImplNames, maDescriptions );
// Load existing settings stored in the tab
LoadSolverSettings();
ShowConditions();
// If no objective cell has been loaded, then use the selected cell if (m_xEdObjectiveCell->GetText().isEmpty())
{
OUString aCursorStr; if (!mrDoc.GetRangeAtBlock(ScRange(rCursorPos), aCursorStr))
aCursorStr = rCursorPos.Format(ScRefFlags::ADDR_ABS, nullptr, mrDoc.GetAddressConvention());
m_xEdObjectiveCell->SetRefString(aCursorStr);
}
// Loads solver settings into the dialog void ScOptSolverDlg::LoadSolverSettings()
{
m_xEdObjectiveCell->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL));
m_xEdTargetValue->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL));
m_xEdVariableCells->SetRefString(m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS));
// Objective type
sc::ObjectiveType eType = m_pSolverSettings->GetObjectiveType(); switch (eType)
{ case sc::OT_MAXIMIZE : m_xRbMax->set_active(true); break; case sc::OT_MINIMIZE : m_xRbMin->set_active(true); break; case sc::OT_VALUE : m_xRbValue->set_active(true); break;
}
// Model constraints
m_aConditions = m_pSolverSettings->GetConstraints();
// Loads solver engine name // If the solver engine in the current settings are not supported, use the first available
maEngine = m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE); if (!IsEngineAvailable(maEngine))
{
maEngine = maImplNames[0];
m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
}
// Query current engine options
maProperties = ScSolverUtil::GetDefaults(maEngine);
m_pSolverSettings->GetEngineOptions(maProperties);
}
// Set solver settings and save them to the file // But first, checks if the settings have changed void ScOptSolverDlg::SaveSolverSettings()
{ // tdf#160104 If file does not have a solver model and the Solver dialog is set to its // default initial values (maximize is selected; no variable cells; no target value // and no constraints defined) then nothing needs to be saved if (!m_pSolverSettings->TabHasSolverModel() && m_xRbMax->get_active()
&& m_xEdTargetValue->GetText().isEmpty() && m_xEdVariableCells->GetText().isEmpty()
&& m_aConditions.size() == 0) return;
// The current tab has a model; now we need to determined if it has been modified bool bModified = false;
// Check objective cell, objective value and variable cells if (m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL) != m_xEdObjectiveCell->GetText()
|| m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL) != m_xEdTargetValue->GetText()
|| m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS) != m_xEdVariableCells->GetText())
bModified = true;
// Check selected objective type and save it if changed
sc::ObjectiveType aType = sc::OT_MAXIMIZE; if (m_xRbMin->get_active())
aType = sc::OT_MINIMIZE; elseif (m_xRbValue->get_active())
aType = sc::OT_VALUE;
if (m_pSolverSettings->GetObjectiveType() != aType)
bModified = true;
// Check if model constraints changed
std::vector<sc::ModelConstraint> vCurConditions = m_pSolverSettings->GetConstraints(); if (!bModified && vCurConditions.size() != m_aConditions.size())
bModified = true; else
{ // Here the size of both vectors is the same // Now it needs to check the contents of the constraints for (size_t i = 0; i < vCurConditions.size(); i++)
{ if (vCurConditions[i].aLeftStr != m_aConditions[i].aLeftStr
|| vCurConditions[i].nOperator != m_aConditions[i].nOperator
|| vCurConditions[i].aRightStr != m_aConditions[i].aRightStr)
bModified = true;
if (bModified) break;
}
}
// Check if the solver engine name and its options have changed if (m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE) != maEngine)
{
bModified = true;
} else
{ // The solver engine hasn't changed, so we need to check if engine options changed // Query current engine options; here we start by creating a copy of maProperties // to ensure the order is the same
css::uno::Sequence<css::beans::PropertyValue> vCurOptions(maProperties);
m_pSolverSettings->GetEngineOptions(vCurOptions);
for (sal_Int32 i = 0; i < vCurOptions.getLength(); i++)
{ if (vCurOptions[i].Value != maProperties[i].Value)
{
bModified = true; break;
}
}
}
// Effectively save settings to file if modifications were made if (bModified)
{
m_pSolverSettings->SetParameter(sc::SP_OBJ_CELL, m_xEdObjectiveCell->GetText());
m_pSolverSettings->SetParameter(sc::SP_OBJ_VAL, m_xEdTargetValue->GetText());
m_pSolverSettings->SetParameter(sc::SP_VAR_CELLS, m_xEdVariableCells->GetText());
m_pSolverSettings->SetObjectiveType(aType);
m_pSolverSettings->SetConstraints(m_aConditions);
m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
m_pSolverSettings->SetEngineOptions(maProperties);
m_pSolverSettings->SaveSolverSettings();
}
} // Test if a LO engine implementation exists bool ScOptSolverDlg::IsEngineAvailable(std::u16string_view sEngineName)
{ auto nIndex = comphelper::findValue(maImplNames, sEngineName); return nIndex != -1;
}
if ( bHadFocus && !rBtn.get_sensitive() )
{ // If the button is disabled, focus would normally move to the next control, // (left edit of the next row). Move it to left edit of this row instead.
IMPL_LINK_NOARG(ScOptSolverDlg, TargetModifyHdl, formula::RefEdit&, void)
{ // modify handler for the target edit: // select "Value of" if something is input into the edit if ( !m_xEdTargetValue->GetText().isEmpty() )
m_xRbValue->set_active(true);
}
IMPL_LINK_NOARG(ScOptSolverDlg, CondModifyHdl, formula::RefEdit&, void)
{ // modify handler for the condition edits, just to enable/disable "delete" buttons
ReadConditions();
EnableButtons();
}
IMPL_LINK_NOARG(ScOptSolverDlg, SelectHdl, weld::ComboBox&, void)
{ // select handler for operator list boxes, just to enable/disable "delete" buttons
ReadConditions();
EnableButtons();
}
// Converts the position of the operator in the dropdown menu to a ConstraintOperator type
sc::ConstraintOperator ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex)
{ switch(nIndex)
{ case 0 : return sc::CO_LESS_EQUAL; break; case 1 : return sc::CO_EQUAL; break; case 2 : return sc::CO_GREATER_EQUAL; break; case 3 : return sc::CO_INTEGER; break; case 4 : return sc::CO_BINARY; break; default : return sc::CO_LESS_EQUAL; break;
}
}
uno::Sequence<sheet::SolverConstraint> aConstraints;
sal_Int32 nConstrPos = 0; for ( constauto& rConstr : m_aConditions )
{ if ( !rConstr.aLeftStr.isEmpty() )
{
sheet::SolverConstraint aConstraint; // Order of list box entries must match enum values. // The enum SolverConstraintOperator starts at zero, whereas ConstraintOperator starts at 1 // hence we need to subtract -1 here
aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(rConstr.nOperator - 1);
// set options
uno::Reference<beans::XPropertySet> xOptProp(xSolver, uno::UNO_QUERY); if ( xOptProp.is() )
{ for (const beans::PropertyValue& rValue : maProperties)
{ try
{
xOptProp->setPropertyValue( rValue.Name, rValue.Value );
} catch ( uno::Exception & )
{
OSL_FAIL("Exception in solver option property");
}
}
}
// tdf#162760 The solver engine may crash unexpectedly, so we need a try...catch here bool bSuccess(false); try
{
xSolver->solve();
bSuccess = xSolver->getSuccess();
} catch (const uno::RuntimeException&)
{
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Error, VclButtonsType::Ok,
ScResId(STR_SOLVER_ENGINE_ERROR)));
xBox->run();
}
xProgress->response(RET_CLOSE);
bool bClose = false; bool bRestore = true; // restore old values unless a solution is accepted if ( bSuccess )
{ // put solution into document so it is visible when asking
uno::Sequence<double> aSolution = xSolver->getSolution(); if ( aSolution.getLength() == nVarCount )
{
mrDocShell.LockPaint();
ScDocFunc &rFunc = mrDocShell.GetDocFunc(); for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
{
ScAddress aCellPos;
ScUnoConversion::FillScAddress(aCellPos, aVariables[nVarPos]);
rFunc.SetValueCell(aCellPos, aSolution[nVarPos], false);
}
mrDocShell.UnlockPaint();
} //! else error?
// take formatted result from document (result value from component is ignored)
OUString aResultStr = mrDoc.GetString( static_cast<SCCOL>(aObjective.Column), static_cast<SCROW>(aObjective.Row), static_cast<SCTAB>(aObjective.Sheet));
ScSolverSuccessDialog aDialog(m_xDialog.get(), aResultStr); if (aDialog.run() == RET_OK)
{ // keep results and close dialog
bRestore = false;
bClose = true;
}
} else
{
OUString aError;
uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY ); if ( xDesc.is() )
aError = xDesc->getStatusDescription(); // error description from component
ScSolverNoSolutionDialog aDialog(m_xDialog.get(), aError);
aDialog.run();
}
// Generate sensitivity report if user wants it
uno::Reference<css::beans::XPropertySetInfo> xInfo = xOptProp->getPropertySetInfo(); bool bUserWantsReport = false; if (xInfo->hasPropertyByName("GenSensitivityReport"))
xOptProp->getPropertyValue("GenSensitivityReport") >>= bUserWantsReport;
if (bSuccess && bUserWantsReport)
{ // Retrieve the sensitivity analysis report
css::sheet::SensitivityReport aSensitivity; bool bHasReportObj = xOptProp->getPropertyValue("SensitivityReport") >>= aSensitivity;
if (bHasReportObj && aSensitivity.HasReport)
{ // Define the Tab name where the sensitivity analysis will be written to
OUString sNewTabName;
SCTAB nNewTab;
mrDoc.GetName(mnCurTab, sNewTabName);
sNewTabName += "_" + ScResId(STR_SENSITIVITY); // Check if the new Tab name exists if (mrDoc.GetTable(sNewTabName, nNewTab))
{ // Add numbers to the end of the Tab name to make it unique
SCTAB i = 1;
OUString aName; do
{
i++;
aName = sNewTabName + "_" + OUString::number(static_cast<sal_Int32>(i));
} while(mrDoc.GetTable(aName, nNewTab));
sNewTabName = aName;
}
// Insert new sheet to the document and start writing the report
ScDocFunc &rFunc = mrDocShell.GetDocFunc();
rFunc.InsertTable(mnCurTab + 1, sNewTabName, false, false);
SCTAB nReportTab; if (!mrDoc.GetTable(sNewTabName, nReportTab))
{
SAL_WARN("sc", "Could not get the just inserted table!"); returnfalse;
}
// Used to input data in the new sheet
ScAddress aOutputAddress(0, 0, nReportTab);
ScAddress::Details mAddressDetails(mrDoc, aOutputAddress);
AddressWalkerWriter aOutput(aOutputAddress, mrDocShell, mrDoc,
formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
aOutput.writeBoldString(ScResId(STR_SENSITIVITY_TITLE));
aOutput.newLine();
aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine);
aOutput.newLine();
aOutput.newLine();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.