Skip to content
Snippets Groups Projects
SampleEditorController.cpp 15.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • //  ************************************************************************************************
    //
    //  BornAgain: simulate and fit reflection and scattering
    //
    
    //! @file      GUI/View/SampleDesigner/SampleEditorController.cpp
    
    //! @brief     Implements class SampleEditorController
    //!
    //! @homepage  http://www.bornagainproject.org
    //! @license   GNU General Public License v3 or higher (see COPYING)
    //! @copyright Forschungszentrum Jülich GmbH 2021
    //! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
    //
    //  ************************************************************************************************
    
    
    #include "GUI/View/SampleDesigner/SampleEditorController.h"
    
    #include "GUI/Model/Material/MaterialModel.h"
    
    #include "GUI/Model/Project/ProjectDocument.h"
    
    #include "GUI/Model/Sample/InterferenceItems.h"
    
    #include "GUI/Model/Sample/LayerItem.h"
    #include "GUI/Model/Sample/MesoCrystalItem.h"
    #include "GUI/Model/Sample/MultiLayerItem.h"
    #include "GUI/Model/Sample/ParticleCompositionItem.h"
    #include "GUI/Model/Sample/ParticleCoreShellItem.h"
    #include "GUI/Model/Sample/ParticleItem.h"
    #include "GUI/Model/Sample/ParticleLayoutItem.h"
    #include "GUI/Model/Types/UIntDescriptor.h"
    #include "GUI/View/Edit/DoubleSpinBox.h"
    #include "GUI/View/SampleDesigner/InterferenceForm.h"
    
    #include "GUI/View/SampleDesigner/LatticeTypeSelectionForm.h"
    
    #include "GUI/View/SampleDesigner/LayerForm.h"
    #include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
    #include "GUI/View/SampleDesigner/MesoCrystalForm.h"
    #include "GUI/View/SampleDesigner/MultiLayerForm.h"
    #include "GUI/View/SampleDesigner/ParticleCompositionForm.h"
    #include "GUI/View/SampleDesigner/ParticleCoreShellForm.h"
    #include "GUI/View/SampleDesigner/ParticleLayoutForm.h"
    #include "GUI/View/SampleDesigner/SampleEditorCommands.h"
    
    SampleEditorController::SampleEditorController(ProjectDocument* document, MultiLayerItem* multi)
        : m_multiLayerItem(multi), m_multiLayerForm(nullptr), m_document(document)
    
    {
    }
    
    void SampleEditorController::setMultiLayerForm(MultiLayerForm* view)
    {
        m_multiLayerForm = view;
    }
    
    MultiLayerForm* SampleEditorController::multiLayerForm() const
    {
        return m_multiLayerForm;
    }
    
    MultiLayerItem* SampleEditorController::multiLayerItem() const
    {
        return m_multiLayerItem;
    }
    
    void SampleEditorController::addLayer(LayerItem* before)
    {
        const int rowInMultiLayer = (before != nullptr) ? m_multiLayerItem->layers().indexOf(before)
                                                        : m_multiLayerItem->layers().size();
    
    
        // -- find a color for the new layer
        QColor color;
        auto unusedColors = LayerEditorUtils::predefinedLayerColors();
    
        for (auto* l : m_multiLayerItem->layers())
    
            unusedColors.removeAll(l->color());
        if (!unusedColors.isEmpty())
            color = unusedColors.first();
        else {
            // search for a color which has been used the less, and which is not the same as in the
            // layers above and below
            QMap<QString, int> usage;
    
            for (auto* l : m_multiLayerItem->layers())
    
                usage[l->color().name()] += 1;
    
            auto sortedByUsage = LayerEditorUtils::predefinedLayerColors();
            std::stable_sort(
                sortedByUsage.begin(), sortedByUsage.end(),
                [&](const QColor& a, const QColor& b) { return usage[a.name()] < usage[b.name()]; });
    
    
            const QColor above = (rowInMultiLayer > 0)
                                     ? m_multiLayerItem->layers()[rowInMultiLayer - 1]->color()
                                     : QColor();
            const QColor below = (rowInMultiLayer < m_multiLayerItem->layers().size())
                                     ? m_multiLayerItem->layers()[rowInMultiLayer]->color()
                                     : QColor();
    
    
                if (col != above && col != below) {
                    color = col;
                    break;
                }
        }
    
        // - create new layer
    
        LayerItem* layer = m_multiLayerItem->addLayer(rowInMultiLayer);
    
        layer->setMaterialModel(materialModel());
        layer->setMaterial(materialModel()->defaultMaterial());
    
        layer->setColor(color);
    
    
        ASSERT(m_multiLayerForm);
        m_multiLayerForm->onLayerAdded(layer);
    
        m_multiLayerForm->updateUnits();
    
    }
    
    void SampleEditorController::addLayout(LayerForm* layerItemWidget)
    {
    
        auto* newLayoutItem = layerItemWidget->layerItem()->addLayout();
    
        layerItemWidget->onLayoutAdded(newLayoutItem);
    
        m_multiLayerForm->updateUnits();
    
    
        for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
            layoutForms->updateTitle(layerItemWidget->layerItem());
    
    }
    
    void SampleEditorController::removeLayer(LayerItem* layerItem)
    {
        m_undoStack.push(new CommandRemoveLayer(this, layerItem));
    }
    
    void SampleEditorController::removeLayout(LayerForm* layerItemWidget, ParticleLayoutItem* layout)
    {
    
        emit aboutToRemoveItem(layout);
    
        layerItemWidget->onAboutToRemoveLayout(layout);
        layerItemWidget->layerItem()->removeLayout(layout);
    
    
        for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
            layoutForms->updateTitle(layerItemWidget->layerItem());
    
    }
    
    void SampleEditorController::addParticle(ParticleLayoutItem* layoutItem, const QString& classname)
    {
        SessionItem* newItem = nullptr;
        if (ItemCatalog::isFormFactorModelType(classname)) {
    
            auto* new_particle = layoutItem->model()->insertItem<ParticleItem>(layoutItem);
    
            new_particle->setMaterialModel(materialModel());
    
            new_particle->setFormFactor(classname);
    
            new_particle->setMaterial(materialModel()->defaultMaterial());
    
            newItem = new_particle;
        } else
            newItem = layoutItem->model()->insertNewItem(classname, layoutItem);
    
    
        if (auto* cs = dynamic_cast<ParticleCoreShellItem*>(newItem)) {
    
            cs->createCore(materialModel());
            cs->createShell(materialModel());
    
        //  search for particle layout widget for notification
        ASSERT(m_multiLayerForm);
    
        for (auto* w : m_multiLayerForm->findChildren<ParticleLayoutForm*>())
    
            if (w->layoutItem() == layoutItem)
                w->onParticleAdded(newItem);
    
        m_multiLayerForm->updateUnits();
    
    }
    
    void SampleEditorController::addParticle(ParticleCompositionItem* compositionItem,
                                             const QString& classname)
    {
        SessionItem* newItem = nullptr;
        if (ItemCatalog::isFormFactorModelType(classname)) {
    
            auto* new_particle = compositionItem->model()->insertItem<ParticleItem>(compositionItem);
    
            new_particle->setMaterialModel(materialModel());
    
            new_particle->setFormFactor(classname);
    
            new_particle->setMaterial(materialModel()->defaultMaterial());
    
            newItem = compositionItem->model()->insertNewItem(classname, compositionItem);
    
            if (auto* p = dynamic_cast<ItemWithMaterial*>(newItem)) {
                p->setMaterialModel(materialModel());
                p->setMaterial(materialModel()->defaultMaterial());
            }
        }
    
        if (auto* cs = dynamic_cast<ParticleCoreShellItem*>(newItem)) {
    
            cs->createCore(materialModel());
            cs->createShell(materialModel());
    
        //  search for composition widget for notification
        ASSERT(m_multiLayerForm);
    
        for (auto* c : m_multiLayerForm->findChildren<ParticleCompositionForm*>())
    
            if (c->compositionItem() == compositionItem)
                c->onParticleAdded(newItem);
    
        m_multiLayerForm->updateUnits();
    
    }
    
    void SampleEditorController::setCoreFormFactor(ParticleCoreShellForm* widget,
    
                                                   const QString& formfactorModelType)
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (formfactorModelType.isEmpty()) {
    
            particleCoreShell->clearCore();
    
            return;
        }
    
        if (particleCoreShell->core() == nullptr)
    
            particleCoreShell->createCore(materialModel());
    
        particleCoreShell->core()->setFormFactor(formfactorModelType);
    
        widget->createCoreWidgets();
    
        m_multiLayerForm->updateUnits();
    
        emit modified();
    
    }
    
    void SampleEditorController::setShellFormFactor(ParticleCoreShellForm* widget,
    
                                                    const QString& formfactorModelType)
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (formfactorModelType.isEmpty()) {
    
            particleCoreShell->clearShell();
    
            return;
        }
    
        if (particleCoreShell->shell() == nullptr)
    
            particleCoreShell->createShell(materialModel());
    
        particleCoreShell->shell()->setFormFactor(formfactorModelType);
    
        widget->createShellWidgets();
    
        m_multiLayerForm->updateUnits();
    
        emit modified();
    
    }
    
    void SampleEditorController::removeParticle(ItemWithParticles* item)
    {
        ASSERT(m_multiLayerForm);
        //  search for particle layout parent (the first which can be found)
        ParticleLayoutItem* layout = nullptr;
        ParticleCompositionItem* composition = nullptr;
        SessionItem* parent = item->parent();
        do {
            layout = dynamic_cast<ParticleLayoutItem*>(parent);
            composition = dynamic_cast<ParticleCompositionItem*>(parent);
            parent = parent->parent();
        } while (parent && layout == nullptr && composition == nullptr);
    
        if (layout != nullptr) {
    
            for (auto* c : m_multiLayerForm->findChildren<ParticleLayoutForm*>())
    
                if (c->layoutItem() == layout)
                    c->onAboutToRemoveParticle(item);
    
    
            emit aboutToRemoveItem(item);
    
            layout->removeParticle(item);
    
            emit modified();
    
            for (auto* c : m_multiLayerForm->findChildren<ParticleCompositionForm*>())
    
                if (c->compositionItem() == composition)
                    c->onAboutToRemoveParticle(item);
    
    
            emit aboutToRemoveItem(item);
    
            composition->removeParticle(item);
    
            emit modified();
    
            return;
        }
    }
    
    void SampleEditorController::setDouble(double newValue, DoubleDescriptor d)
    {
        m_undoStack.push(new CommandChangeValue(d.label, this, d.get(), newValue, d.path()));
        d.set(newValue);
    
        emit modified();
    
    }
    
    void SampleEditorController::setDoubleFromUndo(double newValue, const QString& path)
    {
        ASSERT(m_multiLayerForm);
    
        DoubleSpinBox* spinBox = nullptr;
    
        for (auto* s : m_multiLayerForm->findChildren<DoubleSpinBox*>()) {
    
            if (s->valueDescriptor().path() == path) {
                spinBox = s;
                break;
            }
        }
    
        if (!spinBox)
            return;
    
        spinBox->valueDescriptor().set(newValue);
    
        m_multiLayerForm->ensureVisible(spinBox);
        QSignalBlocker b(spinBox);
        spinBox->setBaseValue(newValue);
        spinBox->setFocus();
        spinBox->selectAll();
    
    }
    
    void SampleEditorController::setInt(int newValue, UIntDescriptor d)
    {
        d.set(newValue);
    
        emit modified();
    
    void SampleEditorController::setCurrentIndex(AbstractSelectionContainerForm* widget, int index,
    
                                                 const AbstractSelectionDescriptor& d)
    
    {
        d.setCurrentIndex(index);
    
        widget->createContent();
    
        m_multiLayerForm->updateUnits();
    
        emit modified();
    
    QUndoStack* SampleEditorController::undoStack()
    {
        return &m_undoStack;
    }
    
    
    MaterialModel* SampleEditorController::materialModel() const
    {
    
        return m_document->materialModel();
    }
    
    ProjectDocument* SampleEditorController::projectDocument() const
    {
        return m_document;
    
    void SampleEditorController::selectMaterial(ItemWithMaterial* item,
                                                const QString& newMaterialIdentifier)
    {
        item->setMaterial(newMaterialIdentifier);
    
        //  update Layer title
        ASSERT(m_multiLayerForm);
    
        for (auto* c : m_multiLayerForm->findChildren<LayerForm*>())
    
            if (c->layerItem() == item)
                c->updateTitle();
    
        // #baLayerEditor notify all material users (update link info)
    
        emit modified();
    
    void SampleEditorController::setMaterialValue(ItemWithMaterial* item, double newValue,
    
                                                  DoubleDescriptor d)
    {
        setDouble(newValue, d);
    
    
        // -- notify all other users of this material (update values in the UI)
        ASSERT(m_multiLayerForm);
    
        for (auto* c : m_multiLayerForm->findChildren<MaterialInplaceForm*>())
    
            if (c->itemWithMaterial() != item
                && c->itemWithMaterial()->materialIdentifier() == item->materialIdentifier())
                c->updateValues();
    
    void SampleEditorController::setDensityRelatedValue(InterferenceItem* interferenceItem,
                                                        double newValue, DoubleDescriptor d)
    {
        setDouble(newValue, d);
    
        // -- notify the containing particle layout about changed value
        auto* particlelayoutItem = dynamic_cast<ParticleLayoutItem*>(interferenceItem->parent());
        if (!particlelayoutItem)
            return;
    
        // -- notify the containing particle layout UI about changed value
        ASSERT(m_multiLayerForm);
        for (auto* c : m_multiLayerForm->findChildren<ParticleLayoutForm*>())
            if (c->layoutItem() == particlelayoutItem) {
                c->updateDensityValue();
                break;
            }
    }
    
    
    void SampleEditorController::onStartingToMoveLayer()
    {
        ASSERT(m_multiLayerForm);
        m_multiLayerForm->showAddLayerButtons(false);
    }
    
    void SampleEditorController::onStoppedToMoveLayer(QWidget* widgetToMove,
                                                      QWidget* moveAboveThisWidget)
    {
        ASSERT(m_multiLayerForm);
        m_multiLayerForm->showAddLayerButtons(true);
        const auto* moveAboveThisLayerForm = m_multiLayerForm->findNextLayerForm(moveAboveThisWidget);
        auto* itemToMove = dynamic_cast<LayerForm*>(widgetToMove)->layerItem();
        auto* moveBeforeThisItem =
            moveAboveThisLayerForm != nullptr ? moveAboveThisLayerForm->layerItem() : nullptr;
    
        m_multiLayerItem->moveLayer(itemToMove, moveBeforeThisItem);
        m_multiLayerForm->onLayerMoved(itemToMove);
    
        // #baLayerEditor: tab order!
    
    }
    
    void SampleEditorController::setMesoCrystalBasis(MesoCrystalForm* widget, const QString& classname)
    {
        auto* meso = widget->mesoCrystalItem();
    
        if (classname.isEmpty()) {
    
            if (meso->basisParticle() != nullptr) {
                emit aboutToRemoveItem(meso->basisParticle());
    
                meso->model()->removeItem(meso->basisParticle());
    
        if (classname == ParticleCompositionItem::M_TYPE)
    
            meso->createBasis<ParticleCompositionItem>();
    
        else if (classname == MesoCrystalItem::M_TYPE)
    
            meso->createBasis<MesoCrystalItem>();
    
        else if (classname == ParticleCoreShellItem::M_TYPE)
    
            meso->createBasis<ParticleCoreShellItem>();
    
        else if (ItemCatalog::isFormFactorModelType(classname)) {
    
            auto* particle = dynamic_cast<ParticleItem*>(meso->basisParticle());
    
            if (particle == nullptr)
                particle = meso->createBasis<ParticleItem>();
    
            particle->setFormFactor(classname);
        }
    
        widget->createBasisWidgets();
    
        m_multiLayerForm->updateUnits();
    
        emit modified();
    
    void SampleEditorController::selectInterference(InterferenceForm* widget, int newIndex)
    
        widget->layoutItem()->interference().setCurrentIndex(newIndex);
        widget->onInterferenceTypeChanged();
    
        m_multiLayerForm->updateUnits();
    
    
        // Disable/enable total density property in the particle layout, depending on type of
        // interference function.
        QWidget* parent = widget->parentWidget();
        while (parent != nullptr && dynamic_cast<ParticleLayoutForm*>(parent) == nullptr)
            parent = parent->parentWidget();
    
    
        if (auto* particleLayoutForm = dynamic_cast<ParticleLayoutForm*>(parent)) {
    
            particleLayoutForm->updateDensityEnabling();
    
            particleLayoutForm->updateDensityValue();
    
    
    void SampleEditorController::setIntegrateOverXi(LatticeTypeSelectionForm* widget, bool newValue)
    {
        widget->interferenceItem()->setXiIntegration(newValue);
        widget->onIntegrateOverXiChanged();
    
        emit modified();