From 17ec14f66bdba3af750ec73fcd37960b5d80dc77 Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Tue, 30 Nov 2021 15:40:39 +0100
Subject: [PATCH] remove SessionModel from MaterialModel

---
 GUI/Model/Material/MaterialModel.cpp | 158 ++++++++++++++++-----------
 GUI/Model/Material/MaterialModel.h   |  30 +++--
 2 files changed, 115 insertions(+), 73 deletions(-)

diff --git a/GUI/Model/Material/MaterialModel.cpp b/GUI/Model/Material/MaterialModel.cpp
index 9a6eb9099db..b70ba9e0bb8 100644
--- a/GUI/Model/Material/MaterialModel.cpp
+++ b/GUI/Model/Material/MaterialModel.cpp
@@ -13,11 +13,18 @@
 //  ************************************************************************************************
 
 #include "GUI/Model/Material/MaterialModel.h"
+#include "Base/Util/Assert.h"
 #include "GUI/Model/Material/MaterialItem.h"
+#include "GUI/Model/Session/SessionXML.h"
+#include "GUI/Util/DeserializationException.h"
 #include <QColor>
 #include <QUuid>
+#include <QXmlStreamWriter>
 #include <random>
 
+using namespace GUI::Session::XML;
+
+
 namespace {
 
 QColor suggestMaterialColor(const QString& name)
@@ -42,45 +49,56 @@ QColor suggestMaterialColor(const QString& name)
 } // namespace
 
 
-MaterialModel::MaterialModel(QObject* parent)
-    : SessionModel(GUI::Session::XML::MaterialModelTag, parent)
+MaterialModel::MaterialModel(QObject* parent) : QObject(parent) {}
+
+MaterialModel::~MaterialModel()
 {
-    setObjectName(GUI::Session::XML::MaterialModelTag);
+    clear();
 }
 
 void MaterialModel::clear()
 {
-    SessionModel::clear();
+    qDeleteAll(m_materialItems);
+    m_materialItems.clear();
 }
 
