// ************************************************************************************************ // // 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/InterferenceForm.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) } 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(InterferenceForm* widget, int newIndex) { // #baLayerEditor ++ change the visibility of particle density (compare to // ParticleLayoutItem::updateDensityAppearance() and ParticleLayoutItem::updateDensityValue()) widget->layoutItem()->interference().setCurrentIndex(newIndex); widget->onInterferenceTypeChanged(); m_multiLayerForm->updateUnits(); }