Skip to content
Snippets Groups Projects
SampleEditorController.cpp 12.3 KiB
Newer Older
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Views/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/Views/SampleDesigner/SampleEditorController.h"
#include "GUI/Models/LayerItem.h"
#include "GUI/Models/MesoCrystalItem.h"
#include "GUI/Models/MultiLayerItem.h"
#include "GUI/Models/ParticleCompositionItem.h"
#include "GUI/Models/ParticleCoreShellItem.h"
#include "GUI/Models/ParticleItem.h"
#include "GUI/Models/ParticleLayoutItem.h"
#include "GUI/Models/UIntDescriptor.h"
#include "GUI/Views/CommonWidgets/DoubleSpinBox.h"
#include "GUI/Views/SampleDesigner/LayerForm.h"
#include "GUI/Views/SampleDesigner/MesoCrystalForm.h"
#include "GUI/Views/SampleDesigner/MultiLayerForm.h"
#include "GUI/Views/SampleDesigner/ParticleCompositionForm.h"
#include "GUI/Views/SampleDesigner/ParticleCoreShellForm.h"
#include "GUI/Views/SampleDesigner/ParticleLayoutForm.h"
#include "GUI/Views/SampleDesigner/SampleEditorCommands.h"

SampleEditorController::SampleEditorController(MultiLayerItem* multi)
    : m_multiLayerItem(multi), m_multiLayerForm(nullptr)
{
}

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();

        for (auto col : sortedByUsage)
            if (col != above && col != below) {
                color = col;
                break;
            }
    }

    // - create new layer
    LayerItem* layer = m_multiLayerItem->addLayer(rowInMultiLayer);
    layer->setColor(color);

    ASSERT(m_multiLayerForm);
    m_multiLayerForm->onLayerAdded(layer);
    m_multiLayerForm->updateUnits();
}

void SampleEditorController::addLayout(LayerForm* layerItemWidget)
{
    ParticleLayoutItem* newLayoutItem =
        layerItemWidget->layerItem()->model()->insertItem<ParticleLayoutItem>(
            layerItemWidget->layerItem());
    layerItemWidget->onLayoutAdded(newLayoutItem);
    m_multiLayerForm->updateUnits();
}

void SampleEditorController::removeLayer(LayerItem* layerItem)
{
    m_undoStack.push(new CommandRemoveLayer(this, layerItem));
}

void SampleEditorController::removeLayout(LayerForm* layerItemWidget, ParticleLayoutItem* layout)
{
    layerItemWidget->onAboutToRemoveLayout(layout);
    layerItemWidget->layerItem()->removeLayout(layout);
}

void SampleEditorController::addParticle(ParticleLayoutItem* layoutItem, const QString& classname)
{
    SessionItem* newItem = nullptr;
    if (ItemCatalog::isFormFactorModelType(classname)) {
        ParticleItem* new_particle = layoutItem->model()->insertItem<ParticleItem>(layoutItem);
        new_particle->setFormFactor(classname);
        newItem = new_particle;
    } else
        newItem = layoutItem->model()->insertNewItem(classname, layoutItem);

    if (auto cs = dynamic_cast<ParticleCoreShellItem*>(newItem)) {
        cs->createCore();
        cs->createShell();
    }

    //  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)) {
        ParticleItem* new_particle =
            compositionItem->model()->insertItem<ParticleItem>(compositionItem);
        new_particle->setFormFactor(classname);
        newItem = new_particle;
    } else
        newItem = compositionItem->model()->insertNewItem(classname, compositionItem);

    if (auto cs = dynamic_cast<ParticleCoreShellItem*>(newItem)) {
        cs->createCore();
        cs->createShell();
    }

    //  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->setCore(nullptr);
        return;
    }

    if (particleCoreShell->core() == nullptr)
        particleCoreShell->createCore();

    particleCoreShell->core()->setFormFactor(formFactorModelType);
    widget->createCoreWidgets();
    m_multiLayerForm->updateUnits();
}

void SampleEditorController::setShellFormFactor(ParticleCoreShellForm* widget,
                                                const QString& formFactorModelType)
{
    auto* particleCoreShell = widget->coreShellItem();

    if (formFactorModelType.isEmpty()) {
        particleCoreShell->setShell(nullptr);
        return;
    }

    if (particleCoreShell->shell() == nullptr)
        particleCoreShell->createShell();

    particleCoreShell->shell()->setFormFactor(formFactorModelType);
    widget->createShellWidgets();
    m_multiLayerForm->updateUnits();
}

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);

        layout->removeParticle(item);
        return;
    }

    if (composition != nullptr) {
        for (auto c : m_multiLayerForm->findChildren<ParticleCompositionForm*>())
            if (c->compositionItem() == composition)
                c->onAboutToRemoveParticle(item);

        composition->removeParticle(item);
        return;
    }
}

void SampleEditorController::setDouble(double newValue, DoubleDescriptor d)
{
    m_undoStack.push(new CommandChangeValue(d.label, this, d.get(), newValue, d.path()));
    d.set(newValue);
}

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);
}

void SampleEditorController::setCurrentIndex(SelectionContainerForm* widget, int index,
                                             const AbstractSelectionDescriptor& d)
{
    d.setCurrentIndex(index);
    widget->createDoubleEdits();
    m_multiLayerForm->updateUnits();
QUndoStack* SampleEditorController::undoStack()
{
    return &m_undoStack;
}

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)
}

Matthias Puchner's avatar
Matthias Puchner committed
void SampleEditorController::setMaterialValue(ItemWithMaterial* /*item*/, double newValue,
                                              DoubleDescriptor d)
{
    setDouble(newValue, d);
    // #baLayerEditor notify all other users of this material (update values)
}

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)
            meso->model()->removeItem(meso->basisParticle());
        return;
    }

    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();
}

void SampleEditorController::selectInterference(ParticleLayoutItem* layoutItem, int newIndex)
{
    // #baLayerEditor ++ change the visibility of particle density (compare to
    // ParticleLayoutItem::updateDensityAppearance() and ParticleLayoutItem::updateDensityValue())
    layoutItem->interference().setCurrentIndex(newIndex);
    m_multiLayerForm->updateUnits();