From 8f1debf14c118675b6c990329f1dcee521190c9c Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Fri, 26 Nov 2021 12:04:50 +0100
Subject: [PATCH] store backup values in ParamContainer instead of each leaf

---
 GUI/Model/Fit/ParameterTreeItems.cpp | 92 ++++++++++++++++++++++++----
 GUI/Model/Fit/ParameterTreeItems.h   | 16 +++--
 GUI/Model/Job/JobModel.cpp           | 12 +---
 GUI/Model/Job/JobModel.h             |  1 -
 GUI/Model/Job/ParameterTreeUtils.cpp | 20 +++---
 5 files changed, 101 insertions(+), 40 deletions(-)

diff --git a/GUI/Model/Fit/ParameterTreeItems.cpp b/GUI/Model/Fit/ParameterTreeItems.cpp
index 342aeb8c7b1..3136a9d8962 100644
--- a/GUI/Model/Fit/ParameterTreeItems.cpp
+++ b/GUI/Model/Fit/ParameterTreeItems.cpp
@@ -15,6 +15,21 @@
 #include "GUI/Model/Fit/ParameterTreeItems.h"
 #include "GUI/Model/Job/JobItem.h"
 #include "GUI/Model/Session/ModelPath.h"
+#include "GUI/Model/Session/SessionXML.h"
+#include "GUI/Util/DeserializationException.h"
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+using namespace GUI::Session::XML;
+
+namespace {
+namespace Tags {
+const QString BackupValues("BackupValues");
+const QString BackupValue("BackupValue");
+const QString Link("Link");
+const QString Value("Value");
+} // namespace Tags
+} // namespace
 
 // ----------------------------------------------------------------------------
 
@@ -31,8 +46,6 @@ ParameterItem::ParameterItem() : SessionItem(M_TYPE)
 {
     // Link to original PropertyItem in one of components of MultiLayerItem or InstrumentItem
     addProperty(P_LINK, QString());
-    // The back up value of PropertyItem to be able to reset parameter tuning tree to initial state
-    addProperty(P_BACKUP, 0.0);
 }
 
 //! Sets current value to the original PropertyItem of MultiLayerItem/InstrumentItem.
@@ -60,17 +73,9 @@ void ParameterItem::setLink(const QString& link)
     setItemValue(P_LINK, link);
 }
 
-//! Restore the value from backup and propagate it to the linked item.
-
-void ParameterItem::restoreFromBackup()
-{
-    double newValue = getItemValue(P_BACKUP).toDouble();
-    propagateValueToLink(newValue);
-}
-
-void ParameterItem::setBackup(double value)
+QString ParameterItem::link() const
 {
-    setItemValue(P_BACKUP, value);
+    return getItemValue(P_LINK).toString();
 }
 
 // ----------------------------------------------------------------------------
@@ -81,3 +86,66 @@ ParameterContainerItem::ParameterContainerItem() : SessionItem(M_TYPE)
     registerTag(T_CHILDREN, 0, -1, {ParameterLabelItem::M_TYPE});
     setDefaultTag(T_CHILDREN);
 }
