1379 lines
48 KiB
C++
Executable File
1379 lines
48 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_selection_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 ParticipantSelectionModel::CheckedTreeItem *p1,
|
|
const ParticipantSelectionModel::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;
|
|
}
|
|
|
|
//=============================================================================================================================
|
|
// ParticipantSelectionModel
|
|
//=============================================================================================================================
|
|
ParticipantSelectionModel::ParticipantSelectionModel(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("Participant Id"),
|
|
tr("Info"),
|
|
tr("Sign")
|
|
},
|
|
new CheckedTreeItem(QVariant()),
|
|
parent,
|
|
getLogger("ParticipantSelectionModel")
|
|
)
|
|
, m_partyId(-1)
|
|
, m_bRowsAreCheckable(false)
|
|
, m_bUnitsSelectable(true)
|
|
, m_missingSuperiorIdTimer(new QTimer(this))
|
|
{
|
|
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
|
|
this, &ParticipantSelectionModel::onParticipantChanged);
|
|
setMimeType(QStringLiteral("ParticipantSelectionModel_%1").arg(qlonglong(this)));
|
|
|
|
m_missingSuperiorIdTimer->setInterval(500);
|
|
m_missingSuperiorIdTimer->setSingleShot(true);
|
|
connect(m_missingSuperiorIdTimer, &QTimer::timeout,
|
|
this, &ParticipantSelectionModel::processMissingSuperiorId);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// PUBLIC MEMBER FUNCTIONS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
Qt::ItemFlags ParticipantSelectionModel::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;
|
|
|
|
return ret;
|
|
}
|
|
|
|
QVariant ParticipantSelectionModel::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 ParticipantSelectionModel::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 = ParticipantSelectionModel::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(ParticipantSelectionModel::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 ColParticipantId:
|
|
return participantData.id();
|
|
|
|
case ColInfo:
|
|
return QString::fromStdString(participantData.info());
|
|
|
|
case ColSign:
|
|
return QString::fromStdString(participantData.identification());
|
|
|
|
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 ParticipantSelectionModel::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 ParticipantSelectionModel::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 ParticipantSelectionModel::insertRow(int row, const QModelIndex &parent, NGLTreeItem *item)
|
|
{
|
|
return ParticipantIconModelBase::insertRow(row, parent, item);
|
|
}
|
|
|
|
bool ParticipantSelectionModel::removeRow(int row, const QModelIndex &parent)
|
|
{
|
|
return ParticipantIconModelBase::removeRow(row, parent);
|
|
}
|
|
|
|
bool ParticipantSelectionModel::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 ParticipantSelectionModel::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, &ParticipantSelectionModel::dataCommitted);
|
|
|
|
resetModifedFlag(m_rootItem);
|
|
}
|
|
|
|
void ParticipantSelectionModel::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 &p = m_dtc->participant(id);
|
|
if (p.id() != id)
|
|
continue;
|
|
onParticipantChanged(p, DataModification::eModified);
|
|
|
|
if (!parentId2id.contains(id))
|
|
continue;
|
|
|
|
const QSet<int> &childIds = parentId2id.value(id);
|
|
for (int childId : qAsConst(childIds))
|
|
{
|
|
const ParticipantManagement::Participant &pChild = m_dtc->participant(childId);
|
|
if (pChild.id() != childId)
|
|
continue;
|
|
onParticipantChanged(pChild, DataModification::eModified);
|
|
}
|
|
}
|
|
|
|
// finish -> clear
|
|
m_missingSuperiorId.clear();
|
|
}
|
|
|
|
void ParticipantSelectionModel::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 ParticipantSelectionModel::getIndexesByParticipantIds(const std::vector<int32_t> &participantIds, const QModelIndex &parent) const
|
|
{
|
|
QModelIndexList indexes; // clazy:exclude=reserve-candidates
|
|
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 ParticipantSelectionModel::setRowsCheckable(bool bCheckable)
|
|
{
|
|
if (m_bRowsAreCheckable == bCheckable)
|
|
return;
|
|
m_bRowsAreCheckable = bCheckable;
|
|
onReinitModel(m_partyId); // TODO: not really needed, only invalidate first column
|
|
}
|
|
|
|
bool ParticipantSelectionModel::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 ParticipantSelectionModel::getParticipantData(const QModelIndex &idx) const
|
|
{
|
|
return privateData(idx)->participant;
|
|
}
|
|
|
|
std::vector<ParticipantData> ParticipantSelectionModel::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> ParticipantSelectionModel::participantIds() const
|
|
{
|
|
return participantIds(m_rootItem);
|
|
}
|
|
|
|
QSet<int> ParticipantSelectionModel::checkStateForParticipants(Qt::CheckState cs) const
|
|
{
|
|
if (m_rootItem->childCount() < 1)
|
|
QSet<int>();
|
|
|
|
CheckedTreeItem *item = static_cast<CheckedTreeItem *>(m_rootItem);
|
|
return checkState(item, cs);
|
|
}
|
|
|
|
|
|
ParticipantSelectionModel::CheckedTreeItem *ParticipantSelectionModel::privateData(const QModelIndex &index)
|
|
{
|
|
return static_cast<CheckedTreeItem *>(ParticipantIconModelBase::privateData(index));
|
|
}
|
|
|
|
const ParticipantSelectionModel::CheckedTreeItem *ParticipantSelectionModel::privateData(const QModelIndex &index) const
|
|
{
|
|
return static_cast<CheckedTreeItem *>(ParticipantIconModelBase::privateData(index));
|
|
}
|
|
|
|
|
|
QModelIndex ParticipantSelectionModel::indexFromParticipantId(int participantId) const
|
|
{
|
|
return indexFromParticipantId(participantId, static_cast<CheckedTreeItem *>(m_rootItem));
|
|
}
|
|
|
|
|
|
QString ParticipantSelectionModel::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 ParticipantSelectionModel::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> ParticipantSelectionModel::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 ParticipantSelectionModel::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)
|
|
{
|
|
ParticipantSelectionModel::CheckedTreeItem *childItem = static_cast<ParticipantSelectionModel::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 ParticipantSelectionModel::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 ParticipantSelectionModel::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> ParticipantSelectionModel::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> ParticipantSelectionModel::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 ParticipantSelectionModel::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 ParticipantSelectionModel::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 ParticipantSelectionModel::participantIdFromItem(NGLTreeItem *item) const
|
|
{
|
|
const CheckedTreeItem *cti = static_cast<CheckedTreeItem *>(item);
|
|
return cti->participant.id();
|
|
}
|
|
|
|
ParticipantSelectionModel::CheckedTreeItem *ParticipantSelectionModel::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 *ParticipantSelectionModel::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<ParticipantSelectionModel *>(this);
|
|
that->tryRenderSymbol(QString::fromStdString(cti->participant.symbol()), cti);
|
|
}
|
|
|
|
return cti;
|
|
}
|
|
|
|
QVariant ParticipantSelectionModel::getItemData(const NGLTreeItem *item) const
|
|
{
|
|
const CheckedTreeItem *cti = static_cast<const CheckedTreeItem *>(item);
|
|
|
|
return participantToQVariant(cti->participant);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// SLOTS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
int ParticipantSelectionModel::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 ParticipantSelectionModel::onInsertRow(const QModelIndex &parent, const ParticipantData &participantData)
|
|
{
|
|
return insertParticipant(parent, participantData, true);
|
|
}
|
|
|
|
bool ParticipantSelectionModel::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 ParticipantSelectionModel::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 ParticipantSelectionModel::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);
|
|
}
|
|
|
|
|
|
//=============================================================================================================================
|
|
// PARTICIPANTTREEFILTERPROXYMODEL
|
|
//=============================================================================================================================
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// PUBLIC MEMBER FUNCTIONS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
void ParticipantTreeFilterProxyModel::setAllowedTypes(const QSet<int> &allowedTypes)
|
|
{
|
|
if (m_allowedTypes == allowedTypes)
|
|
return;
|
|
m_allowedTypes = allowedTypes;
|
|
invalidateFilter();
|
|
}
|
|
|
|
void ParticipantTreeFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
|
{
|
|
m_sourceModel = qobject_cast<ParticipantSelectionModel *>(sourceModel);
|
|
setRecursiveFilteringEnabled(true);
|
|
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// PROTECTED MEMBER FUNCTIONS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
bool ParticipantTreeFilterProxyModel::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 ParticipantSelectionModel::CheckedTreeItem *left = m_sourceModel->privateData(source_left);
|
|
const ParticipantSelectionModel::CheckedTreeItem *right = m_sourceModel->privateData(source_right);
|
|
switch (source_left.column())
|
|
{
|
|
case ParticipantSelectionModel::ColDesignator:
|
|
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
|
|
case ParticipantSelectionModel::ColIdentifier:
|
|
return compareStrings(left->participant.tacticalIdentifier(),
|
|
right->participant.tacticalIdentifier(),
|
|
left, right);
|
|
case ParticipantSelectionModel::ColTypeClass:
|
|
case ParticipantSelectionModel::ColType:
|
|
break; // string compare
|
|
case ParticipantSelectionModel::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 ParticipantSelectionModel::ColParticipantId:
|
|
return left->participant.id() < right->participant.id();
|
|
case ParticipantSelectionModel::ColInfo:
|
|
return compareStrings(left->participant.info(),
|
|
right->participant.info(),
|
|
left, right);
|
|
case ParticipantSelectionModel::ColSign:
|
|
return compareStrings(left->participant.identification(),
|
|
right->participant.identification(),
|
|
left, right);
|
|
}
|
|
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
|
|
}
|
|
|
|
bool ParticipantTreeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
|
{
|
|
if ((!m_sourceModel) || (m_allowedTypes.isEmpty()))
|
|
return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
|
const ParticipantSelectionModel::CheckedTreeItem *parentItem = m_sourceModel->privateData(source_parent);
|
|
if (!parentItem)
|
|
return false;
|
|
const ParticipantSelectionModel::CheckedTreeItem *childItem =
|
|
static_cast<const ParticipantSelectionModel::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 ParticipantTreeFilterProxyModel::filterTypeRecursive(const ParticipantSelectionModel::CheckedTreeItem *item) const
|
|
{
|
|
if (m_allowedTypes.contains(item->participant.typeId()))
|
|
return true;
|
|
for (int i = 0; i < item->childCount(); ++i)
|
|
{
|
|
const ParticipantSelectionModel::CheckedTreeItem *child = static_cast<const ParticipantSelectionModel::CheckedTreeItem *>(item->child(i));
|
|
if (m_allowedTypes.contains(child->participant.typeId()))
|
|
return true;
|
|
if (filterTypeRecursive(child))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
|
|
//=============================================================================================================================
|
|
// PARTICIPANTTABLEFILTERPROXYMODEL
|
|
//=============================================================================================================================
|
|
ParticipantTableFilterProxyModel::ParticipantTableFilterProxyModel(QObject *parent)
|
|
: LtsWidgets::LtsColumnFilterProxyModel(parent)
|
|
, m_logger(ParticipantManagement::getLogger(QStringLiteral("ParticipantTableFilterProxyModel")))
|
|
{
|
|
setRecursiveFilteringEnabled(false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// PUBLIC MEMBER FUNCTIONS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
void ParticipantTableFilterProxyModel::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 ParticipantTableFilterProxyModel::resetMapping()
|
|
{
|
|
m_mapFromSourceInternal.clear();
|
|
m_mapToSourceInternal.clear();
|
|
m_mapFromSource.clear();
|
|
m_mapToSource.clear();
|
|
m_mapFromSourceFiltered.clear();
|
|
m_mapToSourceFiltered.clear();
|
|
m_sortMap.clear();
|
|
}
|
|
|
|
void ParticipantTableFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
|
{
|
|
m_sourceModel = qobject_cast<ParticipantSelectionModel *>(sourceModel);
|
|
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
|
|
}
|
|
|
|
|
|
int ParticipantTableFilterProxyModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
return parent.isValid() ? 0 : m_mapFromSourceInternal.size();
|
|
}
|
|
|
|
int ParticipantTableFilterProxyModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
return m_sourceModel->columnCount(mapToSource(parent));
|
|
}
|
|
|
|
|
|
QVariant ParticipantTableFilterProxyModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if ((index.column() == ParticipantSelectionModel::Columns::ColDesignator) && (role == Qt::DisplayRole))
|
|
return m_sourceModel->getHierarchyFromIndex(mapToSource(index));
|
|
return LtsColumnFilterProxyModel::data(index, role);
|
|
}
|
|
|
|
QVariant ParticipantTableFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
return m_sourceModel->headerData(section, orientation, role);
|
|
}
|
|
|
|
|
|
QModelIndex ParticipantTableFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
|
|
{
|
|
if (!m_mapFromSourceInternal.contains(sourceIndex))
|
|
return {};
|
|
return createIndex(m_mapFromSourceInternal.value(sourceIndex), sourceIndex.column());
|
|
}
|
|
|
|
QModelIndex ParticipantTableFilterProxyModel::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 ParticipantTableFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
|
|
{
|
|
return parent.isValid() ? QModelIndex() : createIndex(row, column);
|
|
}
|
|
|
|
QModelIndex ParticipantTableFilterProxyModel::parent(const QModelIndex &child) const
|
|
{
|
|
Q_UNUSED(child);
|
|
return {};
|
|
}
|
|
|
|
QModelIndex ParticipantTableFilterProxyModel::sibling(int row, int column, const QModelIndex &idx) const
|
|
{
|
|
return idx.row() == row ? createIndex(row, column) : QModelIndex();
|
|
}
|
|
|
|
|
|
bool ParticipantTableFilterProxyModel::hasChildren(const QModelIndex &parent) const
|
|
{
|
|
return !parent.isValid();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
// PROTECTED MEMBER FUNCTIONS
|
|
//-----------------------------------------------------------------------------------------------------------------------------
|
|
bool ParticipantTableFilterProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
|
|
{
|
|
if (sourceLeft.column() != sourceRight.column())
|
|
return false;
|
|
|
|
const ParticipantSelectionModel::CheckedTreeItem *left = m_sourceModel->privateData(sourceLeft);
|
|
const ParticipantSelectionModel::CheckedTreeItem *right = m_sourceModel->privateData(sourceRight);
|
|
|
|
switch (sourceLeft.column())
|
|
{
|
|
case ParticipantSelectionModel::ColDesignator:
|
|
return left->participant.hierarchyNumber() < right->participant.hierarchyNumber();
|
|
|
|
case ParticipantSelectionModel::ColIdentifier:
|
|
return compareStrings(left->participant.tacticalIdentifier(), right->participant.tacticalIdentifier(), left, right);
|
|
|
|
case ParticipantSelectionModel::ColTypeClass:
|
|
case ParticipantSelectionModel::ColType:
|
|
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(sourceLeft, sourceRight);
|
|
|
|
case ParticipantSelectionModel::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 ParticipantSelectionModel::ColParticipantId:
|
|
return left->participant.id() < right->participant.id();
|
|
|
|
case ParticipantSelectionModel::ColInfo:
|
|
return compareStrings(left->participant.info(), right->participant.info(), left, right);
|
|
|
|
case ParticipantSelectionModel::ColSign:
|
|
return compareStrings(left->participant.identification(), right->participant.identification(), left, right);
|
|
|
|
default:
|
|
return false;
|
|
} // switch
|
|
}
|
|
|
|
bool ParticipantTableFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
|
|
{
|
|
Q_UNUSED(source_column);
|
|
Q_UNUSED(source_parent);
|
|
return true;
|
|
}
|
|
|
|
bool ParticipantTableFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
|
{
|
|
Q_UNUSED(source_row);
|
|
Q_UNUSED(source_parent);
|
|
return true;
|
|
}
|
|
|
|
bool ParticipantTableFilterProxyModel::filterAcceptsRowInternal(int sourceRow, const QModelIndex &sourceParent) const
|
|
{
|
|
const QModelIndex index = m_sourceModel->index(sourceRow, 0, sourceParent);
|
|
if (m_sourceModel->hasChildren(index))
|
|
{
|
|
const ParticipantSelectionModel::CheckedTreeItem *parent = static_cast<const ParticipantSelectionModel::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 ®Expr = 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 ParticipantTableFilterProxyModel::mapTreeToTable(const QModelIndex &parent, int row)
|
|
{
|
|
const ParticipantSelectionModel::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 ParticipantTableFilterProxyModel::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 ParticipantTableFilterProxyModel::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
|