/** * @RDE_HEADER@ * AreaEffectSimulation - Explosion Edit Dialog * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2016-07-05 */ #include "aes_explosion_edit.h" #include "ui_aes_explosion_edit.h" #include #include #include #include #include #include #include #include #include #include #include namespace AreaEffectSimulation { double ExplosionEdit::s_minWidth; double ExplosionEdit::s_maxWidth; double ExplosionEdit::s_stepWidth; double ExplosionEdit::s_minHeight; double ExplosionEdit::s_maxHeight; double ExplosionEdit::s_stepHeight; double ExplosionEdit::s_minRadius; double ExplosionEdit::s_maxRadius; double ExplosionEdit::s_stepRadius; int ExplosionEdit::s_maxChargeUsage; double ExplosionEdit::s_minAltitude; double ExplosionEdit::s_maxAltitude; qint64 ExplosionEdit::s_maxDurationSeconds; static int s_lastChargeTypeA = 0; static int s_lastChargeTypeM = 0; ExplosionEdit::ExplosionEdit(const QString &menuGroup, ExplosionType type, ProjectDataManagement::GearClass gc, QWidget *parent) : BaseEdit(menuGroup, QLatin1String("ExplosionEdit"), parent) , m_type(type) , m_editable(false) , m_gearClass(gc) , m_previewCircleRadius(0.0) , m_ui(new Ui::ExplosionEdit) { m_ui->setupUi(this); // Todo: Get these values s_minWidth = 5; s_maxWidth = 1275; s_stepWidth = 5; s_minHeight = 5; s_maxHeight = 1275; s_minRadius = 5; s_maxRadius = 255; s_stepRadius = 1; s_stepHeight = 5; s_minAltitude = 0; s_maxAltitude = 1000; s_maxChargeUsage = 1000; s_maxDurationSeconds = 1800; // 30 Minutes m_ui->sbDimensionX->setMinimum(s_minWidth); m_ui->sbDimensionX->setMaximum(s_maxWidth); m_ui->sbDimensionX->setSingleStep(s_stepWidth); m_ui->sbDimensionY->setMinimum(s_minHeight); m_ui->sbDimensionY->setMaximum(s_maxHeight); m_ui->sbDimensionY->setSingleStep(s_stepHeight); m_ui->sbAltitude->setMinimum(s_minAltitude); m_ui->sbAltitude->setMaximum(s_maxAltitude); m_ui->sbCannons->setMinimum(1); m_ui->sbCannons->setMaximum(s_maxChargeUsage); m_ui->sbRounds->setMinimum(1); m_ui->sbRounds->setMaximum(s_maxChargeUsage); m_ui->sbDurationSeconds->setMinimum(1); m_ui->sbDurationSeconds->setMaximum(s_maxDurationSeconds); m_ui->sbAimingAxis->setMaximum(6400); m_ui->lblDesignator->clear(); m_ui->lblIcon->clear(); m_ui->lblTactIdentifier->clear(); m_uiRequirements->addRequirement({m_ui->lblPosition, [this]() { return (!m_ui->positionEdit->coordinate().isNull()); }}); m_uiRequirements->addRequirement({m_ui->lblName, [this]() { return (!m_ui->leIdentifier->text().isEmpty()); }}); m_uiRequirements->addRequirement({m_ui->lblChargeType, [this]() { return (!m_ui->cbChargeType->currentData().isNull()); }}); // either no effect start is set or effect start is greater than request time m_uiRequirements->addRequirement({m_ui->lblEffectStart, [this]() { return (hasValidEffectStart(m_ui->dteEffectStart) || m_ui->dteEffectStart->msecsSinceEpoch() >= m_ui->dteRequestTime->msecsSinceEpoch()); }}); if (m_type != etArea) { m_ui->lblAimingAxis->hide(); m_ui->sbAimingAxis->hide(); m_ui->lblDuration->hide(); m_ui->sbDurationSeconds->hide(); m_ui->sbDimensionX->setEnabled(false); m_ui->lblOperatorA->hide(); m_ui->sbDimensionY->hide(); m_ui->sbDimensionX->setMinimum(s_minRadius); m_ui->sbDimensionX->setMaximum(s_maxRadius); m_ui->sbDimensionX->setSingleStep(s_stepRadius); m_ui->sbCannons->setEnabled(false); m_ui->sbCannons->hide(); m_ui->lblOperatorB->hide(); m_ui->sbRounds->setEnabled(false); } else { m_ui->sbDurationSeconds->setValue(60); m_ui->sbCannons->setValue(4); m_ui->sbRounds->setValue(9); } if (m_type != etSingleCentral) { m_ui->lbxFiringAngle->hide(); m_ui->sbFiringAngle->hide(); m_ui->lbxIgniterType->hide(); m_ui->cbIgniterType->hide(); m_ui->lbxParticipant->hide(); m_ui->lblDesignator->hide(); m_ui->lblIcon->hide(); m_ui->lblTactIdentifier->hide(); m_ui->pbParticipant->hide(); m_ui->cbParty->setEnabled(false); } else m_uiRequirements->addRequirement({m_ui->lbxParticipant, [this]() { return (m_participant.id() != 0); }}); enaDisa(); connect(m_ui->cbParty, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionEdit::onPartyChanged); connect(m_ui->cbChargeType, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionEdit::onChargeTypeChanged); connect(m_ui->sbDimensionX, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); connect(m_ui->sbDimensionY, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); connect(m_ui->sbAltitude, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); // Not yet needed (no singleStep defined) // connect(m_ui->sbAimingAxis, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); // connect(m_ui->sbCannons, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); // connect(m_ui->sbRounds, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); // connect(m_ui->sbDurationSeconds, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue); connect(m_ui->sbCannons, QOverload::of(&QSpinBox::valueChanged), this, [this](int val) { onCannonsChanged(val); }); connect(m_ui->sbRounds, QOverload::of(&QSpinBox::valueChanged), this, [this](int val) { onRoundsChanged(val); }); connect(m_ui->pbFire, &QPushButton::clicked, this, &ExplosionEdit::onFireClicked); connect(m_ui->pbClear, &QPushButton::clicked, m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::clear); connect(m_ui->pbParticipant, &QPushButton::clicked, this, &ExplosionEdit::onParticipantClicked); m_symbolWatcher = new AdvancedSymbology::SymbolResultWatcherLabel(m_ui->lblIcon); } ExplosionEdit::~ExplosionEdit() { delete m_ui; } void ExplosionEdit::retranslate() { QString title; switch (m_gearClass.underlying()) { case ProjectDataManagement::GearClass::Artillery: if (m_type == etSingle) title = tr("Single Artillery"); else if (m_type == etArea) title = tr("Area Artillery"); else title = tr ("Single Artillery Central"); break; case ProjectDataManagement::GearClass::Mortar: if (m_type == etSingle) title = tr("Single Mortar"); else if (m_type == etArea) title = tr("Area Mortar"); else title = tr ("Single Mortar Central"); break; default: Q_ASSERT(false); } translate(m_ui, m_aesItem, title); if (m_type != etArea) { m_ui->sbCannons->setValue(1); m_ui->sbRounds->setValue(1); m_ui->lblRounds->setText(tr("Rounds:")); m_ui->lblDimension->setText(tr("Diameter [m]:")); } else { m_ui->lblRounds->setText(tr("Cannons x Rounds:")); m_ui->lblDimension->setText(tr("Dimension [m]:")); } setFireButton(m_aesItem.effectStateId()); updateMaxLabel(false); } void ExplosionEdit::onCannonsChanged(int cannons, bool modifiedFromExternal) { updateMaxLabel(modifiedFromExternal); if (cannons == 0 || maxAvailableRounds() == 0) return; m_ui->sbRounds->setMaximum(maxAvailableRounds() / cannons); updateMaxLabel(modifiedFromExternal); } void ExplosionEdit::onRoundsChanged(int rounds, bool modifiedFromExternal) { updateMaxLabel(modifiedFromExternal); if (rounds == 0 || maxAvailableRounds() == 0) return; m_ui->sbCannons->setMaximum(maxAvailableRounds() / rounds); updateMaxLabel(modifiedFromExternal); } void ExplosionEdit::enableUiElements(bool enable) { ERROR_LOG(m_logger) << " enableUiElements enable=" << enable; m_ui->leIdentifier->setMaxLength(30); m_ui->cbChargeType->setEnabled(enable); m_ui->sbDurationSeconds->setEnabled(enable); m_ui->cbParty->setEnabled(enable && m_type != etSingleCentral); // Set via Participant setEffectStartEnabled(m_ui->dteEffectStart, enable); m_ui->dteRequestTime->setEnabled(enable); m_ui->pbPosition->setEnabled(m_sitMapGuiIfc && enable); m_ui->positionEdit->setEnabled(enable); m_ui->sbAltitude->setEnabled(enable); m_ui->sbDimensionX->setEnabled(enable && m_type == etArea); m_ui->sbDimensionY->setEnabled(enable); m_ui->sbAimingAxis->setEnabled(enable); m_ui->cbPurpose->setEnabled(enable); m_ui->sbCannons->setEnabled(enable && m_type == etArea); m_ui->sbRounds->setEnabled(enable && m_type == etArea); m_ui->cbEffectMarkingInTa->setEnabled(enable); m_ui->leIdentifier->setEnabled(enable || isOnlyNameModifiable()); m_ui->pbClear->setEnabled(enable); m_ui->sbFiringAngle->setEnabled(enable); m_ui->pbParticipant->setEnabled(enable); m_ui->cbIgniterType->setEnabled(enable); if (!enable) { bool hasChanges = hasUnsavedChanges(); m_ui->sbDurationSeconds->setValue(m_aesItem.effectDuration() / 1000); // ms -> s if (!hasChanges) __unchange__(); } setFireButton(m_aesItem.effectStateId()); if (m_aesItem.effectStateId() == AesEffectState_def::EsExpired) { m_ui->leIdentifier->setText(QString::fromStdString(m_aesItem.name())); __unchange__(); } if (m_aesItem.effectStateId() == AesEffectState_def::EsActivated) { QString name = m_ui->leIdentifier->text(); AesItem baseData; if (m_type == etSingle) { ExplosionSingle singleData = m_dtc->explosionSingle(m_aesItem.id()); setSingleData(singleData); baseData = singleData; } else if (m_type == etArea) { ExplosionArea areaData = m_dtc->explosionArea(m_aesItem.id()); setAreaData(areaData); baseData = areaData; } setBaseData(baseData, false); setFireButton(baseData.effectStateId()); __unchange__(); if (name != m_ui->leIdentifier->text()) m_ui->leIdentifier->setText(name); } if (m_aesItem.effectStateId() == AesEffectState_def::EsPaused) __unchange__(); } int ExplosionEdit::maxAvailableRounds() const { int mapMax = m_chargeTypeMap[m_ui->cbChargeType->currentData().toInt()].available[m_ui->cbParty->currentData().toInt()]; return mapMax > s_maxChargeUsage ? s_maxChargeUsage : mapMax; } void ExplosionEdit::setBaseData(const AesItem &baseData, bool copy) { m_aesItem = baseData; if (copy) { m_aesItem.id(0); m_aesItem.effectStateId(AreaEffectSimulation::AesEffectState_def::get_default()); } m_ui->leIdentifier->setText(QString::fromUtf8(baseData.name().c_str())); int idx = m_ui->cbChargeType->findData(baseData.chargeStruct().chargeTypeId()); if (idx < 0) { auto ct = m_chargeTypeMap[baseData.chargeStruct().chargeTypeId()].chargeType; m_ui->cbChargeType->addItem(m_pdmDtc->trChargeTypeShortName(ct.id(), true), ct.id()); idx = m_ui->cbChargeType->findData(ct.id()); } m_ui->cbChargeType->setCurrentIndex(idx); m_ui->dteEffectStart->setMsecsSinceEpoch(baseData.effectStart().is_set() ? baseData.effectStart().get() : 0); m_ui->sbDurationSeconds->setValue(baseData.effectDuration() / 1000); // ms -> s m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(partyForItem(baseData))); m_ui->cbEffectMarkingInTa->setChecked(baseData.effectMarkingInTa()); m_editable = m_editable && isModifiable() && !isOnlyNameModifiable() && m_session->app() != LtsAppDescriptor::App::OC; } bool ExplosionEdit::init(int aesItemId, bool copy, LtsSession::LtsSession *session) { if (!BaseEdit::init(aesItemId, session)) return false; if (!m_session->getForeignDataInterface(m_asbIfc)) return false; if (!m_session->getDtc(m_ppmDtc)) { ERROR_LOG(m_logger) << " failed to get ppmDtc"; return false; } if (!m_session->getForeignGuiInterface(m_pdmCbx)) return false; bool readable = false; switch (m_gearClass.underlying()) { case ProjectDataManagement::GearClass::Artillery: m_editable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsEditArtillery) && m_type == etSingle) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsEditArtillery) && m_type == etArea) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditArtillery) && m_type == etSingleCentral) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExplosionEditCluster) && m_type == etArea) ; readable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsShowArtillery) && m_type == etSingle) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsShowArtillery) && m_type == etArea) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditArtillery) && m_type == etSingleCentral) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExplosionShowCluster) && m_type == etArea); if (m_type == etSingle) // Single Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Artillery")); else if (m_type == etArea) // Area Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Area_Artillery")); else if (m_type == etSingleCentral) // Single Central Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Central_Artillery")); break; case ProjectDataManagement::GearClass::Mortar: m_editable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsEditMortar) && m_type == etSingle) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsEditMortar) && m_type == etArea) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditMortar) && m_type == etSingleCentral) ; readable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsShowMortar) && m_type == etSingle) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsShowMortar) && m_type == etArea) || (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralShowMortar) && m_type == etSingleCentral) ; if (m_type == etSingle) // Single Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Mortar")); else if (m_type == etArea) // Area Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Area_Mortar")); else if (m_type == etSingleCentral) // Single Central Artillery setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Central_Mortar")); break; default: Q_ASSERT(false); break; } if (!readable && !m_editable) return false; if (!m_session->getDtc(m_pdmDtc)) return false; if (!m_session->getDtc(m_ppmDtc)) return false; m_ui->dteEffectStart->setMinimumTimeMSec(m_session->partitionTime()); m_ui->dteEffectStart->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); }); m_ui->dteRequestTime->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); }); LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableEffectShowManagement = ps->value(QStringLiteral("esm"), QStringLiteral("enableEffectShowManagement")).toBool(); m_ui->cbEffectMarkingInTa->setVisible(enableEffectShowManagement && ((m_session->app() == LtsAppDescriptor::App::Situation || m_session->app() == LtsAppDescriptor::App::SituationTablet) && (!m_session->isReplayPartition()))); if (m_sitMapGuiIfc) connect(m_ui->pbPosition, &QPushButton::clicked, this, &ExplosionEdit::getPosition); else m_ui->pbPosition->setEnabled(false); const auto firePurposes = m_pdmDtc->firePurposes(); for (const auto &firePurpose : firePurposes) m_ui->cbPurpose->addItem(m_pdmDtc->trFirePurposeName(firePurpose.id()), firePurpose.id()); const auto chargesForGearClass = m_pdmDtc->chargesForGearClass(m_gearClass); for (const ProjectDataManagement::ChargeType &chargeType : chargesForGearClass) m_chargeTypeMap[chargeType.id()].chargeType = chargeType; const auto chargeAdmins = m_dtc->chargeAdministrations(); for (const ChargeAdministration &val : chargeAdmins) { if (!m_chargeTypeMap.contains(val.chargeTypeId())) continue; m_chargeTypeMap[val.chargeTypeId()].available.insert(val.partyId(), val.amount() - val.planned()); } m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty); onPartyChanged(); if (aesItemId > 0) { AesItem baseData; if (m_type == etSingle) { ExplosionSingle singleData = m_dtc->explosionSingle(aesItemId); setSingleData(singleData); baseData = singleData; } else if (m_type == etArea) { ExplosionArea areaData = m_dtc->explosionArea(aesItemId); setAreaData(areaData, copy); baseData = areaData; } setBaseData(baseData, copy); setFireButton(baseData.effectStateId()); } else setFireButton(AreaEffectSimulation::AesEffectState::EsUnknown); enableUiElements(m_editable); connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::chargeAdministrationChanged, this, &ExplosionEdit::onChargeCountChanged); connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged, this, &BaseEdit::onAreaChanged); onChargeTypeChanged(); return true; } QString ExplosionEdit::dialogIdentifier() const { switch (m_gearClass.underlying()) { case ProjectDataManagement::GearClass::Artillery: if (m_type == etSingle) return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleArtillery") : QStringLiteral("EditExplosionSingleArtillery%1").arg(m_aesItem.id()); else if (m_type == etArea) return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionAreaArtillery") : QStringLiteral("EditExplosionAreaArtillery%1").arg(m_aesItem.id()); else return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleCentralArtillery") : QStringLiteral("EditExplosionSingleCentralArtillery%1").arg(m_aesItem.id()); case ProjectDataManagement::GearClass::Mortar: if (m_type == etSingle) return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleMortar") : QStringLiteral("EditExplosionSingleMortar%1").arg(m_aesItem.id()); else if (m_type == etArea) return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionAreaMortar") : QStringLiteral("EditExplosionAreaMortar%1").arg(m_aesItem.id()); else return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleCentralMortar") : QStringLiteral("EditExplosionSingleCentralMortar%1").arg(m_aesItem.id()); default: Q_ASSERT(false); break; } return QString(); } void ExplosionEdit::onChargeCountChanged(const AreaEffectSimulation::ChargeAdministration &data, DataModification modType) { // All relevant Charge Types are in Map. So we can just skip the others. if (!m_chargeTypeMap.contains(data.chargeTypeId())) return; if (modType == DataModification::eRemoved || data.amount() - data.planned() <= 0) m_chargeTypeMap[data.chargeTypeId()].available.remove(data.partyId()); else m_chargeTypeMap[data.chargeTypeId()].available.insert(data.partyId(), data.amount() - data.planned()); onPartyChanged(); updateCannonsRounds(true); updateMaxLabel(true); } void ExplosionEdit::showPreviewObject() { if (!m_sitMapGuiIfc) return; if (m_previewCircleCenter.isNull() && m_previewPolygon.isEmpty()) return; if (m_previewCircleCenter.isNull()) m_sitMapGuiIfc->displayPreviewObject(this, m_previewPolygon, true); else m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius); } void ExplosionEdit::setFireButton(AreaEffectSimulation::AesEffectState effectState) { if (effectState == AesEffectState::EsActivated) { m_ui->pbFire->setText(tr("Pause")); m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_pause.svg"))); } else { m_ui->pbFire->setText(tr("Start")); m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_start.svg"))); } if ((effectState == AesEffectState::EsActivated || effectState == AesEffectState::EsPrepared || effectState == AesEffectState::EsCreated || effectState == AesEffectState::EsPaused) && m_aesItem.id() > 0) m_ui->pbFire->setEnabled(true); else m_ui->pbFire->setEnabled(false); } void ExplosionEdit::updateCannonsRounds(bool modifiedFromExternal) { // If Cannons are already more than availRounds, Reset both to One if (m_ui->sbCannons->value() > maxAvailableRounds() && m_editable && m_aesItem.id() <= 0) { m_ui->sbCannons->setValue(1); m_ui->sbRounds->setValue(1); } // setMaximum on one or both. onCannonsChanged(m_ui->sbCannons->value(), modifiedFromExternal); onRoundsChanged(m_ui->sbRounds->value(), modifiedFromExternal); } void ExplosionEdit::updateMaxLabel(bool modifiedFromExternal) { QString text = tr("= %1 (avail: %2%3)").arg(m_ui->sbCannons->value() * m_ui->sbRounds->value()) .arg(maxAvailableRounds() == s_maxChargeUsage ? QLatin1String(">=") : QString()) .arg(maxAvailableRounds()); if (modifiedFromExternal) // Todo: Use palette text = QStringLiteral("%1").arg(text); if (text == m_ui->lblMaxCount->text()) return; m_ui->lblMaxCount->setText(text); m_ui->lblMaxCount->setToolTip(modifiedFromExternal ? tr("Cannons / Rounds potentially modified by external source!") : QString()); } quint32 ExplosionEdit::partyForItem(const AesItem &aesItem) const { if (aesItem.participantId().is_set()) { if (m_participant.id() == aesItem.participantId().get()) return m_participant.partyId(); else return m_ppmDtc->participant(aesItem.participantId().get()).partyId(); } else if (aesItem.partyId().is_set()) return aesItem.partyId().get(); return {}; } void ExplosionEdit::setSingleData(const ExplosionSingle &singleData) { QPointF impactPosition(singleData.impactPosition().lon(), singleData.impactPosition().lat()); if (!impactPosition.isNull()) m_ui->positionEdit->setCoordinate(impactPosition); m_ui->dteRequestTime->setMsecsSinceEpoch(singleData.fireRequestTime()); m_ui->cbPurpose->setCurrentText(QString::fromUtf8(singleData.purpose().c_str())); m_ui->sbAltitude->setValue(singleData.altitude()); if (m_aesItem.id() > 0) m_chargeTypeMap[singleData.chargeStruct().chargeTypeId()].available[partyForItem(singleData)] += 1; const double radius = m_pdmDtc->impactRadius(singleData.chargeStruct().chargeTypeId(),singleData.chargeStruct().igniterTypeId(), singleData.chargeStruct().igniterFunctionId()); m_ui->sbDimensionX->setValue(2 * radius); } void ExplosionEdit::setAreaData(const ExplosionArea &areaData, bool copy) { const Area &area = areaData.area(); if (!qFuzzyCompare(area.cornerBottomLeft().lon(), 0) || !qFuzzyCompare(area.cornerBottomLeft().lat(), 0)) { QPointF bottomLeft(area.cornerBottomLeft().lon(), area.cornerBottomLeft().lat()); m_ui->positionEdit->setCoordinate(Polygons::bottomLeft2CenterPoint(bottomLeft, area.width(), area.height(), area.rotation())); } m_ui->sbDimensionX->setValue(area.width()); m_ui->sbDimensionY->setValue(area.height()); m_ui->sbAimingAxis->setValue(fmod((90.0 + area.rotation()) * 6400 / 360, 6400)); m_ui->sbCannons->setValue(areaData.cannonCount() == 0 ? 4 : areaData.cannonCount()); m_ui->sbRounds->setValue(areaData.cannonRounds() == 0 ? 9 : areaData.cannonRounds()); if (m_aesItem.id() > 0 && !copy) { if (!m_chargeTypeMap.contains(areaData.chargeStruct().chargeTypeId())) ERROR_LOG(m_logger) << "SetAreaData called with a charge Type, that has doesn't match. Id " << areaData.chargeStruct().chargeTypeId() << "is not in m_chargeTypeMap"; if (!areaData.partyId().is_set()) ERROR_LOG(m_logger) << "SetAreaData called without a party set. m_chargeTypeMap might be corrupted."; m_chargeTypeMap[areaData.chargeStruct().chargeTypeId()].available[areaData.partyId().get()] += areaData.cannonCount() * areaData.cannonRounds(); } m_ui->dteRequestTime->setMsecsSinceEpoch(areaData.fireRequestTime()); m_ui->cbPurpose->setCurrentText(QString::fromUtf8(areaData.purpose().c_str())); m_ui->sbAltitude->setValue(areaData.altitude()); } //slots void ExplosionEdit::getPosition() { if (!has2DMap()) return; bool isNull = qFuzzyCompare(m_ui->positionEdit->coordinate().x(), 0) && qFuzzyCompare(m_ui->positionEdit->coordinate().y(), 0); QPointF p(Polygons::centerPoint2BottomLeft(m_ui->positionEdit->coordinate(), m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) / 6400. * 360)); if (m_type != etArea) m_sitMapGuiIfc->getCoordinate(this, SLOT(onCoordinate(QPointF)), nullptr); else m_sitMapGuiIfc->getRectangle(this, SLOT(onMapRecData(QPointF,double,double,double,QPolygonF)), nullptr, s_minWidth, s_maxWidth, s_minHeight, s_maxHeight, isNull ? m_ui->positionEdit->coordinate() : p, m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) / 6400. * 360); } void ExplosionEdit::onCoordinate(const QPointF ¢erPoint) { m_previewPolygon.clear(); m_previewCircleCenter = centerPoint; m_previewCircleRadius = 0.5 * m_ui->sbDimensionX->value(); m_ui->positionEdit->setCoordinate(centerPoint); showPreviewObject(); } void ExplosionEdit::onMapRecData(const QPointF &bottomLeft, double width, double height, double angleDegNorth, const QPolygonF &bufferOutlinePolygon) { m_previewCircleCenter = QPointF(); m_previewPolygon = bufferOutlinePolygon; m_ui->positionEdit->setCoordinate(Polygons::bottomLeft2CenterPoint(bottomLeft, width, height, angleDegNorth)); m_ui->sbDimensionX->setValue(round(width)); m_ui->sbDimensionY->setValue(round(height)); m_ui->sbAimingAxis->setValue(fmod((90.0 + angleDegNorth) * 6400 / 360, 6400)); showPreviewObject(); } void ExplosionEdit::onChargeTypeChanged() { int chargeTypeId =m_ui->cbChargeType->currentData().toInt(); // update comboBox IgniterType: m_pdmCbx->fillIgniterComboBox(m_ui->cbIgniterType, chargeTypeId); updateCannonsRounds(); if (m_type == etArea) return; // NOTE: igniterType/IgniterFunciton = 0 as long as ExplosionEdit does not contain values double impactRadius = m_pdmDtc->impactRadius(m_ui->cbChargeType->currentData().toInt(), 0, 0); m_ui->sbDimensionX->setValue(2 * impactRadius); } void ExplosionEdit::onPartyChanged() { int partyId = m_ui->cbParty->currentData().toInt(); int chargeTypeId = m_ui->cbChargeType->currentData().toInt(); if (chargeTypeId <= 0) chargeTypeId = m_gearClass == ProjectDataManagement::GearClass::Artillery ? s_lastChargeTypeA : s_lastChargeTypeM; QSignalBlocker sb(m_ui->cbChargeType); m_ui->cbChargeType->clear(); for (const Charges &charge : qAsConst(m_chargeTypeMap)) { if (charge.available.contains(partyId) && charge.available[partyId] > 0) m_ui->cbChargeType->addItem(m_pdmDtc->trChargeTypeShortName(charge.chargeType.id(), true), charge.chargeType.id()); } m_ui->cbChargeType->setCurrentIndex(m_ui->cbChargeType->findData(chargeTypeId) < 0 ? 0 : m_ui->cbChargeType->findData(chargeTypeId)); sb.unblock(); updateCannonsRounds(); } bool ExplosionEdit::apply() { std::string name = m_ui->leIdentifier->text().toStdString(); qint64 effectStart = m_ui->dteEffectStart->msecsSinceEpoch(); int chargeType = m_ui->cbChargeType->currentData().toInt(); if (m_gearClass == ProjectDataManagement::GearClass::Artillery) s_lastChargeTypeA = chargeType; else s_lastChargeTypeM = chargeType; int partyId = m_ui->cbParty->currentData().toInt(); // int playerId = m_participant.id(); int cannons = m_ui->sbCannons->value(); int rounds = m_ui->sbRounds->value(); int duration = m_ui->sbDurationSeconds->value() * 1000; // secs --> msecs // Area: QPointF bottomLeft(Polygons::centerPoint2BottomLeft(m_ui->positionEdit->coordinate(), m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400)); const Core::GeoPosition2D bottomLeftGeo(bottomLeft.x(), bottomLeft.y()); const Area area(bottomLeftGeo, m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400); // Single: const Core::GeoPosition2D pos(m_ui->positionEdit->coordinate().x(), m_ui->positionEdit->coordinate().y()); const Core::GeoPosition pos3D(m_ui->positionEdit->coordinate().x(), m_ui->positionEdit->coordinate().y(), 0.0); qint64 fireRequestTime = m_ui->dteRequestTime->msecsSinceEpoch(); int altitude = m_ui->sbAltitude->value(); std::string purpose = m_ui->cbPurpose->currentText().toStdString(); if (m_type == etSingle) { const ExplosionSingleModifyRequest req(m_aesItem.id(), name, LtsUtils::toOptional(effectStart), m_aesItem.effectSourceId(), chargeType, pos, LtsUtils::toOptional(partyId), LtsUtils::toOptional(0), // PlayerId fireRequestTime, altitude, purpose, m_ui->cbEffectMarkingInTa->isChecked(), dds::core::optional()); m_requestHelper->addRequest([this, req]() { return m_ftc->explosionSingleModifyRequest(req, this); }); } else if (m_type == etArea) // area { const ExplosionAreaModifyRequest req(m_aesItem.id(), name, LtsUtils::toOptional(effectStart), duration, m_aesItem.effectSourceId(), chargeType, area, LtsUtils::toOptional(partyId), LtsUtils::toOptional(0), // PlayerId cannons, rounds, fireRequestTime, altitude, purpose, m_ui->cbEffectMarkingInTa->isChecked(), dds::core::optional()); m_requestHelper->addRequest([this, req]() { return m_ftc->explosionAreaModifyRequest(req, this); }); } return m_requestHelper->startRequests(); } void ExplosionEdit::onFireClicked() { m_ui->pbFire->setEnabled(false); if ((m_aesItem.effectStateId() != AesEffectState_def::EsActivated && m_aesItem.effectStateId() != AesEffectState_def::EsPaused && m_aesItem.effectStateId() != AesEffectState_def::EsCreated && m_aesItem.effectStateId() != AesEffectState_def::EsPrepared) || m_aesItem.id() <= 0) return; // Catches all single fires... if (m_aesItem.effectStateId() == AesEffectState_def::EsCreated || m_aesItem.effectStateId() == AesEffectState_def::EsPrepared) { m_ui->dteEffectStart->setMsecsSinceEpoch(m_session->partitionTime() + 3000); m_ui->dteEffectStart->setChecked(true); // Todo: Triggering by simulated button press is a little awkward... if (m_uiRequirements->checkRequirements(this)) this->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->click(); else m_ui->pbFire->setEnabled(true); return; } // No more single fires handled here... m_requestHelper->addRequest([ = ]() { return m_ftc->explosionAreaPauseResumeRequest(ExplosionAreaPauseResumeRequest(m_aesItem.id(), m_aesItem.effectStateId() != AesEffectState_def::EsActivated, dds::core::optional(), dds::core::optional(), dds::core::optional()) , this); }); m_requestHelper->startRequests(); } void ExplosionEdit::onParticipantClicked() { ParticipantManagement::ParticipantSelection *ps = new ParticipantManagement::ParticipantSelection(m_session, this); ps->init(); ps->setParticipantSelected(m_participant.partyId(), m_participant.id()); connect(ps, &ParticipantManagement::ParticipantSelection::selectedParticipants, this, &ExplosionEdit::onParticipant); ps->exec(); } void ExplosionEdit::onParticipant(const QVector &participants) { if (participants.isEmpty()) return; if (participants[0] == m_participant.id()) return; m_participant = m_ppmDtc->participant(participants[0]); m_symbolWatcher->setSymbolResult(m_asbIfc->findImage(m_participant)); m_ui->lblDesignator->setText(QString::fromStdString(m_participant.designator())); m_ui->lblTactIdentifier->setText(QString::fromStdString(m_participant.tacticalIdentifier())); // m_ui->lblParticipant->setText(participant.equipmentNumber().is_set() ? QString::number(participant.equipmentNumber().get()) : tr("Id: %1").arg(participant.id())); m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(m_participant.partyId())); slotChangesMade(); } } // namespace AreaEffectSimulation