diff options
author | Rafael Lima <rafael.palma.lima@gmail.com> | 2024-03-10 22:26:15 +0100 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2024-03-20 11:16:34 +0100 |
commit | 6c97cd3691ea0f7d9e580b1c4ea0fca81d47758a (patch) | |
tree | b4579f0944eea0f8d10381db0eb06d94970989ad | |
parent | ce7af0dc500efce2975576b1cd401ffc7121c529 (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.hxx | 1 | ||||
-rw-r--r-- | sc/qa/uitest/data/tdf160104.ods | bin | 0 -> 18176 bytes | |||
-rw-r--r-- | sc/qa/uitest/solver/solver.py | 74 | ||||
-rw-r--r-- | sc/source/core/data/SolverSettings.cxx | 18 | ||||
-rw-r--r-- | sc/source/ui/miscdlgs/optsolver.cxx | 97 |
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 Binary files differnew file mode 100644 index 000000000000..a98340f80a50 --- /dev/null +++ b/sc/qa/uitest/data/tdf160104.ods 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) { |