tdf#38948 Save solver settings to file

This patch implements the mechanism to save solver settings in LO Calc as well as export/import them from XLSX files.

In MS Excel solver settings are saved as hidden named ranges, so in this patch I used the same strategy to save solver settings in Calc, i.e. by creating named ranges to store the solver settings using the same terminology used in Excel.

With this we gain the ability to save solver settings by tab, as well as export/import since we already have "named ranges/expressions" import/export implemented in LO.

Change-Id: Id41bca261dc3cd8e6888643f0ed6a97b26097876
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148112
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Rafael Lima 2023-03-03 14:35:51 +00:00 committed by Tomaž Vajngerl
parent 9a762f9ea3
commit 5d86cc81fe
13 changed files with 1066 additions and 127 deletions

View file

@ -0,0 +1,75 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#*************************************************************************
#
# 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/.
#
#*************************************************************************
$(eval $(call gb_CppunitTest_CppunitTest,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_use_common_precompiled_header,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_add_exception_objects,sc_ucalc_solver, \
sc/qa/unit/ucalc_solver \
))
$(eval $(call gb_CppunitTest_use_externals,sc_ucalc_solver, \
boost_headers \
mdds_headers \
libxml2 \
))
$(eval $(call gb_CppunitTest_use_libraries,sc_ucalc_solver, \
basegfx \
comphelper \
cppu \
cppuhelper \
i18nlangtag \
sal \
sax \
sc \
scqahelper \
sfx \
subsequenttest \
svl \
svx \
svxcore \
test \
tl \
unotest \
utl \
vcl \
))
$(eval $(call gb_CppunitTest_set_include,sc_ucalc_solver,\
-I$(SRCDIR)/sc/source/ui/inc \
-I$(SRCDIR)/sc/inc \
$$(INCLUDE) \
))
$(eval $(call gb_CppunitTest_use_api,sc_ucalc_solver,\
offapi \
udkapi \
))
$(eval $(call gb_CppunitTest_use_sdk_api,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_use_ure,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_use_vcl,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_use_rdb,sc_ucalc_solver,services))
$(eval $(call gb_CppunitTest_use_components,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_use_configuration,sc_ucalc_solver))
$(eval $(call gb_CppunitTest_add_arguments,sc_ucalc_solver, \
-env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}" \
))
# vim: set noet sw=4 ts=4:

View file

@ -186,6 +186,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
sc/source/core/data/segmenttree \
sc/source/core/data/sheetevents \
sc/source/core/data/simpleformulacalc \
sc/source/core/data/SolverSettings \
sc/source/core/data/sortparam \
sc/source/core/data/stlpool \
sc/source/core/data/stlsheet \

View file

@ -54,6 +54,7 @@ $(eval $(call gb_Module_add_check_targets,sc,\
CppunitTest_sc_ucalc_range \
CppunitTest_sc_ucalc_sharedformula \
CppunitTest_sc_ucalc_sparkline \
CppunitTest_sc_ucalc_solver \
CppunitTest_sc_ucalc_sort \
CppunitTest_sc_filters_test \
CppunitTest_sc_mark_test \

209
sc/inc/SolverSettings.hxx Normal file
View file

@ -0,0 +1,209 @@
/* -*- 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/.
*
*/
#pragma once
#include <memory>
#include <utility>
#include <variant>
#include <rtl/ustring.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
class ScTable;
class ScDocShell;
namespace sc
{
// These values are MS compatible
enum ObjectiveType
{
OT_MAXIMIZE = 1,
OT_MINIMIZE = 2,
OT_VALUE = 3
};
enum SolverParameter
{
SP_OBJ_CELL, // Objective cell
SP_OBJ_TYPE, // Objective type (max, min, value)
SP_OBJ_VAL, // Value (used when objective is of type "value")
SP_VAR_CELLS, // Variable cells
SP_CONSTR_COUNT, // Number of constraints (MSO only)
SP_LO_ENGINE, // Engine name used in LO
SP_MS_ENGINE, // Engine ID used in MSO
SP_INTEGER, // Assume all variables are integer (0: no, 1: yes)
SP_NON_NEGATIVE, // Assume non negativity (1: yes, 2: no)
SP_EPSILON_LEVEL, // Epsilon level
SP_LIMIT_BBDEPTH, // Branch and bound depth
SP_TIMEOUT, // Time limit to return a solution
SP_ALGORITHM // Algorithm used by the SwarmSolver (1, 2 or 3)
};
// Starts at 1 to maintain MS compatibility
enum ConstraintOperator
{
CO_LESS_EQUAL = 1,
CO_EQUAL = 2,
CO_GREATER_EQUAL = 3,
CO_INTEGER = 4,
CO_BINARY = 5
};
// Parts of a constraint
enum ConstraintPart
{
CP_LEFT_HAND_SIDE,
CP_OPERATOR,
CP_RIGHT_HAND_SIDE
};
// Stores the information of a single constraint (condition)
struct ModelConstraint
{
OUString aLeftStr;
ConstraintOperator nOperator;
OUString aRightStr;
ModelConstraint()
: nOperator(CO_LESS_EQUAL)
{
}
bool IsDefault() const
{
return aLeftStr.isEmpty() && aRightStr.isEmpty() && nOperator == CO_LESS_EQUAL;
}
};
/* Class SolverSettings
*
* This class is used to load/save and manipulate solver settings in a Calc tab.
*
* During initialization, (see Initialize() method) all settings stored in the tab are loaded onto
* the object. Settings that are not defined use default values.
*
* Read/Write methods are private and are used internally to load/write solver settings from
* named ranges associated with the sheet.
*
* Get/Set methods are public methods used to change object properties (they do not save data
* to the file).
*
* The method SaveSolverSettings() is used to create the named ranges containing the current
* property values into the file.
*
*/
class SolverSettings
{
private:
ScTable& m_rTable;
ScDocument& m_rDoc;
ScDocShell* m_pDocShell;
// Used to read/write the named ranges in the tab
ScRangeName* m_pRangeName;
OUString m_sObjCell;
ObjectiveType m_eObjType;
OUString m_sObjVal;
OUString m_sVariableCells;
OUString m_sLOEngineName;
OUString m_sMSEngineId;
// Solver engine options
OUString m_sInteger;
OUString m_sNonNegative;
OUString m_sEpsilonLevel;
OUString m_sLimitBBDepth;
OUString m_sTimeout;
OUString m_sAlgorithm;
css::uno::Sequence<css::beans::PropertyValue> m_aEngineOptions;
std::vector<ModelConstraint> m_aConstraints;
void Initialize();
// Used to create or read a single solver parameter based on its named range
bool ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes = false);
void WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted = false);
// Creates or reads all constraints stored in named ranges
void ReadConstraints();
void WriteConstraints();
// Used to create or get a single constraint part
bool ReadConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString& rValue);
void WriteConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString sValue);
// Creates or reads all named ranges associated with solver engine options
void ReadEngine();
void WriteEngine();
void DeleteAllNamedRanges();
// Maps solver parameters to named ranges
std::map<SolverParameter, OUString> m_mNamedRanges
= { { SP_OBJ_CELL, "solver_opt" }, { SP_OBJ_TYPE, "solver_typ" },
{ SP_OBJ_VAL, "solver_val" }, { SP_VAR_CELLS, "solver_adj" },
{ SP_CONSTR_COUNT, "solver_num" }, { SP_LO_ENGINE, "solver_lo_eng" },
{ SP_MS_ENGINE, "solver_eng" }, { SP_INTEGER, "solver_int" },
{ SP_NON_NEGATIVE, "solver_neg" }, { SP_EPSILON_LEVEL, "solver_eps" },
{ SP_LIMIT_BBDEPTH, "solver_bbd" }, { SP_TIMEOUT, "solver_tim" },
{ SP_ALGORITHM, "solver_alg" } };
// Maps LO solver implementation names to MS engine codes
std::map<OUString, OUString> SolverNamesToExcelEngines = {
{ "com.sun.star.comp.Calc.CoinMPSolver", "2" }, // Simplex LP
{ "com.sun.star.comp.Calc.LpsolveSolver", "2" }, // Simplex LP
{ "com.sun.star.comp.Calc.SwarmSolver", "1" } // GRG Nonlinear
};
// Maps MS solver engine codes to LO solver implementation names
std::map<OUString, OUString> SolverCodesToLOEngines = {
{ "1", "com.sun.star.comp.Calc.SwarmSolver" }, // GRG Nonlinear
{ "2", "com.sun.star.comp.Calc.CoinMPSolver" }, // Simplex LP
{ "3", "com.sun.star.comp.Calc.SwarmSolver" } // Evolutionary
};
// Maps LO solver parameters to named ranges to be used
// NonNegative: for MS compatibility, use 1 for selected and 2 for not selected
typedef std::vector<std::variant<OUString, SolverParameter>> TParamInfo;
std::map<OUString, TParamInfo> SolverParamNames
= { { "Integer", { SP_INTEGER, "solver_int", "bool" } },
{ "NonNegative", { SP_NON_NEGATIVE, "solver_neg", "bool" } },
{ "EpsilonLevel", { SP_EPSILON_LEVEL, "solver_eps", "int" } },
{ "LimitBBDepth", { SP_LIMIT_BBDEPTH, "solver_bbd", "bool" } },
{ "Timeout", { SP_TIMEOUT, "solver_tim", "int" } },
{ "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } } };
// Stores the roots used for named ranges of constraint parts
// Items here must be in the same order as in ConstraintPart enum
std::vector<OUString> m_aConstraintParts{ "solver_lhs", "solver_rel", "solver_rhs" };
public:
/* A SolverSettings object is linked to the ScTable where solver parameters
* are located and saved to */
SolverSettings(ScTable& pTable);
SC_DLLPUBLIC OUString GetParameter(SolverParameter eParam);
SC_DLLPUBLIC void SetParameter(SolverParameter eParam, OUString sValue);
SC_DLLPUBLIC ObjectiveType GetObjectiveType() { return m_eObjType; }
SC_DLLPUBLIC void SetObjectiveType(ObjectiveType eType);
SC_DLLPUBLIC void GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions);
SC_DLLPUBLIC void SetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions);
SC_DLLPUBLIC std::vector<ModelConstraint> GetConstraints() { return m_aConstraints; }
SC_DLLPUBLIC void SetConstraints(std::vector<ModelConstraint> aConstraints);
SC_DLLPUBLIC void SaveSolverSettings();
SC_DLLPUBLIC void ResetToDefaults();
};
} // namespace sc
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -2691,7 +2691,7 @@ private:
bool HasPartOfMerged( const ScRange& rRange );
public:
ScTable* FetchTable( SCTAB nTab );
SC_DLLPUBLIC ScTable* FetchTable( SCTAB nTab );
const ScTable* FetchTable( SCTAB nTab ) const;
ScRefCellValue GetRefCellValue( const ScAddress& rPos );

