Newer
Older
// ************************************************************************************************
//
// 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();

Wuttke, Joachim
committed
for (const auto& col : sortedByUsage)
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();
Matthias Puchner
committed
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);
Matthias Puchner
committed
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)) {

Wuttke, Joachim
committed
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 = new_particle;
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);
m_multiLayerForm->updateUnits();
}
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);
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);
emit aboutToRemoveItem(item);
layout->removeParticle(item);
return;
}
if (composition != nullptr) {
for (auto* c : m_multiLayerForm->findChildren<ParticleCompositionForm*>())
if (c->compositionItem() == composition)
c->onAboutToRemoveParticle(item);
emit aboutToRemoveItem(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(AbstractSelectionContainerForm* widget, int index,
widget->createContent();
m_multiLayerForm->updateUnits();
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)
Matthias Puchner
committed
void SampleEditorController::setMaterialValue(ItemWithMaterial* item, double newValue,
DoubleDescriptor d)
{
setDouble(newValue, d);
Matthias Puchner
committed
// -- notify all other users of this material (update values in the UI)
ASSERT(m_multiLayerForm);
for (auto* c : m_multiLayerForm->findChildren<MaterialInplaceForm*>())
Matthias Puchner
committed
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());
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);
}
m_multiLayerForm->updateUnits();
void SampleEditorController::selectInterference(InterferenceForm* widget, int newIndex)
widget->layoutItem()->interference().setCurrentIndex(newIndex);
widget->onInterferenceTypeChanged();
m_multiLayerForm->updateUnits();
Matthias Puchner
committed
// 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)) {
Matthias Puchner
committed
particleLayoutForm->updateDensityEnabling();
particleLayoutForm->updateDensityValue();
void SampleEditorController::setIntegrateOverXi(LatticeTypeSelectionForm* widget, bool newValue)
{
widget->interferenceItem()->setXiIntegration(newValue);
widget->onIntegrateOverXiChanged();