+
+void ParameterContainerItem::writeNonSessionItems(QXmlStreamWriter* writer) const
+{
+    writeAttribute(writer, GUI::Session::XML::Version, int(1));
+
+    writer->writeStartElement(Tags::BackupValues);
+
+    for (auto v = m_backupValues.cbegin(); v != m_backupValues.cend(); v++) {
+        writer->writeEmptyElement(Tags::BackupValue);
+        writeAttribute(writer, Tags::Link, v.key());
+        writeAttribute(writer, Tags::Value, v.value());
+    }
+    writer->writeEndElement();
+}
+
+void ParameterContainerItem::readNonSessionItems(QXmlStreamReader* reader)
+{
+    m_backupValues.clear();
+    const int version = reader->attributes().value(GUI::Session::XML::Version).toInt();
+
+    if (version < 1)
+        throw DeserializationException::tooOld();
+
+    if (version > 1)
+        throw DeserializationException::tooNew();
+
+    while (reader->readNextStartElement()) {
+        if (reader->name().toString() == Tags::BackupValues) {
+
+            while (reader->readNextStartElement()) {
+                ASSERT(reader->name().toString() == Tags::BackupValue);
+                QString link;
+                double d;
+                readAttribute(reader, Tags::Link, &link);
+                readAttribute(reader, Tags::Value, &d);
+                m_backupValues[link] = d;
+                reader->skipCurrentElement();
+            }
+        }
+    }
+}
+
+void ParameterContainerItem::setBackupValue(const QString& link, double d)
+{
+    m_backupValues[link] = d;
+}
+
+void ParameterContainerItem::restoreBackupValue(SessionItem* item)
+{
+    ASSERT(item);
+    if (auto* parameter = dynamic_cast<ParameterItem*>(item))
+        if (m_backupValues.contains(parameter->link()))
+            parameter->propagateValueToLink(m_backupValues[parameter->link()]);
+
+    for (auto* child : item->children())
+        restoreBackupValue(child);
+}
+
+void ParameterContainerItem::restoreBackupValues()
+{
+    for (auto* child : children())
+        restoreBackupValue(child);
+}
diff --git a/GUI/Model/Fit/ParameterTreeItems.h b/GUI/Model/Fit/ParameterTreeItems.h
index f293defdd03..2490ba5b34b 100644
--- a/GUI/Model/Fit/ParameterTreeItems.h
+++ b/GUI/Model/Fit/ParameterTreeItems.h
@@ -35,7 +35,6 @@ public:
 class ParameterItem : public SessionItem {
 private:
     static constexpr auto P_LINK{"Link"};
-    static constexpr auto P_BACKUP{"Backup"};
 
 public:
     static constexpr auto M_TYPE{"Parameter"};
@@ -45,9 +44,7 @@ public:
     void propagateValueToLink(double newValue);
     SessionItem* linkedItem();
     void setLink(const QString& link);
-
-    void restoreFromBackup();
-    void setBackup(double value);
+    QString link() const;
 };
 
 //! The ParameterContainerItem is a top item to hold all ParameterItem, represents an entry
@@ -58,6 +55,17 @@ public:
     static constexpr auto M_TYPE{"Parameter Container"};
 
     ParameterContainerItem();
+
+    void writeNonSessionItems(QXmlStreamWriter* writer) const override;
+    void readNonSessionItems(QXmlStreamReader* reader) override;
+
+    void setBackupValue(const QString& link, double d);
+    void restoreBackupValues();
+
+private:
+    void restoreBackupValue(SessionItem* item);
+
+    QMap<QString, double> m_backupValues;
 };
 
 #endif // BORNAGAIN_GUI_MODEL_FIT_PARAMETERTREEITEMS_H
diff --git a/GUI/Model/Job/JobModel.cpp b/GUI/Model/Job/JobModel.cpp
index 72c433103d4..5be45711265 100644
--- a/GUI/Model/Job/JobModel.cpp
+++ b/GUI/Model/Job/JobModel.cpp
@@ -108,7 +108,7 @@ QVector<JobItem*> JobModel::jobItems() const
 //! restore instrument and sample model from backup for given JobItem
 void JobModel::restore(JobItem* jobItem)
 {
-    restoreItem(jobItem->parameterContainerItem());
+    jobItem->parameterContainerItem()->restoreBackupValues();
 }
 
 bool JobModel::hasUnfinishedJobs()
@@ -221,13 +221,3 @@ QString JobModel::generateJobName()
     }
     return QString("job") + QString::number(++glob_index);
 }