View file

@ -33,6 +33,7 @@
#include "document.hxx"
#include "drwlayer.hxx"
#include "SparklineList.hxx"
#include "SolverSettings.hxx"
#include <algorithm>
#include <atomic>
@ -257,6 +258,9 @@ private:
/** this is touched from formula group threading context */
std::atomic<bool> bStreamValid;
// Solver settings in current tab
std::shared_ptr<sc::SolverSettings> m_pSolverSettings;
// Default attributes for the unallocated columns.
ScColumnData aDefaultColData;
@ -442,6 +446,8 @@ public:
void SetFormula(
SCCOL nCol, SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram );
SC_DLLPUBLIC std::shared_ptr<sc::SolverSettings> GetSolverSettings();
/**
* Takes ownership of pCell
*

133
sc/qa/unit/ucalc_solver.cxx Normal file
View file

@ -0,0 +1,133 @@
/* -*- 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 <sal/config.h>
#include "helper/qahelper.hxx"
#include <document.hxx>
#include <table.hxx>
#include <SolverSettings.hxx>
using namespace sc;
class SolverTest : public ScModelTestBase
{
public:
SolverTest()
: ScModelTestBase("sc/qa/unit/data")
{
}
std::vector<ModelConstraint> CreateConstraintsModelA();
void TestConstraintsModelA(SolverSettings* pSettings);
};
// Creates a simple set of constraints for testing
std::vector<ModelConstraint> SolverTest::CreateConstraintsModelA()
{
std::vector<ModelConstraint> aConstraints;
ModelConstraint aConstr1;
aConstr1.aLeftStr = "C1:C10";
aConstr1.nOperator = CO_LESS_EQUAL;
aConstr1.aRightStr = "100";
aConstraints.push_back(aConstr1);
ModelConstraint aConstr2;
aConstr2.aLeftStr = "F5";
aConstr2.nOperator = CO_EQUAL;
aConstr2.aRightStr = "500";
aConstraints.push_back(aConstr2);
ModelConstraint aConstr3;
aConstr3.aLeftStr = "D1:D5";
aConstr3.nOperator = CO_BINARY;
aConstr3.aRightStr = "";
aConstraints.push_back(aConstr3);
return aConstraints;
}
// Tests the contents of the three constraints
void SolverTest::TestConstraintsModelA(SolverSettings* pSettings)
{
std::vector<ModelConstraint> aConstraints = pSettings->GetConstraints();
CPPUNIT_ASSERT_EQUAL(OUString("C1:C10"), aConstraints[0].aLeftStr);
CPPUNIT_ASSERT_EQUAL(CO_LESS_EQUAL, aConstraints[0].nOperator);
CPPUNIT_ASSERT_EQUAL(OUString("100"), aConstraints[0].aRightStr);
CPPUNIT_ASSERT_EQUAL(OUString("F5"), aConstraints[1].aLeftStr);
CPPUNIT_ASSERT_EQUAL(CO_EQUAL, aConstraints[1].nOperator);
CPPUNIT_ASSERT_EQUAL(OUString("500"), aConstraints[1].aRightStr);
CPPUNIT_ASSERT_EQUAL(OUString("D1:D5"), aConstraints[2].aLeftStr);
CPPUNIT_ASSERT_EQUAL(CO_BINARY, aConstraints[2].nOperator);
CPPUNIT_ASSERT_EQUAL(OUString(""), aConstraints[2].aRightStr);
}
/* This test creates a model in a single tab and test if the model info
* is correctly stored in the object
*/
CPPUNIT_TEST_FIXTURE(SolverTest, testSingleModel)
{
createScDoc();
ScDocument* pDoc = getScDoc();
ScTable* pTable = pDoc->FetchTable(0);
std::shared_ptr<sc::SolverSettings> pSettings = pTable->GetSolverSettings();
CPPUNIT_ASSERT(pSettings);
// Test solver default settings on an empty tab
// Here we only test default settings that are not engine-dependent
CPPUNIT_ASSERT_EQUAL(OUString(""), pSettings->GetParameter(SP_OBJ_CELL));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(OT_MAXIMIZE),
pSettings->GetParameter(SP_OBJ_TYPE).toInt32());
CPPUNIT_ASSERT_EQUAL(OUString(""), pSettings->GetParameter(SP_OBJ_VAL));
CPPUNIT_ASSERT_EQUAL(OUString(""), pSettings->GetParameter(SP_VAR_CELLS));
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pSettings->GetParameter(SP_CONSTR_COUNT).toInt32());
// Create a simple model
pSettings->SetParameter(SP_OBJ_CELL, OUString("A1"));
pSettings->SetParameter(SP_OBJ_TYPE, OUString::number(OT_MINIMIZE));
pSettings->SetParameter(SP_OBJ_VAL, OUString::number(0));
pSettings->SetParameter(SP_VAR_CELLS, OUString("D1:D5"));
std::vector<ModelConstraint> aConstraints = CreateConstraintsModelA();
pSettings->SetConstraints(aConstraints);
// Test if the model parameters were set
CPPUNIT_ASSERT_EQUAL(OUString("A1"), pSettings->GetParameter(SP_OBJ_CELL));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(OT_MINIMIZE),
pSettings->GetParameter(SP_OBJ_TYPE).toInt32());
CPPUNIT_ASSERT_EQUAL(OUString("0"), pSettings->GetParameter(SP_OBJ_VAL));
CPPUNIT_ASSERT_EQUAL(OUString("D1:D5"), pSettings->GetParameter(SP_VAR_CELLS));
// Test if the constraints were correctly set before saving
CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pSettings->GetParameter(SP_CONSTR_COUNT).toInt32());
TestConstraintsModelA(pSettings.get());
// Save and reload the file
pSettings->SaveSolverSettings();
saveAndReload("calc8");
pDoc = getScDoc();
pTable = pDoc->FetchTable(0);
pSettings = pTable->GetSolverSettings();
CPPUNIT_ASSERT(pSettings);
// Test if the model parameters remain set in the file
CPPUNIT_ASSERT_EQUAL(OUString("A1"), pSettings->GetParameter(SP_OBJ_CELL));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(OT_MINIMIZE),
pSettings->GetParameter(SP_OBJ_TYPE).toInt32());
CPPUNIT_ASSERT_EQUAL(OUString("0"), pSettings->GetParameter(SP_OBJ_VAL));
CPPUNIT_ASSERT_EQUAL(OUString("D1:D5"), pSettings->GetParameter(SP_VAR_CELLS));
// Test if the constraints remain correct after saving
CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pSettings->GetParameter(SP_CONSTR_COUNT).toInt32());
TestConstraintsModelA(pSettings.get());
}
CPPUNIT_PLUGIN_IMPLEMENT();

