/** * @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 #include #include #include #include #include #include namespace ParticipantManagement { //---------------------------------------------------------------------------- ParticipantPoolModel::ParticipantPoolModel(QObject *parent, LtsSession::LtsSessionDataDds *session, const std::shared_ptr &dtc, bool asSupportService) : LtsWidgets::LtsAbstractTableModel({tr("Delete"), tr("Designator/Identifier"), tr("Type Class"), tr("Type"), tr("Participant Id") }, parent) , AdvancedSymbology::TableModelSymbolRenderer(ColDesignatorIdentifier) , m_asSupportService(asSupportService) , m_dtc(dtc) , m_logger(getLogger("ParticipantPoolModel")) { // optional std::shared_ptr asb; session->getForeignDataInterface(asb); AdvancedSymbology::TableModelSymbolRenderer::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 datasToDelete; ParticipantManagement::ParticipantData dataToDelete = getRowData(row); removeRenderedSymbol(dataToDelete.id()); if (dataToDelete.id() != 0) //remove from DDS { dataToDelete.isDeleted(true); datasToDelete.append(dataToDelete); } LtsWidgets::LtsAbstractTableModel::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::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::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 ParticipantPoolModel::getPoolData() const { return m_dataToDelete + m_fullPoolData; } bool ParticipantPoolModel::onReinitModel() { clear(); const QMap 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 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 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 newParticipants = getSuperiorParticipants(part.id()); QVector 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 ParticipantPoolModel::getSuperiorParticipants(int participantId) { Participant part = m_dtc->participant(participantId); QVector 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::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::removeRow(i); break; } } } } //namespace ParticipantManagement