-
-void JobModel::restoreItem(SessionItem* item)
-{
-    ASSERT(item);
-    if (auto* parameter = dynamic_cast<ParameterItem*>(item))
-        parameter->restoreFromBackup();
-
-    for (auto* child : item->children())
-        restoreItem(child);
-}
diff --git a/GUI/Model/Job/JobModel.h b/GUI/Model/Job/JobModel.h
index 62fea049428..656f4ddc84f 100644
--- a/GUI/Model/Job/JobModel.h
+++ b/GUI/Model/Job/JobModel.h
@@ -61,7 +61,6 @@ public slots:
 
 private:
     QString generateJobName();
-    void restoreItem(SessionItem* item);
     JobQueueData* m_queue_data;
 };
 
diff --git a/GUI/Model/Job/ParameterTreeUtils.cpp b/GUI/Model/Job/ParameterTreeUtils.cpp
index c4339454f5c..85c4aef7f47 100644
--- a/GUI/Model/Job/ParameterTreeUtils.cpp
+++ b/GUI/Model/Job/ParameterTreeUtils.cpp
@@ -18,9 +18,9 @@
 #include "GUI/Model/Group/PropertyItem.h"
 #include "GUI/Model/Instrument/InstrumentItems.h"
 #include "GUI/Model/Job/JobItem.h"
-#include "GUI/Model/Session/ModelPath.h"
 #include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
+#include "GUI/Model/Session/ModelPath.h"
 #include "GUI/Util/Error.h"
 #include <QStack>
 
@@ -29,7 +29,7 @@ using boost::polymorphic_downcast;
 
 namespace {
 
-void handleItem(SessionItem* tree, const SessionItem* source)
+void handleItem(ParameterContainerItem* container, SessionItem* tree, const SessionItem* source)
 {
     if (tree->hasModelType<ParameterLabelItem>())
         tree->setDisplayName(source->itemName());
@@ -44,7 +44,7 @@ void handleItem(SessionItem* tree, const SessionItem* source)
         path = path.mid(firstSlash + 1);
         auto* parItem = polymorphic_downcast<ParameterItem*>(tree);
         parItem->setLink(path);
-        parItem->setBackup(sourceValue);
+        container->setBackupValue(parItem->link(), sourceValue);
         return;
     }
 
@@ -56,18 +56,18 @@ void handleItem(SessionItem* tree, const SessionItem* source)
             if (child->hasModelType<PropertyItem>()) {
                 if (child->value().type() == QVariant::Double) {
                     auto* branch = tree->model()->insertItem<ParameterItem>(tree);
-                    handleItem(branch, child);
+                    handleItem(container, branch, child);
                 }
 
             } else if (child->hasModelType<GroupItem>()) {
                 SessionItem* currentItem = dynamic_cast<GroupItem*>(child)->currentItem();
                 if (currentItem && currentItem->numberOfChildren() > 0) {
                     auto* branch = tree->model()->insertItem<ParameterLabelItem>(tree);
-                    handleItem(branch, currentItem);
+                    handleItem(container, branch, currentItem);
                 }
             } else {
                 auto* branch = tree->model()->insertItem<ParameterLabelItem>(tree);
-                handleItem(branch, child);
+                handleItem(container, branch, child);
             }
         }
     }
@@ -76,14 +76,10 @@ void handleItem(SessionItem* tree, const SessionItem* source)
 //! Populates ParameterContainer with ParameterItem's corresponding to all properties found
 //! in a source item.
 
-void populateParameterContainer(SessionItem* container, const SessionItem* source)
+void populateParameterContainer(ParameterContainerItem* container, const SessionItem* source)
 {
-    if (!container->hasModelType<ParameterContainerItem>())
-        throw Error("GUI::Model::ParameterTreeUtils::populateParameterContainer() -> Error. "
-                    "Not a ParameterContainerType.");
-
     auto* sourceLabel = container->model()->insertItem<ParameterLabelItem>(container);
-    handleItem(sourceLabel, source);
+    handleItem(container, sourceLabel, source);
 }
 
 } // namespace
-- 
GitLab