/** * @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 #include #include #include #include #include namespace ParticipantManagement { //---------------------------------------------------------------------------- ParticipantOrgaModel::ParticipantOrgaModel(PartyOrganisationType orgType, const std::shared_ptr &ppmDtc, const std::shared_ptr &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()) { const ParticipantManagement::ParticipantData participantData = item->data().value(); 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(); 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 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()) return false; const ParticipantManagement::ParticipantData participantData = item->data().value(); 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 tmap; tmap.reserve(movedParticipants.size()); QHash> 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()) return false; const ParticipantManagement::ParticipantData participantData = childItem->data().value(); return !isUnitType(participantData.typeId()); } QVector ParticipantOrgaModel::inferiorParticipants(int partId) const { QVector 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 ParticipantOrgaModel::getChildrenOf(NGLTreeItem *parentItem, int parentId, int hierarchyNumber, int &highestModId) const { QVector ret; if (!parentItem) return ret; ParticipantManagement::ParticipantData participantData = parentItem->data().value(); 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 ParticipantOrgaModel::participantData(int partyId) const { QVector 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 ParticipantOrgaModel::rootParticipantIds() const { QVector ret; for (int i = 0; i < m_rootItem->childCount(); ++i) { const NGLTreeItem *rootKnot = m_rootItem->child(i); const ParticipantManagement::ParticipantData participantData = rootKnot->data().value(); // 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 tmap; QHash> 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> &parentId2id, const QHash &tmap) { const QSet childIds = parentId2id.value(parentId); QMap 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(); if (participantData.symbol().size() != 0) { ParticipantOrgaModel *that = const_cast(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(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