View file

@ -0,0 +1,504 @@
/* -*- 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 <global.hxx>
#include <table.hxx>
#include <docsh.hxx>
#include <solverutil.hxx>
#include <unotools/charclass.hxx>
#include <SolverSettings.hxx>
namespace sc
{
SolverSettings::SolverSettings(ScTable& rTable)
: m_rTable(rTable)
, m_rDoc(m_rTable.GetDoc())
, m_pDocShell(dynamic_cast<ScDocShell*>(m_rDoc.GetDocumentShell()))
{
// Get the named range manager for this tab
std::map<OUString, ScRangeName*> rRangeMap;
m_rDoc.GetRangeNameMap(rRangeMap);
m_pRangeName = rRangeMap.find(m_rTable.GetName())->second;
Initialize();
}
void SolverSettings::Initialize()
{
// Assign default values for the solver parameters
ResetToDefaults();
// Read the parameter values in the sheet
ReadParamValue(SP_OBJ_CELL, m_sObjCell);
ReadParamValue(SP_OBJ_VAL, m_sObjVal);
ReadParamValue(SP_VAR_CELLS, m_sVariableCells);
// Read the objective type
OUString sObjType;
if (ReadParamValue(SP_OBJ_TYPE, sObjType))
{
switch (sObjType.toInt32())
{
case 1:
m_eObjType = ObjectiveType::OT_MAXIMIZE;
break;
case 2:
m_eObjType = ObjectiveType::OT_MINIMIZE;
break;
case 3:
m_eObjType = ObjectiveType::OT_VALUE;
break;
default:
m_eObjType = ObjectiveType::OT_MAXIMIZE;
}
}
// Read all constraints in the tab
ReadConstraints();
// Read the solver engine being used
ReadEngine();
// Read engine options
ReadParamValue(SP_INTEGER, m_sInteger);
ReadParamValue(SP_NON_NEGATIVE, m_sNonNegative);
ReadParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
ReadParamValue(SP_TIMEOUT, m_sTimeout);
ReadParamValue(SP_ALGORITHM, m_sAlgorithm);
}
// Returns the current value of the parameter in the object as a string
OUString SolverSettings::GetParameter(SolverParameter eParam)
{
switch (eParam)
{
case SP_OBJ_CELL:
return m_sObjCell;
break;
case SP_OBJ_TYPE:
return OUString::number(m_eObjType);
break;
case SP_OBJ_VAL:
return m_sObjVal;
break;
case SP_VAR_CELLS:
return m_sVariableCells;
break;
case SP_CONSTR_COUNT:
return OUString::number(m_aConstraints.size());
break;
case SP_LO_ENGINE:
return m_sLOEngineName;
break;
case SP_MS_ENGINE:
return m_sMSEngineId;
break;
case SP_INTEGER:
return m_sInteger;
break;
case SP_NON_NEGATIVE:
return m_sNonNegative;
break;
case SP_EPSILON_LEVEL:
return m_sEpsilonLevel;
break;
case SP_LIMIT_BBDEPTH:
return m_sLimitBBDepth;
break;
case SP_TIMEOUT:
return m_sTimeout;
break;
case SP_ALGORITHM:
return m_sAlgorithm;
break;
default:
return "";
}
}
// Sets the value of a single solver parameter in the object
void SolverSettings::SetParameter(SolverParameter eParam, OUString sValue)
{
switch (eParam)
{
case SP_OBJ_CELL:
m_sObjCell = sValue;
break;
case SP_OBJ_TYPE:
{
sal_Int32 nObjType = sValue.toInt32();
switch (nObjType)
{
case OT_MAXIMIZE:
m_eObjType = ObjectiveType::OT_MAXIMIZE;
break;
case OT_MINIMIZE:
m_eObjType = ObjectiveType::OT_MINIMIZE;
break;
case OT_VALUE:
m_eObjType = ObjectiveType::OT_VALUE;
break;
default:
m_eObjType = ObjectiveType::OT_MAXIMIZE;
break;
}
break;
}
case SP_OBJ_VAL:
m_sObjVal = sValue;
break;
case SP_VAR_CELLS:
m_sVariableCells = sValue;
break;
case SP_LO_ENGINE:
m_sLOEngineName = sValue;
break;
case SP_INTEGER:
{
if (sValue == "0" || sValue == "1")
m_sInteger = sValue;
}
break;
case SP_NON_NEGATIVE:
{
if (sValue == "1" || sValue == "2")
m_sNonNegative = sValue;
}
break;
case SP_EPSILON_LEVEL:
m_sEpsilonLevel = sValue;
break;
case SP_LIMIT_BBDEPTH:
m_sLimitBBDepth = sValue;
break;
case SP_TIMEOUT:
m_sTimeout = sValue;
break;
case SP_ALGORITHM:
{
if (sValue == "1" || sValue == "2" || sValue == "3")
m_sAlgorithm = sValue;
}
break;
default:
break;
}
}
void SolverSettings::SetObjectiveType(ObjectiveType eType) { m_eObjType = eType; }
// Loads all constraints in the tab
void SolverSettings::ReadConstraints()
{
// Condition indices start at 1 for MS compatibility
// The number of "lhs", "rel" and "rhs" entries will always be the same
tools::Long nConstraint = 1;
m_aConstraints.clear();
OUString sValue;
while (ReadConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, sValue))
{
// Left hand side
ModelConstraint aNewCondition;
aNewCondition.aLeftStr = sValue;
// Right hand side
if (ReadConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, sValue))
aNewCondition.aRightStr = sValue;
// Relation (operator)
if (ReadConstraintPart(CP_OPERATOR, nConstraint, sValue))
aNewCondition.nOperator = static_cast<sc::ConstraintOperator>(sValue.toInt32());
m_aConstraints.push_back(aNewCondition);
nConstraint++;
}
}
// Writes all constraints to the file
void SolverSettings::WriteConstraints()
{
// Condition indices start at 1 for MS compatibility
tools::Long nConstraint = 1;
for (auto& aConstraint : m_aConstraints)
{
// Left hand side
WriteConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, aConstraint.aLeftStr);
// Relation (operator)
WriteConstraintPart(CP_OPERATOR, nConstraint, OUString::number(aConstraint.nOperator));
// Right hand side
WriteConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, aConstraint.aRightStr);
nConstraint++;
}
}
// Write a single constraint part to the file
void SolverSettings::WriteConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString sValue)
{
// Empty named ranges cannot be written to the file (this corrupts MS files)
if (sValue.isEmpty())
return;
OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
m_pRangeName->insert(pNewEntry);
}
// Reads a single constraint part from its associated named range; returns false if the named
// range does not exist in the file
bool SolverSettings::ReadConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString& rValue)
{
OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
ScRangeData* pRangeData
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
if (pRangeData)
{
rValue = pRangeData->GetSymbol();
return true;
}
return false;
}
/* Reads the engine name parameter as informed in the file in the format used in LO.
* If only a MS engine is informed, then it is converted to a LO-equivalent engine
*/
void SolverSettings::ReadEngine()
{
if (!ReadParamValue(SP_LO_ENGINE, m_sLOEngineName, true))
{
// If no engine is defined, use CoinMP solver as default
m_sLOEngineName = "com.sun.star.comp.Calc.CoinMPSolver";
}
if (SolverNamesToExcelEngines.count(m_sLOEngineName))
{
// Find equivalent MS engine code
m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
}
}
// Write solver LO and MS-equivalent engine names
void SolverSettings::WriteEngine()
{
WriteParamValue(SP_LO_ENGINE, m_sLOEngineName, true);
// Find equivalent MS engine code
if (SolverNamesToExcelEngines.count(m_sLOEngineName))
{
m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
WriteParamValue(SP_MS_ENGINE, m_sMSEngineId);
}
}
// Assigns a new constraints vector
void SolverSettings::SetConstraints(std::vector<ModelConstraint> aConstraints)
{
m_aConstraints = std::move(aConstraints);
}
// Saves all solver settings into the file
void SolverSettings::SaveSolverSettings()
{
// Before saving, remove all existing named ranges related to the solver
DeleteAllNamedRanges();
WriteParamValue(SP_OBJ_CELL, m_sObjCell);
WriteParamValue(SP_OBJ_TYPE, OUString::number(m_eObjType));
WriteParamValue(SP_OBJ_VAL, m_sObjVal);
WriteParamValue(SP_VAR_CELLS, m_sVariableCells);
WriteConstraints();
WriteEngine();
sal_Int32 nConstrCount = m_aConstraints.size();
WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount));
WriteParamValue(SP_INTEGER, m_sInteger);
WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative);
WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
WriteParamValue(SP_TIMEOUT, m_sTimeout);
WriteParamValue(SP_ALGORITHM, m_sAlgorithm);
if (m_pDocShell)
m_pDocShell->SetDocumentModified();
}
/* Reads the current value of the parameter in the named range into rValue
* If the value does not exist, the rValue is left unchanged
* This is private because it is only used during initialization
* Returns true if the value exits; returns false otherwise
*/
bool SolverSettings::ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes)
{
OUString sRange = m_mNamedRanges.find(eParam)->second;
ScRangeData* pRangeData
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
if (pRangeData)
{
rValue = pRangeData->GetSymbol();
if (bRemoveQuotes)
ScGlobal::EraseQuotes(rValue, '"');
return true;
}
return false;
}
/* Writes a parameter value to the file as a named range.
* Argument bQuoted indicates whether the value should be enclosed with quotes or not (used
* for string expressions that must be enclosed with quotes)
*/
void SolverSettings::WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted)
{
// Empty parameters cannot be written to the file (this corrupts MS files)
// There's no problem if the parameter is missing both for LO and MS
if (sValue.isEmpty())
return;
if (bQuoted)
ScGlobal::AddQuotes(sValue, '"');
OUString sRange = m_mNamedRanges.find(eParam)->second;
ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
m_pRangeName->insert(pNewEntry);
}
void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions)
{
sal_Int32 nOptionsSize = aOptions.getLength();
auto pParamValues = aOptions.getArray();
for (auto i = 0; i < nOptionsSize; i++)
{
css::beans::PropertyValue aProp = aOptions[i];
OUString sLOParamName = aProp.Name;
// Only try to get the parameter value if it is an expected parameter name
if (SolverParamNames.count(sLOParamName))
{
TParamInfo aParamInfo;
aParamInfo = SolverParamNames.find(sLOParamName)->second;
SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
OUString sParamType = std::get<OUString>(aParamInfo[2]);
OUString sParamValue = GetParameter(eParamId);
if (sParamType == "int")
{
css::uno::Any nValue(sParamValue.toInt32());
pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, nValue,
css::beans::PropertyState_DIRECT_VALUE);
}
if (sParamType == "bool")
{
// The parameter NonNegative is a special case for MS compatibility
// It uses "1" for "true" and "2" for "false"
bool bTmpValue;
if (sLOParamName == "NonNegative")
bTmpValue = sParamValue == "1" ? true : false;
else
bTmpValue = sParamValue.toBoolean();
css::uno::Any bValue(bTmpValue);
pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, bValue,
css::beans::PropertyState_DIRECT_VALUE);
}
}
}
}
// Updates the object members related to solver engine options using aOptions info
void SolverSettings::SetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions)
{
sal_Int32 nOptionsSize = aOptions.getLength();
for (auto i = 0; i < nOptionsSize; i++)
{
css::beans::PropertyValue aProp = aOptions[i];
OUString sLOParamName = aProp.Name;
// Only try to set the parameter value if it is an expected parameter name
if (SolverParamNames.count(sLOParamName))
{
TParamInfo aParamInfo;
aParamInfo = SolverParamNames.find(sLOParamName)->second;
SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
OUString sParamType = std::get<OUString>(aParamInfo[2]);
if (sParamType == "int")
{
sal_Int32 nValue;
aProp.Value >>= nValue;
SetParameter(eParamId, OUString::number(nValue));
}
if (sParamType == "bool")
{
bool bValue;
aProp.Value >>= bValue;
if (sLOParamName == "NonNegative")
{
// The parameter NonNegative is a special case for MS compatibility
// It uses "1" for "true" and "2" for "false"
if (bValue)
SetParameter(eParamId, OUString::number(1));
else
SetParameter(eParamId, OUString::number(2));
}
else
{
SetParameter(eParamId, OUString::number(sal_Int32(bValue)));
}
}
}
}
}
// Deletes all named ranges in the current tab that are related to the solver (i.e. start with "solver_")
void SolverSettings::DeleteAllNamedRanges()
{
std::vector<ScRangeData*> aItemsToErase;
// Indices in m_pRangeName start at 1
for (size_t i = 1; i <= m_pRangeName->size(); ++i)
{
ScRangeData* pData = m_pRangeName->findByIndex(i);
if (pData && pData->GetName().startsWith("solver_"))
aItemsToErase.push_back(pData);
}
for (auto pItem : aItemsToErase)
m_pRangeName->erase(*pItem);
}
/* Sets all solver parameters to their default values and clear all constraints.
* This method only resets the object properties, but does not save changes to the
* document. To save changes, call SaveSolverSettings().
*/
void SolverSettings::ResetToDefaults()
{
m_sObjCell = "";
m_eObjType = ObjectiveType::OT_MAXIMIZE;
m_sObjVal = "";
m_sVariableCells = "";
m_sMSEngineId = "1";
// The default solver engine is the first implementation available
css::uno::Sequence<OUString> aEngineNames;
css::uno::Sequence<OUString> aDescriptions;
ScSolverUtil::GetImplementations(aEngineNames, aDescriptions);
m_sLOEngineName = aEngineNames[0];
// Default engine options
m_aEngineOptions = ScSolverUtil::GetDefaults(m_sLOEngineName);
// Default solver engine options
SetEngineOptions(m_aEngineOptions);
// Clear all constraints
m_aConstraints.clear();
}
} // namespace sc

