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

469 lines
16 KiB
C++
Executable File

/**
* @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 <ParticipantManagement/ppm_translation.h>
#include "participant_firing_unit_validator.h"
#include <nglwidgets/ngltreeitem.h>
#include <QSet>
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<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<AdvancedSymbology::ForeignDataInterface> &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<ParticipantManagement::TemplateData>();
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>())
{
ParticipantManagement::TemplateData templateData = item->data().value<ParticipantManagement::TemplateData>();
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<TemplateData> ParticipantTemplateEditModel::getChildrenOf(NGLTreeItem *item,
int parentId,
int hierarchyNumber,
bool bOnlyModified,
int &highestModId) const
{
if (!item)
return {};
std::vector<TemplateData> ret;
ParticipantManagement::TemplateData templateData = item->data().value<ParticipantManagement::TemplateData>();
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<ParticipantManagement::TemplateData>();
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<ParticipantManagement::TemplateData> ParticipantTemplateEditModel::templateData(bool bOnlyModified) const
{
return templateData(QModelIndex(), false, bOnlyModified);
}
std::vector<ParticipantManagement::TemplateData> ParticipantTemplateEditModel::templateDataAsCopy(const QModelIndex &idx) const
{
return templateData(idx, true, false);
}
std::vector<ParticipantManagement::TemplateData> 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<int> ParticipantTemplateEditModel::usedEquipmentNumbers() const
{
return usedEquipmentNumbers(m_rootItem);
}
TemplateData ParticipantTemplateEditModel::getTemplateData(const QModelIndex &index) const
{
return privateData(index)->data().value<ParticipantManagement::TemplateData>();
}
QSet<int> ParticipantTemplateEditModel::usedEquipmentNumbers(NGLTreeItem *item) const
{
QSet<int> ret;
const TemplateData data = item->data().value<ParticipantManagement::TemplateData>();
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<NGLTreeItem *> tiSources = getMimeDataItems(data, m_mimeType);
if (tiSources.isEmpty())
return false;
// copy due to qDeleteAll!
const TemplateData pdSource = tiSources.first()->data().value<ParticipantManagement::TemplateData>();
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<int, TemplateData> tmap;
QHash<int, QSet<int>> parentId2id;
const QSet<int> usedEquipNo(usedEquipmentNumbers());
QVector<ParticipantManagement::TemplateParticipant> 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<int, QSet<int>> &parentId2id,
const QHash<int, TemplateData> &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 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<TemplateData>();
if (templateData.symbol().size() != 0)
{
ParticipantTemplateEditModel *that = const_cast<ParticipantTemplateEditModel*>(this);
// mutable
that->tryRenderSymbol(QString::fromStdString(templateData.symbol()), ret);
}
return ret;
}
} //namespace ParticipantManagement