BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
SampleEditorController.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/SampleDesigner/SampleEditorController.cpp
6 //! @brief Implements class SampleEditorController
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
39 #include "Sample/HardParticle/Cylinder.h"
40 
42  : m_sampleItem(multi)
43  , m_sampleForm(nullptr)
44  , m_document(document)
45 {
46 }
47 
49 {
50  m_sampleForm = view;
51 }
52 
54 {
55  return m_sampleForm;
56 }
57 
59 {
60  return m_sampleItem;
61 }
62 
64 {
65  const int newIndex = (before != nullptr) ? m_sampleItem->layers().indexOf(before)
66  : m_sampleItem->layers().size();
67  m_undoStack.push(new CommandAddLayer(this, newIndex));
68 }
69 
71 {
72 
73  // -- find a color for the new layer
74  QColor color;
75  auto unusedColors = LayerEditorUtils::predefinedLayerColors();
76  for (auto* l : m_sampleItem->layers())
77  unusedColors.removeAll(l->color());
78  if (!unusedColors.isEmpty())
79  color = unusedColors.first();
80  else {
81  // search for a color which has been used the less, and which is not the same as in the
82  // layers above and below
83  QMap<QString, int> usage;
84  for (auto* l : m_sampleItem->layers())
85  usage[l->color().name()] += 1;
86 
87  auto sortedByUsage = LayerEditorUtils::predefinedLayerColors();
88  std::stable_sort(
89  sortedByUsage.begin(), sortedByUsage.end(),
90  [&](const QColor& a, const QColor& b) { return usage[a.name()] < usage[b.name()]; });
91 
92 
93  const QColor above =
94  (atIndex > 0) ? m_sampleItem->layers()[atIndex - 1]->color() : QColor();
95  const QColor below = (atIndex < m_sampleItem->layers().size())
96  ? m_sampleItem->layers()[atIndex]->color()
97  : QColor();
98 
99  for (const auto& col : sortedByUsage)
100  if (col != above && col != below) {
101  color = col;
102  break;
103  }
104  }
105 
106  // - create new layer
107  LayerItem* layer = m_sampleItem->addLayer(atIndex);
108  layer->setMaterial(materialItems()->defaultMaterial());
109  layer->setColor(color);
110 
111  ASSERT(m_sampleForm);
112  m_sampleForm->onLayerAdded(layer);
114 
115  emit modified();
116 
117  // expand the new layer's form for better workflow
118  for (auto* c : m_sampleForm->findChildren<LayerForm*>())
119  if (c->layerItem() == layer)
120  c->expand();
121 }
122 
124 {
125  auto* newLayoutItem = layerItemWidget->layerItem()->addLayout();
126  layerItemWidget->onLayoutAdded(newLayoutItem);
128 
129  for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
130  layoutForms->updateTitle(layerItemWidget->layerItem());
131 
132  emit modified();
133 }
134 
136 {
137  m_undoStack.push(new CommandRemoveLayer(this, layerItem));
138 }
139 
141 {
142  auto* layer = m_sampleItem->layers()[atIndex];
143  emit aboutToRemoveItem(layer);
145  m_sampleItem->removeLayer(layer);
147  emit modified();
148 }
149 
151 {
152  emit aboutToRemoveItem(layout);
153  layerItemWidget->onAboutToRemoveLayout(layout);
154  layerItemWidget->layerItem()->removeLayout(layout);
155 
156  for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
157  layoutForms->updateTitle(layerItemWidget->layerItem());
158 
159  emit modified();
160 }
161 
163  FormFactorItemCatalog::Type formFactorType)
164 {
165  auto* newParticle = createAndInitParticle(formFactorType);
166  layoutItem->addParticle(newParticle);
167 
168  emit modified();
169 
170  // search for particle layout widget for notification
171  ASSERT(m_sampleForm);
172  for (auto* w : m_sampleForm->findChildren<ParticleLayoutForm*>())
173  if (w->layoutItem() == layoutItem)
174  w->onParticleAdded(newParticle);
176 }
177 
180 {
181  auto* newItem = createAndInitParticle(type);
182  layoutItem->addParticle(newItem);
183 
184  emit modified();
185 
186  // search for particle layout widget for notification
187  ASSERT(m_sampleForm);
188  for (auto* w : m_sampleForm->findChildren<ParticleLayoutForm*>())
189  if (w->layoutItem() == layoutItem)
190  w->onParticleAdded(newItem);
192 }
193 
196 {
197  auto* newItem = createAndInitParticle(type);
198  compositionItem->addParticle(newItem);
199 
200  emit modified();
201 
202  // search for composition widget for notification
203  ASSERT(m_sampleForm);
204  for (auto* c : m_sampleForm->findChildren<ParticleCompositionForm*>())
205  if (c->compositionItem() == compositionItem)
206  c->onParticleAdded(newItem);
208 }
209 
211  FormFactorItemCatalog::Type formFactorType)
212 {
213  auto* newParticle = createAndInitParticle(formFactorType);
214  compositionItem->addParticle(newParticle);
215 
216  emit modified();
217 
218  // search for composition widget for notification
219  ASSERT(m_sampleForm);
220  for (auto* c : m_sampleForm->findChildren<ParticleCompositionForm*>())
221  if (c->compositionItem() == compositionItem)
222  c->onParticleAdded(newParticle);
224 }
225 
228 {
229  auto* newParticle = new ParticleItem(materialItems());
230  newParticle->setFormFactor(FormFactorItemCatalog::create(formFactorType));
231  newParticle->setMaterial(materialItems()->defaultMaterial());
232  return newParticle;
233 }
234 
237 {
238  auto* newItem = ItemWithParticlesCatalog::create(itemType, materialItems());
239 
240  if (auto* p = dynamic_cast<ItemWithMaterial*>(newItem))
241  p->setMaterial(materialItems()->defaultMaterial());
242 
243  if (auto* cs = dynamic_cast<ParticleCoreShellItem*>(newItem)) {
244  cs->createCore(materialItems());
245  cs->createShell(materialItems());
246  cs->core()->setFormFactor(new CylinderItem());
247  cs->shell()->setFormFactor(new CylinderItem());
248  }
249 
250  if (auto* meso = dynamic_cast<MesoCrystalItem*>(newItem); meso && meso->basisParticle())
251  if (auto* p = dynamic_cast<ItemWithMaterial*>(meso->basisParticle()))
252  p->setMaterial(materialItems()->defaultMaterial());
253 
254  return newItem;
255 }
256 
259 {
260  auto* particleCoreShell = widget->coreShellItem();
261 
262  if (particleCoreShell->core() == nullptr)
263  particleCoreShell->createCore(materialItems());
264 
265  particleCoreShell->core()->setFormFactor(FormFactorItemCatalog::create(type));
266  widget->createCoreWidgets();
268  emit modified();
269 }
270 
273 {
274  auto* particleCoreShell = widget->coreShellItem();
275 
276  if (particleCoreShell->shell() == nullptr)
277  particleCoreShell->createShell(materialItems());
278 
279  particleCoreShell->shell()->setFormFactor(FormFactorItemCatalog::create(type));
280  widget->createShellWidgets();
282  emit modified();
283 }
284 
286 {
287  ASSERT(m_sampleForm);
288 
289  for (auto* layoutForm : m_sampleForm->findChildren<ParticleLayoutForm*>())
290  if (layoutForm->layoutItem()->particles().contains(itemToRemove)) {
291  layoutForm->onAboutToRemoveParticle(itemToRemove);
292 
293  emit aboutToRemoveItem(itemToRemove);
294  layoutForm->layoutItem()->removeParticle(itemToRemove);
295  emit modified();
296  return;
297  }
298 
299  for (auto* c : m_sampleForm->findChildren<ParticleCompositionForm*>())
300  if (c->compositionItem()->particles().contains(itemToRemove)) {
301  c->onAboutToRemoveParticle(itemToRemove);
302 
303  emit aboutToRemoveItem(itemToRemove);
304  c->compositionItem()->removeParticle(itemToRemove);
305  emit modified();
306  return;
307  }
308 }
309 
311 {
312  m_undoStack.push(new CommandChangeValue(d.label, this, d.get(), newValue, d.path()));
313  d.set(newValue);
314  emit modified();
315 }
316 
317 void SampleEditorController::setDoubleFromUndo(double newValue, const QString& path)
318 {
319  ASSERT(m_sampleForm);
320 
321  DoubleSpinBox* spinBox = nullptr;
322  for (auto* s : m_sampleForm->findChildren<DoubleSpinBox*>()) {
323  if (s->valueDescriptor().path() == path) {
324  spinBox = s;
325  break;
326  }
327  }
328 
329  if (!spinBox)
330  return;
331 
332  spinBox->valueDescriptor().set(newValue);
333 
334  m_sampleForm->ensureVisible(spinBox);
335  QSignalBlocker b(spinBox);
336  spinBox->setBaseValue(newValue);
337  spinBox->setFocus();
338  spinBox->selectAll();
339 
340  emit modified();
341 }
342 
344 {
345  d.set(newValue);
346  emit modified();
347 }
348 
351 {
352  d.setCurrentIndex(index);
353  widget->createContent();
355  emit modified();
356 }
357 
359 {
360  return &m_undoStack;
361 }
362 
364 {
365  return &m_sampleItem->materialItems();
366 }
367 
369 {
370  return m_document;
371 }
372 
374  const QString& newMaterialIdentifier)
375 {
376  item->setMaterial(newMaterialIdentifier);
377 
378  // update Layer title
379  ASSERT(m_sampleForm);
380  for (auto* c : m_sampleForm->findChildren<LayerForm*>())
381  if (c->layerItem() == item)
382  c->updateTitle();
383 
384  // #baLayerEditor notify all material users (update link info)
385  emit modified();
386 }
387 
390 {
391  setDouble(newValue, d);
392 
393  // -- notify all other users of this material (update values in the UI)
394  ASSERT(m_sampleForm);
395  for (auto* c : m_sampleForm->findChildren<MaterialInplaceForm*>())
396  if (c->itemWithMaterial() != item
397  && c->itemWithMaterial()->materialIdentifier() == item->materialIdentifier())
398  c->updateValues();
399  emit modified();
400 }
401 
403  double newValue, DoubleDescriptor d)
404 {
405  setDouble(newValue, d);
406 
407  // -- notify the containing particle layout UI about changed value
408  ASSERT(m_sampleForm);
409  for (auto* c : m_sampleForm->findChildren<ParticleLayoutForm*>())
410  if (c->layoutItem()->interference() == interferenceItem) {
411  c->updateDensityValue();
412  break;
413  }
414 }
415 
417 {
418  ASSERT(m_sampleForm);
420 }
421 
423  QWidget* moveAboveThisWidget)
424 {
425  ASSERT(m_sampleForm);
427  const auto* moveAboveThisLayerForm = m_sampleForm->findNextLayerForm(moveAboveThisWidget);
428  auto* itemToMove = dynamic_cast<LayerForm*>(widgetToMove)->layerItem();
429  auto* moveBeforeThisItem =
430  moveAboveThisLayerForm != nullptr ? moveAboveThisLayerForm->layerItem() : nullptr;
431 
432  m_sampleItem->moveLayer(itemToMove, moveBeforeThisItem);
433  m_sampleForm->onLayerMoved(itemToMove);
434 
435  // #baLayerEditor: tab order!
436 
437  emit modified();
438 }
439 
441 {
443  emit modified();
444 }
445 
446 void SampleEditorController::setSampleDescription(const QString& description)
447 {
448  m_sampleItem->setDescription(description);
449  emit modified();
450 }
451 
454 {
455  auto* meso = widget->mesoCrystalItem();
456  meso->setBasis(createAndInitParticle(type));
457  widget->createBasisWidgets();
459  emit modified();
460 }
461 
464 {
465  auto* meso = widget->mesoCrystalItem();
466  meso->setBasis(createAndInitParticle(type));
467  widget->createBasisWidgets();
469  emit modified();
470 }
471 
473 {
474  widget->layoutItem()->interference().setCurrentIndex(newIndex);
475  widget->onInterferenceTypeChanged();
477 
478  // Disable/enable total density property in the particle layout, depending on type of
479  // interference function.
480  QWidget* parent = widget->parentWidget();
481  while (parent != nullptr && dynamic_cast<ParticleLayoutForm*>(parent) == nullptr)
482  parent = parent->parentWidget();
483 
484  if (auto* particleLayoutForm = dynamic_cast<ParticleLayoutForm*>(parent)) {
485  particleLayoutForm->updateDensityEnabling();
486  particleLayoutForm->updateDensityValue();
487  }
488 
489  emit modified();
490 }
491 
493 {
494  widget->interferenceItem()->setXiIntegration(newValue);
495  widget->onIntegrateOverXiChanged();
496  emit modified();
497 }
Defines class DoubleSpinBox.
Defines class FormFactorItemCatalog.
Defines class InterferenceForm.
Defines InterferenceItems's classes.
Defines class LatticeTypeSelectionForm.
Defines class LayerForm.
Defines class LayerItem.
Defines class MaterialInplaceForm.
Defines class MaterialItems.
Defines class MesoCrystalForm.
Defines class MesoCrystalItem.
Defines class MultiLayerForm.
Defines class MultiLayerItem.
Defines class ParticleCompositionForm.
Defines class ParticleCompositionItem.
Defines class ParticleCoreShellForm.
Defines class ParticleCoreShellItem.
Defines class ParticleItem.
Defines class ParticleLayoutForm.
Defines class ParticleLayoutItem.
Defines class ProjectDocument.
Defines command classes for LayerOrientedSampleEditor.
Defines class SampleEditorController.
Defines class UIntDescriptor.
Abstract widget base class to contain a selection, defined by a SelectionDescriptor.
virtual void createContent()=0
Abstract base class for SelectionDescriptor to ease referencing.
virtual void setCurrentIndex(int newIndex) const =0
Set currently selected option.
Command to add a layer to a sample.
Command to change a double value.
Command to remove a layer from a sample.
Describes properties of a double value which are necessary to allow GUI representation,...
QString label
A label text (short, no trailing colon)
function< void(double)> set
function to set the value
function< double()> get
function to get the current value
function< QString()> path
Path describing this value. Used e.g. for undo/redo.
SpinBox for DoubleDescriptors, supporting units.
Definition: DoubleSpinBox.h:22
const DoubleDescriptor & valueDescriptor() const
The descriptor on which this spinbox operates.
void setBaseValue(double baseValue)
Set the base value (unit is the one of the contained descriptor).
static FormFactorItem * create(Type type)
Creates the item of the given type.
void setXiIntegration(bool xiIntegration)
Form for editing interference functions.
void onInterferenceTypeChanged()
ParticleLayoutItem * layoutItem() const
QString materialIdentifier() const
void setMaterial(const MaterialItem *materialItem)
Set the material this item shall use. Stores the identifier, not the pointer!
static ItemWithParticles * create(Type type, const MaterialItems *materials)
Creates the item of the given type.
Form for editing lattice type values.
Interference2DAbstractLatticeItem * interferenceItem() const
Form for editing a layer.
Definition: LayerForm.h:29
void onAboutToRemoveLayout(ParticleLayoutItem *layoutItem)
Definition: LayerForm.cpp:179
LayerItem * layerItem() const
Definition: LayerForm.cpp:188
void onLayoutAdded(ParticleLayoutItem *layoutItem)
Definition: LayerForm.cpp:171
ParticleLayoutItem * addLayout()
Definition: LayerItem.cpp:141
void setColor(const QColor &color)
Definition: LayerItem.cpp:158
void removeLayout(ParticleLayoutItem *layout)
Definition: LayerItem.cpp:147
Form to select a material and to edit it in-place.
Form for editing a mesocrystal.
MesoCrystalItem * mesoCrystalItem() const
void setBasis(ItemWithParticles *basis)
ItemWithParticles * basisParticle() const
Form to present/edit a MultiLayer (sample)
void ensureVisible(QWidget *w)
void updateRowVisibilities()
LayerForm * findNextLayerForm(QWidget *w)
Search for the next LayerForm, starting from the given widget.
void onLayerAdded(LayerItem *layerItem)
Create widgets for the new layer.
void updateUnits()
Update the presented units in all contained widgets according to current settings.
void showAddLayerButtons(bool show)
Shows or hides the "Add Layer" buttons.
void onAboutToRemoveLayer(LayerItem *layerItem)
Call this before removing (deleting) a LayerItem.
void onLayerMoved(LayerItem *layerItem)
Call this when a layerItem has been moved to a different position.
void setDescription(const QString &description)
LayerItem * addLayer(int index=-1)
Creates and inserts a layer at given index.
MaterialItems & materialItems()
void setSampleName(const QString &name)
void removeLayer(LayerItem *item)
QVector< LayerItem * > layers() const
void moveLayer(LayerItem *item, LayerItem *beforeThisLayer)
Form for editing a particle composition.
void addParticle(ItemWithParticles *particle)
Form for editing a core/shell particle.
ParticleCoreShellItem * coreShellItem() const
ParticleItem * createShell(const MaterialItems *materials)
ParticleItem * createCore(const MaterialItems *materials)
void setFormFactor(FormFactorItem *p)
Form for editing a particle layout.
void addParticle(ItemWithParticles *particle)
SelectionDescriptor< InterferenceItem * > interference() const
Project document class handles all data related to the opened project (sample, job,...
void setInt(int newValue, UIntDescriptor d)
void setMesoCrystalBasis(MesoCrystalForm *widget, ItemWithParticlesCatalog::Type type)
void setCoreFormFactor(ParticleCoreShellForm *widget, FormFactorItemCatalog::Type type)
ProjectDocument * projectDocument() const
The current document.
void removeLayout(LayerForm *layerItem, ParticleLayoutItem *layout)
MultiLayerItem * sampleItem() const
The item on which this controller operates.
void setMultiLayerForm(MultiLayerForm *view)
Set the current form.
void addLayer(LayerItem *before)
ItemWithParticles * createAndInitParticle(FormFactorItemCatalog::Type formFactorType) const
QUndoStack * undoStack()
The contained undo stack.
void setSampleDescription(const QString &description)
void setCurrentIndex(AbstractSelectionContainerForm *widget, int index, const AbstractSelectionDescriptor &d)
void setDouble(double newValue, DoubleDescriptor d)
MaterialItems * materialItems() const
The materials of the current document.
void addParticle(ParticleLayoutItem *layout, ItemWithParticlesCatalog::Type type)
void addLayerFromUndo(int atIndex)
void onStoppedToMoveLayer(QWidget *widgetToMove, QWidget *moveAboveThisWidget)
void setIntegrateOverXi(LatticeTypeSelectionForm *widget, bool newValue)
MultiLayerForm * sampleForm() const
The current form.
void addLayout(LayerForm *layerItem)
void selectMaterial(ItemWithMaterial *item, const QString &newMaterialIdentifier)
void setDensityRelatedValue(InterferenceItem *interferenceItem, double newValue, DoubleDescriptor d)
Set an interference function's value which affects the total particle density of the containing parti...
void setShellFormFactor(ParticleCoreShellForm *widget, FormFactorItemCatalog::Type type)
void aboutToRemoveItem(SampleItem item)
void setDoubleFromUndo(double newValue, const QString &path)
void removeLayer(LayerItem *layerItem)
void setSampleName(const QString &name)
SampleEditorController(ProjectDocument *document, MultiLayerItem *multi)
void removeLayerFromUndo(int atIndex)
void setMaterialValue(ItemWithMaterial *item, double newValue, DoubleDescriptor d)
void selectInterference(InterferenceForm *widget, int newIndex)
void removeParticle(ItemWithParticles *item)
Describes properties of a uint value which are necessary to allow GUI representation,...
function< void(uint)> set
function to set the value
QString const & name(EShape k)
Definition: particles.cpp:20
QList< QColor > predefinedLayerColors()