/** * @RDE_HEADER@ * HMI Plugin for OperatorManangement - Operator Overview Dialog Model * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-02-29 */ #include "charge_table_model.h" #include #include #include namespace ProjectDataManagement { const QVector ChargeTableDialogModel::ALL_ULEIS = {ChargeTableDialogModel::DirectImpactGrandparent, ChargeTableDialogModel::DirectImpactChild, ChargeTableDialogModel::BurstOfFireParent, ChargeTableDialogModel::BurstOfFireChild, ChargeTableDialogModel::AirBurstParent, ChargeTableDialogModel::AirBurstChild, ChargeTableDialogModel::UnarmedParent, ChargeTableDialogModel::UnarmedChild, ChargeTableDialogModel::FireAndForgetParent, ChargeTableDialogModel::FireAndForgetChild, ChargeTableDialogModel::ShortTimeScanningParent, ChargeTableDialogModel::ShortTimeScanningChild }; const QVector ChargeTableDialogModel::ALL_TAS = {ChargeTableDialogModel::ShortTimeChild1, ChargeTableDialogModel::ShortTimeChild2, ChargeTableDialogModel::ShortTimeChild3, ChargeTableDialogModel::ShortTimeChild4, ChargeTableDialogModel::ShortTimeChild5 }; //--------------------------------- // ChargeTableDialogModel //--------------------------------- ChargeTableDialogModel::ChargeTableDialogModel(bool isOrganiser, QObject *parent ) : LtsWidgets::LtsAbstractTableModel({tr("Id"), tr("Is Visible"), tr("Predefined Name"), tr("Short Name"), tr("Long Name"), tr("Direct Impact"), tr("Top Attack"), tr("Burst of Fire"), tr("Air Burst"), tr("Unarmed"), tr("Short Time"), tr("Fire-and-Forget"), tr("Short-Time Scanning"), tr("DIS Entity")}, parent) , m_dtc(nullptr) , m_isOrganiser(isOrganiser) , m_logger(getLogger("ChargeTableDialogModel")) {} ChargeTableDialogModel::~ChargeTableDialogModel() {} bool ChargeTableDialogModel::initModel(const std::shared_ptr &dtc, const QSet &gearClasses) { m_dtc = dtc; m_gearClasses = gearClasses; connect(m_dtc.get(), &ProjectDataManagement::Interface::Dtc::chargeTypeChanged, this, &ChargeTableDialogModel::onDataChanged); m_dtc->chargeTypes([this](const ProjectDataManagement::ChargeType &ct) { if (!m_isOrganiser && !m_dtc->isVisible(ct.id())) return; insertRow(ct); if (m_isOrganiser && m_dtc->isVisible(ct.id())) m_visibleCharges.insert(ct.id()); }); return true; } QVariant ChargeTableDialogModel::data(const QModelIndex &index, int role) const { if (!indexIsValid(index)) return QVariant(); const ProjectDataManagement::ChargeType &ct = m_vecData.at(index.row()); // if (role == Qt::DecorationRole && index.column() == PredefinedName) // return QIcon(QLatin1String(":/style/mortar_ammunition_administration.svg")); if (role == Qt::CheckStateRole) { // CheckBoxes only for columns that are actually checkable if (!flags(index).testFlag(Qt::ItemIsUserCheckable)) return QVariant(); // Organiser: if (m_isOrganiser) { if (index.column() == IsVisible) return (m_visibleCharges.contains(ct.id()) ? Qt::Checked : Qt::Unchecked); return QVariant(); } // Non Organiser: qint32 tasCode = ct.id(); // TAS Codes < 2000 -> One Column fits all if (tasCode < 2000) { if (index.column() == IsVisible) return m_visibleCharges.contains(ct.id()) ? Qt::Checked : Qt::Unchecked; // TAS Codes 1-46 exept 25 and 30 use cols DI, TA, BF, AB: if (tasCode >= 1 && tasCode <= 46 && tasCode != 25 && tasCode != 30) if (index.column() != DirectImpact && index.column() != TopAttack && index.column() != BurstOfFire && index.column() != AirBurst) return QVariant(); // TAS Codes 25 and 30 are for FF charges: if (tasCode == 25 || tasCode == 30) if (index.column() != FireAndForget) return QVariant(); // TAS Codes 77-79 are for SS charges if (tasCode >= 77 && tasCode <=79) if (index.column() != ShortTimeScanning) return QVariant(); // TAS Codes 47-74 and 15xx, 16xx, 17xx, 18xx and 19xx (xx = 47-74) are ST charges // Note: Not ommitting e.g. 13xx! if (tasCode % 100 >= 47 && tasCode % 100 <= 76) if (index.column() != ShortTime) return QVariant(); // Now that all columns not relevant for charge do NOT show any CheckBoxes, // we fill the remaining columns with synchronized checked CheckBoxes. return m_visibleCharges.contains(ct.id()) ? Qt::Checked : Qt::Unchecked; } // TAS Codes > 2000 if (index.column() == IsVisible) { // Parent / Child are treated identically // Direct Hit / Top Attack is the same kind int sum = 0; if (m_visibleCharges.contains(tasCode + DirectImpactGrandparent) || m_visibleCharges.contains(tasCode + DirectImpactChild)) ++sum; if (m_visibleCharges.contains(tasCode + BurstOfFireParent) || m_visibleCharges.contains(tasCode + BurstOfFireChild)) ++sum; if (m_visibleCharges.contains(tasCode + AirBurstParent) || m_visibleCharges.contains(tasCode + AirBurstChild)) ++sum; if (m_visibleCharges.contains(tasCode + UnarmedParent) || m_visibleCharges.contains(tasCode + UnarmedChild)) ++sum; if (m_visibleCharges.contains(tasCode + FireAndForgetParent) || m_visibleCharges.contains(tasCode + FireAndForgetChild)) ++sum; if (m_visibleCharges.contains(tasCode + ShortTimeScanningParent) || m_visibleCharges.contains(tasCode + ShortTimeScanningChild)) ++sum; // Check if all / nothing / intermediate is checked and set CheckBox accordingly: if (sum == 0) return Qt::Unchecked; if (sum == 6) return Qt::Checked; return Qt::PartiallyChecked; } // Now to the single CheckBoxes: // Direct Impact and Top Attack are treated identically: // Do not distinguish unless Offset differs. if (index.column() == DirectImpact || index.column() == TopAttack) return (m_visibleCharges.contains(tasCode + DirectImpactGrandparent) || m_visibleCharges.contains(tasCode + DirectImpactChild) ? Qt::Checked : Qt::Unchecked); if (index.column() == BurstOfFire) return (m_visibleCharges.contains(tasCode + BurstOfFireParent) || m_visibleCharges.contains(tasCode + BurstOfFireChild) ? Qt::Checked : Qt::Unchecked); if (index.column() == AirBurst) return (m_visibleCharges.contains(tasCode + AirBurstParent) || m_visibleCharges.contains(tasCode + AirBurstChild) ? Qt::Checked : Qt::Unchecked); if (index.column() == Unarmed) return (m_visibleCharges.contains(tasCode + UnarmedParent) || m_visibleCharges.contains(tasCode + UnarmedChild) ? Qt::Checked : Qt::Unchecked); if (index.column() == FireAndForget) return (m_visibleCharges.contains(tasCode + FireAndForgetParent) || m_visibleCharges.contains(tasCode + FireAndForgetChild) ? Qt::Checked : Qt::Unchecked); if (index.column() == ShortTimeScanning) return (m_visibleCharges.contains(tasCode + ShortTimeScanningParent) || m_visibleCharges.contains(tasCode + ShortTimeScanning) ? Qt::Checked : Qt::Unchecked); } if (role == Qt::DisplayRole) { switch (index.column()) { case IsVisible: if (m_isOrganiser) return m_dtc->isVisible(ct.id()) ? tr("Yes") : tr("No"); else return ct.id(); case Id: return QString::number(ct.id()); case PredefinedName: return QString::fromStdString(ct.predefinedName()); case ShortName: return QString::fromStdString(ct.shortName()); case LongName: return QString::fromStdString(ct.name()); case DisEntity: return QString::fromStdString(ct.disEntity()); } } return QVariant(); } bool ChargeTableDialogModel::setData(const QModelIndex &idx, const QVariant &value, int role) { if (!indexIsValid(idx)) return false; if (role != Qt::CheckStateRole) return false; bool bVal = value.toBool(); if (m_isOrganiser) { if (idx.column() != IsVisible) return false; // Get JSON Properties ChargeType ct = getRowData(idx.row()); QJsonDocument propsDoc = QJsonDocument::fromJson(QByteArray::fromStdString(ct.properties())); QJsonObject propsObj; if (propsDoc.isObject()) // if the given properties are no valid JSON object, the properties will be made from scratch propsObj = propsDoc.object(); // Update JSON Properties if (bVal) { propsObj.remove(QLatin1String("suppressVisibility")); m_visibleCharges.insert(m_vecData.at(idx.row()).id()); } else { propsObj.insert(QLatin1String("suppressVisibility"), true); m_visibleCharges.remove(m_vecData.at(idx.row()).id()); } // Put back JSON Properties ct.properties(QJsonDocument(propsObj).toJson(QJsonDocument::Compact).toStdString()); setRowData(idx.row(), ct); if (m_dtc->isVisible(ct.id()) != bVal) m_differences.insert(ct.id()); else m_differences.remove(ct.id()); emit chargeVisibilityChanged(!m_differences.isEmpty()); return true; } else { const int tasCode = m_vecData.at(idx.row()).id(); if (tasCode < 1000) { if (bVal) m_visibleCharges.insert(tasCode); else m_visibleCharges.remove(tasCode); // Grandparents 47 - 76 have Children that are implicitly checked: if (tasCode >= 47 && tasCode <= 76) { if (bVal) { for (const auto &type : ALL_TAS) m_visibleCharges.insert(type + tasCode); } else { for (const auto &type : ALL_TAS) m_visibleCharges.remove(type + tasCode); } } } else if (tasCode > 2000) { if (bVal) { // Check all Types if (idx.column() == IsVisible) { for (const auto &type : ALL_ULEIS) m_visibleCharges.insert(tasCode + type); } // For each type check both uleis codes: if (idx.column() == DirectImpact) { m_visibleCharges.insert(tasCode + DirectImpactGrandparent); m_visibleCharges.insert(tasCode + DirectImpactChild); } if (idx.column() == TopAttack) { m_visibleCharges.insert(tasCode + TopAttackGrandparent); m_visibleCharges.insert(tasCode + TopAttackChild); } if (idx.column() == BurstOfFire) { m_visibleCharges.insert(tasCode + BurstOfFireParent); m_visibleCharges.insert(tasCode + BurstOfFireChild); } if (idx.column() == AirBurst) { m_visibleCharges.insert(tasCode + AirBurstParent); m_visibleCharges.insert(tasCode + AirBurstChild); } if (idx.column() == Unarmed) { m_visibleCharges.insert(tasCode + UnarmedParent); m_visibleCharges.insert(tasCode + UnarmedChild); } if (idx.column() == FireAndForget) { m_visibleCharges.insert(tasCode + FireAndForgetParent); m_visibleCharges.insert(tasCode + FireAndForgetChild); } if (idx.column() == ShortTimeScanning) { m_visibleCharges.insert(tasCode + ShortTimeScanningParent); m_visibleCharges.insert(tasCode + ShortTimeScanningChild); } } else { // Uncheck all types if (idx.column() == IsVisible) { for (const auto &type : ALL_ULEIS) m_visibleCharges.remove(tasCode + type); } // For each type uncheck both uleis codes: if (idx.column() == DirectImpact) { m_visibleCharges.remove(tasCode + DirectImpactGrandparent); m_visibleCharges.remove(tasCode + DirectImpactChild); } if (idx.column() == TopAttack) { m_visibleCharges.remove(tasCode + TopAttackGrandparent); m_visibleCharges.remove(tasCode + TopAttackChild); } if (idx.column() == BurstOfFire) { m_visibleCharges.remove(tasCode + BurstOfFireParent); m_visibleCharges.remove(tasCode + BurstOfFireChild); } if (idx.column() == AirBurst) { m_visibleCharges.remove(tasCode + AirBurstParent); m_visibleCharges.remove(tasCode + AirBurstChild); } if (idx.column() == Unarmed) { m_visibleCharges.remove(tasCode + UnarmedParent); m_visibleCharges.remove(tasCode + UnarmedChild); } if (idx.column() == FireAndForget) { m_visibleCharges.remove(tasCode + FireAndForgetParent); m_visibleCharges.remove(tasCode + FireAndForgetChild); } if (idx.column() == ShortTimeScanning) { m_visibleCharges.remove(tasCode + ShortTimeScanningParent); m_visibleCharges.remove(tasCode + ShortTimeScanningChild); } } } else { // Tas Code == 1000 - 2000: Do nothing, they are invalid... } emit dataChanged(index(idx.row(), 0), index(idx.row(), columnCount() - 1)); return true; } return false; } QVector ChargeTableDialogModel::getChargeTypesWithChangedVisibility() const { QVector cts; for (const auto &ct : m_vecData) if (m_differences.contains(ct.id())) cts << ct; return cts; } bool ChargeTableDialogModel::isFiltered(int chargeTypeId) const { // Filter anything? if (m_isOrganiser) return false; if (!m_gearClasses.contains(m_dtc->gearClassFromChargeTypeId(baseCharge(chargeTypeId)))) return true; return !isBaseCharge(chargeTypeId); } bool ChargeTableDialogModel::isFiltered(const ProjectDataManagement::ChargeType &data, DataModification modType) const { Q_UNUSED(modType); return isFiltered(data.id()); } QSet ChargeTableDialogModel::selectedCharges() const { return m_visibleCharges; } int ChargeTableDialogModel::baseCharge(int chargeTypeId) { if (isBaseCharge(chargeTypeId)) return chargeTypeId; if (chargeTypeId < 2000) return (chargeTypeId % 100); return ((chargeTypeId % 1000) + 2000); } } // namespace ProjectDataManagement