View file

@ -655,4 +655,12 @@ void ScTable::CollectBroadcasterState(sc::BroadcasterState& rState) const
pCol->CollectBroadcasterState(rState);
}
std::shared_ptr<sc::SolverSettings> ScTable::GetSolverSettings()
{
if (!m_pSolverSettings)
m_pSolverSettings = std::make_shared<sc::SolverSettings>(*this);
return m_pSolverSettings;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -115,7 +115,6 @@
#include <scextopt.hxx>
#include <compiler.hxx>
#include <warnpassword.hxx>
#include <optsolver.hxx>
#include <sheetdata.hxx>
#include <table.hxx>
#include <tabprotection.hxx>
@ -2948,7 +2947,6 @@ ScDocShell::~ScDocShell()
m_pPaintLockData.reset();
m_pSolverSaveData.reset();
m_pSheetSaveData.reset();
m_pFormatSaveData.reset();
m_pOldAutoDBRange.reset();
@ -3112,11 +3110,6 @@ weld::Window* ScDocShell::GetActiveDialogParent()
return Application::GetDefDialogParent();
}
void ScDocShell::SetSolverSaveData( std::unique_ptr<ScOptSolverSave> pData )
{
m_pSolverSaveData = std::move(pData);
}
ScSheetSaveData* ScDocShell::GetSheetSaveData()
{
if (!m_pSheetSaveData)

View file

@ -54,7 +54,6 @@ class ScPaintLockData;
class ScChangeAction;
class ScImportOptions;
class ScDocShellModificator;
class ScOptSolverSave;
class ScSheetSaveData;
class ScFlatBoolRowSegments;
struct ScColWidthParam;
@ -103,7 +102,6 @@ class SC_DLLPUBLIC ScDocShell final: public SfxObjectShell, public SfxListener
std::unique_ptr<ScAutoStyleList> m_pAutoStyleList;
std::unique_ptr<ScPaintLockData> m_pPaintLockData;
std::unique_ptr<ScOptSolverSave> m_pSolverSaveData;
std::unique_ptr<ScSheetSaveData> m_pSheetSaveData;
std::unique_ptr<ScFormatSaveData> m_pFormatSaveData;
@ -412,8 +410,6 @@ public:
virtual HiddenInformation GetHiddenInformationState( HiddenInformation nStates ) override;
const ScOptSolverSave* GetSolverSaveData() const { return m_pSolverSaveData.get(); } // may be null
void SetSolverSaveData( std::unique_ptr<ScOptSolverSave> pData );
ScSheetSaveData* GetSheetSaveData();
ScFormatSaveData* GetFormatSaveData();

View file

@ -22,6 +22,7 @@
#include <address.hxx>
#include "anyrefdg.hxx"
#include "docsh.hxx"
#include <SolverSettings.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <string_view>
@ -44,49 +45,6 @@ protected:
DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
};
/// The dialog's content for a row, not yet parsed
struct ScOptConditionRow
{
OUString aLeftStr;
sal_uInt16 nOperator;
OUString aRightStr;
ScOptConditionRow() : nOperator(0) {}
bool IsDefault() const { return aLeftStr.isEmpty() && aRightStr.isEmpty() && nOperator == 0; }
};
/// All settings from the dialog, saved with the DocShell for the next call
class ScOptSolverSave
{
OUString maObjective;
bool mbMax;
bool mbMin;
bool mbValue;
OUString maTarget;
OUString maVariable;
std::vector<ScOptConditionRow> maConditions;
OUString maEngine;
css::uno::Sequence<css::beans::PropertyValue> maProperties;
public:
ScOptSolverSave( OUString aObjective, bool bMax, bool bMin, bool bValue,
OUString aTarget, OUString aVariable,
std::vector<ScOptConditionRow>&& rConditions,
OUString aEngine,
const css::uno::Sequence<css::beans::PropertyValue>& rProperties );
const OUString& GetObjective() const { return maObjective; }
bool GetMax() const { return mbMax; }
bool GetMin() const { return mbMin; }
bool GetValue() const { return mbValue; }
const OUString& GetTarget() const { return maTarget; }
const OUString& GetVariable() const { return maVariable; }
const std::vector<ScOptConditionRow>& GetConditions() const { return maConditions; }
const OUString& GetEngine() const { return maEngine; }
const css::uno::Sequence<css::beans::PropertyValue>& GetProperties() const
{ return maProperties; }
};
class ScSolverOptionsDialog;
class ScOptSolverDlg : public ScAnyRefDlgController
@ -110,7 +68,7 @@ private:
const SCTAB mnCurTab;
bool mbDlgLostFocus;
std::vector<ScOptConditionRow> maConditions;
std::vector<sc::ModelConstraint> m_aConditions;
tools::Long nScrollPos;
css::uno::Sequence<OUString> maImplNames;
@ -183,6 +141,7 @@ private:
std::unique_ptr<weld::Widget> m_xContents;
std::shared_ptr<ScSolverOptionsDialog> m_xOptDlg;
std::shared_ptr<sc::SolverSettings> m_pSolverSettings;
void Init(const ScAddress& rCursorPos);
bool CallSolver();
@ -192,6 +151,11 @@ private:
bool ParseRef( ScRange& rRange, const OUString& rInput, bool bAllowRange );
bool FindTimeout( sal_Int32& rTimeout );
void ShowError( bool bCondition, formula::RefEdit* pFocus );
void LoadSolverSettings();
void SaveSolverSettings();
bool IsEngineAvailable(std::u16string_view sEngineName);
static sc::ConstraintOperator OperatorIndexToConstraintOperator(sal_Int32 nIndex);
DECL_LINK( BtnHdl, weld::Button&, void );
DECL_LINK( DelBtnHdl, weld::Button&, void );

