/** * @RDE_HEADER@ * This Model is used by the Force-Tree-Editors * $Id: participant_template_edit_model.cpp 50265 2020-03-05 15:48:19Z ehrlicher $ * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/ParticipantTemplates/participant_template_edit_model.cpp $ * $Author: ehrlicher $ */ #include "participant_template_edit_model.h" #include #include "participant_firing_unit_validator.h" #include #include namespace ParticipantManagement { QDataStream &operator<<(QDataStream &ds, const TemplateData &td) { ds << td.templateId() << td.hierarchyNumber() << td.equipmentNumber() << td.typeId() << QString::fromStdString(td.symbol()) << QString::fromStdString(td.name()) << QString::fromStdString(td.designator()) << QString::fromStdString(td.tacticalIdentifier()) << QString::fromStdString(td.info()) << td.modId() << td.superiorModId() << td.isDeleted(); return ds; } QDataStream &operator>>(QDataStream &ds, TemplateData &td) { int templateId, hierarchyNumber, equipmentNumber, typeId, modId, superiorModId; QString symbol, name, designator, tacticalIdentifier, info; bool isDeleted; ds >> templateId >> hierarchyNumber >> equipmentNumber >> typeId >> symbol >> name >> designator >> tacticalIdentifier >> info >> modId >> superiorModId >> isDeleted; td.templateId(templateId); td.equipmentNumber(equipmentNumber); td.typeId(typeId); td.symbol(symbol.toStdString()); td.name(name.toStdString()); td.designator(designator.toStdString()); td.tacticalIdentifier(tacticalIdentifier.toStdString()); td.info(info.toStdString()); td.modId(modId); td.superiorModId(superiorModId); td.isDeleted(isDeleted); return ds; } //---------------------------------------------------------------------------- ParticipantTemplateEditModel::ParticipantTemplateEditModel(QObject *parent, const std::shared_ptr &ppmDtc, const std::shared_ptr &asbIfc, int rootId) : ParticipantIconModelBase(ppmDtc, asbIfc, { tr("Designator"), tr("Tact. Identifier"), tr("Class"), tr("Type"), tr("Equip. No."), QStringLiteral("TemplateId") }, new NGLTreeItem(QVariant()), parent, getLogger("ParticipantTemplateEditModel")) , m_rootTemplateId(rootId) , m_firingUnitValidator(new FiringUnitValidator(ppmDtc, this)) { connect(this, &ParticipantTemplateEditModel::signalDataDropped, this, &ParticipantTemplateEditModel::internalDataChanged); qRegisterMetaTypeStreamOperators(); setMimeType(QStringLiteral("ParticipantTemplateEditModel_%1").arg(qlonglong(this))); init(); } QVariant ParticipantTemplateEditModel::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::TemplateData templateData = item->data().value(); switch (index.column()) { case ColDesignator: return QString::fromStdString(templateData.designator()); case ColIdentifier: return QString::fromStdString(templateData.tacticalIdentifier()); case ColTypeClass: return typeClassStringFromTypeId(templateData.typeId()); case ColType: return ParticipantManagement::trTypeName(m_types.value(templateData.typeId()).name()); case ColEquipmentNumber: if (templateData.equipmentNumber() != 0) return templateData.equipmentNumber(); return QVariant(); case ColTemplateId: return templateData.templateId(); } } return NGLTreeModel::data(index, role); } int ParticipantTemplateEditModel::columnCount(const QModelIndex &parent) const { int cc = NGLTreeModel::columnCount(parent); #ifndef LTS_DEBUG // template id only in dev mode cc -= 1; #endif return cc; } void ParticipantTemplateEditModel::init() { NGLTreeModel::resetModel(); m_dataToDelete.clear(); onInsertTemplate(m_rootTemplateId, QModelIndex(), false); } std::vector ParticipantTemplateEditModel::getChildrenOf(NGLTreeItem *item, int parentId, int hierarchyNumber, bool bOnlyModified, int &highestModId) const { if (!item) return {}; std::vector ret; ParticipantManagement::TemplateData templateData = item->data().value(); templateData.superiorModId(parentId); templateData.hierarchyNumber(hierarchyNumber); if (templateData.modId() == 0) templateData.modId(++highestModId); // check for children of this item for (int i = 0; i < item->childCount(); i++) { const auto c = getChildrenOf(item->child(i), templateData.modId(), i + 1, bOnlyModified, highestModId); ret.insert(ret.end(), c.begin(), c.end()); } if (!bOnlyModified || item->modified() || !ret.empty()) ret.push_back(std::move(templateData)); return ret; } //slots bool ParticipantTemplateEditModel::onInsertRow(const QModelIndex &parent, ParticipantManagement::TemplateData templateData) { templateData.templateId(0); //reset templateId is new! templateData.modId(0); //need new modId QVariant data; data.setValue(templateData); NGLTreeItem *item = createItem(data, QIcon()); item->setModified(true); emit internalDataChanged(); return NGLTreeModel::insertRow(rowCount(parent), parent, item); } bool ParticipantTemplateEditModel::onUpdateRow(const QModelIndex &index, const ParticipantManagement::TemplateData &templateData) { if (!index.isValid()) return false; QVariant data; data.setValue(templateData); NGLTreeItem *item = privateData(index); item->setIcon(QIcon()); tryRenderSymbol(QString::fromStdString(templateData.symbol()), item); emit internalDataChanged(); return setItemData(index, data, item->icon()); } bool ParticipantTemplateEditModel::onDeleteRow(const QModelIndex &idx, bool isFirstItem) { while (rowCount(idx) > 0) onDeleteRow(NGLTreeModel::index(0, 0, idx), false); if (m_rootTemplateId > 0 && isFirstItem) //edit mode { NGLTreeItem *item = privateData(idx); ParticipantManagement::TemplateData data = item->data().value(); if (data.modId() != 0) //only append old items to remove list { data.isDeleted(true); m_dataToDelete.append(data); } } removeRow(idx.row(), idx.parent()); emit internalDataChanged(); return true; } std::vector ParticipantTemplateEditModel::templateData(bool bOnlyModified) const { return templateData(QModelIndex(), false, bOnlyModified); } std::vector ParticipantTemplateEditModel::templateDataAsCopy(const QModelIndex &idx) const { return templateData(idx, true, false); } std::vector ParticipantTemplateEditModel::templateData(const QModelIndex &idx, bool bAsNewTemplate, bool bOnlyModified) const { if (m_rootItem->childCount() == 0) return {}; NGLTreeItem *root = idx.isValid() ? privateData(idx) : m_rootItem->child(0); // gehen wir mal davon aus, dass man nie mehr als 1mrd template - Einträge angelegt hat... int modId = 1000000000; auto templateDatas(getChildrenOf(root, 0, 1, bOnlyModified, modId)); if (bAsNewTemplate) { // reset templateId for (auto &templ : templateDatas) templ.templateId(0); } else { templateDatas.insert(templateDatas.end(), m_dataToDelete.begin(), m_dataToDelete.end()); } return templateDatas; } void ParticipantTemplateEditModel::dataCommitted() { m_dataToDelete.clear(); resetModifedFlag(m_rootItem); } QSet ParticipantTemplateEditModel::usedEquipmentNumbers() const { return usedEquipmentNumbers(m_rootItem); } TemplateData ParticipantTemplateEditModel::getTemplateData(const QModelIndex &index) const { return privateData(index)->data().value(); } QSet ParticipantTemplateEditModel::usedEquipmentNumbers(NGLTreeItem *item) const { QSet ret; const TemplateData data = item->data().value(); if (data.equipmentNumber() != 0) ret += data.equipmentNumber(); for (int i = 0; i < item->childCount(); ++i) ret += usedEquipmentNumbers(item->child(i)); return ret; } Qt::ItemFlags ParticipantTemplateEditModel::flags(const QModelIndex &index) const { Qt::ItemFlags f = NGLTreeModel::flags(index); // can not drag the whole tree, also we allow only dragging the first column if (index.column() != 0 || !index.parent().isValid()) f &= ~Qt::ItemIsDragEnabled; // drop also only on first column if (index.column() != 0) f &= ~Qt::ItemIsDropEnabled; return f; } bool ParticipantTemplateEditModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { if (!ParticipantIconModelBase::canDropMimeData(data, action, row, column, parent)) return false; // only drop on first column allowed if (column > 0) return false; // we do not allow creating a new root knot if (!parent.isValid() && m_rootItem->childCount() > 0) return false; const TemplateData &parentItem = getTemplateData(parent); if (!isUnitType(parentItem.typeId())) return false; DEBUG_LOG(m_logger) << "can drop on " << row << ", " << column << ", parent: " << parent.row() << "," << parent.column() << ", " << parent.isValid() << ", action: " << action; // make sure to not drop a parent on a child of this parent // QAbstractItemModel takes care for this within internal drops, // but can not do this for external drops (e.g. tactical organisation) const QVector tiSources = getMimeDataItems(data, m_mimeType); if (tiSources.isEmpty()) return false; // copy due to qDeleteAll! const TemplateData pdSource = tiSources.first()->data().value(); qDeleteAll(tiSources); if (m_firingUnitValidator) { FiringUnitValidator::FiringUnitRestrictions restrictions = m_firingUnitValidator->getRestrictions(this, parent); if (restrictions != FiringUnitValidator::NO_RESTRICTION) { if (restrictions.testFlag(FiringUnitValidator::IS_AT_MAX_CAPACITY)) return false; if (restrictions.testFlag(FiringUnitValidator::ONLY_SAME_GEAR_CLASS_ARTILLERY_ALLOWED)) { if (!m_firingUnitValidator->isSameGearClassArtillery(parentItem.typeId(), pdSource.typeId())) return false; if (m_firingUnitValidator->isArtilleryUnit(parentItem.typeId()) && m_firingUnitValidator->isArtilleryUnit(pdSource.typeId())) return false; } if (restrictions.testFlag(FiringUnitValidator::NO_ARTILLERY_PARTICIPANTS_ALLOWED)) { if (m_firingUnitValidator->isArtilleryWeaponSystem(pdSource.typeId())) return false; } if (restrictions.testFlag(FiringUnitValidator::NO_TYPE_CHANGE_ALLOWED)) { const Type allowedType = m_firingUnitValidator->getAllowedType(this, parent); if (pdSource.typeId() != allowedType.id()) return false; if (m_dtc->isAdlerVerbund(pdSource.typeId())) return false; } } } return true; } bool ParticipantTemplateEditModel::onInsertTemplate(int rootId, const QModelIndex &parentIndex, bool asCopy) { if (rootId <= 0) return false; QHash tmap; QHash> parentId2id; const QSet usedEquipNo(usedEquipmentNumbers()); QVector templateMap; m_dtc->templateParticipantsFromTemplateId(rootId, templateMap); for (const ParticipantManagement::TemplateParticipant &tmpl : qAsConst(templateMap)) { //should be there if (!tmpl.templateId()) continue; // parentId -> id for lookup parentId2id[tmpl.superiorTemplateId()] += tmpl.templateId(); auto data = m_dtc->convertFromParticipantTemplateToTemplateData(tmpl); if (asCopy) { data.templateId(0); data.modId(0); } if (usedEquipNo.contains(data.equipmentNumber())) data.equipmentNumber(0); tmap.insert(tmpl.templateId(), data); } NGLTreeItem *root = parentIndex.isValid() ? privateData(parentIndex) : m_rootItem; // insert recursivly insertChildrenOf(0, root, parentIndex, parentId2id, tmap); if (asCopy) emit internalDataChanged(); return true; } //---------------------------------------------------------------------------- void ParticipantTemplateEditModel::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 template with id " << childId << " - ignoring"; continue; } const int templateId = it.key(); // it.value().templateId() is optional and maybe not set const TemplateData &data = it.value(); if (data.hierarchyNumber() <= 0) { WARNING_LOG(m_logger) << "Invalid / no hierarchy number set for participant " << data.templateId() << " - ignoring"; continue; } auto hierIt = currentHierachyNumbers.insert(data.hierarchyNumber(), data.hierarchyNumber()); const int insertPos = std::distance(currentHierachyNumbers.begin(), hierIt); NGLTreeItem *childItem = createItem(QVariant::fromValue(data), QIcon()); DEBUG_LOG(m_logger) << "insert Child " << childItem << "(" << data.templateId() << ", " << data.hierarchyNumber() << ") to parent " << parent << " at pos " << insertPos; insertRow(insertPos, parentIdx, childItem); const QModelIndex childIdx = index(insertPos, 0, parentIdx); insertChildrenOf(templateId, childItem, childIdx, parentId2id, tmap); } } //---------------------------------------------------------------------------- NGLTreeItem *ParticipantTemplateEditModel::createItem(const QVariant &data, const QIcon &icon) const { auto ret = NGLTreeModel::createItem(data, icon); const TemplateData templateData = data.value(); if (templateData.symbol().size() != 0) { ParticipantTemplateEditModel *that = const_cast(this); // mutable that->tryRenderSymbol(QString::fromStdString(templateData.symbol()), ret); } return ret; } } //namespace ParticipantManagement