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

423 lines
14 KiB
C++
Executable File

/**
* @RDE_HEADER@
* HMI Plugin for ParticipantManagement - Participant Pool Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-17
*/
#include "participant_pool_model.h"
#include <Plugins/ParticipantManagement/ppm_mime_datas.h>
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data_dds.h>
#include <Plugins/AdvancedSymbology/asb_data_plugin_interface.h>
#include <Symbology/symbol_data_to_render_role.h>
#include <QMimeData>
#include <QTimer>
namespace ParticipantManagement
{
//----------------------------------------------------------------------------
ParticipantPoolModel::ParticipantPoolModel(QObject *parent,
LtsSession::LtsSessionDataDds *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtc,
bool asSupportService)
: LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>({tr("Delete"),
tr("Designator/Identifier"),
tr("Type Class"),
tr("Type"),
tr("Participant Id") },
parent)
, AdvancedSymbology::TableModelSymbolRenderer<ParticipantPoolModel>(ColDesignatorIdentifier)
, m_asSupportService(asSupportService)
, m_dtc(dtc)
, m_logger(getLogger("ParticipantPoolModel"))
{
// optional
std::shared_ptr<AdvancedSymbology::ForeignDataInterface> asb;
session->getForeignDataInterface<AdvancedSymbology::DataInterface>(asb);
AdvancedSymbology::TableModelSymbolRenderer<ParticipantPoolModel>::init(asb, this);
onReinitModel();
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &ParticipantPoolModel::onParticipantDataChanged);
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::participantOrganisationChanged,
this, &ParticipantPoolModel::onParticipantOrganisationChanged);
m_dtc->participantOrganisations([this](const ParticipantOrganisation &org) { onParticipantOrganisationChanged(org, DataModification::eAdded); });
const ParticipantManagement::Party &party = m_dtc->party(poolPartyId());
setDefaultRoleAndColor(AdvancedSymbology::roleToRole<>(party.role()),
QColor::fromRgb(QRgb(party.color())));
}
bool ParticipantPoolModel::canRemoveRow(int row) const
{
if (row < 0 || row >= m_vecData.size())
return false;
return true;
/*
// commented out - we want to be able to also deleted detached structures.
const ParticipantManagement::ParticipantData &data = m_vecData.at(row);
if (data.id() == 0)
return true;
return !(m_participantOrganization.contains(data.id()));
*/
}
bool ParticipantPoolModel::removeRow(int row)
{
if (row < 0 || row >= m_vecData.size())
return false;
QVector<ParticipantData> datasToDelete;
ParticipantManagement::ParticipantData dataToDelete = getRowData(row);
removeRenderedSymbol(dataToDelete.id());
if (dataToDelete.id() != 0) //remove from DDS
{
dataToDelete.isDeleted(true);
datasToDelete.append(dataToDelete);
}
LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>::removeRow(row);
emit modifyParticipantData(datasToDelete, QString());
return true;
}
QVariant ParticipantPoolModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index) || (role != Qt::DisplayRole && role != Qt::DecorationRole))
return QVariant();
const ParticipantManagement::ParticipantData &participantData = m_vecData.at(index.row());
if (role == Qt::DecorationRole)
return decorationData(index, participantData.id());
switch (index.column())
{
case ColRemove:
break;
case ColDesignatorIdentifier:
return QString(QString::fromStdString(participantData.designator()) + QLatin1Char('/') +
QString::fromStdString(participantData.tacticalIdentifier()));
case ColTypeClass:
return ParticipantManagement::toString(m_dtc->type(participantData.typeId()).typeClass());
case ColType:
return ParticipantManagement::trTypeName(m_dtc->type(participantData.typeId()).name());
case ColParticipantId:
return participantData.id();
}
return QVariant();
}
int ParticipantPoolModel::columnCount(const QModelIndex &parent) const
{
int cc = LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>::columnCount(parent);
#ifndef LTS_DEBUG
// participant id only in dev mode
cc -= 1;
#endif
return cc;
}
QMimeData *ParticipantPoolModel::mimeData(const QModelIndexList &indexes) const
{
// nicht != 1 da eine komplette Zeile markiert ist
if (indexes.size() < 1)
return nullptr;
const QModelIndex &idx = indexes.first();
if (!indexIsValid(idx))
return nullptr;
const ParticipantData &data = m_vecData.at(idx.row());
if (data.id() == 0)
return nullptr;
const QByteArray encodedData(QByteArray::number(data.id()));
QMimeData *mimeData = new QMimeData;
const QString mimeType = m_participantOrganization.contains(data.id()) ? ParticipantPoolDetached : ParticipantPoolRoot;
mimeData->setData(mimeType, encodedData);
return mimeData;
}
Qt::ItemFlags ParticipantPoolModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags retFlags = LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>::flags(index);
retFlags |= Qt::ItemIsDragEnabled;
retFlags |= Qt::ItemIsDropEnabled;
return retFlags;
}
bool ParticipantPoolModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
if (action != Qt::CopyAction && action != Qt::MoveAction)
return false;
DEBUG_LOG(m_logger) << "dropMimeData: " << data->formats().join(QLatin1Char(','));
if (!data->hasFormat(ParticipantTemplateRoot) &&
!data->hasFormat(ParticipantPartyRoot) &&
!data->hasFormat(ParticipantPartyAttached))
return false;
if (data->hasFormat(ParticipantManagement::ParticipantTemplateRoot))
{
bool bOk;
const int id = data->data(ParticipantManagement::ParticipantTemplateRoot).toInt(&bOk);
if (!bOk)
return false;
emit startUnitNamer(id);
}
else // == ParticipantPartyRoot | ParticipantPartyAttached
{
const QString mimeType = data->hasFormat(ParticipantPartyRoot) ? ParticipantPartyRoot : ParticipantPartyAttached;
bool bOk;
const int id = data->data(mimeType).toInt(&bOk);
if (!bOk)
return false;
moveFromPartyToPool(id, mimeType);
}
return true;
}
QStringList ParticipantPoolModel::mimeTypes() const
{
return { ParticipantTemplateRoot, ParticipantPartyRoot, ParticipantPartyAttached};
}
QVector<ParticipantData> ParticipantPoolModel::getPoolData() const
{
return m_dataToDelete + m_fullPoolData;
}
bool ParticipantPoolModel::onReinitModel()
{
clear();
const QMap<qint32, ParticipantManagement::Participant> participantMap(m_dtc->participants());
for (const ParticipantManagement::Participant &part : participantMap)
{
if (part.superiorId() > 0 || part.partyId() != poolPartyId() || part.isDeleted())
continue;
if (!isValidTypeClass(part.typeId()))
continue;
insertRow(part);
}
return true;
}
bool ParticipantPoolModel::addNewTemplateToPool(int id, const QString &designator, const QString &tactIdentifier)
{
QVector<TemplateParticipant> templateDatas;
m_dtc->templateParticipantsFromTemplateId(id, templateDatas);
//replace first UnitName
TemplateParticipant &root = templateDatas[0];
root.superiorTemplateId(0);
root.hierarchyNumber(1);
root.designator(designator.toStdString());
root.tacticalIdentifier(tactIdentifier.toStdString());
QVector<ParticipantManagement::ParticipantData> newPool;
newPool.reserve(templateDatas.size());
for (const TemplateParticipant &participant : qAsConst(templateDatas))
{
ParticipantData newData;
convertFromParticipantTemplateToParticipantData(participant, newData);
newPool.append(newData);
}
emit modifyParticipantData(newPool, QString());
return true;
}
void ParticipantPoolModel::onParticipantDataChanged(const Participant &data, DataModification modType)
{
if (!isValidTypeClass(data.typeId()))
return;
if (data.isDeleted())
modType = DataModification::eRemoved;
switch (modType)
{
case DataModification::eModified:
for (int i = 0; i < m_vecData.size(); ++i)
{
const ParticipantData &part = m_vecData.at(i);
if (part.id() == data.id())
{
DEBUG_LOG(m_logger) << "Modifed participant " << data.id() << ", old party: " << part.partyId() << ", new: " << data.partyId();
// no longer in pool?
if (data.partyId() != poolPartyId())
{
// do not call ParticipantPoolModel::removeRow() since the participant was not removed
// from within ParticipantPoolModel
// delay the remove of this participant since it can happen due to a dnd move operation
// which will cause a removeRow() afterwards and another entry will be removed then too ...
const int participantId = data.id();
QTimer::singleShot(100, this, [this, participantId]() { tryRemoveParticipantFromModel(participantId); });
}
else
{
setRowData(i, m_dtc->convertParticipantToParticipantData(data));
}
return;
}
}
// new item -> try to add
Q_FALLTHROUGH();
case DataModification::eAdded:
if (data.superiorId() != 0 && !m_participantOrganization.contains(data.id()))
return;
if (data.partyId() != poolPartyId())
return;
insertRow(data);
break;
case DataModification::eRemoved:
if (data.partyId() != poolPartyId())
return;
tryRemoveParticipantFromModel(data.id());
break;
}
}
void ParticipantPoolModel::onParticipantOrganisationChanged(const ParticipantOrganisation &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_participantOrganization[data.participantId()] = data;
break;
case DataModification::eRemoved:
m_participantOrganization.remove(data.participantId());
break;
}
}
bool ParticipantPoolModel::moveFromPartyToPool(int rootParticipantId, const QString &mimeType)
{
ERROR_LOG(m_logger) << "Moving participant " << rootParticipantId << " from party to root with mime type " << mimeType;
//start with building the tree
const Participant part = m_dtc->participant(rootParticipantId);
const QVector<Participant> newParticipants = getSuperiorParticipants(part.id());
QVector<ParticipantData> dataToSend;
dataToSend.reserve(newParticipants.size());
for (const Participant &part : newParticipants)
{
ParticipantData data = m_dtc->convertParticipantToParticipantData(part);
data.partyId(poolPartyId());
if (data.id() == rootParticipantId)
{
// the new root has no superior and a hierarchy number of 1
data.superiorModId(0);
data.hierarchyNumber(1);
}
dataToSend.append(data);
}
if (dataToSend.isEmpty())
return false;
emit modifyParticipantData(dataToSend, mimeType);
return true;
}
QVector<Participant> ParticipantPoolModel::getSuperiorParticipants(int participantId)
{
Participant part = m_dtc->participant(participantId);
QVector<Participant> retParticipants;
retParticipants.append(part);
for (auto const &inferierPart : part.inferiorIds())
retParticipants.append(getSuperiorParticipants(inferierPart));
return retParticipants;
}
bool ParticipantPoolModel::convertFromParticipantTemplateToParticipantData(const TemplateParticipant &inputParticipant, ParticipantData &outPutData)
{
//No assignsState out of template
outPutData.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
// no participantId out of template
outPutData.id(0);
outPutData.partyId(poolPartyId());
//this could be intensive.. what about the second template with equipNumbers/
if (inputParticipant.equipmentNumber() != 0)
{
outPutData.equipmentNumber(inputParticipant.equipmentNumber());
outPutData.assignState(ParticipantAssignState::HandoutExcon);
}
outPutData.hierarchyNumber(inputParticipant.hierarchyNumber());
outPutData.info() = inputParticipant.info();
outPutData.name() = inputParticipant.name();
outPutData.designator() = inputParticipant.designator();
outPutData.tacticalIdentifier(inputParticipant.tacticalIdentifier());
outPutData.symbol(inputParticipant.symbol());
outPutData.typeId(inputParticipant.typeId());
outPutData.isSimulated(false);
//modIDgames
outPutData.modId(inputParticipant.templateId());
outPutData.superiorModId(inputParticipant.superiorTemplateId());
outPutData.hierarchyNumber(inputParticipant.hierarchyNumber());
return true;
}
bool ParticipantPoolModel::isValidTypeClass(int typeId) const
{
const TypeClass tc = m_dtc->type(typeId).typeClass();
switch (tc.underlying())
{
case TypeClass_def::Unit:
return !m_asSupportService;
case TypeClass_def::SupportUnit:
return m_asSupportService;
default:
break;
}
return false;
}
void ParticipantPoolModel::insertRow(const Participant &participant)
{
tryRenderSymbol(QString::fromStdString(participant.symbol()), participant.id());
LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>::insertRow(
m_dtc->convertParticipantToParticipantData(participant));
}
void ParticipantPoolModel::tryRemoveParticipantFromModel(int participantId)
{
for (int i = 0; i < m_vecData.size(); ++i)
{
const ParticipantData &part = m_vecData.at(i);
if (part.id() == participantId)
{
DEBUG_LOG(m_logger) << "Removing participant " << participantId;
// we must not call removeRow(i) since this will trigger a ModifyRequest which removes the complete subtree ...
LtsWidgets::LtsAbstractTableModel<ParticipantManagement::ParticipantData>::removeRow(i);
break;
}
}
}
} //namespace ParticipantManagement