View file

@ -35,8 +35,9 @@
#include <solverutil.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <comphelper/sequence.hxx>
#include <optsolver.hxx>
#include <table.hxx>
#include <com/sun/star/sheet/SolverConstraint.hpp>
#include <com/sun/star/sheet/SolverConstraintOperator.hpp>
@ -132,23 +133,6 @@ IMPL_LINK(ScCursorRefEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
return formula::RefEdit::KeyInput(rKEvt);
}
ScOptSolverSave::ScOptSolverSave( OUString aObjective, bool bMax, bool bMin, bool bValue,
OUString aTarget, OUString aVariable,
std::vector<ScOptConditionRow>&& rConditions,
OUString aEngine,
const uno::Sequence<beans::PropertyValue>& rProperties ) :
maObjective( std::move(aObjective) ),
mbMax( bMax ),
mbMin( bMin ),
mbValue( bValue ),
maTarget( std::move(aTarget) ),
maVariable( std::move(aVariable) ),
maConditions( std::move(rConditions) ),
maEngine( std::move(aEngine) ),
maProperties( rProperties )
{
}
ScOptSolverDlg::ScOptSolverDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
ScDocShell* pDocSh, const ScAddress& aCursorPos)
: ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/solverdlg.ui", "SolverDialog")
@ -205,6 +189,7 @@ ScOptSolverDlg::ScOptSolverDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Windo
, m_xBtnResetAll(m_xBuilder->weld_button("resetall"))
, m_xResultFT(m_xBuilder->weld_label("result"))
, m_xContents(m_xBuilder->weld_widget("grid"))
, m_pSolverSettings(mrDoc.FetchTable(mnCurTab)->GetSolverSettings())
{
m_xEdObjectiveCell->SetReferences(this, m_xFtObjectiveCell.get());
m_xRBObjectiveCell->SetReferences(this, m_xEdObjectiveCell.get());
@ -336,33 +321,20 @@ void ScOptSolverDlg::Init(const ScAddress& rCursorPos)
// get available solver implementations
//! sort by descriptions?
ScSolverUtil::GetImplementations( maImplNames, maDescriptions );
bool bImplHasElements = maImplNames.hasElements();
const ScOptSolverSave* pOldData = mpDocShell->GetSolverSaveData();
if ( pOldData )
{
m_xEdObjectiveCell->SetRefString( pOldData->GetObjective() );
m_xRbMax->set_active( pOldData->GetMax() );
m_xRbMin->set_active( pOldData->GetMin() );
m_xRbValue->set_active( pOldData->GetValue() );
m_xEdTargetValue->SetRefString( pOldData->GetTarget() );
m_xEdVariableCells->SetRefString( pOldData->GetVariable() );
maConditions = pOldData->GetConditions();
maEngine = pOldData->GetEngine();
maProperties = pOldData->GetProperties();
}
else
{
m_xRbMax->set_active(true);
OUString aCursorStr;
if ( !mrDoc.GetRangeAtBlock( ScRange(rCursorPos), aCursorStr ) )
aCursorStr = rCursorPos.Format(ScRefFlags::ADDR_ABS, nullptr, mrDoc.GetAddressConvention());
m_xEdObjectiveCell->SetRefString( aCursorStr );
if ( bImplHasElements )
maEngine = maImplNames[0]; // use first implementation
}
// 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);
}
m_xEdObjectiveCell->GrabFocus();
mpEdActive = m_xEdObjectiveCell.get();
}
@ -371,23 +343,23 @@ void ScOptSolverDlg::ReadConditions()
{
for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
{
ScOptConditionRow aRowEntry;
sc::ModelConstraint aRowEntry;
aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText();
aRowEntry.aRightStr = mpRightEdit[nRow]->GetText();
aRowEntry.nOperator = mpOperator[nRow]->get_active();
aRowEntry.nOperator = OperatorIndexToConstraintOperator(mpOperator[nRow]->get_active());
tools::Long nVecPos = nScrollPos + nRow;
if ( nVecPos >= static_cast<tools::Long>(maConditions.size()) && !aRowEntry.IsDefault() )
maConditions.resize( nVecPos + 1 );
if ( nVecPos >= static_cast<tools::Long>(m_aConditions.size()) && !aRowEntry.IsDefault() )
m_aConditions.resize( nVecPos + 1 );
if ( nVecPos < static_cast<tools::Long>(maConditions.size()) )
maConditions[nVecPos] = aRowEntry;
if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
m_aConditions[nVecPos] = aRowEntry;
// remove default entries at the end
size_t nSize = maConditions.size();
while ( nSize > 0 && maConditions[ nSize-1 ].IsDefault() )
size_t nSize = m_aConditions.size();
while ( nSize > 0 && m_aConditions[ nSize-1 ].IsDefault() )
--nSize;
maConditions.resize( nSize );
m_aConditions.resize( nSize );
}
}
@ -395,20 +367,20 @@ void ScOptSolverDlg::ShowConditions()
{
for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
{
ScOptConditionRow aRowEntry;
sc::ModelConstraint aRowEntry;
tools::Long nVecPos = nScrollPos + nRow;
if ( nVecPos < static_cast<tools::Long>(maConditions.size()) )
aRowEntry = maConditions[nVecPos];
if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
aRowEntry = m_aConditions[nVecPos];
mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr );
mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr );
mpOperator[nRow]->set_active( aRowEntry.nOperator );
mpOperator[nRow]->set_active( aRowEntry.nOperator - 1);
}
// allow to scroll one page behind the visible or stored rows
tools::Long nVisible = nScrollPos + EDIT_ROW_COUNT;
tools::Long nMax = std::max( nVisible, static_cast<tools::Long>(maConditions.size()) );
tools::Long nMax = std::max( nVisible, static_cast<tools::Long>(m_aConditions.size()) );
m_xScrollBar->vadjustment_configure(nScrollPos, 0, nMax + EDIT_ROW_COUNT, 1,
EDIT_ROW_COUNT - 1, EDIT_ROW_COUNT);
@ -420,7 +392,7 @@ void ScOptSolverDlg::EnableButtons()
for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
{
tools::Long nVecPos = nScrollPos + nRow;
mpDelButton[nRow]->set_sensitive(nVecPos < static_cast<tools::Long>(maConditions.size()));
mpDelButton[nRow]->set_sensitive(nVecPos < static_cast<tools::Long>(m_aConditions.size()));
}
}
@ -503,6 +475,74 @@ bool ScOptSolverDlg::IsRefInputMode() const
return mpEdActive != nullptr;
}
// 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
void ScOptSolverDlg::SaveSolverSettings()
{
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());
// Objective type
if (m_xRbMax->get_active())
m_pSolverSettings->SetObjectiveType(sc::OT_MAXIMIZE);
else if (m_xRbMin->get_active())
m_pSolverSettings->SetObjectiveType(sc::OT_MINIMIZE);
else if (m_xRbValue->get_active())
m_pSolverSettings->SetObjectiveType(sc::OT_VALUE);
// Model constraints
m_pSolverSettings->SetConstraints(m_aConditions);
// Solver engine name
m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
// Solver engine options
m_pSolverSettings->SetEngineOptions(maProperties);
// Effectively save settings to file
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;
}
// Handler:
IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void)
@ -523,10 +563,7 @@ IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void)
{
// Close: write dialog settings to DocShell for subsequent calls
ReadConditions();
std::unique_ptr<ScOptSolverSave> pSave( new ScOptSolverSave(
m_xEdObjectiveCell->GetText(), m_xRbMax->get_active(), m_xRbMin->get_active(), m_xRbValue->get_active(),
m_xEdTargetValue->GetText(), m_xEdVariableCells->GetText(), std::vector(maConditions), maEngine, maProperties ) );
mpDocShell->SetSolverSaveData( std::move(pSave) );
SaveSolverSettings();
response(RET_CLOSE);
}
else
@ -560,11 +597,7 @@ IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void)
maProperties = ScSolverUtil::GetDefaults( maEngine );
// Clear all conditions (Constraints)
maConditions.clear();
std::unique_ptr<ScOptSolverSave> pEmpty( new ScOptSolverSave(
sEmpty, true, false, false,
sEmpty, sEmpty, std::vector(maConditions), maEngine, maProperties ) );
mpDocShell->SetSolverSaveData( std::move(pEmpty) );
m_aConditions.clear();
ShowConditions();
m_xRbMax->set_active(true);
@ -653,9 +686,9 @@ IMPL_LINK(ScOptSolverDlg, DelBtnHdl, weld::Button&, rBtn, void)
ReadConditions();
tools::Long nVecPos = nScrollPos + nRow;
if ( nVecPos < static_cast<tools::Long>(maConditions.size()) )
if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
{
maConditions.erase( maConditions.begin() + nVecPos );
m_aConditions.erase( m_aConditions.begin() + nVecPos );
ShowConditions();
if ( bHadFocus && !rBtn.get_sensitive() )
@ -761,6 +794,20 @@ IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit&, rEdit, void )
}
}
// 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;
}
}
void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus )
{
OUString aMessage = bCondition ? maConditionError : maInputError;
@ -870,13 +917,15 @@ bool ScOptSolverDlg::CallSolver() // return true -> close dialog after cal
uno::Sequence<sheet::SolverConstraint> aConstraints;
sal_Int32 nConstrPos = 0;
for ( const auto& rConstr : maConditions )
for ( const auto& rConstr : m_aConditions )
{
if ( !rConstr.aLeftStr.isEmpty() )
{
sheet::SolverConstraint aConstraint;
// order of list box entries must match enum values
aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(rConstr.nOperator);
// 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);
ScRange aLeftRange;
if ( !ParseRef( aLeftRange, rConstr.aLeftStr, true ) )