/** * @RDE_HEADER@ * Tactical Organisation of Parties (during exercise execution) * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2016-10-05 */ #include "tactical_organisation.h" #include "ui_tactical_organisation.h" #include #include #include #include #include #include #include #include #include namespace ParticipantManagement { int TacticalOrganisation::s_preselectedPartyLeft = -1; int TacticalOrganisation::s_preselectedPartyRight = -1; TacticalOrganisation::TacticalOrganisation(LtsSession::LtsSession *session, QWidget *parent) : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel) , m_session(session) , m_requestHelper(new LtsUtils::RequestHelper(this)) , m_addEquipmentNumberAction(new QAction(tr("Add Participant"), this)) , m_tvContextMenu(nullptr) , m_newItemDlg(nullptr) , m_ui(new Ui::TacticalOrganisation) , m_logger(getLogger("TacticalOrganisation")) { m_ui->setupUi(this); connect(m_addEquipmentNumberAction, &QAction::triggered, this, &TacticalOrganisation::onAddParticipant); setOnlineHelpFilename(QStringLiteral("Situation-Tactical_Organisation")); // deactivate TopicLockHandler created by framework setNoTopicLock(); } TacticalOrganisation::~TacticalOrganisation() { if (m_unlockId) m_coreFtc->tryUnlockEntityRequest(Core::TryUnlockEntityRequest(m_unlockId)); delete m_ui; } bool TacticalOrganisation::init(const std::shared_ptr &dtc, const std::shared_ptr &ftc, const std::shared_ptr &ppmGuiCbxIfc, const std::shared_ptr &asbIfc) { if (m_dtc) return false; if (!dtc || !ftc || !ppmGuiCbxIfc) return false; if (!m_session) return false; if (!m_session->createTopicClient(m_coreFtc)) { ERROR_LOG(m_logger) << LOGFUNCTION << " create coreFtc failed"; return false; } m_coreFtc->enableAll(); m_dtc = dtc; m_ftc = ftc; m_ppmGuiCbxIfc = ppmGuiCbxIfc; m_asbIfc = asbIfc; connect(m_ui->cbPartyLeft, &LtsWidgets::LtsComboBox::currentTextChanged, this, &TacticalOrganisation::onReinitLeft); connect(m_ui->cbPartyRight, &LtsWidgets::LtsComboBox::currentTextChanged, this, &TacticalOrganisation::onReinitRight); for (auto tv : {m_ui->dvPartyLeft->treeView(), m_ui->dvPartyRight->treeView()}) { tv->setDragEnabled(true); tv->setDragDropMode(QAbstractItemView::DragDrop); tv->setDefaultDropAction(Qt::MoveAction); tv->setContextMenuPolicy(Qt::CustomContextMenu); connect(tv, &QTreeView::customContextMenuRequested, this, &TacticalOrganisation::showContextMenu); } m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbPartyLeft, ParticipantManagement::ForeignGuiComboboxInterface::CbxPartyFilter::NormalPool); m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbPartyRight, ParticipantManagement::ForeignGuiComboboxInterface::CbxPartyFilter::NormalPool); int idx = m_ui->cbPartyLeft->findData(s_preselectedPartyLeft); if (idx >= 0) m_ui->cbPartyLeft->setCurrentIndex(idx); idx = m_ui->cbPartyRight->findData(s_preselectedPartyRight); if (idx >= 0) m_ui->cbPartyRight->setCurrentIndex(idx); onReinitLeft(); onReinitRight(); initLockedState(); return true; } void TacticalOrganisation::retranslate() { translate(m_ui, tr("Tactical Organisation")); } bool TacticalOrganisation::apply() { ModifyParticipantDataRequest req; std::vector &dataToSend = req.data(); for (const ParticipantEditModel *model : qAsConst(m_participantModels)) { const auto c = model->participantData(); dataToSend.insert(dataToSend.end(), c.begin(), c.end()); } if (req.data().size() == 0) return true; return m_requestHelper->startSingleRequest([this, req]() { return m_ftc->modifyParticipantDataRequest(req, this); }); } void TacticalOrganisation::onModifyParticipantDataReply(const Core::FunctionReplyObject &replyObject) { if (!replyObject.error()) { for (ParticipantEditModel *model : qAsConst(m_participantModels)) model->dataCommitted(); } m_requestHelper->requestReply(replyObject); } void TacticalOrganisation::onReinitLeft() { const int partyId = m_ui->cbPartyLeft->currentData().toInt(); s_preselectedPartyLeft = partyId; auto *model = modelForParty(partyId, true); INFO_LOG(m_logger) << "Set model left: " << model; m_ui->dvPartyLeft->setModel(model); #ifndef LTS_DEBUG m_ui->dvPartyLeft->setColumnHidden(ParticipantEditModel::ColParticipantId, true); #endif beautifyTreeView(m_ui->dvPartyLeft->treeView(), model); } void TacticalOrganisation::onReinitRight() { const int partyId = m_ui->cbPartyRight->currentData().toInt(); s_preselectedPartyRight = partyId; auto *model = modelForParty(partyId, false); INFO_LOG(m_logger) << "Set model right: " << model; m_ui->dvPartyRight->setModel(model); #ifndef LTS_DEBUG m_ui->dvPartyRight->setColumnHidden(ParticipantEditModel::ColParticipantId, true); #endif beautifyTreeView(m_ui->dvPartyRight->treeView(), model); } void TacticalOrganisation::onAddParticipant() { ParticipantSearchModal *sm = new ParticipantSearchModal(this); if (!sm->init(m_dtc)) { sm->deleteLater(); return; } connect(sm, &ParticipantSearchModal::participantSelected, this, &TacticalOrganisation::onParticipantSelected); sm->show(); } void TacticalOrganisation::onParticipantSelected(int participantId) { auto s = sender(); if (s) s->deleteLater(); if (!m_tvContextMenu) return; const QModelIndex newIdx = m_tvContextMenu->currentIndex(); if (!newIdx.isValid()) return; Participant participant = m_dtc->participant(participantId); if (participant.id() != participantId) return; ParticipantTreeFilterProxyModel *newModel = static_cast(m_tvContextMenu->model()); ParticipantEditModel *oldPem = editModelForParty(participant.partyId()); ParticipantEditModel *newPem = editModelForParty(newModel->partyId()); QModelIndex oldIdx = oldPem->indexFromParticipantId(participantId); if (!oldIdx.isValid()) { for (ParticipantEditModel *model : qAsConst(m_participantModels)) { oldPem = model; oldIdx = oldPem->indexFromParticipantId(participantId); if (oldIdx.isValid()) break; } if (!oldIdx.isValid()) return; } const ParticipantData pd = oldPem->privateData(oldIdx)->participant; oldPem->removeRow(oldIdx.row(), oldIdx.parent()); newPem->insertParticipant(newModel->mapToSource(newIdx), pd, false); } void TacticalOrganisation::editParticipant(const QModelIndex &idx) { if (m_newItemDlg || !idx.isValid()) return; const ParticipantTreeFilterProxyModel *pfpm = qobject_cast(idx.model()); if (!pfpm) return; const QModelIndex srcIdx = pfpm->mapToSource(idx); if (!srcIdx.isValid()) return; ParticipantEditModel *pem = editModelForParty(pfpm->partyId()); const auto pd = pem->privateData(srcIdx); if (!pd) return; m_newItemDlg = new ParticipantEditItem(false, m_session, this); m_newItemDlg->setAttribute(Qt::WA_DeleteOnClose); if (!m_newItemDlg->init(srcIdx, pfpm->partyId(), m_dtc, m_ppmGuiCbxIfc, m_asbIfc, pd->participant)) { delete m_newItemDlg; return; } QObject::connect(m_newItemDlg, &ParticipantEditItem::destroyed, this, [this]{ m_newItemDlg = nullptr; }); QObject::connect(m_newItemDlg, &ParticipantEditItem::updateItem, pem, &ParticipantEditModel::onUpdateRow); m_newItemDlg->show(); } // duplicated from ParticipantTreeEditHandler since we need slight modifcations here void TacticalOrganisation::showContextMenu(const QPoint position) { m_tvContextMenu = qobject_cast(sender()); if (!m_tvContextMenu) return; QModelIndex idx = m_tvContextMenu->currentIndex(); if (!idx.isValid()) return; if (idx.column() != 0) idx = m_tvContextMenu->model()->index(idx.row(), 0, idx.parent()); QMenu menu; QAction *editParticipantAction = new QAction(tr("Edit"), &menu); connect(editParticipantAction, &QAction::triggered, this, [this, idx]() { editParticipant(idx); }); menu.addAction(editParticipantAction); if (m_tvContextMenu->model()->rowCount(idx) != 0) { menu.addAction(m_addEquipmentNumberAction); if (m_tvContextMenu->isExpanded(idx)) { QAction *collapseAction = new QAction(tr("Collapse"), &menu); connect(collapseAction, &QAction::triggered, this, [this, idx]() { m_tvContextMenu->collapse(idx);}); menu.addAction(collapseAction); QAction *collapseAllAction = new QAction(tr("Collapse all"), &menu); connect(collapseAllAction, &QAction::triggered, this, [this, idx]() { expandNodeChildren(m_tvContextMenu, idx, false); }); menu.addAction(collapseAllAction); } else { QAction *expandAction = new QAction(tr("Expand"), &menu); connect(expandAction, &QAction::triggered, this, [this, idx]() { m_tvContextMenu->expand(idx);}); menu.addAction(expandAction); } QAction *expandAllAction = new QAction(tr("Expand all"), &menu); connect(expandAllAction, &QAction::triggered, this, [this, idx]() { expandNodeChildren(m_tvContextMenu, idx, true); }); menu.addAction(expandAllAction); } if (!menu.actions().isEmpty()) menu.exec(m_tvContextMenu->viewport()->mapToGlobal(position)); } // duplicated from ParticipantTreeEditHandler since we can't use it here :( /* * If 'expand' is true all subnodes of the currently selected node will be expanded. * If 'expand' is false all subnodes of the currently selected node will be collapsed. */ void TacticalOrganisation::expandNodeChildren(QTreeView *pTreeView, const QModelIndex &index, bool expand) { if (!index.isValid()) return; int childCount = index.model()->rowCount(index); for (int i = 0; i < childCount; i++) { const QModelIndex &child = index.model()->index(i,0, index); // Recursively call the function for each child node. expandNodeChildren(pTreeView, child, expand); } if(expand) pTreeView->expand(index); else pTreeView->collapse(index); } ParticipantTreeFilterProxyModel *TacticalOrganisation::modelForParty(int partyId, bool bLeftSide) { // we need a different filter model for left and for right side! int partyIdToSearch = bLeftSide ? partyId : partyId + 10000000; auto ret = m_participantProxyModels.value(partyIdToSearch); if (ret) return ret; ret = new ParticipantTreeFilterProxyModel(this); ret->setSourceModel(editModelForParty(partyId)); m_participantProxyModels.insert(partyIdToSearch, ret); return ret; } ParticipantEditModel *TacticalOrganisation::editModelForParty(int partyId) { auto pem = m_participantModels.value(partyId); if (!pem) { pem = new ParticipantEditModel(this, m_dtc, m_asbIfc); pem->onReinitModel(partyId); pem->setMimeType(QStringLiteral("TacticalOrganisation")); connect(pem, &ParticipantEditModel::internalDataChanged, this, &TacticalOrganisation::slotChangesMade); m_participantModels.insert(partyId, pem); } return pem; } void TacticalOrganisation::initLockedState() { Core::TryLockEntityRequest lockRequest; lockRequest.id(0); lockRequest.entityTypeName(QString(QStringLiteral("PpmTacticalOrganisation")).toStdString()); lockRequest.entityId(m_entityId); lockRequest.exerciseId(m_session->exerciseId()); lockRequest.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); lockRequest.workstationId(LtsSessionData::LtsSessionData::getInstance()->getWorkstationId()); Core::TryLockEntityReply lockReply = m_coreFtc->tryLockEntityRequest(lockRequest); if (lockReply.alreadyLocked().is_set() && QString::fromStdString(lockReply.alreadyLocked().get().operatorName()) != LtsSessionData::LtsSessionData::getInstance()->getOperatorName()) { m_topicLockedBy = QString::fromStdString(lockReply.alreadyLocked().get().operatorName()); setUnchangeable(true); retranslate(); return; } m_unlockId = lockReply.id(); } void TacticalOrganisation::tableColumnOrganiserSetting() { //LtsColumnOrganiser m_ui->dvPartyLeft->setColumnsOrganiserEnabled(identifier(), true); m_ui->dvPartyRight->setColumnsOrganiserEnabled(identifier(), true); } } // namespace ParticipantManagement