/** * @RDE_HEADER@ * GUI Plugin for ParticipantManagement - Participant Edit * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2016-10-17 */ #include "participant_edit.h" #include "ui_participant_edit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ParticipantManagement { int ParticipantEdit::s_preselectedParty = -1; ParticipantEdit::ParticipantEdit(LtsSession::LtsSession *session, bool asSupportService, QWidget *parent) : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel) , m_asSupportService(asSupportService) , m_actionRemove(nullptr) , m_actionEdit(nullptr) , m_dtc(nullptr) , m_ftc(nullptr) , m_ppmGuiCbxIfc(nullptr) , m_session(session) , m_pbTreeView(new QPushButton(tr("Tree View"))) , m_pbTableView(new QPushButton(tr("Table View"))) , m_pbTreeViewDummy(new QPushButton(m_pbTreeView->text())) , m_pbTableViewDummy(new QPushButton(m_pbTableView->text())) , m_ui(new Ui::ParticipantEdit) , m_logger(getLogger("ParticipantEdit")) { m_ui->setupUi(this); m_ui->cbParty->setVisible(!m_asSupportService); // hide when SupportService m_ui->lblParty->setVisible(!m_asSupportService); m_ui->treeViewEdit->treeView()->setDragEnabled(true); m_ui->treeViewEdit->treeView()->setDragDropMode(QAbstractItemView::DragDrop); m_ui->treeViewEdit->treeView()->setDefaultDropAction(Qt::MoveAction); // this will make the tree view look like a table m_ui->tableViewEdit->treeView()->setIndentation(0); m_ui->tableViewEdit->treeView()->setItemsExpandable(false); // add buttons to switch between a tree view and a table view m_pbTreeViewDummy->setEnabled(false); m_pbTableViewDummy->setEnabled(false); QHBoxLayout *hboxTree = new QHBoxLayout; hboxTree->addWidget(m_pbTreeViewDummy); hboxTree->addWidget(m_pbTableView); QWidget *widgetTree = new QWidget; widgetTree->setLayout(hboxTree); m_ui->treeViewEdit->setCustomWidget(widgetTree); m_pbTreeViewDummy->setEnabled(false); m_pbTableViewDummy->setEnabled(false); QHBoxLayout *hboxTable = new QHBoxLayout; hboxTable->addWidget(m_pbTreeView); hboxTable->addWidget(m_pbTableViewDummy); QWidget *widgetTable = new QWidget; widgetTable->setLayout(hboxTable); m_ui->tableViewEdit->setCustomWidget(widgetTable); connect(m_pbTreeView, &QPushButton::clicked, this, &ParticipantEdit::onSwitchToTreeView); connect(m_pbTableView, &QPushButton::clicked, this, &ParticipantEdit::onSwitchToTableView); m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &ParticipantEdit::onAddItem); m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &ParticipantEdit::onEditItem); m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &ParticipantEdit::onPrint); if (m_asSupportService) setOnlineHelpFilename(QStringLiteral("Situation-Edit_Support_Participants")); else setOnlineHelpFilename(QStringLiteral("Situation-Edit_Participants")); m_ui->treeViewEdit->treeView()->setContextMenuPolicy(Qt::CustomContextMenu); m_ui->tableViewEdit->treeView()->setContextMenuPolicy(Qt::CustomContextMenu); setStyleSheet(QStringLiteral("QToolTip { background-color: #ffffff; }")); } ParticipantEdit::~ParticipantEdit() { delete m_ui; } bool ParticipantEdit::init(const std::shared_ptr &dtc, const std::shared_ptr &ftc, const std::shared_ptr &ppmGuiCbxIfc, const std::shared_ptr &asbIfc) { // interfaces ............................................................................................................... if (!dtc || !ftc || !ppmGuiCbxIfc) return false; if (m_dtc) return false; m_dtc = dtc; m_ftc = ftc; m_ppmGuiCbxIfc = ppmGuiCbxIfc; m_asb = asbIfc; // tree model ............................................................................................................... m_model = new ParticipantEditModel(this, m_dtc, m_asb); connect(m_model, &ParticipantEditModel::internalDataChanged, this, &ParticipantEdit::slotChangesMade); // proxy model for the tree view ............................................................................................ m_proxyTree = new ParticipantTreeFilterProxyModel(this); m_proxyTree->setSourceModel(m_model); // firing unit validator .................................................................................................... if (m_firingUnitValidator) delete m_firingUnitValidator; m_firingUnitValidator = new FiringUnitValidator(m_dtc, this); m_ui->treeViewEdit->setModel(m_proxyTree); m_ui->treeViewEdit->setContextMenuPolicy(Qt::CustomContextMenu); #ifndef LTS_DEBUG m_ui->treeViewEdit->setColumnHidden(ParticipantEditModel::ColParticipantId, true); #endif // proxy model for the table view ........................................................................................... m_proxyTable = new ParticipantTableFilterProxyModel(this); m_proxyTable->setSourceModel(m_model); connect(m_ui->tableViewEdit, &LtsDataView::LtsDataView::filterChanged, m_proxyTable, &ParticipantTableFilterProxyModel::onFilter); connect(m_ui->tableViewEdit, &LtsDataView::LtsDataView::sortOrderChanged, this, &ParticipantEdit::onSortTableView); connect(m_model, &ParticipantEditModel::internalDataChanged, m_proxyTable, &ParticipantTableFilterProxyModel::init); m_ui->tableViewEdit->setModel(m_proxyTable); m_ui->tableViewEdit->setContextMenuPolicy(Qt::CustomContextMenu); #ifndef LTS_DEBUG m_ui->tableViewEdit->setColumnHidden(ParticipantEditModel::ColParticipantId, true); #endif // actions .................................................................................................................. m_actionRemove = new QAction(QIcon(QLatin1String("icon:remove.svg")), tr("Remove"), this); m_actionEdit = new QAction(QIcon(QLatin1String("icon:edit.svg")), tr("Edit"), this); // party combo box .......................................................................................................... connect(m_ui->cbParty, static_cast(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &ParticipantEdit::onPartyChanged); m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty, m_asSupportService ? ForeignGuiComboboxInterface::CbxPartyFilter::Oc : ForeignGuiComboboxInterface::CbxPartyFilter::NormalPool); // tree handler ............................................................................................................. m_pTreehandler = new ParticipantTreeEditHandler(m_ui->treeViewEdit, m_dtc, m_ftc, m_asSupportService, this); m_pTreehandler->setEditParticipantsAllowed(true); connect(m_pTreehandler, &ParticipantTreeEditHandler::signalEditParticipant, this, &ParticipantEdit::onEditItem); connect(m_pTreehandler, &ParticipantTreeEditHandler::signalDoubleClicked, this, &ParticipantEdit::onEditItem); connect(m_pTreehandler, &ParticipantTreeEditHandler::signalSelectionChanged, this, &ParticipantEdit::onEnaDisa); connect(m_pTreehandler, &ParticipantTreeEditHandler::signalChangesMade, this, &ParticipantEdit::onChangesMade); connect(m_actionRemove, &QAction::triggered, m_pTreehandler, &ParticipantTreeEditHandler::onRemoveItem); connect(m_actionEdit, &QAction::triggered, this, &ParticipantEdit::onEditItem); connect(m_ui->tableViewEdit->treeView(), &QTreeView::customContextMenuRequested, m_pTreehandler, &ParticipantTreeEditHandler::showContextMenu); connect(m_ui->tableViewEdit->treeView(), &QTreeView::doubleClicked, this, &ParticipantEdit::onEditItem); m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, m_pTreehandler, &ParticipantTreeEditHandler::onRemoveItem); // .......................................................................................................................... const int idx = m_ui->cbParty->findData(s_preselectedParty); if (idx >= 0) m_ui->cbParty->setCurrentIndex(idx); // synchronize tree view and table view ..................................................................................... // filtering connect(m_ui->treeViewEdit, &LtsDataView::LtsDataView::filterChanged, this, [this]() { onFilterChanged(m_ui->treeViewEdit->searchModel(), m_ui->tableViewEdit->searchModel()); }); connect(m_ui->tableViewEdit, &LtsDataView::LtsDataView::filterChanged, this, [this]() { onFilterChanged(m_ui->tableViewEdit->searchModel(), m_ui->treeViewEdit->searchModel()); }); // selection connect(m_ui->treeViewEdit, &LtsDataView::LtsDataView::selectionChanged, this, &ParticipantEdit::onSelectionChangedInTree); connect(m_ui->tableViewEdit, &LtsDataView::LtsDataView::selectionChanged, this, &ParticipantEdit::onSelectionChangedInTable); // column width connect(m_ui->treeViewEdit->searchView()->header(), &QHeaderView::sectionResized, this, [this](int column, int oldWidth, int newWidth) { Q_UNUSED(oldWidth); onColumnWidthChanged(m_ui->tableViewEdit->searchView(), column, newWidth); }); connect(m_ui->tableViewEdit->searchView()->header(), &QHeaderView::sectionResized, this, [this](int column, int oldWidth, int newWidth) { Q_UNUSED(oldWidth); onColumnWidthChanged(m_ui->treeViewEdit->searchView(), column, newWidth); }); // .......................................................................................................................... onSwitchToTreeView(); onPartyChanged(); onEnaDisa(); return true; } void ParticipantEdit::retranslate() { translate(m_ui, m_asSupportService ? tr("Edit Support Participants") : tr("Edit Participants")); } bool ParticipantEdit::apply() { m_proxyTable->resetMapping(); bool ret = m_pTreehandler->onApplyChanges(); m_proxyTable->init(); return ret; } void ParticipantEdit::showIsSimulated() { QHash texts({{LtsWidgets::Button::Yes, tr("Ok")}}); LtsWidgets::LtsMessageDialog::showMessage(this, tr("Participant is simulated"), windowTitle(), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes, texts); } void ParticipantEdit::lock() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; m_ui->cbParty->setEnabled(true); // true!! // avoid BaseClass::lock to be called. partyEdit stays enabled DEBUG_LOG(m_logger) << LOGFUNCTION << " END"; } void ParticipantEdit::unlock() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; m_ui->cbParty->setEnabled(true); DEBUG_LOG(m_logger) << LOGFUNCTION << " END"; } void ParticipantEdit::onPartyChanged() { const int newPartyId = m_ui->cbParty->currentData().toInt(); s_preselectedParty = newPartyId; if (hasUnsavedChanges()) { switch (askApplyDiscardCancel()) { case LtsWidgets::Button::Apply: if (!apply()) return; __unchange__(); break; case LtsWidgets::Button::Cancel: { QSignalBlocker block(m_ui->cbParty); m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(m_model->partyId(), Qt::UserRole)); // reset position s_preselectedParty = m_ui->cbParty->currentData().toInt(); return; } case LtsWidgets::Button::Discard: __unchange__(); // can't call resetChanges() by myself :( break; default: break; } } // DEBUG_LOG(m_logger) << LOGFUNCTION << " ParticipantEdit::onPartyChanged !!! -> newPartyId=" << newPartyId; // m_ui->cbParty->setEnabled(false); // will be re-enabled with reply (::lock, ::unlock) // tryLockTopicNew(QStringLiteral("LockParty"), newPartyId); m_proxyTable->resetMapping(); m_proxyTable->disableSorting(); m_pTreehandler->partyHasChanged(newPartyId); m_proxyTable->init(); // Some icons in the table view won't update automatically after initialization (maybe a timing issue?). // Workaround: emit dataChanged on the first items (up to 50 rows, since more will not be visible after reinit) // with a slight delay of 100 msecs to force an update. QTimer *timer = new QTimer(this); timer->singleShot(100, this, [this, timer]() { m_ui->tableViewEdit->treeView()->dataChanged(m_proxyTable->index(0, 0), m_proxyTable->index(std::min(50, m_proxyTable->rowCount()), 0), {Qt::DecorationRole}); timer->deleteLater(); }); } void ParticipantEdit::onPrint() { LtsPrinting::printView(m_session, this, m_ui->treeViewEdit->treeView()); } void ParticipantEdit::onChangesMade() { onEnaDisa(); if (m_model->onReinitModel(s_preselectedParty)) beautifyTreeView(m_ui->treeViewEdit->treeView(), m_model, true); } void ParticipantEdit::onBeautifyTreeView() { beautifyTreeView(m_ui->treeViewEdit->treeView(), m_model, true); } void ParticipantEdit::onEnaDisa() { bool enableAdd = false; bool enableEdit = false; bool enableRemove = false; if (m_pTreehandler->isSelectedIndexValid()) { if (m_dtc->isAdlerVerbund(m_pTreehandler->getSelectedData().typeId())) { DEBUG_LOG(m_logger) << "Can't Edit Adler SAA Participants"; enableEdit = true; } else { enableAdd = m_pTreehandler->isContainer(); if (m_firingUnitValidator) enableAdd &= m_firingUnitValidator->hasUnitCapacities(m_model, m_pTreehandler->selectedIndexView()); if (m_pTreehandler->isSelectedIndexRemovable()) enableRemove = true; enableEdit = true; } } m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, enableAdd && m_ui->treeViewEdit->isVisible()); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, enableEdit); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, enableRemove && m_ui->treeViewEdit->isVisible()); m_actionRemove->setEnabled(enableRemove); m_actionEdit->setEnabled(enableEdit); } void ParticipantEdit::onAddItem() { ParticipantEditItem *newItemDlg = new ParticipantEditItem(m_asSupportService, m_session, this); ParticipantManagement::ParticipantData dataAll = m_pTreehandler->getSelectedData(); ParticipantData data = ParticipantData(); data.designator(dataAll.designator()); data.tacticalIdentifier(dataAll.tacticalIdentifier()); const bool addItem = true; newItemDlg->setAttribute(Qt::WA_DeleteOnClose); QModelIndex workingIndex = m_pTreehandler->selectedIndexView(); if (!newItemDlg->init(workingIndex, m_ui->cbParty->currentData().toInt(), m_dtc, m_ppmGuiCbxIfc, m_asb, static_cast(data), addItem, m_pTreehandler->usedEquipmentNumbers())) { delete newItemDlg; return; } connect(newItemDlg, &ParticipantEditItem::addItem, m_model, &ParticipantEditModel::onInsertRow); connect(newItemDlg, &ParticipantEditItem::addItem, this, &ParticipantEdit::onBeautifyTreeView); connect(newItemDlg, &ParticipantEditItem::addItem, m_proxyTable, &ParticipantTableFilterProxyModel::init); if (m_firingUnitValidator) { newItemDlg->setIsAdler(m_firingUnitValidator->isAdler(m_model, workingIndex)); const int unitGearClassType = m_firingUnitValidator->getGearClassType(m_model, workingIndex); newItemDlg->setFiringUnitRestrictions(m_model, false, m_firingUnitValidator->getRestrictions(m_model, workingIndex), m_firingUnitValidator, unitGearClassType); connect(newItemDlg, &ParticipantEditItem::addItem, this, [this, newItemDlg, unitGearClassType](const QModelIndex &parent, const ParticipantData &newData) { Q_UNUSED(newData) if (!newItemDlg || !m_firingUnitValidator) return; newItemDlg->setFiringUnitRestrictions(m_model, false, m_firingUnitValidator->getRestrictions(m_model, parent), m_firingUnitValidator, unitGearClassType); }); } newItemDlg->show(); } void ParticipantEdit::onEditItem() { if (!m_pTreehandler->isSelectedIndexValid() || m_newItemDlg) return; ParticipantManagement::ParticipantData data = m_pTreehandler->getSelectedData(); if (data.isSimulated()) showIsSimulated(); m_newItemDlg = new ParticipantEditItem(m_asSupportService, m_session, this); m_newItemDlg->setAttribute(Qt::WA_DeleteOnClose); QModelIndex workingIndex = m_pTreehandler->selectedIndexView(); if (!m_newItemDlg->init(workingIndex, m_ui->cbParty->currentData().toInt(), m_dtc, m_ppmGuiCbxIfc, m_asb, data, false, m_pTreehandler->usedEquipmentNumbers())) { delete m_newItemDlg; return; } QObject::connect(m_newItemDlg, &ParticipantEditItem::destroyed, this, [this] { m_newItemDlg = nullptr; onEnaDisa(); }); QObject::connect(m_newItemDlg, &ParticipantEditItem::updateItem, m_model, &ParticipantEditModel::onUpdateRow); if (m_firingUnitValidator) { const int unitGearClassType = m_firingUnitValidator->getGearClassType(m_model, workingIndex); m_newItemDlg->setFiringUnitRestrictions(m_model, true, m_firingUnitValidator->getRestrictions(m_model, workingIndex), m_firingUnitValidator, unitGearClassType); } m_newItemDlg->show(); } void ParticipantEdit::onSwitchToTreeView() { m_ui->tableViewEdit->hide(); m_ui->treeViewEdit->show(); m_ui->treeViewEdit->expandAll(); if (!m_ui->treeViewEdit->selectionModel()->selectedIndexes().isEmpty()) // scroll to selection { const QModelIndex &proxyIndex = m_ui->treeViewEdit->selectionModel()->selectedIndexes().first(); m_ui->treeViewEdit->scrollTo(proxyIndex); } // if onEnaDisa(); } void ParticipantEdit::onSwitchToTableView() { m_ui->treeViewEdit->hide(); m_ui->tableViewEdit->show(); if (!m_ui->tableViewEdit->selectionModel()->selectedIndexes().isEmpty()) // scroll to selection { const QModelIndex &proxyIndex = m_ui->tableViewEdit->selectionModel()->selectedIndexes().first(); m_ui->tableViewEdit->scrollTo(proxyIndex); } // if onEnaDisa(); } void ParticipantEdit::onSelectionChangedInTree(const QItemSelection &selected, const QItemSelection &deselected) { m_ui->tableViewEdit->blockSignals(true); QModelIndexList indexes = deselected.indexes(); for (const QModelIndex &sourceIndex : qAsConst(indexes)) { QModelIndex proxyIndex = m_proxyTable->mapFromSource(sourceIndex); if (m_ui->tableViewEdit->selectionModel()->selectedIndexes().contains(proxyIndex)) m_ui->tableViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Deselect, QItemSelectionModel::Rows}); } // for indexes = selected.indexes(); for (const QModelIndex &sourceIndex : qAsConst(indexes)) { QModelIndex proxyIndex = m_proxyTable->mapFromSource(sourceIndex); if (!m_ui->tableViewEdit->selectionModel()->selectedIndexes().contains(proxyIndex)) m_ui->tableViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Select, QItemSelectionModel::Rows}); } // for m_ui->tableViewEdit->blockSignals(false); } void ParticipantEdit::onSelectionChangedInTable(const QItemSelection &selected, const QItemSelection &deselected) { m_ui->treeViewEdit->blockSignals(true); QModelIndexList indexes = deselected.indexes(); for (const QModelIndex &sourceIndex : qAsConst(indexes)) { QModelIndex proxyIndex = m_proxyTree->mapFromSource(sourceIndex); if (m_ui->treeViewEdit->selectionModel()->selectedIndexes().contains(proxyIndex)) m_ui->treeViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Deselect, QItemSelectionModel::Rows}); } // for indexes = selected.indexes(); for (const QModelIndex &sourceIndex : qAsConst(indexes)) { QModelIndex proxyIndex = m_proxyTree->mapFromSource(sourceIndex); if (!m_ui->treeViewEdit->selectionModel()->selectedIndexes().contains(proxyIndex)) m_ui->treeViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Select, QItemSelectionModel::Rows}); } // for m_ui->treeViewEdit->blockSignals(false); } void ParticipantEdit::onFilterChanged(LtsDataView::SearchModel *from, LtsDataView::SearchModel *to) { for (int column = 0; column < to->columnCount(); ++column) { QModelIndex fromIndex = from->index(0, column, QModelIndex()); QModelIndex toIndex = to->index(0, column, QModelIndex()); QVariant columnFilter = from->data(fromIndex, Qt::EditRole); QVariant comboBoxData = from->data(fromIndex, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole); if (columnFilter != to->data(toIndex, Qt::EditRole)) { to->setData(toIndex, columnFilter, Qt::EditRole); to->setData(toIndex, from->data(fromIndex, Qt::DisplayRole).toString(), Qt::DisplayRole); } // if else if (comboBoxData != to->data(toIndex, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole)) { to->setData(toIndex, comboBoxData, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole); LtsWidgets::ComboboxFilterContent comboFilterContent = LtsWidgets::ComboboxFilterContent::fromVariant(m_model->headerData(column, Qt::Horizontal, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole)); if (comboBoxData.toInt() == 0) { to->setData(toIndex, tr(""), Qt::DisplayRole); } // if else { for (const LtsWidgets::ComboboxFilterData &data : qAsConst(comboFilterContent.data)) if (data.data == comboBoxData) { to->setData(toIndex, data.text, Qt::DisplayRole); break; } // if } // else } // else if } // for // undo selection m_ui->tableViewEdit->blockSignals(true); m_ui->tableViewEdit->selectionModel()->clearSelection(); m_ui->tableViewEdit->blockSignals(false); m_ui->treeViewEdit->blockSignals(true); m_ui->treeViewEdit->selectionModel()->clearSelection(); m_ui->treeViewEdit->blockSignals(false); } void ParticipantEdit::onSortTableView(int column, Qt::SortOrder order) { QModelIndex sourceIndex; if (!m_ui->treeViewEdit->selectionModel()->selectedIndexes().isEmpty()) // store selected item { QModelIndex proxyIndex = m_ui->tableViewEdit->selectionModel()->selectedIndexes().first(); sourceIndex = m_proxyTable->mapToSource(proxyIndex); m_ui->tableViewEdit->blockSignals(true); m_ui->tableViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Deselect, QItemSelectionModel::Rows}); m_ui->tableViewEdit->blockSignals(false); } // if m_proxyTable->onSort(column, order); if (sourceIndex != QModelIndex()) // reselect item with new index { QModelIndex proxyIndex = m_proxyTable->mapFromSource(sourceIndex); m_ui->tableViewEdit->blockSignals(true); m_ui->tableViewEdit->selectionModel()->select(proxyIndex, {QItemSelectionModel::Select, QItemSelectionModel::Rows}); m_ui->tableViewEdit->blockSignals(false); m_ui->tableViewEdit->scrollTo(proxyIndex); } // if } void ParticipantEdit::onColumnWidthChanged(QTreeView *to, const int &column, const int &width) { if (to->columnWidth(column) != width) to->setColumnWidth(column, width); } void ParticipantEdit::tableColumnOrganiserSetting() { // LtsColumnOrganiser m_ui->tableViewEdit->setColumnsOrganiserEnabled(identifier(), true); m_ui->treeViewEdit->setColumnsOrganiserEnabled(identifier(), true); } } // namespace ParticipantManagement