summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Lima <rafael.palma.lima@gmail.com>2024-03-10 22:26:15 +0100
committerTomaž Vajngerl <quikee@gmail.com>2024-03-20 11:16:34 +0100
commit6c97cd3691ea0f7d9e580b1c4ea0fca81d47758a (patch)
treeb4579f0944eea0f8d10381db0eb06d94970989ad
parentce7af0dc500efce2975576b1cd401ffc7121c529 (diff)
tdf#160104 Do not mark file as modified if nothing changed in the Solver dialog
Change-Id: I1bef38a21179bb725c7fb7a08fe90309d26239ee Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164616 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r--sc/inc/SolverSettings.hxx1
-rw-r--r--sc/qa/uitest/data/tdf160104.odsbin0 -> 18176 bytes
-rw-r--r--sc/qa/uitest/solver/solver.py74
-rw-r--r--sc/source/core/data/SolverSettings.cxx18
-rw-r--r--sc/source/ui/miscdlgs/optsolver.cxx97
5 files changed, 171 insertions, 19 deletions
diff --git a/sc/inc/SolverSettings.hxx b/sc/inc/SolverSettings.hxx
index 985e8d30f796..0f01f1020356 100644
--- a/sc/inc/SolverSettings.hxx
+++ b/sc/inc/SolverSettings.hxx
@@ -299,6 +299,7 @@ public:
SC_DLLPUBLIC void SaveSolverSettings();
SC_DLLPUBLIC void ResetToDefaults();
+ SC_DLLPUBLIC bool TabHasSolverModel();
};
} // namespace sc
diff --git a/sc/qa/uitest/data/tdf160104.ods b/sc/qa/uitest/data/tdf160104.ods
new file mode 100644
index 000000000000..a98340f80a50
--- /dev/null
+++ b/sc/qa/uitest/data/tdf160104.ods
Binary files differ
diff --git a/sc/qa/uitest/solver/solver.py b/sc/qa/uitest/solver/solver.py
index faabdfb1ce50..2a164b90f6c5 100644
--- a/sc/qa/uitest/solver/solver.py
+++ b/sc/qa/uitest/solver/solver.py
@@ -58,4 +58,78 @@ class solver(UITestCase):
#verify
self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 1).getValue(), 400)
+
+ # Tests the isModified property on a blank Calc file
+ def test_tdf160104_blank_file(self):
+ with self.ui_test.create_doc_in_start_center("calc") as calc_doc:
+ self.assertFalse(calc_doc.isModified())
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:SolverDialog", close_button="") as xDialog:
+ xCloseBtn = xDialog.getChild("close")
+ xCloseBtn.executeAction("CLICK", ())
+
+ # Here isModified needs to be False because the dialog was opened and closed with no changes
+ self.assertFalse(calc_doc.isModified())
+
+ # Now open the dialog again and make some changes
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:SolverDialog", close_button="") as xDialog:
+ xCloseBtn = xDialog.getChild("close")
+ xMin = xDialog.getChild("min")
+ xChangeEdit = xDialog.getChild("changeedit")
+
+ # Click the Minimize option and change variable cells
+ xMin.executeAction("CLICK", ())
+ xChangeEdit.executeAction("TYPE", mkPropertyValues({"TEXT": "$A$1:$A$10"}))
+ xCloseBtn.executeAction("CLICK", ())
+
+ # Here isModified needs to be True because changes were made to the dialog
+ self.assertTrue(calc_doc.isModified())
+
+
+ # Tests the isModified property on an existing file that contains a solver model
+ def test_tdf160104_with_file(self):
+ with self.ui_test.load_file(get_url_for_data_file("tdf160104.ods")) as calc_doc:
+ self.assertFalse(calc_doc.isModified())
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:SolverDialog", close_button="") as xDialog:
+ xTargetEdit = xDialog.getChild("targetedit")
+ xMax = xDialog.getChild("max")
+ xChangeEdit = xDialog.getChild("changeedit")
+ xRef1Edit = xDialog.getChild("ref1edit")
+ xVal1Edit = xDialog.getChild("val1edit")
+ xOp1List = xDialog.getChild("op1list")
+ xRef2Edit = xDialog.getChild("ref2edit")
+ xVal2Edit = xDialog.getChild("val2edit")
+ xOp2List = xDialog.getChild("op2list")
+
+ # Checks whether the solver model was loaded correctly
+ self.assertEqual("$F$2", get_state_as_dict(xTargetEdit)["Text"])
+ self.assertEqual("true", get_state_as_dict(xMax)["Checked"])
+ self.assertEqual("$D$2:$D$11", get_state_as_dict(xChangeEdit)["Text"])
+ self.assertEqual("$F$5", get_state_as_dict(xRef1Edit)["Text"])
+ self.assertEqual("$F$8", get_state_as_dict(xVal1Edit)["Text"])
+ self.assertEqual("≤", get_state_as_dict(xOp1List)["SelectEntryText"])
+ self.assertEqual("$D$2:$D$11", get_state_as_dict(xRef2Edit)["Text"])
+ self.assertEqual("", get_state_as_dict(xVal2Edit)["Text"])
+ self.assertEqual("Binary", get_state_as_dict(xOp2List)["SelectEntryText"])
+
+ # Closes the dialog without making changes
+ xCloseBtn = xDialog.getChild("close")
+ xCloseBtn.executeAction("CLICK", ())
+
+ # Here isModified needs to be False no changes were made to the solver dialog
+ self.assertFalse(calc_doc.isModified())
+
+ # Now open the dialog again and make some changes
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:SolverDialog", close_button="") as xDialog:
+ xCloseBtn = xDialog.getChild("close")
+ xMin = xDialog.getChild("min")
+ xChangeEdit = xDialog.getChild("changeedit")
+
+ # Click the Minimize option and change variable cells
+ xMin.executeAction("CLICK", ())
+ xChangeEdit.executeAction("TYPE", mkPropertyValues({"TEXT": "$E$2:$E$11"}))
+ xCloseBtn.executeAction("CLICK", ())
+
+ # Here isModified needs to be True because changes were made to the Solver dialog
+ self.assertTrue(calc_doc.isModified())
+
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sc/source/core/data/SolverSettings.cxx b/sc/source/core/data/SolverSettings.cxx
index dd618d1e868b..806ca85bbd6e 100644
--- a/sc/source/core/data/SolverSettings.cxx
+++ b/sc/source/core/data/SolverSettings.cxx
@@ -776,4 +776,22 @@ void SolverSettings::ResetToDefaults()
m_aConstraints.clear();
}
+/* Returns true if the current sheet already has a solver model.
+ This is determined by checking if the current tab has the SP_OBJ_CELL named range
+ which is associated with solver models.
+ Note that the named ranges are only created after SaveSolverSettings is called,
+ so before it is called, no solver-related named ranges exist.
+*/
+bool SolverSettings::TabHasSolverModel()
+{
+ // Check if the named range for the objective value exists in the sheet
+ const auto iter = m_mNamedRanges.find(SP_OBJ_CELL);
+ OUString sRange = iter->second;
+ ScRangeData* pRangeData
+ = m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
+ if (pRangeData)
+ return true;
+ return false;
+}
+
} // namespace sc
diff --git a/sc/source/ui/miscdlgs/optsolver.cxx b/sc/source/ui/miscdlgs/optsolver.cxx
index b6de0f3bcae8..ba3a2ac070e8 100644
--- a/sc/source/ui/miscdlgs/optsolver.cxx
+++ b/sc/source/ui/miscdlgs/optsolver.cxx
@@ -508,34 +508,93 @@ void ScOptSolverDlg::LoadSolverSettings()
m_pSolverSettings->GetEngineOptions(maProperties);
}
-// Set solver settings and save them
+// Set solver settings and save them to the file
+// But first, checks if the settings have changed
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());
+ // 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;
- // 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);
+ // 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;
else if (m_xRbValue->get_active())
- m_pSolverSettings->SetObjectiveType(sc::OT_VALUE);
+ aType = sc::OT_VALUE;
- // Model constraints
- m_pSolverSettings->SetConstraints(m_aConditions);
+ if (m_pSolverSettings->GetObjectiveType() != aType)
+ bModified = true;
- // Solver engine name
- m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
+ // 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;
- // Solver engine options
- m_pSolverSettings->SetEngineOptions(maProperties);
+ if (bModified)
+ break;
+ }
+ }
- // Effectively save settings to file
- m_pSolverSettings->SaveSolverSettings();
-}
+ // 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)
{