/** * @RDE_HEADER@ * AreaEffectSimulation - MineArea Edit Dialog * $Id$ * $HeadURL$ * $Author$ * * Created by Mike Wille, SLSE, 2016-09-26 */ #include "aes_mine_area_edit.h" #include "ui_aes_mine_area_edit.h" #include #include #include #include #include #include #include #include #include #include #include namespace AreaEffectSimulation { static int s_lastChargeType = 5005; // PzAbwVMiV as default static int s_DM72ChargeTypeId = 395; MineAreaEdit::MineAreaEdit(const QString &menuGroup, QWidget *parent) : BaseEdit(menuGroup, QLatin1String("MineAreaEdit"), parent) , m_ui(new Ui::MineAreaEdit) , m_segments(5) { m_ui->setupUi(this); m_ui->cbMineAreaInputMode->addItem(tr("Front line"), false); m_ui->cbMineAreaInputMode->addItem(tr("Center line"), true); // Todo: Constraints from wherever m_ui->cbDurationHours->setValidator(new QIntValidator(1, s_maxDurationHours, this)); m_ui->sbDensity->setDecimals(1); m_ui->sbDensity->setSingleStep(s_stepDensity); m_ui->sbDensity->setMinimum(s_minDensity); m_ui->sbDensity->setMaximum(s_maxDensity); m_ui->sbWidth->setDecimals(2); m_ui->sbWidth->setSingleStep(s_stepWidth); m_ui->sbWidth->setMinimum(s_minWidth); m_ui->sbWidth->setMaximum(s_maxWidth); m_ui->stackedWidget->addWidget(m_ui->sbDensity); m_ui->stackedWidget->addWidget(m_ui->cbxDensity); m_uiRequirements->addRequirement({m_ui->lbIdentifier, [this]() { return (!m_ui->leIdentifier->text().isEmpty()); }}); m_uiRequirements->addRequirement({m_ui->lbChargeType, [this]() { return (m_ui->cbChargeType->currentData().toInt() > 0); }}); m_uiRequirements->addRequirement({m_ui->lbEffectStart, [this]() { return hasValidEffectStart(m_ui->dteEffectStart); }}); m_uiRequirements->addRequirement({m_ui->lbParty, [this]() { return (m_ui->cbParty->currentData().toInt() > 0); }}); m_uiRequirements->addRequirement({m_ui->lbLayingMethod, [this]() { return (m_ui->cbLayingProcedure->currentData().toInt() > 0); }}); //m_uiRequirements->addRequirement({m_ui->lbGeometry, [this]() { return (!(m_ui->posEdit1->pos().isNull() || (m_ui->posEdit3->pos().isNull() && !m_ui->posEdit4->pos().isNull()) || (m_ui->posEdit4->pos().isNull() && !m_ui->posEdit5->pos().isNull()) || (m_ui->posEdit5->pos().isNull() && !m_ui->posEdit6->pos().isNull()))); }}); connect(m_ui->sbWidth, &QDoubleSpinBox::editingFinished, this, &MineAreaEdit::correctSpinBoxValue); connect(m_ui->sbDensity, &QDoubleSpinBox::editingFinished, this, &MineAreaEdit::correctSpinBoxValue); connect(m_ui->cbLayingProcedure, QOverload::of(&LtsWidgets::LtsComboBox::activated), this, &MineAreaEdit::onLayingProcedureChanged); connect(m_ui->pbClear, &QPushButton::clicked, this, &MineAreaEdit::clearCoordinates); setOnlineHelpFilename(QStringLiteral("Situation-Add_Mine_Area")); enaDisa(); } MineAreaEdit::~MineAreaEdit() { delete m_ui; } std::vector MineAreaEdit::coordWidgets() const { return {m_coordMap.begin(), m_coordMap.end()}; } void MineAreaEdit::retranslate() { translate(m_ui, m_aesItem, tr("Mine Area")); bool isReactivatable = m_dtc->mineArea(m_aesItem.id()).isReactivatable(); m_ui->pbResetDusMines->setText(isReactivatable ? tr("Reset DUS Mines") : tr("Hide DUS Mines")); m_ui->pbResetDusMines->setToolTip(isReactivatable ? tr("Reset all DUS Mines matched to this Minearea") : tr("Hide all DUS Mines matched to this Minearea")); } void MineAreaEdit::enableUiElements(bool enable) { m_ui->leIdentifier->setMaxLength(30); m_ui->cbChargeType->setEnabled(enable); m_ui->cbDurationHours->setEnabled(enable); m_ui->cbLayingProcedure->setEnabled(enable); m_ui->cbLayingMethod->setEnabled(enable); m_ui->cbParty->setEnabled(enable); m_ui->pbClear->setEnabled(enable); setEffectStartEnabled(m_ui->dteEffectStart, enable); m_ui->pbPosition->setEnabled(m_sitMapGuiIfc && enable); enabledCoordinates(enable); m_ui->sbDensity->setEnabled(enable); m_ui->cbxDensity->setEnabled(enable); m_ui->sbWidth->setEnabled(enable); m_ui->leIdentifier->setEnabled(enable || isOnlyNameModifiable()); const MineArea &ma = m_dtc->mineArea(m_aesItem.id()); if (m_aesItem.id() > 0 && (ma.isDus() || ma.isConcrete()) && // m_dtc->mineArea(m_aesItem.id()).isReactivatable() && // Always show button! LtsSessionData::hasOperatorRight(OperatorRights::MineAreasResetDusMines)) m_ui->pbResetDusMines->setEnabled(isOnlyNameModifiable()); else m_ui->pbResetDusMines->hide(); 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(); auto baseData = m_dtc->mineArea(m_aesItem.id()); setData(baseData, false); __unchange__(); if (name != m_ui->leIdentifier->text()) m_ui->leIdentifier->setText(name); } } bool MineAreaEdit::init(int aesItemId, bool copy, LtsSession::LtsSession *session) { if (!BaseEdit::init(aesItemId, session)) return false; bool isCenterLine = true; //default is true on OC if (m_session->app() != LtsAppDescriptor::App::OC) isCenterLine = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("MineAeraInputMode"), QStringLiteral("CenterLine"), false).toBool(); m_ui->cbMineAreaInputMode->setCurrentIndex(m_ui->cbMineAreaInputMode->findData(isCenterLine)); bool editable = LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::MineAreasEdit) || m_session->app() == LtsAppDescriptor::App::OC; bool readable = LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::MineAreasShow); if (!readable && !editable) return false; if (!m_session->getForeignGuiInterface(m_pdmGuiIfc)) { ERROR_LOG(m_logger)<<"Couldn't get pdmGuifc"; return false; } if (!m_session->getDtc(m_pdmDtc)) { ERROR_LOG(m_logger)<<"Couldn't get pdmDtc"; return false; } if (!m_session->getDtc(m_aesDtc)) { ERROR_LOG(m_logger)<<"Couldn't get aesDtc"; return false; } m_ui->dteEffectStart->setChecked(false); m_ui->dteEffectStart->setMinimumTimeMSec(m_session->partitionTime()); m_ui->dteEffectStart->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); }); if (m_sitMapGuiIfc) connect(m_ui->pbPosition, &QPushButton::clicked, this, &MineAreaEdit::getPosition); else m_ui->pbPosition->setEnabled(false); m_pdmGuiIfc->fillMineFieldComboBox(m_ui->cbChargeType); #ifndef TABLET_MODE // since the following call crashes on the ABT and i can not figure out why and we don't need // the functionality of the call on the ABT, it it configured out m_pdmGuiIfc->setChargeTypePropertyFilter(m_ui->cbChargeType, QStringLiteral("canBeCreated"), true); #endif connect(m_ui->cbChargeType, QOverload::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &MineAreaEdit::onChargeTypeChanged); setCombobxToValueIfPossible(m_ui->cbChargeType, s_lastChargeType); // Todo: get Values from Whereever for (int duration : {1, 3, 6, 12, 24, 48, 96}) m_ui->cbDurationHours->addItem(QString::number(duration), duration); m_ui->cbDurationHours->setCurrentIndex(m_ui->cbDurationHours->findData(96)); if (strcmp(PROJECTBUILD, "guez") == 0) m_ui->cbLayingProcedure->addItem(tr("Artillery"), AesMaLayingProcedureType_def::LptArtillery); m_ui->cbLayingProcedure->addItem(tr("Pioneer"), AesMaLayingProcedureType_def::LptPioneer); m_ui->cbLayingProcedure->setCurrentIndex(m_ui->cbLayingProcedure->findData(AesMaLayingProcedureType_def::LptPioneer)); // Pioneer m_ui->cbLayingMethod->addItem(tr("Open"), 0); m_ui->cbLayingMethod->addItem(tr("Covered"), 1); m_ui->sbDensity->setValue(0.5); for (double density : {5.0, 7.5, 15.0}) m_ui->cbxDensity->addItem(QString::number(density), density); onChargeTypeChanged(m_ui->cbChargeType->currentData().toInt()); m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty); LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableGuezDusMines = ps->value(QStringLiteral("aes"), QStringLiteral("enableGuezDusMines")).toBool(); if (enableGuezDusMines) connect(m_ui->pbResetDusMines, &QPushButton::clicked, this, &MineAreaEdit::onResetDusMines); 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 (aesItemId > 0) { auto baseData = m_dtc->mineArea(aesItemId); setData(baseData, copy); } onLayingProcedureChanged(); enableUiElements(editable && isModifiable() && !isOnlyNameModifiable()); connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::mineAreaChanged, this, &BaseEdit::onAreaChanged); return true; } void MineAreaEdit::setData(const MineArea &data, bool copy) { m_aesItem = data; if (copy) { m_aesItem.id(0); m_aesItem.effectStateId(AreaEffectSimulation::AesEffectState_def::get_default()); } m_ui->leIdentifier->setText(QString::fromUtf8(data.name().c_str())); m_ui->cbChargeType->setCurrentIndex(m_ui->cbChargeType->findData(data.chargeStruct().chargeTypeId())); m_ui->sbDensity->setValue(data.density()); double distance = qRound(1.0 / data.density()); if (distance == 8.0) distance = 7.5; const int densityIndex = m_ui->cbxDensity->findData(distance); if (densityIndex != -1) m_ui->cbxDensity->setCurrentIndex(densityIndex); else WARNING_LOG(m_logger) << "Got invalid density = " << data.density() << " / distance = " << distance; //-- start -- hide Density for DM72 munition // Anbindung ADLER III E01562-458 const QJsonObject &propObj = QJsonDocument::fromJson(data.properties().c_str()).object(); const int refAesItemId = propObj.value(QStringLiteral("referencedAesItemId")).toInt(-1); int effectClusterChargeTypeId = 0; if (refAesItemId > 0) { const auto &effectCluster = m_aesDtc->effectCluster(refAesItemId); effectClusterChargeTypeId = (effectCluster.id() > 0) ? effectCluster.chargeStruct().chargeTypeId() : 0 ; } const bool showDensity = (effectClusterChargeTypeId != s_DM72ChargeTypeId); m_ui->lbDensity->setVisible(showDensity); m_ui->sbDensity->setVisible(showDensity); //--- ende -- hide Density for DM72 munition // Anbindung ADLER III E01562-458 m_ui->sbWidth->setDecimals(0); m_ui->sbWidth->setValue(qRound(data.width())); m_ui->cbEffectMarkingInTa->setChecked(data.effectMarkingInTa()); if (m_session->app() != LtsAppDescriptor::App::OC) m_ui->cbMineAreaInputMode->setCurrentIndex(m_ui->cbMineAreaInputMode->findData(data.isCenterLine())); QVector coords; MineSegment seg; coords.reserve(data.segments().size()); for (const MineSegment &ms : data.segments()) { coords << QPointF(ms.positionStart().lon(), ms.positionStart().lat()); seg = ms; } if (coords.size() > 0) // Need at least one segment. coords << QPointF(seg.positionEnd().lon(), seg.positionEnd().lat()); for (int i = 0; i < coords.size(); ++i) { LtsCoordWidget::LtsCoordWidget *w = m_coordMap.value(i); if (!w) break; w->setCoordinate(coords[i]); } m_ui->dteEffectStart->setMsecsSinceEpoch(data.effectStart().is_set() ? data.effectStart().get() : 0); double duration = data.effectDuration() / (1000 * 60 * 60); // ms -> h m_ui->cbDurationHours->setCurrentText(QString::number(duration)); m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(data.partyId().is_set() ? data.partyId().get() : 0)); m_ui->cbLayingProcedure->setCurrentIndex(m_ui->cbLayingProcedure->findData(static_cast(data.layingProcedureType().underlying()))); m_ui->cbLayingMethod->setCurrentIndex(m_ui->cbLayingMethod->findData(data.isLaidHidden() ? 1 : 0)); } //slots void MineAreaEdit::getPosition() { if (!has2DMap()) return; double width = m_ui->sbWidth->value(); int maxSegments = m_segments; if (m_ui->cbLayingProcedure->currentData().toInt() == AesMaLayingProcedureType_def::LptArtillery) maxSegments = 1; // NOTE: igniterType/IgniterFunciton = 0 as long as MineAr Dialog does not contain values if (m_pdmDtc->isLine(m_ui->cbChargeType->currentData().toInt(),0,0)) { m_sitMapGuiIfc->getPolyline(this, SLOT(onPolygonData(QPolygonF)), nullptr, 1, m_segments + 1, 0.0, 0.0, getCoordinatesQPointF() ); } else { m_sitMapGuiIfc->getCorridor(this, SLOT(onMapRecData(double,QVector,QPolygonF)), nullptr, s_minWidth, s_maxWidth, s_maxLength, maxSegments, m_ui->cbMineAreaInputMode->currentData().toBool(), // true == centerline, otherwise frontline getCoordinatesQPointF(), width); } } void MineAreaEdit::onMapRecData(const double width, const QVector &polygon, const QPolygonF &bufferOutlinePolygon) { m_ui->sbWidth->setValue(std::abs(width)); // Reverse Coordinates only if Frontline setCoordinates(polygon, width < 0 && !m_ui->cbMineAreaInputMode->currentData().toBool()); m_previewPolygon = bufferOutlinePolygon; showPreviewObject(); } void MineAreaEdit::onPolygonData(const QPolygonF &polygon) { setCoordinates(polygon, false); m_previewPolygon = polygon; showPreviewObject(); } bool MineAreaEdit::onResetDusMines() { return m_requestHelper->startSingleRequest([this]() { return m_ftc->mineResetRequest(MineResetRequest(m_aesItem.id(), dds::core::optional()), this); }); } bool MineAreaEdit::apply() { qint64 effectStart = m_ui->dteEffectStart->msecsSinceEpoch(); std::string name = m_ui->leIdentifier->text().toStdString(); int chargeTypeId = m_ui->cbChargeType->currentData().toInt(); s_lastChargeType = chargeTypeId; AesMaLayingProcedureType layingProcedureType = static_cast(m_ui->cbLayingProcedure->currentData().toInt()); bool isLaidHidden = m_ui->cbLayingMethod->currentData().toInt() != 0; int partyId = m_ui->cbParty->currentData().toInt(); int playerId = 0; qint64 effectDuration = m_ui->cbDurationHours->currentText().toInt() * 60 * 60 * 1000L; // h --> msecs double density = 0.0; double width = 0.0; if (m_ui->stackedWidget->currentWidget() == m_ui->sbDensity) { density = m_ui->sbDensity->value(); width = m_ui->sbWidth->value(); } else { density = 1.0 / m_ui->cbxDensity->currentData().toInt(); width = 0.0; } if (!checkSegementLengths(this)) return false; const double distance = 500.0; const MineAreaModifyRequest req(m_aesItem.id(), name, LtsUtils::toOptional(effectStart), effectDuration, m_aesItem.effectSourceId(), density, width, {chargeTypeId,0,0}, getCoordinates(), LtsUtils::toOptional(partyId), LtsUtils::toOptional(playerId), layingProcedureType, isLaidHidden, m_ui->cbEffectMarkingInTa->isChecked(), m_ui->cbMineAreaInputMode->currentData().toBool(), distance, LtsUtils::toOptional(0LL), true // sort Mine with algorithm ); return m_requestHelper->startSingleRequest([this, req]() { return m_ftc->mineAreaModifyRequest(req, this); }); } void MineAreaEdit::onLayingProcedureChanged() { const bool islptArtillery = m_ui->cbLayingProcedure->currentData().toInt() == AesMaLayingProcedureType_def::LptArtillery; if (islptArtillery) m_pdmGuiIfc->setChargeTypePropertyFilter(m_ui->cbChargeType, QStringLiteral("canBeThrown"), true); else m_pdmGuiIfc->setChargeTypePropertyFilter(m_ui->cbChargeType, QStringLiteral("canBeCreated"), true); if (m_ui->cbChargeType->currentIndex() < 0) m_ui->cbChargeType->setCurrentIndex(0); m_ui->cbChargeType->setEnabled(m_ui->cbChargeType->count() > 1); // adjust posEdits: // LptArtillery -> one segment ; LptPioneer -> 5 segments if (islptArtillery) { for (int i = 2; i < m_coordMap.size(); i++) { LtsCoordWidget::LtsCoordWidget *w = m_coordMap.value(i); w->clear(); w->setVisible(false); } } else onChargeTypeChanged(m_ui->cbChargeType->currentData().toInt()); } void MineAreaEdit::onChargeTypeChanged(int index) { Q_UNUSED(index); int chargeTypeId = m_ui->cbChargeType->currentData().toInt(); DEBUG_LOG(m_logger) << "chargeType: " << chargeTypeId; // NOTE: igniterType/IgniterFunciton = 0 as long as MineAr Dialog does not contain values m_segments = m_pdmDtc->segments(chargeTypeId,0,0); DEBUG_LOG(m_logger) << "segments count: " << m_segments; if (m_pdmDtc->isLine(chargeTypeId,0,0)) { m_ui->stackedWidget->setCurrentWidget(m_ui->cbxDensity); m_ui->lbDensity->setText(tr("Distance [m]")); m_ui->lbWidth->setVisible(false); m_ui->sbWidth->setVisible(false); } else { m_ui->stackedWidget->setCurrentWidget(m_ui->sbDensity); m_ui->lbDensity->setText(tr("Density")); m_ui->lbWidth->setVisible(true); m_ui->sbWidth->setVisible(true); } DEBUG_LOG(m_logger) << "before remove: m_coordMap.size(): " << m_coordMap.size(); int size = m_coordMap.size(); for (int i = m_coordMap.size(); i <= size && i > m_segments; --i) { if (m_coordMap.size() == 0) break; LtsCoordWidget::LtsCoordWidget *w = m_coordMap.value(i - 1); m_ui->vLayoutCoord->removeWidget(w); delete w; m_coordMap.remove(i - 1); } for (int i = 0; i <= m_segments; i++) { DEBUG_LOG(m_logger) << "segment: " << i; LtsCoordWidget::LtsCoordWidget *w; if (!m_coordMap.contains(i)) w = new LtsCoordWidget::LtsCoordWidget(); else w = m_coordMap.value(i); m_ui->vLayoutCoord->addWidget(w); w->setVisible(true); m_coordMap.insert(i, w); } DEBUG_LOG(m_logger) << "m_coordMap.size(): " << m_coordMap.size(); } void MineAreaEdit::showPreviewObject() { if (!m_sitMapGuiIfc) return; if (m_previewPolygon.isEmpty()) return; // NOTE: igniterType/IgniterFunciton = 0 as long as MieAreaEdit does not contain values m_sitMapGuiIfc->displayPreviewObject(this, m_previewPolygon, !m_pdmDtc->isLine(m_ui->cbChargeType->currentData().toInt(),0,0)); } } // namespace AreaEffectSimulation