-MaterialModel* MaterialModel::createCopy(SessionItem* parent)
+MaterialModel* MaterialModel::createCopy() const
 {
     auto* result = new MaterialModel();
-    result->initFrom(this, parent);
+
+    for (const auto* m : m_materialItems)
+        result->m_materialItems << new MaterialItem(*m);
+
     return result;
 }
 
 MaterialItem* MaterialModel::addRefractiveMaterial(const QString& name, double delta, double beta)
 {
-    auto* materialItem = insertItem<MaterialItem>();
+    auto* materialItem = new MaterialItem();
     materialItem->setMaterialName(name);
     materialItem->setColor(suggestMaterialColor(name));
     materialItem->setRefractiveIndex(delta, beta);
+    m_materialItems << materialItem;
+    emit materialAddedOrRemoved();
+
     return materialItem;
 }
 
 MaterialItem* MaterialModel::addSLDMaterial(const QString& name, double sld, double abs_term)
 {
-    auto* materialItem = insertItem<MaterialItem>();
+    auto* materialItem = new MaterialItem();
     materialItem->setMaterialName(name);
     materialItem->setColor(suggestMaterialColor(name));
     materialItem->setScatteringLengthDensity(complex_t(sld, abs_term));
+    m_materialItems << materialItem;
+    emit materialAddedOrRemoved();
+
     return materialItem;
 }
 
 MaterialItem* MaterialModel::materialFromName(const QString& name) const
 {
-    for (auto* materialItem : topItems<MaterialItem>())
+    for (auto* materialItem : m_materialItems)
         if (materialItem->materialName() == name)
             return materialItem;
 
@@ -89,33 +107,32 @@ MaterialItem* MaterialModel::materialFromName(const QString& name) const
 
 MaterialItem* MaterialModel::materialFromIdentifier(const QString& identifier) const
 {
-    for (auto* materialItem : topItems<MaterialItem>())
+    for (auto* materialItem : m_materialItems)
         if (materialItem->identifier() == identifier)
             return materialItem;
 
     return nullptr;
 }
 
-MaterialItem* MaterialModel::cloneMaterial(MaterialItem* material)
+MaterialItem* MaterialModel::insertCopy(const MaterialItem& material)
 {
-    if (!material)
-        return nullptr;
+    auto* newMaterial = new MaterialItem(material);
+    newMaterial->createNewIdentifier();
+    newMaterial->setMaterialName(material.materialName() + " (copy)");
+    m_materialItems << newMaterial;
+    emit materialAddedOrRemoved();
 
-    auto* clonedMaterial = copyItem(material, nullptr);
-    clonedMaterial->setIdentifier(QUuid::createUuid().toString());
-    clonedMaterial->setMaterialName(material->materialName() + " (copy)");
-    return clonedMaterial;
+    return newMaterial;
 }
 
-QVector<MaterialItem*> MaterialModel::materialItems() const
+const QVector<MaterialItem*>& MaterialModel::materialItems() const
 {
-    return topItems<MaterialItem>();
+    return m_materialItems;
 }
 
-
 MaterialItem* MaterialModel::findMaterialItem(const QString& identifier) const
 {
-    for (auto* m : materialItems())
+    for (auto* m : m_materialItems)
         if (m->identifier() == identifier)
             return m;
 
@@ -135,69 +152,84 @@ void MaterialModel::removeMaterial(const QString& identifier)
 
 void MaterialModel::removeMaterial(MaterialItem* materialItem)
 {
-    if (materialItem != nullptr)
-        if (const auto index = indexOfItem(materialItem); index.isValid())
-            removeRows(index.row(), 1, index.parent());
+    m_materialItems.removeAll(materialItem);
+    delete materialItem;
+    emit materialAddedOrRemoved();
 }
 
-void MaterialModel::readFrom(QXmlStreamReader* reader, MessageService* messageService /*= 0*/)
+void MaterialModel::writeContentTo(QXmlStreamWriter* writer) const
 {
-    // do not send added-notifications until completely read - otherwise partially
-    // initialized items will be notified
-    disconnect(this, &SessionModel::rowsInserted, this, &MaterialModel::onRowsChange);
-
-    SessionModel::readFrom(reader, messageService);
-
-    connect(this, &SessionModel::rowsInserted, this, &MaterialModel::onRowsChange);
-
-    for (auto* materialItem : materialItems()) {
-        materialItem->mapper()->setOnAnyChildChange(
-            [this, materialItem](SessionItem*) { materialChanged(materialItem); }, this);
+    writeAttribute(writer, GUI::Session::XML::Version, int(1));
+    for (auto m : m_materialItems) {
+        writer->writeStartElement("Material");
+        m->writeContentTo(writer);
+        writer->writeEndElement();
     }
 }
 
-void MaterialModel::initFrom(SessionModel* model, SessionItem* parent)
+void MaterialModel::readContentFrom(QXmlStreamReader* reader)
 {
-    // since initFrom does not send materialChanged signals, this has to be done manually.
+    const int version = reader->attributes().value(GUI::Session::XML::Version).toInt();
 
-    QStringList identifiersOfChangedMaterials;
-    auto* sourceModel = dynamic_cast<MaterialModel*>(model);
-    for (auto* destItem : materialItems()) {
-        auto* srcItem = sourceModel->findMaterialItem(destItem->identifier());
-        if (srcItem && (*srcItem != *destItem))
-            identifiersOfChangedMaterials << destItem->identifier();
-    }
+    if (version < 1)
+        throw DeserializationException::tooOld();
 
-    clear();
-    SessionModel::initFrom(model, parent);
+    if (version > 1)
+        throw DeserializationException::tooNew();
 
-    for (const auto& identifier : identifiersOfChangedMaterials)
-        emit materialChanged(findMaterialItem(identifier));
+    while (reader->readNextStartElement()) {
+        if (reader->name() == "Material") {
+            MaterialItem* m = new MaterialItem;
+            m->readContentFrom(reader);
+            m_materialItems << m;
+            reader->skipCurrentElement();
+        }
+    }
 }
 
-void MaterialModel::onRowsChange(const QModelIndex& parent, int, int)
+void MaterialModel::initFrom(const MaterialModel& from)
 {
-    // valid parent means not a material (which is top level item) but something below
-    if (parent.isValid())
-        return;
+    // update existing to new contents (do not delete and recreate to keep references valid)
+    for (auto* destItem : m_materialItems)
+        if (auto* fromItem = from.findMaterialItem(destItem->identifier()))
+            destItem->updateFrom(*fromItem);
 
-    for (auto* materialItem : materialItems()) {
-        materialItem->mapper()->unsubscribe(this);
+    bool anyAddedOrRemoved = false;
 
-        materialItem->mapper()->setOnAnyChildChange(
-            [this, materialItem](SessionItem*) { materialChanged(materialItem); }, this);
-    }
+    // remove non-existing
+    auto iter = m_materialItems.begin();
+    while (iter != m_materialItems.end())
+        if (!from.findMaterialItem((*iter)->identifier())) {
+            delete *iter;
+            iter = m_materialItems.erase(iter);
+            anyAddedOrRemoved = true;
+        } else
+            iter++;
+
+    // copy new ones
+    for (const auto* m : from.materialItems())
+        if (!findMaterialItem(m->identifier())) {
+            m_materialItems << new MaterialItem(*m);
+            anyAddedOrRemoved = true;
+        }
+
+    // copy order
+    QVector<MaterialItem*> tmp;
+    for (const auto* m : from.materialItems())
+        tmp << findMaterialItem(m->identifier());
+    m_materialItems = tmp;
+
+    if (anyAddedOrRemoved)
+        emit materialAddedOrRemoved();
 }
 
 bool MaterialModel::operator==(const MaterialModel& other) const
 {
-    const auto myItems = materialItems();
-    const auto otherItems = other.materialItems();
-    if (myItems.size() != otherItems.size())
+    if (m_materialItems.size() != other.m_materialItems.size())
         return false;
 
-    for (int i = 0; i < myItems.size(); i++)
-        if (*myItems[i] != *otherItems[i])
+    for (int i = 0; i < m_materialItems.size(); i++)
+        if (*m_materialItems[i] != *other.m_materialItems[i])
             return false;
 
     return true;
diff --git a/GUI/Model/Material/MaterialModel.h b/GUI/Model/Material/MaterialModel.h
index cc7d3676185..1156e4a4dfa 100644
--- a/GUI/Model/Material/MaterialModel.h
+++ b/GUI/Model/Material/MaterialModel.h
@@ -15,19 +15,23 @@
 #ifndef BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
 #define BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
 
-#include "GUI/Model/Session/SessionModel.h"
+#include <QObject>
+#include <QVector>
 
 class MaterialItem;
+class QXmlStreamReader;
+class QXmlStreamWriter;
 
-class MaterialModel : public SessionModel {
+class MaterialModel : public QObject {
     Q_OBJECT
 
 public:
     explicit MaterialModel(QObject* parent = nullptr);
+    ~MaterialModel();
 
-    void clear() override;
+    void clear();
 
-    MaterialModel* createCopy(SessionItem* parent = nullptr) override;
+    MaterialModel* createCopy() const;
 
     MaterialItem* addRefractiveMaterial(const QString& name, double delta, double beta);
     MaterialItem* addSLDMaterial(const QString& name, double sld, double abs_term);
@@ -35,10 +39,12 @@ public:
     MaterialItem* materialFromName(const QString& name) const;
     MaterialItem* materialFromIdentifier(const QString& identifier) const;
 
-    //! Returns clone of given material. Clone will get the same material identifier!
-    MaterialItem* cloneMaterial(MaterialItem* material);
+    //! Inserts a copy of the given material and returns the newly inserted item.
+    //!
+    //! The copy will have a different material identifier and a different name.
+    MaterialItem* insertCopy(const MaterialItem& material);
 
-    QVector<MaterialItem*> materialItems() const;
+    const QVector<MaterialItem*>& materialItems() const;
     MaterialItem* findMaterialItem(const QString& identifier) const;
 
     MaterialItem* defaultMaterial() const;
@@ -46,18 +52,22 @@ public:
     void removeMaterial(const QString& identifier);
     void removeMaterial(MaterialItem* materialItem);
 
-    void readFrom(QXmlStreamReader* reader, MessageService* messageService = nullptr) override;
-    void initFrom(SessionModel* model, SessionItem* parent) override;
+    void writeContentTo(QXmlStreamWriter* writer) const;
+    void readContentFrom(QXmlStreamReader* reader);
+
+    //! Copies the complete content, emitting signals for existing and changed materials
+    void initFrom(const MaterialModel& from);
 
     //! Compares for complete equality (same material identifiers, same order of materials,...)
     bool operator==(const MaterialModel& other) const;
     bool operator!=(const MaterialModel& other) const;
 
 signals:
+    void materialAddedOrRemoved();
     void materialChanged(MaterialItem* materialItem);
 
 private:
-    void onRowsChange(const QModelIndex& parent, int, int);
+    QVector<MaterialItem*> m_materialItems;
 };
 
 #endif // BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
-- 
GitLab