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

514 lines
17 KiB
C++
Executable File

/**
* @RDE_HEADER@
* HMI Plugin for ParticipantManagement - Participant Organisation Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-17
*/
#include "participant_orga_model.h"
#include <Plugins/ParticipantManagement/ppm_mime_datas.h>
#include <Common/participant_edit_model.h>
#include <ParticipantManagement/ppm_translation.h>
#include <Symbology/symbol_data_to_render_role.h>
#include <QMimeData>
#include <nglwidgets/ngltreeitem.h>
namespace ParticipantManagement
{
//----------------------------------------------------------------------------
ParticipantOrgaModel::ParticipantOrgaModel(PartyOrganisationType orgType,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<AdvancedSymbology::ForeignDataInterface> &asbIfc,
QObject *parent)
: ParticipantIconModelBase(ppmDtc,
asbIfc,
{ headerFromOrganisationType(orgType), tr("Participant Id") },
new NGLTreeItem(QVariant()),
parent,
getLogger(QStringLiteral("ParticipantOrgaModel.") + headerFromOrganisationType(orgType)))
, m_partyId(-1)
, m_orgType(orgType)
{
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::participantOrganisationChanged,
this, &ParticipantOrgaModel::onParticipantOrganisationChanged);
m_dtc->participantOrganisations([this](const ParticipantOrganisation &org) { m_participantOrganization[org.participantId()] = org;});
}
QVariant ParticipantOrgaModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || role != Qt::DisplayRole)
return NGLTreeModel::data(index, role);
NGLTreeItem *item = privateData(index);
if (item->data().canConvert<ParticipantManagement::ParticipantData>())
{
const ParticipantManagement::ParticipantData participantData = item->data().value<ParticipantManagement::ParticipantData>();
switch (index.column())
{
case ColDescription:
return QVariant(QString::fromStdString(participantData.designator()) + QLatin1Char('/') +
QString::fromStdString(participantData.tacticalIdentifier()));
case ColParticipantId:
return participantData.id();
}
}
return NGLTreeModel::data(index, role);
}
int ParticipantOrgaModel::columnCount(const QModelIndex &parent) const
{
int cc = NGLTreeModel::columnCount(parent);
#ifndef LTS_DEBUG
// participant id only in dev mode
cc -= 1;
#endif
return cc;
}
Qt::ItemFlags ParticipantOrgaModel::flags(const QModelIndex &index) const
{
return NGLTreeModel::flags(index) | Qt::ItemIsDropEnabled;
}
QStringList ParticipantOrgaModel::mimeTypes() const
{
switch (m_orgType)
{
case PartyOrganisationType::Party:
return { ParticipantPoolRoot, ParticipantPartyDetached };
case PartyOrganisationType::Attached:
case PartyOrganisationType::Cooperate:
return { ParticipantPoolRoot, ParticipantPoolDetached };
case PartyOrganisationType::Detached:
return { ParticipantPartyChild };
}
return QStringList();
}
QMimeData *ParticipantOrgaModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.size() < 1)
return nullptr;
const QModelIndex &idx = indexes.first();
if (!idx.isValid())
return nullptr;
// we only allow to move the complete tree here
if (m_orgType == PartyOrganisationType::Detached ||
m_orgType == PartyOrganisationType::Attached ||
m_orgType == PartyOrganisationType::Cooperate)
{
if (idx.parent().isValid())
return nullptr;
}
const ParticipantData data = privateData(idx)->data().value<ParticipantManagement::ParticipantData>();
if (data.id() == 0)
return nullptr;
if (m_orgType == PartyOrganisationType::Detached)
{
// if attached to another party, do not allow to move it back to origin party ...
auto it = m_participantOrganization.find(data.id());
if (it != m_participantOrganization.end() && it.value().currentPartyId() != 0)
return nullptr;
}
const QByteArray encodedData(QByteArray::number(data.id()));
QMimeData *mimeData = new QMimeData;
switch (m_orgType)
{
case PartyOrganisationType::Party:
mimeData->setData(idx.parent().isValid() ? ParticipantPartyChild : ParticipantPartyRoot, encodedData);
break;
case PartyOrganisationType::Detached:
mimeData->setData(ParticipantPartyDetached, encodedData);
break;
case PartyOrganisationType::Attached:
case PartyOrganisationType::Cooperate:
mimeData->setData(ParticipantPartyAttached, encodedData);
break;
}
return mimeData;
}
bool ParticipantOrgaModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
if (!NGLTreeModel::canDropMimeData(data, action, row, column, parent))
return false;
// in Party - Mode we don't accept a new pool template since only one tree is allowed
if (m_orgType == PartyOrganisationType::Party)
{
if (data->hasFormat(ParticipantPoolRoot) && m_rootItem->childCount() > 0)
return false;
if (data->hasFormat(ParticipantPartyDetached) && !parent.isValid())
return false;
}
if (m_orgType == PartyOrganisationType::Attached ||
m_orgType == PartyOrganisationType::Cooperate)
{
if (data->hasFormat(ParticipantPoolRoot))
return true;
// don't allow attach/cooperate of own subunits
bool bOk;
const int participantId = data->data(ParticipantPoolDetached).toInt(&bOk);
if (bOk)
{
auto it = m_participantOrganization.find(participantId);
if (it != m_participantOrganization.end() &&
it.value().originPartyId() == m_partyId)
return false;
}
}
return true;
}
bool ParticipantOrgaModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(parent)
// only in PartyOrganisationType::Party mode
if (action != Qt::MoveAction)
{
ERROR_LOG(m_logger) << "Drop: action is not move action: " << action;
return false;
}
// all data we accept here is packaged the same, the different mime type
// is only used to (dis)allow the moves to the different models
// since we can only move one item, the data is a simple integer (=participantId)
int participantId = -1;
for (const auto &mt : { ParticipantPoolRoot, ParticipantPartyChild, ParticipantPartyDetached, ParticipantPoolDetached })
{
if (data->hasFormat(mt))
{
bool bOk;
participantId = data->data(mt).toInt(&bOk);
if (bOk)
break;
}
}
DEBUG_LOG(m_logger) << "Drop: participantId: " << participantId;
if (participantId <= 0)
{
WARNING_LOG(m_logger) << "Can not decode mime data - ignoring";
return false;
}
// since inferiorParticipants is walking recursivly the tree, the vector is sorted
QVector<Participant> movedParticipants = inferiorParticipants(participantId);
if (movedParticipants.isEmpty())
{
WARNING_LOG(m_logger) << "Can not find valid data for participant " << participantId;
return false;
}
// when we are attached/detached/cooperate then the participant is a new leader -> superiorId = 0, hierarchyNumber = 1
movedParticipants[0].superiorId(0);
movedParticipants[0].hierarchyNumber(1);
int rootParticipantId = 0;
NGLTreeItem *rootItem = m_rootItem;
QModelIndex rootModelIndex;
if (m_orgType == PartyOrganisationType::Party && data->hasFormat(ParticipantPartyDetached))
{
if (!parent.isValid())
return false;
NGLTreeItem *item = privateData(parent);
if (!item)
return false;
if (!item->data().canConvert<ParticipantManagement::ParticipantData>())
return false;
const ParticipantManagement::ParticipantData participantData = item->data().value<ParticipantManagement::ParticipantData>();
if (participantData.id() == 0)
return false;
rootParticipantId = participantData.id();
rootItem = item;
rootModelIndex = parent;
movedParticipants[0].superiorId(rootParticipantId);
movedParticipants[0].hierarchyNumber(-1);
}
DEBUG_LOG(m_logger) << "Drop: #movedParticipants: " << movedParticipants.size();
QHash<int, ParticipantManagement::ParticipantData> tmap;
tmap.reserve(movedParticipants.size());
QHash<int, QSet<int>> parentId2id;
parentId2id.reserve(movedParticipants.size());
for (const Participant &part : qAsConst(movedParticipants))
{
if (part.isDeleted())
continue;
// parentId -> id for lookup
parentId2id[part.superiorId()] += part.id();
tmap.insert(part.id(), m_dtc->convertParticipantToParticipantData(part));
}
// insert recursivly
insertChildrenOf(rootParticipantId, rootItem, rootModelIndex, parentId2id, tmap);
// save current party config
emit participantDataChanged(movedParticipants.at(0).id());
return true;
}
bool ParticipantOrgaModel::isParticipantType(int source_row, const QModelIndex &source_parent) const
{
NGLTreeItem *parentItem = privateData(source_parent);
if (!parentItem)
return false;
const NGLTreeItem *childItem = parentItem->child(source_row);
if (!childItem)
return false;
if (!childItem->data().canConvert<ParticipantManagement::ParticipantData>())
return false;
const ParticipantManagement::ParticipantData participantData = childItem->data().value<ParticipantManagement::ParticipantData>();
return !isUnitType(participantData.typeId());
}
QVector<Participant> ParticipantOrgaModel::inferiorParticipants(int partId) const
{
QVector<Participant> retVector;
const Participant participant = m_dtc->participant(partId);
retVector.reserve(participant.inferiorIds().size() + retVector.size() + 1);
retVector.append(participant);
for (int inferiorId : participant.inferiorIds())
retVector.append(inferiorParticipants(inferiorId));
return retVector;
}
QVector<ParticipantData> ParticipantOrgaModel::getChildrenOf(NGLTreeItem *parentItem, int parentId, int hierarchyNumber, int &highestModId) const
{
QVector<ParticipantData> ret;
if (!parentItem)
return ret;
ParticipantManagement::ParticipantData participantData = parentItem->data().value<ParticipantManagement::ParticipantData>();
participantData.superiorModId(parentId);
participantData.hierarchyNumber(hierarchyNumber);
if (participantData.modId() == 0)
participantData.modId(++highestModId);
ret += participantData;
for (int i = 0; i < parentItem->childCount(); i++)
ret.append(getChildrenOf(parentItem->child(i), participantData.modId(), i + 1, highestModId));
return ret;
}
QVector<ParticipantData> ParticipantOrgaModel::participantData(int partyId) const
{
QVector<ParticipantData> ret;
// gehen wir mal davon aus, dass man nie mehr als 1mrd template - Einträge angelegt hat...
int modId = 1000000000;
for (int i = 0; i < m_rootItem->childCount(); ++i)
{
NGLTreeItem *rootKnot = m_rootItem->child(0);
ret += getChildrenOf(rootKnot, 0, 1, modId);
}
for (int i = 0; i < ret.size(); ++i)
ret[i].partyId(partyId);
return ret;
}
QVector<int> ParticipantOrgaModel::rootParticipantIds() const
{
QVector<int> ret;
for (int i = 0; i < m_rootItem->childCount(); ++i)
{
const NGLTreeItem *rootKnot = m_rootItem->child(i);
const ParticipantManagement::ParticipantData participantData = rootKnot->data().value<ParticipantManagement::ParticipantData>();
// should not happen but who knows...
if (participantData.id() == 0)
continue;
ret += participantData.id();
}
return ret;
}
void ParticipantOrgaModel::onReinitModel(int partyId)
{
m_partyId = partyId;
const ParticipantManagement::Party &party = m_dtc->party(m_partyId);
m_partyRole = AdvancedSymbology::roleToRole<>(party.role());
m_partyColor = QColor::fromRgb(QRgb(party.color()));
m_symbolResultMapping.clear();
NGLTreeModel::resetModel();
QHash<int, ParticipantData> tmap;
QHash<int, QSet<int>> parentId2id;
auto cb = [this, &tmap, &parentId2id](const Participant & part)
{
if (part.isDeleted())
return;
if (m_orgType != PartyOrganisationType::Detached && part.partyId() != m_partyId)
return;
if (m_orgType == PartyOrganisationType::Detached && part.partyId() == m_partyId)
return;
if (part.superiorId() == 0 && !isRootParticipantRelevant(part, m_partyId))
return;
// parentId -> id for lookup
parentId2id[part.superiorId()] += part.id();
tmap.insert(part.id(), m_dtc->convertParticipantToParticipantData(part));
};
m_dtc->participants(cb);
// insert recursivly
insertChildrenOf(0, m_rootItem, QModelIndex(), parentId2id, tmap);
}
void ParticipantOrgaModel::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;
}
}
void ParticipantOrgaModel::insertChildrenOf(int parentId,
NGLTreeItem *parent,
const QModelIndex &parentIdx,
const QHash<int, QSet<int>> &parentId2id,
const QHash<int, ParticipantData> &tmap)
{
const QSet<int> childIds = parentId2id.value(parentId);
QMap<int, int> currentHierachyNumbers; // no QSet, must be sorted!
for (int childId : childIds)
{
auto it = tmap.constFind(childId);
if (it == tmap.constEnd())
{
WARNING_LOG(m_logger) << "Can not find participant with id " << childId << " - ignoring";
continue;
}
const ParticipantData &data = it.value();
int insertPos = 0;
if (data.hierarchyNumber() > 0)
{
auto hierIt = currentHierachyNumbers.insert(data.hierarchyNumber(), data.hierarchyNumber());
insertPos = std::distance(currentHierachyNumbers.begin(), hierIt);
}
else
{
// append if no valid hierarchy number is set
const NGLTreeItem *treeItemParent = privateData(parentIdx);
if (treeItemParent)
insertPos = treeItemParent->childCount();
}
NGLTreeItem *childItem = createItem(QVariant::fromValue(data), QIcon());
DEBUG_LOG(m_logger) << "insert Child " << childItem << "(" << data.id() << ", " << data.hierarchyNumber()
<< ") to parent " << parent << " at pos " << insertPos;
insertRow(insertPos, parentIdx, childItem);
const QModelIndex childIdx = index(insertPos, 0, parentIdx);
insertChildrenOf(data.id(), childItem, childIdx, parentId2id, tmap);
}
}
QString ParticipantOrgaModel::headerFromOrganisationType(PartyOrganisationType orgType)
{
switch (orgType)
{
case PartyOrganisationType::Party:
return tr("Party");
case PartyOrganisationType::Attached:
return tr("Attached");
case PartyOrganisationType::Detached:
return tr("Detached");
case PartyOrganisationType::Cooperate:
return tr("Directed to Cooperate");
}
return QString();
}
bool ParticipantOrgaModel::isRootParticipantRelevant(const Participant &participant, int partyId) const
{
// this function assumes, that the participant is in the correct party
const int participantId = participant.id();
const int participantPartyId = participant.partyId();
const ParticipantOrganisation org = m_participantOrganization.value(participantId);
switch (m_orgType)
{
case PartyOrganisationType::Party:
return participantPartyId == partyId && org.participantId() == 0; // is 0 when not in m_participantOrganization
case PartyOrganisationType::Attached:
return participantPartyId == partyId && org.participantId() == participantId && org.isAttached();
case PartyOrganisationType::Detached:
return org.participantId() == participantId && org.originPartyId() == partyId;
case PartyOrganisationType::Cooperate:
return participantPartyId == partyId && org.participantId() == participantId && org.isCooperate();
}
return false;
}
NGLTreeItem *ParticipantOrgaModel::createItem(const QVariant &data, const QIcon &icon) const
{
auto ret = NGLTreeModel::createItem(data, icon);
const ParticipantData participantData = data.value<ParticipantData>();
if (participantData.symbol().size() != 0)
{
ParticipantOrgaModel *that = const_cast<ParticipantOrgaModel*>(this);
// mutable
that->tryRenderSymbol(QString::fromStdString(participantData.symbol()), ret);
}
return ret;
}
//----------------------------------------------------------------------------
void ParticipantOrgaProxyModel::onReinitModel(int partyId)
{
if (m_sourceModel)
m_sourceModel->onReinitModel(partyId);
}
//----------------------------------------------------------------------------
void ParticipantOrgaProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<ParticipantOrgaModel*>(sourceModel);
QSortFilterProxyModel::setSourceModel(sourceModel);
}
//----------------------------------------------------------------------------
bool ParticipantOrgaProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel || m_sourceModel->isParticipantType(source_row, source_parent))
return false;
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
} //namespace ParticipantManagement