-
Wuttke, Joachim authoredWuttke, Joachim authored
InstrumentLibraryEditor.cpp 13.02 KiB
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file GUI/View/Instrument/InstrumentLibraryEditor.cpp
//! @brief Implements class InstrumentLibraryEditor.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "GUI/View/Instrument/InstrumentLibraryEditor.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/Model/Device/InstrumentLibrary.h"
#include "GUI/Support/Style/Style.h"
#include "GUI/View/Instrument/DepthprobeInstrumentEditor.h"
#include "GUI/View/Instrument/GISASInstrumentEditor.h"
#include "GUI/View/Instrument/OffspecInstrumentEditor.h"
#include "GUI/View/Instrument/SpecularInstrumentEditor.h"
#include "GUI/View/Item/ItemViewOverlayButtons.h"
#include "GUI/View/Tool/ItemDelegateForHTML.h"
#include "GUI/View/Tool/mainwindow_constants.h"
#include "GUI/View/Widget/ApplicationSettings.h"
#include <QAction>
#include <QFormLayout>
#include <QInputDialog>
#include <QPushButton>
#include <QSettings>
#include <QSplitter>
#include <QTextEdit>
#include <QVBoxLayout>
InstrumentLibraryEditor::InstrumentLibraryEditor(QWidget* parent,
InstrumentLibrary* instrumentLibrary)
: QDialog(parent)
, m_instrumentLibrary(instrumentLibrary)
, m_treeModel(new TreeModel(this, instrumentLibrary->instrumentModel()))
, m_chosenItem(nullptr)
{
// object name is needed to reload application settings
if (this->objectName().isEmpty())
this->setObjectName("InstrumentLibraryEditor");
setGeometry(0, 0, 780, 429);
auto* verticalLayout = new QVBoxLayout(this);
setLayout(verticalLayout);
auto* splitter = new QSplitter;
verticalLayout->addWidget(splitter);
splitter->setOrientation(Qt::Horizontal);
m_treeView = new QTreeView;
splitter->addWidget(m_treeView);
m_scrollArea = new QScrollArea;
splitter->addWidget(m_scrollArea);
m_scrollArea->setWidgetResizable(true);
auto* scrollAreaWidgetContents = new QWidget;
scrollAreaWidgetContents->setGeometry(0, 0, 69, 380);
m_scrollArea->setWidget(scrollAreaWidgetContents);
m_buttonBox = new QDialogButtonBox;
verticalLayout->addWidget(m_buttonBox);
m_buttonBox->setOrientation(Qt::Horizontal);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
setWindowIcon(QIcon(":/images/library.svg"));
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
m_treeModel->enableEmptyHeadlines(false);
m_treeView->setItemsExpandable(false);
m_treeView->setRootIsDecorated(false);
m_treeView->setHeaderHidden(true);
m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
m_treeView->setModel(m_treeModel);
m_treeView->expandAll();
m_treeView->setVerticalScrollMode(QTreeView::ScrollPerPixel);
m_treeView->setIndentation(0);
m_treeView->setItemDelegate(new ItemDelegateForHTML(this));
m_treeView->setIconSize(QSize(128, 128));
connect(m_treeModel, &QAbstractItemModel::modelReset, [this] { m_treeView->expandAll(); });
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &InstrumentLibraryEditor::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &InstrumentLibraryEditor::reject);
// ensure a current item when widget is shown
GUI::Style::setResizable(this);
appSettings->loadWindowSizeAndPos(this);
applySplitterPos();
// unfix m_treeView width only on manual resize
connect(splitter, &QSplitter::splitterMoved, [this] {
m_treeView->setMinimumWidth(0);
m_treeView->setMaximumWidth(QWIDGETSIZE_MAX);
});
}
InstrumentLibraryEditor::~InstrumentLibraryEditor()
{
appSettings->saveWindowSizeAndPos(this);
saveSplitterPos();
}
void InstrumentLibraryEditor::applySplitterPos()
{
QSettings settings;
if (settings.childGroups().contains(GUI::Style::S_INSTRUMENT_LIBRARY_EDITOR)) {
settings.beginGroup(GUI::Style::S_INSTRUMENT_LIBRARY_EDITOR);
m_treeView->setFixedWidth(
settings.value(GUI::Style::S_INSTRUMENT_LIBRARY_EDITOR_TREE_WIDTH).toInt());
settings.endGroup();
} else
m_treeView->setFixedWidth(GUI::Style::INSTRUMENT_LIBRARY_EDITOR_TREE_WIDTH);
}
void InstrumentLibraryEditor::saveSplitterPos()
{
QSettings settings;
settings.beginGroup(GUI::Style::S_INSTRUMENT_LIBRARY_EDITOR);
settings.setValue(GUI::Style::S_INSTRUMENT_LIBRARY_EDITOR_TREE_WIDTH, m_treeView->width());
settings.endGroup();
settings.sync();
}
InstrumentItem* InstrumentLibraryEditor::execChoose()
{
setWindowTitle("Instrument Library - Choose instrument");
ItemViewOverlayButtons::install(
m_treeView, [this](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
m_treeView->setItemDelegate(new ItemDelegateForHTML(this));
connect(m_treeView, &QTreeView::doubleClicked, this,
&InstrumentLibraryEditor::onItemDoubleClickedForChoose);
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
&InstrumentLibraryEditor::onCurrentChangedForChoose);
onCurrentChangedForChoose();
// select the first instrument
QModelIndex index = m_treeModel->indexForItem(m_instrumentLibrary->instrumentItems().first());
m_treeView->setCurrentIndex(index);
if (exec() == QDialog::Accepted)
return m_chosenItem;
return nullptr;
}
void InstrumentLibraryEditor::execAdd(const InstrumentItem& instrumentToAdd)
{
const QString& newName = m_instrumentLibrary->suggestName(instrumentToAdd.instrumentName());
auto* addedInstrument = m_instrumentLibrary->addItemCopy(newName, instrumentToAdd);
setWindowTitle("Instrument Library - Add instrument");
m_treeModel->setNewInstrument(addedInstrument);
ItemViewOverlayButtons::install(
m_treeView, [this](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
m_treeView->setItemDelegate(new ItemDelegateForHTML(this));
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
&InstrumentLibraryEditor::createWidgetsForCurrentInstrument);
QModelIndex index = m_treeModel->indexForItem(addedInstrument);
m_treeView->expandAll();
m_treeView->setCurrentIndex(index);
m_treeView->scrollTo(index, QAbstractItemView::PositionAtTop);
createWidgetsForCurrentInstrument();
exec();
}
void InstrumentLibraryEditor::onItemDoubleClickedForChoose(const QModelIndex& index)
{
m_chosenItem = m_treeModel->itemForIndex(index);
if (m_chosenItem != nullptr)
accept();
}
void InstrumentLibraryEditor::onCurrentChangedForChoose()
{
m_chosenItem = m_treeModel->itemForIndex(m_treeView->currentIndex());
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_chosenItem != nullptr);
createWidgetsForCurrentInstrument();
}
QList<QAction*> InstrumentLibraryEditor::getOverlayActions(const QModelIndex& index, bool asHover)
{
if (m_treeModel->isHeadline(index))
return {};
// -- index belongs to item
if (!asHover)
return {};
auto* item = m_treeModel->itemForIndex(index);
if (item == nullptr)
return {};
auto* removeAction = new QAction(this);
removeAction->setText("Remove");
removeAction->setIcon(QIcon(":/images/delete.svg"));
removeAction->setIconText("Remove");
removeAction->setToolTip("Remove this instrument");
connect(removeAction, &QAction::triggered, [this, item] { m_treeModel->removeItem(item); });
return {removeAction};
}
void InstrumentLibraryEditor::createWidgetsForCurrentInstrument()
{
auto* currentInstrument = m_treeModel->itemForIndex(m_treeView->currentIndex());
if (!currentInstrument) {
m_scrollArea->setWidget(new QWidget(m_scrollArea)); // blank widget
return;
}
auto* w = new QWidget(m_scrollArea);
auto* layout = new QVBoxLayout(w);
auto title = QString("Summary (%1 instrument)").arg(currentInstrument->instrumentType());
auto* g = new CollapsibleGroupBox(title, m_scrollArea, currentInstrument->expandInfo);
g->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
auto* formLayout = new QFormLayout(g->body());
formLayout->setContentsMargins(17, 17, 17, 17);
layout->addWidget(g);
auto* nameEdit = new QLineEdit(g);
formLayout->addRow("Name:", nameEdit);
nameEdit->setText(currentInstrument->instrumentName());
connect(nameEdit, &QLineEdit::textEdited, this,
&InstrumentLibraryEditor::onInstrumentNameEdited);
auto* descriptionEdit = new QTextEdit(g);
descriptionEdit->setMinimumWidth(300);
descriptionEdit->setFixedHeight(60); // TODO replace by 2*line_height
descriptionEdit->setAcceptRichText(false);
descriptionEdit->setTabChangesFocus(true);
descriptionEdit->setPlainText(currentInstrument->description());
formLayout->addRow("Description:", descriptionEdit);
connect(descriptionEdit, &QTextEdit::textChanged, [this, descriptionEdit] {
onInstrumentDescriptionEdited(descriptionEdit->toPlainText());
});
auto* ec = m_instrumentLibrary->editController();
if (auto* sp = dynamic_cast<SpecularInstrumentItem*>(currentInstrument)) {
auto* editor = new SpecularInstrumentEditor(m_scrollArea, sp, ec);
connect(editor, &SpecularInstrumentEditor::dataChanged, this,
&InstrumentLibraryEditor::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* os = dynamic_cast<OffspecInstrumentItem*>(currentInstrument)) {
auto* editor = new OffspecInstrumentEditor(m_scrollArea, os, ec);
connect(editor, &OffspecInstrumentEditor::dataChanged, this,
&InstrumentLibraryEditor::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* gisas = dynamic_cast<GISASInstrumentItem*>(currentInstrument)) {
auto* editor = new GISASInstrumentEditor(m_scrollArea, gisas);
connect(editor, &GISASInstrumentEditor::dataChanged, this,
&InstrumentLibraryEditor::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* dp = dynamic_cast<DepthprobeInstrumentItem*>(currentInstrument)) {
auto* editor = new DepthprobeInstrumentEditor(m_scrollArea, dp, ec);
connect(editor, &DepthprobeInstrumentEditor::dataChanged, this,
&InstrumentLibraryEditor::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else
ASSERT_NEVER;
m_scrollArea->setWidget(w);
}
void InstrumentLibraryEditor::onInstrumentNameEdited(const QString& newName)
{
QModelIndex index = m_treeView->currentIndex();
m_treeModel->setData(index, newName, Qt::EditRole);
}
void InstrumentLibraryEditor::onInstrumentDescriptionEdited(const QString& t)
{
QModelIndex index = m_treeView->currentIndex();
m_treeModel->setData(index, t, Qt::ToolTipRole);
}
void InstrumentLibraryEditor::onInstrumentChangedByEditor()
{
// uses 'MultiInstrumentNotifier::instrumentChanged' signal to set
// 'InstrumentLibrary:m_modified' flag to true ==> save library on close.
auto* currentInstrument = m_treeModel->itemForIndex(m_treeView->currentIndex());
m_instrumentLibrary->editController()->notifyInstrumentChanged(currentInstrument);
}
/*********************************************************************************************/
TreeModel::TreeModel(QObject* parent, InstrumentModel* model)
: InstrumentsTreeModel(parent, model)
{
}
void TreeModel::setNewInstrument(InstrumentItem* addedInstrument)
{
m_newInstrument = addedInstrument;
}
QVariant TreeModel::data(const QModelIndex& index, int role) const
{
if (isHeadline(index))
return InstrumentsTreeModel::data(index, role);
auto* const item = itemForIndex(index);
if (role == Qt::DisplayRole) {
auto descr = item->description();
if (!descr.isEmpty()) {
descr.prepend("<br><br>");
// max 4 lines
while (descr.count("\n") > 3) {
descr.truncate(descr.lastIndexOf("\n"));
descr += " [...]";
}
descr.replace("\n", "<br>");
}
return "<b>" + item->instrumentName() + "</b>" + descr;
}
if (role == Qt::DecorationRole && (item == m_newInstrument)) {
if (role == Qt::DecorationRole)
switch (instrumentType(item)) {
case Gisas:
return QIcon(":/images/gisas_instrument_new.svg");
case Offspec:
return QIcon(":/images/offspec_instrument_new.svg");
case Specular:
return QIcon(":/images/specular_instrument_new.svg");
case Depthprobe:
return QIcon(":/images/depth_instrument_new.svg");
default:
break;
}
}
return InstrumentsTreeModel::data(index, role);
}