sc/inc/SolverSettings.hxx              |    1 
 sc/qa/uitest/data/tdf160104.ods        |binary
 sc/qa/uitest/solver/solver.py          |   74 +++++++++++++++++++++++++
 sc/source/core/data/SolverSettings.cxx |   18 ++++++
 sc/source/ui/miscdlgs/optsolver.cxx    |   97 ++++++++++++++++++++++++++-------
 5 files changed, 171 insertions(+), 19 deletions(-)

New commits:
commit 6c97cd3691ea0f7d9e580b1c4ea0fca81d47758a
Author:     Rafael Lima <rafael.palma.l...@gmail.com>
AuthorDate: Sun Mar 10 22:26:15 2024 +0100
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Mar 20 11:16:34 2024 +0100

    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 <qui...@gmail.com>

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
Binary files /dev/null and b/sc/qa/uitest/data/tdf160104.ods 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)
 {

Reply via email to