Files
qt5-legatus-legacy/Lts/Plugins/ParticipantManagement/Gui/ParticipantEdit/tactical_organisation.cpp
T
2026-08-13 18:33:10 +02:00

389 lines
13 KiB
C++
Executable File

/**
* @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 <Common/participant_beautify_view.h>
#include <Common/participant_edit_item.h>
#include <Plugins/ParticipantManagement/Gui/Common/participant_edit_model.h>
#include <Plugins/ParticipantManagement/Gui/Common/participant_search_modal.h>
#include <LtsSession/lts_session.h>
#include <QMenu>
#include <QTreeView>
#include <Core/core_ftc.h>
#include <LtsSession/lts_session_data.h>
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<ParticipantManagement::Interface::Dtc> &dtc,
const std::shared_ptr<ParticipantManagement::Interface::Ftc> &ftc,
const std::shared_ptr<ForeignGuiComboboxInterface> &ppmGuiCbxIfc,
const std::shared_ptr<AdvancedSymbology::ForeignDataInterface> &asbIfc)
{
if (m_dtc)
return false;
if (!dtc || !ftc || !ppmGuiCbxIfc)
return false;
if (!m_session)
return false;
if (!m_session->createTopicClient<Core::Interface::Ftc>(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<ParticipantData> &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<ParticipantTreeFilterProxyModel *>(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<const ParticipantTreeFilterProxyModel *>(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<QTreeView *>(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