514 lines
17 KiB
C++
Executable File
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
|