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