389 lines
13 KiB
C++
Executable File
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
|