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

1536 lines
53 KiB
C++
Executable File

/**
* @RDE_HEADER@
* HMI Plugin for ParticipantManagement - Participant Edit Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-17
*/
#include "participant_config_model.h"
#include <ParticipantManagement/ppm_translation.h>
#include <Plugins/AdvancedSymbology/asb_data_plugin_interface.h>
#include <Symbology/symbol_data_to_render_role.h>
#include <nglwidgets/ngltreeitem.h>
#include <LtsSession/lts_session_data_dds.h>
#include <algorithm>
#include <QTreeView>
#include <QHeaderView>
#include <QTimer>
#include <QToolTip>
namespace dds
{
namespace core
{
static inline uint qHash(const ParticipantManagement::TypeClass &tc)
{
return ::qHash(tc.underlying());
}
}
}
typedef QVector<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
namespace ParticipantManagement
{
static QVariant participantToQVariant(const ParticipantData &pd)
{
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << pd.id()
<< pd.partyId()
<< pd.hierarchyNumber()
<< (pd.equipmentNumber().is_set() ? pd.equipmentNumber().get() : -1)
<< pd.typeId()
<< pd.externalId()
<< QString::fromStdString(pd.symbol())
<< QString::fromStdString(pd.name())
<< QString::fromStdString(pd.identification())
<< QString::fromStdString(pd.designator())
<< QString::fromStdString(pd.tacticalIdentifier())
<< QString::fromStdString(pd.info())
<< pd.assignState().underlying()
<< pd.isSimulated()
<< pd.isDeleted()
<< pd.modId()
<< pd.superiorModId();
return ba;
}
static ParticipantData qVariantToParticipant(const QVariant &data)
{
ParticipantData pd;
const QByteArray ba = data.toByteArray();
QDataStream ds(ba);
int id, partyId, hierarchyNumber, equipmentNumber, typeId, assignState, modId, superiorModId;
QString symbol, name, identification, designator, tacticalIdentifier, info;
bool isSimulated, isDeleted;
qint64 externalId;
ds >> id
>> partyId
>> hierarchyNumber
>> equipmentNumber
>> typeId
>> externalId
>> symbol
>> name
>> identification
>> designator
>> tacticalIdentifier
>> info
>> assignState
>> isSimulated
>> isDeleted
>> modId
>> superiorModId;
pd.id(id);
pd.partyId(partyId);
pd.hierarchyNumber(hierarchyNumber);
if (equipmentNumber >= 0)
pd.equipmentNumber(equipmentNumber);
pd.typeId(typeId);
pd.externalId(externalId);
pd.symbol(symbol.toStdString());
pd.name(name.toStdString());
pd.identification(identification.toStdString());
pd.designator(designator.toStdString());
pd.tacticalIdentifier(tacticalIdentifier.toStdString());
pd.info(info.toStdString());
pd.assignState(static_cast<ParticipantManagement::ParticipantAssignState::inner_enum>(assignState));
pd.isSimulated(isSimulated);
pd.isDeleted(isDeleted);
pd.modId(modId);
pd.superiorModId(superiorModId);
return pd;
}
static bool isChecked(bool bIsInSet, Qt::CheckState cs)
{
if (cs == Qt::Checked)
return bIsInSet;
// Qt::Unchecked
return !bIsInSet;
}
bool compareStrings(const std::string &s1, const std::string &s2,
const ParticipantConfigModel::CheckedTreeItem *p1,
const ParticipantConfigModel::CheckedTreeItem *p2)
{
const int iRet = strcmp(s1.c_str(), s2.c_str());
if (iRet == 0)
return p1->participant.hierarchyNumber() < p2->participant.hierarchyNumber();
return iRet < 0;
}
//=============================================================================================================================
// ParticipantConfigModel
//=============================================================================================================================
ParticipantConfigModel::ParticipantConfigModel(QObject *parent,
const std::shared_ptr<Interface::Dtc> &ppmDtc,
const std::shared_ptr<AdvancedSymbology::ForeignDataInterface> &asbIfc)
: ParticipantIconModelBase(
ppmDtc,
asbIfc,
{
tr("Hierarchy/Designator"),
tr("Tact. Identifier"),
tr("Class"),
tr("Type"),
tr("Equip. No."),
tr("Protection Helm / Vest / Niveau"),
tr("International Mode"),
tr("Info"),
tr("Sign")
},
new CheckedTreeItem(QVariant()),
parent,
getLogger("ParticipantConfigModel")
)
, m_partyId(-1)
, m_bRowsAreCheckable(false)
, m_bUnitsSelectable(true)
, m_missingSuperiorIdTimer(new QTimer(this))
{
connect(this, &ParticipantConfigModel::signalDataDropped,
this, &ParticipantConfigModel::internalDataChanged);
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &ParticipantConfigModel::onParticipantChanged);
setMimeType(QStringLiteral("ParticipantConfigModel_%1").arg(qlonglong(this)));
m_missingSuperiorIdTimer->setInterval(500);
m_missingSuperiorIdTimer->setSingleShot(true);
connect(m_missingSuperiorIdTimer, &QTimer::timeout,
this, &ParticipantConfigModel::processMissingSuperiorId);
}
//-----------------------------------------------------------------------------------------------------------------------------
// PUBLIC MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
Qt::ItemFlags ParticipantConfigModel::flags(const QModelIndex &index) const
{
auto ret = ParticipantIconModelBase::flags(index);
if (m_bRowsAreCheckable && index.column() == 0)
ret |= Qt::ItemIsUserCheckable;
// are units not selectable?
if (!m_bUnitsSelectable && isUnitType(privateData(index)->participant.typeId()))
ret &= ~Qt::ItemIsSelectable;
// can not drag the whole tree, also we allow only dragging the first column
if (index.column() != 0 || !index.parent().isValid())
ret &= ~Qt::ItemIsDragEnabled;
// drop also only on first column
if (index.column() != 0)
ret &= ~Qt::ItemIsDropEnabled;
return ret;
}
QVariant ParticipantConfigModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole &&
orientation == Qt::Horizontal &&
(section == ColTypeClass || section == ColType))
{
LtsWidgets::ComboboxFilterContent cfc;
cfc.data.reserve(m_usedTypeIds.size());
LtsWidgets::ComboboxFilterData cfd;
if (section == ColType)
{
for (const int typeId : m_usedTypeIds)
{
cfd.data = typeId;
cfd.text = ParticipantManagement::trTypeName(m_types.value(typeId).name());
cfc.data.push_back(cfd);
}
}
else if (section == ColTypeClass)
{
for (const auto &typeClassId : m_usedTypeClassIds)
{
cfd.data = typeClassId.underlying();
cfd.text = ParticipantManagement::toString(typeClassId);
cfc.data.push_back(cfd);
}
}
cfc.sortItems = true;
return cfc.toVariant();
}
return ParticipantIconModelBase::headerData(section, orientation, role);
}
QVariant ParticipantConfigModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return ParticipantIconModelBase::data(index, role);
if (role != Qt::DisplayRole &&
role != Qt::CheckStateRole &&
role != Qt::ToolTipRole &&
role != LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole)
return ParticipantIconModelBase::data(index, role);
const CheckedTreeItem *item = privateData(index);
const ParticipantManagement::ParticipantData &participantData = item->participant;
if (role == Qt::ToolTipRole) // special treatment of tooltip to avoid unnecessary memory allocation
{
if (index.column() != ColDesignator)
return QVariant();
QString toolTip = QStringLiteral("<html>");
QModelIndex idx = ParticipantConfigModel::index(index.row(), 0, index.parent());
QIcon icon = privateData(idx)->icon();
if (!icon.isNull())
{
const QPixmap &img = icon.pixmap(100, 100);
QByteArray data;
QBuffer buffer(&data);
img.save(&buffer, "PNG", 100);
toolTip += QStringLiteral("<center><img src='data:image/png;base64,%1'></center>").arg(QString::fromLatin1(data.toBase64()));
} // if
for (int i = 0; i < columnCount(); ++i)
toolTip.append(QStringLiteral("<br>%1: %2").arg(
headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(),
data(ParticipantConfigModel::index(index.row(), i, index.parent()), Qt::DisplayRole).toString())
);
return toolTip;
} // if
switch (role)
{
case Qt::DisplayRole:
switch (index.column())
{
case ColDesignator:
return QString::fromStdString(participantData.designator());
case ColIdentifier:
return QString::fromStdString(participantData.tacticalIdentifier());
case ColTypeClass:
return typeClassStringFromTypeId(participantData.typeId());
case ColType:
return ParticipantManagement::trTypeName(m_types.value(participantData.typeId()).name());
case ColEquipmentNumber:
if (!participantData.equipmentNumber().is_set())
{
if (participantData.assignState() == ParticipantAssignState::HandoutExcon)
return 0;
return QVariant();
}
if (participantData.assignState() == ParticipantAssignState::ExconWmsOverruled)
return tr("%1 (+)").arg(participantData.equipmentNumber().get());
if (participantData.externalId() > 0)
return tr("%1 (external)").arg(participantData.equipmentNumber().get());
return participantData.equipmentNumber().get();
case ColInfo:
return QString::fromStdString(participantData.info());
case ColSign:
return QString::fromStdString(participantData.identification());
//TODO: Fix
case ColProtection:
if (participantData.equipmentNumber().is_set() && m_dtc->type(participantData.typeId()).typeClass() == TypeClass::Human)
{
return buildProtectionString(m_dtc->participantMode(participantData.id()));
}
else return QVariant();
case ColInternationalMode:
if (participantData.equipmentNumber().is_set() && m_dtc->type(participantData.typeId()).typeClass() == TypeClass::Human)
{
return buildInternationalModeString(m_dtc->participantMode(participantData.id()).internationalMode());
}
else
return QVariant();
default:
Q_UNREACHABLE();
return QVariant();
} // switch
case Qt::CheckStateRole:
if (!m_bRowsAreCheckable || index.column() != 0)
return ParticipantIconModelBase::data(index, role);
return item->bIsChecked ? Qt::Checked : Qt::Unchecked;
case LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole:
if (index.column() == ColTypeClass)
return typeClassIdFromTypeId(participantData.typeId()).underlying();
if (index.column() == ColType)
return participantData.typeId();
return QVariant();
default:
Q_UNREACHABLE();
return QVariant();
} // switch
}
bool ParticipantConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::CheckStateRole || index.column() != 0 || !index.isValid())
return ParticipantIconModelBase::setData(index, value, role);
if (!value.canConvert<bool>())
return false;
CheckedTreeItem *item = privateData(index);
if (item->bIsChecked != value.toBool())
{
item->bIsChecked = value.toBool();
emit dataChanged(index, index, {role});
}
return true;
}
bool ParticipantConfigModel::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 CheckedTreeItem *parentItem = privateData(parent);
if (!isUnitType(parentItem->participant.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 ParticipantData pdSource = static_cast<CheckedTreeItem *>(tiSources.first())->participant;
qDeleteAll(tiSources);
TypeClass dataTypeClass = typeClassIdFromTypeId(pdSource.typeId());
bool isDataTypeClassSupport = dataTypeClass == TypeClass::inner_enum::SupportEntity || dataTypeClass == TypeClass::inner_enum::SupportUnit;
bool isOwnTypeClassSupport = m_partyId == 1;
if (isOwnTypeClassSupport != isDataTypeClassSupport)
return false;
// can only happen when same party
if (pdSource.partyId() == parentItem->participant.partyId())
{
const int idSource = pdSource.id();
// 0 -> new item, no dnd between different models - all is fine
if (idSource != 0 && isParentOf(parentItem, idSource))
{
DEBUG_LOG(m_logger) << "isParentOf() for idSource: " << idSource << " is true - no drop";
return false;
}
}
// maybe some more checks on hierarchy level
return true;
}
bool ParticipantConfigModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
if (!ParticipantIconModelBase::dropMimeData(data, action, row, column, parent))
return false;
// update hierarchy numbers
const CheckedTreeItem *cti = privateData(parent);
if (!cti)
return false;
const auto children = cti->children();
for (int r = 0; r < children.size(); ++r)
{
CheckedTreeItem *child = static_cast<CheckedTreeItem *>(cti->child(r));
// hopefully qsfpm will sort *after* dropMimeData has finished...
child->participant.hierarchyNumber(r + 1);
}
return true;
}
bool ParticipantConfigModel::onReinitModel(int partyId, const QSet<int> &checkedParticipants, Qt::CheckState cs)
{
m_symbolResultMapping.clear();
m_missingSuperiorId.clear();
m_usedTypeIds.clear();
m_usedTypeClassIds.clear();
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()));
ParticipantIconModelBase::resetModel();
QHash<int, ParticipantData> tmap;
QHash<int, QSet<int>> parentId2id;
gatherParticipants(tmap, parentId2id);
// insert recursivly
insertChildrenOf(0, m_rootItem, QModelIndex(), parentId2id, tmap, checkedParticipants, cs);
// unset modifed flag
resetModifedFlag(m_rootItem);
return true;
}
bool ParticipantConfigModel::insertRow(int row, const QModelIndex &parent, NGLTreeItem *item)
{
return ParticipantIconModelBase::insertRow(row, parent, item);
}
bool ParticipantConfigModel::removeRow(int row, const QModelIndex &parent)
{
return ParticipantIconModelBase::removeRow(row, parent);
}
bool ParticipantConfigModel::isRowRemovable(const CheckedTreeItem *parentItem) const
{
if (!parentItem)
return false;
const ParticipantManagement::ParticipantData &participantData = parentItem->participant;
if (participantData.equipmentNumber().is_set() && participantData.equipmentNumber().get() > 0)
return false;
for (int i = 0; i < parentItem->childCount(); i++)
{
if (!isRowRemovable(static_cast<CheckedTreeItem *>(parentItem->child(i))))
return false;
}
return true;
}
void ParticipantConfigModel::dataCommitted()
{
m_dataToDelete.clear();
QHash<int, ParticipantData> tmap;
QHash<int, QSet<int>> parentId2id;
gatherParticipants(tmap, parentId2id);
// update recursivly, reupdate later if a new participant was not yet found
if (!updateChildrenOf(0, m_rootItem, parentId2id, tmap))
QTimer::singleShot(500, this, &ParticipantConfigModel::dataCommitted);
resetModifedFlag(m_rootItem);
}
void ParticipantConfigModel::processMissingSuperiorId()
{
if (m_missingSuperiorId.isEmpty())
return;
// Consider if a complex subtree is move from one party to another...
// Edit Participant dialog should be updated accurately:
// build tree
QHash<int, ParticipantData> tmap;
QHash<int, QSet<int>> parentId2id;
gatherParticipants(tmap, parentId2id);
// traverse every subtree:
for (int id : qAsConst(m_missingSuperiorId))
{
if (!tmap.contains(id))
continue;
const ParticipantManagement::Participant &participant = m_dtc->participant(id);
if (participant.id() != id)
continue;
onParticipantChanged(participant, DataModification::eModified);
if (!parentId2id.contains(id))
continue;
const QSet<int> &childIds = parentId2id.value(id);
for (int childId : qAsConst(childIds))
{
const ParticipantManagement::Participant &p = m_dtc->participant(childId);
if (p.id() != childId)
continue;
onParticipantChanged(p, DataModification::eModified);
}
}
// finish -> clear
m_missingSuperiorId.clear();
}
void ParticipantConfigModel::selectItemByParticipantId(int participantId, const std::function<void(const QModelIndex &)> &selectItem, const QModelIndex &parent)
{
for (int i = 0; i < rowCount(parent); ++i)
{
const QModelIndex idx = index(i, 0, parent);
const ParticipantManagement::ParticipantData &data = privateData(idx)->participant;
if (hasChildren(idx))
selectItemByParticipantId(participantId, selectItem, idx);
if (data.id() == participantId)
return selectItem(idx);
}
return;
}
QModelIndexList ParticipantConfigModel::getIndexesByParticipantIds(const std::vector<int32_t> &participantIds, const QModelIndex &parent) const
{
QModelIndexList indexes; // clazy:exclude=reserve-candidate
for (int row = 0; row < rowCount(parent); ++row)
{
const QModelIndex idx = index(row, 0, parent);
const ParticipantManagement::ParticipantData &data = privateData(idx)->participant;
if (hasChildren(idx))
indexes += getIndexesByParticipantIds(participantIds, idx);
auto it = std::find(participantIds.begin(), participantIds.end(), data.id());
if (it != participantIds.end())
indexes.append(idx);
} // for
return indexes;
}
void ParticipantConfigModel::setRowsCheckable(bool bCheckable)
{
if (m_bRowsAreCheckable == bCheckable)
return;
m_bRowsAreCheckable = bCheckable;
onReinitModel(m_partyId); // TODO: not really needed, only invalidate first column
}
bool ParticipantConfigModel::setCheckStateForParticipants(const QSet<int> &checkedParticipants, Qt::CheckState cs)
{
if (cs != Qt::Checked && cs != Qt::Unchecked)
return false;
if (m_rootItem->childCount() < 1)
QVector<int>();
CheckedTreeItem *item = static_cast<CheckedTreeItem *>(m_rootItem);
setCheckState(item, checkedParticipants, cs);
return true;
}
ParticipantManagement::ParticipantData ParticipantConfigModel::getParticipantData(const QModelIndex &idx) const
{
return privateData(idx)->participant;
}
std::vector<ParticipantData> ParticipantConfigModel::participantData(bool bOnlyModified) const
{
std::vector<ParticipantData> ret;
if (m_rootItem->childCount() < 1)
return ret;
int modId = 1000000000;
for (int i = 0; i < m_rootItem->childCount(); ++i)
{
const auto c = getChildrenOf(m_rootItem->child(i), 0, 1, bOnlyModified, modId);
ret.insert(ret.end(), c.begin(), c.end());
}
ret.insert(ret.end(), m_dataToDelete.begin(), m_dataToDelete.end());
return ret;
}
QSet<int> ParticipantConfigModel::participantIds() const
{
return participantIds(m_rootItem);
}
QSet<int> ParticipantConfigModel::checkStateForParticipants(Qt::CheckState cs) const
{
if (m_rootItem->childCount() < 1)
QSet<int>();
CheckedTreeItem *item = static_cast<CheckedTreeItem *>(m_rootItem);
return checkState(item, cs);
}
ParticipantConfigModel::CheckedTreeItem *ParticipantConfigModel::privateData(const QModelIndex &index)
{
return static_cast<CheckedTreeItem *>(ParticipantIconModelBase::privateData(index));
}
const ParticipantConfigModel::CheckedTreeItem *ParticipantConfigModel::privateData(const QModelIndex &index) const
{
return static_cast<CheckedTreeItem *>(ParticipantIconModelBase::privateData(index));
}
QModelIndex ParticipantConfigModel::indexFromParticipantId(int participantId) const
{
return indexFromParticipantId(participantId, static_cast<CheckedTreeItem *>(m_rootItem));
}
QString ParticipantConfigModel::getHierarchyFromIndex(const QModelIndex &index) const
{
if (!index.isValid())
return QString();
const ParticipantManagement::ParticipantData &participantData = privateData(index)->participant;
return m_dtc->hierarchyFromParticipant(participantData.id());
}
//-----------------------------------------------------------------------------------------------------------------------------
// PROTECTED MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
void ParticipantConfigModel::gatherParticipants(QHash<int, ParticipantData> &tmap,
QHash<int, QSet<int>> &parentId2id) const
{
auto cb = [this, &tmap, &parentId2id](const Participant & part)
{
if (part.isDeleted())
return;
if (part.partyId() != m_partyId)
return;
// parentId -> id for lookup
parentId2id[part.superiorId()] += part.id();
tmap.insert(part.id(), m_dtc->convertParticipantToParticipantData(part));
};
m_dtc->participants(cb);
}
std::vector<ParticipantData> ParticipantConfigModel::getChildrenOf(NGLTreeItem *item, int parentId, int hierarchyNumber, bool bOnlyModifed, int &highestModId) const
{
if (!item)
return {};
std::vector<ParticipantData> ret;
const CheckedTreeItem *cti = static_cast<CheckedTreeItem *>(item);
ParticipantManagement::ParticipantData participantData = cti->participant;
participantData.superiorModId(parentId);
participantData.hierarchyNumber(hierarchyNumber);
participantData.partyId(m_partyId); // when moved from one party to another (e.g. tactical organisation)
if (participantData.modId() == 0)
participantData.modId(++highestModId);
// check for children of this item
for (int i = 0; i < item->childCount(); i++)
{
const auto c = getChildrenOf(item->child(i), participantData.modId(), i + 1, bOnlyModifed, highestModId);
ret.insert(ret.end(), c.begin(), c.end());
}
if (!bOnlyModifed || item->modified() || !ret.empty())
ret.push_back(std::move(participantData));
return ret;
}
bool ParticipantConfigModel::updateChildrenOf(int parentId,
NGLTreeItem *parent,
const QHash<int, QSet<int>> &parentId2id,
const QHash<int, ParticipantData> &tmap)
{
bool bRet = true;
const int childCount = parent->childCount();
for (int i = 0; i < childCount; ++i)
{
ParticipantConfigModel::CheckedTreeItem *childItem = static_cast<ParticipantConfigModel::CheckedTreeItem *>(parent->child(i));
ParticipantData &pdItem = childItem->participant;
m_usedTypeIds += pdItem.typeId();
m_usedTypeClassIds += typeClassIdFromTypeId(pdItem.typeId());
if (pdItem.id() == 0)
{
const QSet<int> &childIds = parentId2id.value(parentId);
for (int childId : childIds)
{
const auto &childData = tmap.value(childId);
if (childData.hierarchyNumber() - 1 == i)
{
pdItem.id(childData.id());
childItem->setModified(false);
break;
}
}
}
if (!pdItem.id())
{
bRet = false;
continue;
}
if (!updateChildrenOf(pdItem.id(), childItem, parentId2id, tmap))
bRet = false;
}
return bRet;
}
void ParticipantConfigModel::insertChildrenOf(int parentId,
NGLTreeItem *parent,
const QModelIndex &parentIdx,
const QHash<int, QSet<int>> &parentId2id,
const QHash<int, ParticipantData> &tmap,
const QSet<int> &checkedParticipants,
Qt::CheckState cs)
{
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();
m_usedTypeIds += data.typeId();
m_usedTypeClassIds += typeClassIdFromTypeId(data.typeId());
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();
}
CheckedTreeItem *childItem = static_cast<CheckedTreeItem *>(createItem(QVariant(), QIcon()));
childItem->participant = data;
childItem->bIsChecked = isChecked(checkedParticipants.contains(data.id()), cs);
DEBUG_LOG(m_logger) << "insert Child " << childItem << "(" << data.id() << ", " << data.hierarchyNumber()
<< ") to parent " << parent << " at pos " << insertPos;
insertRow(insertPos, parentIdx, childItem);
tryRenderSymbol(QString::fromStdString(data.symbol()), childItem); // ToDO add role here
const QModelIndex childIdx = index(insertPos, 0, parentIdx);
insertChildrenOf(data.id(), childItem, childIdx, parentId2id, tmap, checkedParticipants, cs);
}
}
bool ParticipantConfigModel::isParentOf(const NGLTreeItem *item, int participantId) const
{
if (item == m_rootItem)
return false;
const CheckedTreeItem *cti = static_cast<const CheckedTreeItem *>(item);
if (cti->participant.id() == participantId)
return true;
return isParentOf(item->parent(), participantId);
}
QSet<int> ParticipantConfigModel::participantIds(NGLTreeItem *parentItem) const
{
QSet<int> ret;
ret.reserve(parentItem->childCount() + 1);
ret += participantIdFromItem(parentItem);
for (int i = 0; i < parentItem->childCount(); ++i)
ret += participantIds(parentItem->child(i));
return ret;
}
QSet<int> ParticipantConfigModel::checkState(CheckedTreeItem *parentItem, Qt::CheckState cs) const
{
QSet<int> ret;
if (parentItem != m_rootItem)
{
if ((parentItem->bIsChecked && cs == Qt::Checked) ||
(!parentItem->bIsChecked && cs == Qt::Unchecked))
ret += participantIdFromItem(parentItem);
}
for (int i = 0; i < parentItem->childCount(); ++i)
ret += checkState(static_cast<CheckedTreeItem *>(parentItem->child(i)), cs);
return ret;
}
void ParticipantConfigModel::setCheckState(CheckedTreeItem *parentItem, const QSet<int> &checkedParticipants, Qt::CheckState cs)
{
if (parentItem != m_rootItem)
{
int participantId = participantIdFromItem(parentItem);
const auto newState = isChecked(checkedParticipants.contains(participantId), cs);
if (newState != parentItem->bIsChecked)
{
parentItem->bIsChecked = newState;
const QModelIndex idx = indexFromItem(parentItem);
emit dataChanged(idx, idx, {Qt::CheckStateRole});
}
}
for (int i = 0; i < parentItem->childCount(); ++i)
setCheckState(static_cast<CheckedTreeItem *>(parentItem->child(i)), checkedParticipants, cs);
}
QModelIndex ParticipantConfigModel::indexFromParticipantId(int participantId, const CheckedTreeItem *cti) const
{
if (cti->participant.id() == participantId)
return indexFromItem(cti);
for (int row = 0; row < cti->childCount(); ++row)
{
const QModelIndex idx = indexFromParticipantId(participantId, static_cast<CheckedTreeItem *>(cti->child(row)));
if (idx.isValid())
return idx;
}
return {};
}
int ParticipantConfigModel::participantIdFromItem(NGLTreeItem *item) const
{
const CheckedTreeItem *cti = static_cast<CheckedTreeItem *>(item);
return cti->participant.id();
}
ParticipantConfigModel::CheckedTreeItem *ParticipantConfigModel::searchParticipant(const NGLTreeItem *parentItem, int participantId) const
{
const int rowCount = parentItem->childCount();
for (int r = 0; r < rowCount; ++r)
{
CheckedTreeItem *child = static_cast<CheckedTreeItem *>(parentItem->child(r));
if (child->participant.id() == participantId)
return child;
child = searchParticipant(child, participantId);
if (child)
return child;
}
return nullptr;
}
NGLTreeItem *ParticipantConfigModel::createItem(const QVariant &data, const QIcon &icon) const
{
CheckedTreeItem *cti = new CheckedTreeItem(data, icon);
cti->participant = qVariantToParticipant(data);
if (cti->participant.symbol().size() != 0)
{
auto *that = const_cast<ParticipantConfigModel *>(this);
that->tryRenderSymbol(QString::fromStdString(cti->participant.symbol()), cti);
}
return cti;
}
QVariant ParticipantConfigModel::getItemData(const NGLTreeItem *item) const
{
const CheckedTreeItem *cti = static_cast<const CheckedTreeItem *>(item);
return participantToQVariant(cti->participant);
}
QString ParticipantConfigModel::buildProtectionString(const ParticipantMode &mode) const
{
QString protLevel;
switch(mode.protectionLevel().underlying())
{
case ProjectDataManagement::ProtectionLevel::IdzEs:
protLevel = tr("IdzEs");
break;
case ProjectDataManagement::ProtectionLevel::IdzBs:
protLevel = tr("IdzBs");
break;
case ProjectDataManagement::ProtectionLevel::Ksk:
protLevel = tr("Ksk");
break;
case ProjectDataManagement::ProtectionLevel::SoldierWithBodyArmor:
protLevel = tr("SoldierWithBodyArmor");
break;
case ProjectDataManagement::ProtectionLevel::SoldierActor:
protLevel = tr("SoldierActor");
break;
default:
protLevel = tr("No Change");
break;
}
return tr("%1 / %2 / %3")
.arg(buildWphModeString(mode.modeHelmet()))
.arg(buildWphModeString(mode.modeVest()))
.arg(protLevel);
}
QString ParticipantConfigModel::buildWphModeString(const ProjectDataManagement::WphMode &wphMode) const
{
switch(wphMode.underlying())
{
case ProjectDataManagement::WphMode::Without:
return tr("Without");
case ProjectDataManagement::WphMode::With:
return tr("With");
default:
return tr("No Change");
}
}
QString ParticipantConfigModel::buildInternationalModeString(const ProjectDataManagement::InternationalMode &inMode) const
{
switch(inMode.underlying())
{
case ProjectDataManagement::InternationalMode::On:
return tr("On");
case ProjectDataManagement::InternationalMode::Off:
return tr("Off");
default:
return tr("No Change");
}
}
//-----------------------------------------------------------------------------------------------------------------------------
// SLOTS
//-----------------------------------------------------------------------------------------------------------------------------
int ParticipantConfigModel::insertParticipant(const QModelIndex &parent, ParticipantManagement::ParticipantData participantData, bool bNewParticipant)
{
const QModelIndex parentCol0 = parent.column() == 0 ? parent : indexFromItem(privateData(parent));
const int row = rowCount(parentCol0);
if (bNewParticipant)
{
participantData.id(0); // need new participantId
participantData.modId(0); // need new modId
}
participantData.partyId(m_partyId);
participantData.hierarchyNumber(row + 1);
CheckedTreeItem *item = static_cast<CheckedTreeItem *>(createItem(participantToQVariant(participantData), QIcon()));
item->setModified(true);
if (!insertRow(row, parentCol0, item))
return -1;
tryRenderSymbol(QString::fromStdString(participantData.symbol()), item);
emit internalDataChanged();
return row;
}
int ParticipantConfigModel::onInsertRow(const QModelIndex &parent, const ParticipantData &participantData)
{
return insertParticipant(parent, participantData, true);
}
bool ParticipantConfigModel::onUpdateRow(const QModelIndex &index, const ParticipantManagement::ParticipantData &participantData)
{
if (!index.isValid())
return false;
CheckedTreeItem *item = privateData(index);
const ParticipantManagement::ParticipantData &oldData = item->participant;
if (participantData == oldData)
return true;
if (oldData.symbol() != participantData.symbol())
tryRenderSymbol(QString::fromStdString(participantData.symbol()), item);
emit internalDataChanged();
item->participant = participantData;
return setItemData(index, QVariant(), item->icon());
}
bool ParticipantConfigModel::onDeleteRow(const QModelIndex &idx, bool isFirstItem)
{
while (rowCount(idx) > 0)
onDeleteRow(ParticipantIconModelBase::index(0, 0, idx), false);
if (isFirstItem) //edit mode
{
CheckedTreeItem *item = privateData(idx);
ParticipantManagement::ParticipantData data = item->participant;
data.isDeleted(true);
data.assignState() = ParticipantAssignState::NotAssigned;
data.equipmentNumber().reset();
if (data.modId() != 0) //only append old items to remove list
m_dataToDelete.push_back(std::move(data));
}
removeRow(idx.row(), idx.parent());
emit internalDataChanged();
return true;
}
void ParticipantConfigModel::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType)
{
if (data.partyId() != m_partyId)
return;
Q_UNUSED(modType);
CheckedTreeItem *cti = searchParticipant(m_rootItem, data.id());
if (cti)
{
cti->participant = m_dtc->convertParticipantToParticipantData(data);
emit dataChanged(indexFromItem(cti, 0), indexFromItem(cti, ColSign));
return;
}
CheckedTreeItem *ctiParent = searchParticipant(m_rootItem, data.superiorId());
if (ctiParent)
{
int insertPos = 0;
if (data.hierarchyNumber() > 0)
{
for (int i = 0; i < ctiParent->childCount(); ++i)
{
CheckedTreeItem *child = static_cast<CheckedTreeItem *>(ctiParent->child(i));
if (child->participant.hierarchyNumber() > data.hierarchyNumber())
{
insertPos = i;
break;
}
}
}
else
{
// append if no valid hierarchy number is set
insertPos = ctiParent->childCount();
}
CheckedTreeItem *childItem = static_cast<CheckedTreeItem *>(createItem(QVariant(), QIcon()));
childItem->participant = m_dtc->convertParticipantToParticipantData(data);
DEBUG_LOG(m_logger) << "insert Child " << childItem << "(" << data.id() << ", " << data.hierarchyNumber()
<< ") to parent " << ctiParent << " at pos " << insertPos;
insertRow(insertPos, indexFromItem(ctiParent), childItem);
tryRenderSymbol(QString::fromStdString(data.symbol()), childItem); // ToDO add role here
return;
}
else
{
// superiorId() was not found, most likely due to order of incomming updates:
// Like dialog 'Edit Participant'
m_missingSuperiorId.insert(data.superiorId());
m_missingSuperiorIdTimer->start();
}
// onReinitModel(m_partyId);
}
//=============================================================================================================================
// ParticipantTreeFilterProxyConfigModel
//=============================================================================================================================
//-----------------------------------------------------------------------------------------------------------------------------
// PUBLIC MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
void ParticipantTreeFilterProxyConfigModel::setAllowedTypes(const QSet<int> &allowedTypes)
{
if (m_allowedTypes == allowedTypes)
return;
m_allowedTypes = allowedTypes;
invalidateFilter();
}
void ParticipantTreeFilterProxyConfigModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<ParticipantConfigModel *>(sourceModel);
setRecursiveFilteringEnabled(true);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
//-----------------------------------------------------------------------------------------------------------------------------
// PROTECTED MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
bool ParticipantTreeFilterProxyConfigModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if ((!m_sourceModel) || (source_left.column() != source_right.column()))
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const ParticipantConfigModel::CheckedTreeItem *left = m_sourceModel->privateData(source_left);
const ParticipantConfigModel::CheckedTreeItem *right = m_sourceModel->privateData(source_right);
switch (source_left.column())
{
case ParticipantConfigModel::ColDesignator:
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
case ParticipantConfigModel::ColIdentifier:
return compareStrings(left->participant.tacticalIdentifier(),
right->participant.tacticalIdentifier(),
left, right);
case ParticipantConfigModel::ColTypeClass:
case ParticipantConfigModel::ColType:
break; // string compare
case ParticipantConfigModel::ColEquipmentNumber:
if (!left->participant.equipmentNumber().is_set() && !right->participant.equipmentNumber().is_set())
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
if (!left->participant.equipmentNumber().is_set())
return true;
if (!right->participant.equipmentNumber().is_set())
return false;
return left->participant.equipmentNumber().get() < right->participant.equipmentNumber().get();
case ParticipantConfigModel::ColInfo:
return compareStrings(left->participant.info(),
right->participant.info(),
left, right);
case ParticipantConfigModel::ColSign:
return compareStrings(left->participant.identification(),
right->participant.identification(),
left, right);
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
bool ParticipantTreeFilterProxyConfigModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if ((!m_sourceModel) || (m_allowedTypes.isEmpty()))
return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent);
const ParticipantConfigModel::CheckedTreeItem *parentItem = m_sourceModel->privateData(source_parent);
if (!parentItem)
return false;
const ParticipantConfigModel::CheckedTreeItem *childItem =
static_cast<const ParticipantConfigModel::CheckedTreeItem *>(parentItem->child(source_row));
if (!childItem)
return false;
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
if (!m_allowedTypes.contains(childItem->participant.typeId()))
return false;
#else
// we have to do recursive filtering by ourselves (until we only support Qt >= 5.10)
if ((recursiveFilteringEnabled()) && (!filterTypeRecursive(childItem)))
return false;
#endif
return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
bool ParticipantTreeFilterProxyConfigModel::filterTypeRecursive(const ParticipantConfigModel::CheckedTreeItem *item) const
{
if (m_allowedTypes.contains(item->participant.typeId()))
return true;
for (int i = 0; i < item->childCount(); ++i)
{
const ParticipantConfigModel::CheckedTreeItem *child = static_cast<const ParticipantConfigModel::CheckedTreeItem *>(item->child(i));
if (m_allowedTypes.contains(child->participant.typeId()))
return true;
if (filterTypeRecursive(child))
return true;
}
return false;
}
#endif
//=============================================================================================================================
// ParticipantTableFilterProxyConfigModel
//=============================================================================================================================
ParticipantTableFilterProxyConfigModel::ParticipantTableFilterProxyConfigModel(QObject *parent)
: LtsWidgets::LtsColumnFilterProxyModel(parent)
, m_logger(ParticipantManagement::getLogger(QStringLiteral("ParticipantTableFilterProxyConfigModel")))
{
setRecursiveFilteringEnabled(false);
}
//-----------------------------------------------------------------------------------------------------------------------------
// PUBLIC MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
void ParticipantTableFilterProxyConfigModel::init()
{
if ((!m_mapFromSource.isEmpty()) || (!m_mapToSource.isEmpty()))
resetMapping();
mapTreeToTable();
// m_mapFromSource and m_mapToSource are ready
m_mapFromSourceInternal = m_mapFromSource;
m_mapToSourceInternal = m_mapToSource;
m_mapFromSourceFiltered = m_mapFromSource;
m_mapToSourceFiltered = m_mapToSource;
onFilter();
if (m_sortData.isActive)
onSort(m_sortData.column, m_sortData.order);
}
void ParticipantTableFilterProxyConfigModel::resetMapping()
{
m_mapFromSourceInternal.clear();
m_mapToSourceInternal.clear();
m_mapFromSource.clear();
m_mapToSource.clear();
m_mapFromSourceFiltered.clear();
m_mapToSourceFiltered.clear();
m_sortMap.clear();
}
void ParticipantTableFilterProxyConfigModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<ParticipantConfigModel *>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
int ParticipantTableFilterProxyConfigModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_mapFromSourceInternal.size();
}
int ParticipantTableFilterProxyConfigModel::columnCount(const QModelIndex &parent) const
{
return m_sourceModel->columnCount(mapToSource(parent));
}
QVariant ParticipantTableFilterProxyConfigModel::data(const QModelIndex &index, int role) const
{
if ((index.column() == ParticipantConfigModel::Columns::ColDesignator) && (role == Qt::DisplayRole))
return m_sourceModel->getHierarchyFromIndex(mapToSource(index));
return LtsColumnFilterProxyModel::data(index, role);
}
QVariant ParticipantTableFilterProxyConfigModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return m_sourceModel->headerData(section, orientation, role);
}
QModelIndex ParticipantTableFilterProxyConfigModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if (!m_mapFromSourceInternal.contains(sourceIndex))
return {};
return createIndex(m_mapFromSourceInternal.value(sourceIndex), sourceIndex.column());
}
QModelIndex ParticipantTableFilterProxyConfigModel::mapToSource(const QModelIndex &proxyIndex) const
{
if (!m_mapToSourceInternal.contains(proxyIndex.row())) // this includes !proxyIndex.isValid()
return {};
QModelIndex idx = m_mapToSourceInternal.value(proxyIndex.row());
return m_sourceModel->index(idx.row(), proxyIndex.column(), idx.parent());
}
QModelIndex ParticipantTableFilterProxyConfigModel::index(int row, int column, const QModelIndex &parent) const
{
return parent.isValid() ? QModelIndex() : createIndex(row, column);
}
QModelIndex ParticipantTableFilterProxyConfigModel::parent(const QModelIndex &child) const
{
Q_UNUSED(child);
return {};
}
QModelIndex ParticipantTableFilterProxyConfigModel::sibling(int row, int column, const QModelIndex &idx) const
{
return idx.row() == row ? createIndex(row, column) : QModelIndex();
}
bool ParticipantTableFilterProxyConfigModel::hasChildren(const QModelIndex &parent) const
{
return !parent.isValid();
}
//-----------------------------------------------------------------------------------------------------------------------------
// PROTECTED MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
bool ParticipantTableFilterProxyConfigModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
{
if (sourceLeft.column() != sourceRight.column())
return false;
const ParticipantConfigModel::CheckedTreeItem *left = m_sourceModel->privateData(sourceLeft);
const ParticipantConfigModel::CheckedTreeItem *right = m_sourceModel->privateData(sourceRight);
switch (sourceLeft.column())
{
case ParticipantConfigModel::ColDesignator:
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
case ParticipantConfigModel::ColIdentifier:
return compareStrings(left->participant.tacticalIdentifier(), right->participant.tacticalIdentifier(), left, right);
case ParticipantConfigModel::ColTypeClass:
case ParticipantConfigModel::ColType:
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(sourceLeft, sourceRight);
case ParticipantConfigModel::ColEquipmentNumber:
if (!left->participant.equipmentNumber().is_set() && !right->participant.equipmentNumber().is_set())
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
if (!left->participant.equipmentNumber().is_set())
return true;
if (!right->participant.equipmentNumber().is_set())
return false;
return left->participant.equipmentNumber().get() < right->participant.equipmentNumber().get();
case ParticipantConfigModel::ColInfo:
return compareStrings(left->participant.info(), right->participant.info(), left, right);
case ParticipantConfigModel::ColSign:
return compareStrings(left->participant.identification(), right->participant.identification(), left, right);
default:
return false;
} // switch
}
bool ParticipantTableFilterProxyConfigModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
{
Q_UNUSED(source_column);
Q_UNUSED(source_parent);
return true;
}
bool ParticipantTableFilterProxyConfigModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_row);
Q_UNUSED(source_parent);
return true;
}
bool ParticipantTableFilterProxyConfigModel::filterAcceptsRowInternal(int sourceRow, const QModelIndex &sourceParent) const
{
const QModelIndex index = m_sourceModel->index(sourceRow, 0, sourceParent);
if (m_sourceModel->hasChildren(index))
{
const ParticipantConfigModel::CheckedTreeItem *parent = static_cast<const ParticipantConfigModel::CheckedTreeItem *>(index.internalPointer());
for (int i = 0; i < parent->childCount(); ++i)
if (filterAcceptsRowInternal(i, index))
return true;
} // if
// column data filters
const QHash<int, QVariant> cdf = columnDataFilters();
for (auto it = cdf.begin(); it != cdf.end(); ++it)
{
const QModelIndex idx = m_sourceModel->index(sourceRow, it.key(), sourceParent);
const QVariant var = m_sourceModel->data(idx, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole).toString();
if (var != it.value())
return false;
} // for
// column string filters
const QHash<int, QRegularExpression> csf = columnStringFilters();
for (auto it = csf.begin(); it != csf.end(); ++it)
{
const QRegularExpression &regExpr = it.value();
if (!regExpr.isValid())
continue;
const QModelIndex idx = m_sourceModel->index(sourceRow, it.key(), sourceParent);
const QString str = m_sourceModel->data(idx, Qt::DisplayRole).toString();
if (!str.contains(regExpr))
return false;
} // for
return true;
}
//-----------------------------------------------------------------------------------------------------------------------------
// PRIVATE MEMBER FUNCTIONS
//-----------------------------------------------------------------------------------------------------------------------------
int ParticipantTableFilterProxyConfigModel::mapTreeToTable(const QModelIndex &parent, int row)
{
const ParticipantConfigModel::CheckedTreeItem *item = m_sourceModel->privateData(parent); // static_cast<CheckedTreeItem *>(parent.internalPointer())
for (int i = 0; i < item->childCount(); ++i)
{
QModelIndex child = m_sourceModel->index(i, 0, parent);
m_mapFromSource.insert(child, row);
m_mapToSource.insert(row, child);
++row;
if (m_sourceModel->hasChildren(child))
row = mapTreeToTable(child, row);
} // for
return row;
}
//-----------------------------------------------------------------------------------------------------------------------------
// SLOTS
//-----------------------------------------------------------------------------------------------------------------------------
void ParticipantTableFilterProxyConfigModel::onFilter()
{
const QHash<int, QVariant> &colDataFilters = columnDataFilters();
const QHash<int, QRegularExpression> &colStringFilters = columnStringFilters();
m_isFilterActive = !colDataFilters.isEmpty() || !colStringFilters.isEmpty();
if (m_isFilterActive)
{
m_mapToSourceFiltered.clear();
m_mapFromSourceFiltered.clear();
for (auto it = m_mapToSource.begin(); it != m_mapToSource.end(); ++it) // filter all source model indexes
{
int row = it.key();
QModelIndex index = it.value();
if (filterAcceptsRowInternal(index.row(), index.parent()))
{
m_mapToSourceFiltered.insert(row, index);
m_mapFromSourceFiltered.insert(index, row);
} // if
} // for
if (m_mapToSourceFiltered.size() != m_mapToSource.size())
{
QList<int> rows = m_mapToSourceFiltered.keys();
std::sort(rows.begin(), rows.end());
int count = 0;
for (const auto &row : qAsConst(rows))
{
if (row == count)
{
++count;
continue;
} // if
QModelIndex index = m_mapToSourceFiltered.value(row);
m_mapToSourceFiltered.remove(row);
m_mapFromSourceFiltered.remove(index);
m_mapToSourceFiltered.insert(count, index);
m_mapFromSourceFiltered.insert(index, count);
++count;
} // for
} // if
m_mapToSourceInternal = m_mapToSourceFiltered;
m_mapFromSourceInternal = m_mapFromSourceFiltered;
} // if
else
{
m_mapToSourceInternal = m_mapToSource;
m_mapFromSourceInternal = m_mapFromSource;
m_mapToSourceFiltered = m_mapToSource;
m_mapFromSourceFiltered = m_mapFromSource;
} // else
if ((m_sortData.isActive) && !m_mapFromSourceInternal.isEmpty())
onSort(m_sortData.column, m_sortData.order);
emit layoutChanged();
}
void ParticipantTableFilterProxyConfigModel::onSort(int column, Qt::SortOrder order)
{
if ((column < 0) || (column >= m_sourceModel->columnCount()))
return;
m_sortData.isActive = true;
m_sortData.column = column;
m_sortData.order = order;
m_sortMap.clear();
m_sortMap.reserve(m_mapToSourceFiltered.size()); // m_sortMap is a QVector<int>
// undo previous sorting
m_mapFromSourceInternal = m_mapFromSourceFiltered;
m_mapToSourceInternal = m_mapToSourceFiltered;
// insertion sort
for (int oldRow = 0; oldRow < rowCount(); ++oldRow)
{
QModelIndex currentIndex = mapToSource(index(oldRow, column));
int newRow = 0;
for (; newRow < oldRow; ++newRow)
{
QModelIndex compareIndex = mapToSource(index(m_sortMap.value(newRow), column));
if (lessThan(currentIndex, compareIndex))
break;
} // for
m_sortMap.insert(newRow, oldRow);
} // for
if (m_sortData.descending())
std::reverse(m_sortMap.begin(), m_sortMap.end());
m_mapToSourceInternal.clear();
m_mapFromSourceInternal.clear();
for (int newRow = 0; newRow < m_sortMap.size(); ++newRow)
{
QModelIndex index = m_mapToSourceFiltered.value(m_sortMap[newRow]);
m_mapFromSourceInternal.insert(index, newRow);
m_mapToSourceInternal.insert(newRow, index);
} // for
emit layoutChanged();
}
} //namespace ParticipantManagement