/** * @RDE_HEADER@ * ExerciseManangement - Exercise Edit Dialog * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-02-29 */ #include "exm_edit_dialog.h" #include "ui_exm_edit_dialog.h" #include "exm_edit_phase_dialog.h" #include "exm_phase_model.h" #include "exm_operator_model.h" #include "exm_dialog_model.h" // for GetExerciseImageRequester #include "exm_edit_party_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ExerciseManagement { // -------------------------------------------------------- // ExerciseManangementEditDialog // -------------------------------------------------------- ExerciseManagementEditDialog::ExerciseManagementEditDialog(QWidget *parent) : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel) , m_ui(new Ui::ExerciseManagementEditDialog) , m_currentState(ExerciseState::Unknown) , m_menuGroup() , m_requestHelper(new LtsUtils::RequestHelper(this)) , m_logger(getLogger("ExerciseManangementEditDialog")) { m_ui->setupUi(this); // TODO: replace with ui requirements (but after the constraints concept is done - I think they would perfectly fit together) connect(m_ui->imageEditor, &LtsWidgets::LtsImageEditor::signalClicked, this, &ExerciseManagementEditDialog::slotChangeImage); m_ui->dvOperators->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_ui->dvOperators, &LtsDataView::LtsDataView::customContextMenuRequested, this, &ExerciseManagementEditDialog::onContextMenuRequested); // Symbol Filltype derzeit von ESRI nicht unterstützt m_ui->lbSymbolFilltype->setVisible(false); m_ui->cbSymbolFilltype->setVisible(false); for(int tz = 'Y'; tz <= 'Z'; ++tz) m_ui->cbTimeZone->addItem(QString(QChar(tz)), tz); for(int tz = 'A'; tz <= 'F'; ++tz) m_ui->cbTimeZone->addItem(QString(QChar(tz)), tz); const bool hasLinkedExercises = LtsUtils::ProjectSettings::isFunctionEnabled(QStringLiteral("Exercises"), QStringLiteral("CanBeLinked")); const bool hasHlaInterface = LtsUtils::ProjectSettings::isFunctionEnabled(QStringLiteral("Exercises"), QStringLiteral("HasHlaInterface")); m_ui->cbLinkedExercise->setVisible(hasLinkedExercises); m_ui->lblLinkedExercise->setVisible(hasLinkedExercises); m_ui->cbStartHlaInterface->setVisible(hasHlaInterface); } ExerciseManagementEditDialog::~ExerciseManagementEditDialog() { delete m_ui; } void ExerciseManagementEditDialog::retranslate() { // sample: m_ui->retranslateUi(this); translate(m_ui, m_dtc->exercise(m_exerciseId), tr("Exercise")); // Content in those comboboxes are not translated - no need to refill them m_uiRequirements->clear(); m_uiRequirements->addRequirement( { m_ui->lblName, [this]() { return (!m_ui->leName->text().isEmpty()); } } ); m_uiRequirements->addRequirement( { m_ui->lblStart, [this]() { return (!m_ui->dtBegin->isEmpty()); } } ); m_uiRequirements->addRequirement( { m_ui->lblEnd, [this]() { return (!m_ui->dtEnd->isEmpty() && (m_ui->dtEnd->msecsSinceEpoch() > m_ui->dtBegin->msecsSinceEpoch())); }, tr("The end time must be bigger than the start time") }); m_ui->btbDvOperators->setText(LtsWidgets::DialogButton::Edit, tr("Edit Visible Parties")); m_buttonBox->setText(LtsWidgets::DialogButton::Export, tr("Save to library")); m_buttonBox->setText(LtsWidgets::DialogButton::Import, tr("Load from library")); m_buttonBox->setText(LtsWidgets::DialogButton::Load, tr("Create Folders")); } bool ExerciseManagementEditDialog::init(LtsSession::LtsSessionDataDds *session, const QString &menuGroup, const int exerciseId) { m_session = session; m_menuGroup = menuGroup; m_exerciseId = exerciseId; if (!m_session->getDtc(m_dtc)) return false; if (!m_session->getFtc(m_ftc)) return false; if (!m_session->getDtc(m_pdmDtc)) // TODO: not obli return false; if (!m_session->getDtc(m_asmDtc)) // TODO: not obli return false; if (!m_session->getDtc(m_opmDtc)) return false; if (!m_session->getDtc(m_flmDtc)) return false; if (!m_session->getFtc(m_flmFtc)) return false; ExerciseManagement::Exercise data = m_dtc->exercise(exerciseId); m_currentState = data.state(); m_ui->dtBegin->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); }); m_ui->dtEnd->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); }); m_phaseModel = new ExercisePhaseModel(this); m_phaseModel->initModel(m_exerciseId, m_dtc); m_phaseProxyModel = new ExercisePhaseProxyModel(this); m_phaseProxyModel->setSourceModel(m_phaseModel); m_operatorModel = new ExerciseOperatorModel(m_session, m_exerciseId, this); connect(m_operatorModel, &ExerciseOperatorModel::dataChanged, this, &ExerciseManagementEditDialog::slotChangesMade); // set data from dds if (data.id() > 0) { m_ui->leName->setText(QString::fromStdString(data.name())); m_ui->dtBegin->setTimezone(data.defaultTimeZone()); m_ui->dtBegin->setMsecsSinceEpoch(data.beginTime()); m_ui->dtEnd->setTimezone(data.defaultTimeZone()); m_ui->dtEnd->setMsecsSinceEpoch(data.endTime()); m_ui->pteDescription->setPlainText(QString::fromStdString(data.description())); m_ui->pteForces->setPlainText(QString::fromStdString(data.forces())); m_ui->cbTimeZone->setCurrentIndex(m_ui->cbTimeZone->findData(data.defaultTimeZone())); m_ui->cbStartHlaInterface->setChecked(data.hasHlaInterface()); } else { m_ui->cbTimeZone->setCurrentIndex(m_ui->cbTimeZone->findData('Z')); m_ui->dtBegin->setMsecsSinceEpoch(m_session->partitionTime()); m_ui->dtEnd->setMsecsSinceEpoch(m_session->partitionTime() + 1000 * 60 * 60 * 24); // 1day } // stuff for exercise phases m_ui->dvPhases->setModel(m_phaseProxyModel); m_ui->dvPhases->hideColumn(ExercisePhaseModel::Description); // do not show the description m_ui->dvPhases->hideColumn(ExercisePhaseModel::ExerciseId); // do not show the ExerciseId m_ui->dvPhases->seamless(); m_ui->btbDvPhases->connectButton(LtsWidgets::DialogButton::Add, this, &ExerciseManagementEditDialog::onAddExercisePhase); m_ui->btbDvPhases->connectButton(LtsWidgets::DialogButton::Edit, this, &ExerciseManagementEditDialog::onEditExercisePhase); m_ui->btbDvPhases->connectButton(LtsWidgets::DialogButton::Remove, this, &ExerciseManagementEditDialog::onRemoveExercisePhase); m_ui->dvOperators->setModel(m_operatorModel); m_ui->dvOperators->seamless(); m_ui->btbDvOperators->connectButton(LtsWidgets::DialogButton::Edit, this, &ExerciseManagementEditDialog::onEditVisibleParties); connect(m_ui->dvOperators, &LtsDataView::LtsDataView::signalDoubleClicked, this, &ExerciseManagementEditDialog::onEditVisibleParties); m_ui->btbDvOperators->getButton(LtsWidgets::DialogButton::Ok)->setVisible(false); m_ui->btbDvOperators->getButton(LtsWidgets::DialogButton::Close)->setVisible(false); connect(m_operatorModel, &ExerciseOperatorModel::dataChanged, this, &ExerciseManagementEditDialog::enaDisaOperatorsButtons); connect(m_ui->dvOperators, &LtsDataView::LtsDataView::signalSelectionChanged, this, &ExerciseManagementEditDialog::enaDisaOperatorsButtons); enaDisaOperatorsButtons(); // workaround (mw) m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Ok)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Apply)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Close)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Help)->setVisible(false); LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableEditPhases = ps->value(QStringLiteral("exm"), QStringLiteral("enableEditPhases")).toBool(); if (!enableEditPhases) { m_ui->lblPhases->hide(); m_ui->dvPhases->hide(); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Add)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Edit)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Remove)->setVisible(false); m_ui->btbDvPhases->getButton(LtsWidgets::DialogButton::Close)->setVisible(false); m_ui->lblActivePhase->hide(); m_ui->cbActivePhase->hide(); } connect(m_ui->dvPhases, &LtsDataView::LtsDataView::signalDoubleClicked, this, &ExerciseManagementEditDialog::onEditExercisePhase); connect(m_ui->dvPhases, &LtsDataView::LtsDataView::signalSelectionChanged, this, &ExerciseManagementEditDialog::enaDisaPhaseButtons); connect(m_ui->cbTimeZone, &QComboBox::currentTextChanged, this, &ExerciseManagementEditDialog::onTimeZoneChanged); enaDisaPhaseButtons(); // save and load description m_buttonBox->connectButton(LtsWidgets::DialogButton::Export, this, &ExerciseManagementEditDialog::onExport); m_buttonBox->connectButton(LtsWidgets::DialogButton::Import, this, &ExerciseManagementEditDialog::onImport); m_buttonBox->connectButton(LtsWidgets::DialogButton::Load, this, &ExerciseManagementEditDialog::onCreateFolders); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false); if (!session->getForeignGuiInterface(m_flmGui)) { m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Import, false); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Export, false); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false); } // combobox fill fillAllCbx(); // state cbx m_ui->cbState->setEnabled(m_ui->cbState->count() > 1); // active phase cbx connect(m_dtc.get(), &Interface::Dtc::exercisePhaseChanged, this, &ExerciseManagementEditDialog::onExercisePhaseChanged); m_ui->cbActivePhase->setCurrentIndex(m_ui->cbActivePhase->findData(QVariant(data.activePhaseId()))); // radio group cbx connect(m_dtc.get(), &Interface::Dtc::exerciseChanged, this, &ExerciseManagementEditDialog::onExerciseChanged); m_initialRadioGroupId.clear(); if (data.radioGroupId().is_set()) { int curIdx = m_ui->cbRadioGroup->findData(QVariant(data.radioGroupId().get())); if (curIdx < 0) curIdx = 0; // 'No Radio Group' else m_initialRadioGroupId = data.radioGroupId().get(); m_ui->cbRadioGroup->setCurrentIndex(curIdx); } m_ui->cbVisibleInWarehouse->setChecked(data.sendToWMS()); m_ui->cbLinkedExercise->setCurrentIndex(m_ui->cbLinkedExercise->findData(data.linkedExerciseId())); // training area cbx if (data.trainingAreaId() > 0) { int idx = m_ui->cbTrainingArea->findData(QVariant(data.trainingAreaId())); m_ui->cbTrainingArea->setCurrentIndex(idx); } if (m_exerciseId < 1) // new exercise { // no state, state is always 'created' m_ui->lblState->hide(); m_ui->cbState->hide(); // no forces text //m_ui->lblForces->hide(); //m_ui->pteForces->hide(); // no desc //m_ui->lblDesc->hide(); //m_ui->pteDescription->hide(); // no active phase yet, as the exercise has to exist first m_ui->cbActivePhase->hide(); m_ui->lblActivePhase->hide(); // no phases yet, as the exercise has to exist first m_ui->dvPhases->hide(); m_ui->lblPhases->hide(); m_ui->btbDvPhases->hide(); // styling m_ui->line->hide(); // m_ui->line_2->hide(); QString projectLogoFilename(QStringLiteral(":project/login_logo.png")); if (!QFile(projectLogoFilename).exists()) projectLogoFilename = QStringLiteral(":/project/logo.png"); m_ui->imageEditor->setPixmap(QPixmap(projectLogoFilename)); // new exercise's default logo m_pictureChanged = true; } if (m_exerciseId < 0) setOnlineHelpFilename(QStringLiteral("Administrator-Add_Exercise")); else setOnlineHelpFilename(QStringLiteral("Administrator-Edit_Exercise")); enaDisa(); // request exercise image to put it into the image editor requestImage(); return true; } void ExerciseManagementEditDialog::requestImage() { GetExerciseImageRequester *requester = GetExerciseImageRequester::createGetExerciseImageRequester(); connect(requester, &GetExerciseImageRequester::signalDone, this, [this, requester]() { onGetExerciseImageReply(requester->reply()); }); m_ftc->enableGetExerciseImage(); m_ftc->getExerciseImageRequest(GetExerciseImageRequest(m_exerciseId, m_ui->imageEditor->height(), m_ui->imageEditor->width(), false), requester); } void ExerciseManagementEditDialog::onGetExerciseImageReply(const GetExerciseImageReply &reply) { if (reply.errorString().empty()) { m_ui->imageEditor->setPixmap(LtsUtils::getPixmap(reply.imageData())); return; } QString msg(QString::fromStdString(reply.errorString())); for (const std::string &arg : qAsConst(reply.errorParams())) msg = msg.arg(QString::fromStdString(arg)); ERROR_LOG(m_logger) << "loading exercise image failed: " << msg; //new LtsWidgets::LtsMessageDialogNew(this, tr("Couldn't get exercise image due to an server error")); // set project logo as exc img QString projectLogoFilename(QStringLiteral(":project/login_logo.png")); if (!QFile(projectLogoFilename).exists()) projectLogoFilename = QStringLiteral(":/project/logo.png"); m_ui->imageEditor->setPixmap(QPixmap(projectLogoFilename)); } void ExerciseManagementEditDialog::onTimeZoneChanged() { QChar tz = m_ui->cbTimeZone->currentData().toChar(); m_ui->dtBegin->setTimezone(tz.toLatin1()); m_ui->dtEnd->setTimezone(tz.toLatin1()); } bool ExerciseManagementEditDialog::hasVisibleParties(const std::vector vpbe) const { for (const auto &pbe : vpbe) { if (pbe.exerciseId() != m_exerciseId) continue; return pbe.visibleParties().is_set(); //has_value not support on windows yet so we need depricated is_set } return false; } void ExerciseManagementEditDialog::enaDisaOperatorsButtons() { const QModelIndex idx = m_ui->dvOperators->currentIndex(); if (!idx.isValid()) { m_ui->btbDvOperators->setButtonEnabled(LtsWidgets::DialogButton::Edit, false); return; } const QPair row = m_operatorModel->getRowData(idx.row()); bool visibleParties = hasVisibleParties(row.second.visiblePartiesByExercise()); bool isChecked = row.first; if (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExerciseOperatorEdit)) m_ui->btbDvOperators->setButtonEnabled(LtsWidgets::DialogButton::Edit, (visibleParties && isChecked)); else m_ui->btbDvOperators->setButtonEnabled(LtsWidgets::DialogButton::Edit, false); } void ExerciseManagementEditDialog::enaDisaPhaseButtons() { QModelIndexList selectedRows = m_ui->dvPhases->selectedRows(); const int cntSelectedRows = selectedRows.size(); if (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExercisePhaseEdit)) { m_ui->btbDvPhases->setButtonEnabled(LtsWidgets::DialogButton::Edit, cntSelectedRows == 1); m_ui->btbDvPhases->setButtonEnabled(LtsWidgets::DialogButton::Remove, cntSelectedRows == 1); } else { m_ui->btbDvPhases->setButtonEnabled(LtsWidgets::DialogButton::Add, false); m_ui->btbDvPhases->setButtonEnabled(LtsWidgets::DialogButton::Edit, false); m_ui->btbDvPhases->setButtonEnabled(LtsWidgets::DialogButton::Remove, false); } } void ExerciseManagementEditDialog::onAddExercisePhase() { ExerciseManagementPhaseEditDialog *dialog = new ExerciseManagementPhaseEditDialog(this); dialog->init(m_session, m_exerciseId, -1 /* new exercise phase */); addDialog(dialog, m_menuGroup, QLatin1String("NewExercisePhase"), LtsWidgets::LayoutArea::eDialogTab); } void ExerciseManagementEditDialog::onRemoveExercisePhase() { const int phId = m_phaseModel->getRowData(m_ui->dvPhases->currentIndex().row()).id(); const int folderId = m_flmDtc->getPhaseFolderId(m_exerciseId, phId); if (folderId > 0) { Core::RemoveElementRequest req(folderId); m_requestHelper->startSingleRequest([req, this]() { return m_flmFtc->folderRemoveRequest(req, this); }); } Core::RemoveElementRequest req(m_phaseModel->getRowData(m_ui->dvPhases->currentIndex().row()).id()); m_requestHelper->startSingleRequest([req, this]() { m_ftc->enableExercisePhaseRemove(); return m_ftc->exercisePhaseRemoveRequest(req, this); }); } void ExerciseManagementEditDialog::onEditVisibleParties() { if (m_exerciseId == -1) // new exercise return; const QModelIndex idx = m_ui->dvOperators->currentIndex(); const QPair row = m_operatorModel->getRowData(idx.row()); const int operatorId = row.second.id(); bool isChecked = row.first; bool visibleParties = hasVisibleParties(row.second.visiblePartiesByExercise()); if (operatorId <= 0 || isChecked == false || !visibleParties) // only checked entries with parties (operator_exercise) editable return; DEBUG_LOG(m_logger) << "current Operator Id: " << operatorId; ExerciseEditPartyDialog *dialog = new ExerciseEditPartyDialog(operatorId, m_exerciseId, m_menuGroup, this); if (!dialog->init(m_session)) return; const QString identifier = QLatin1String("EditParties") + QString::number(operatorId) + QString::number(m_exerciseId); addDialog(dialog, m_menuGroup, identifier, LtsWidgets::LayoutArea::eDialogTab); } void ExerciseManagementEditDialog::onEditExercisePhase() { const QModelIndex idx = m_ui->dvPhases->currentIndex(); const ExercisePhase exPhase = m_phaseModel->getRowData(idx.row()); if (exPhase.id() <= 0) return; DEBUG_LOG(m_logger) << "current exPhase Id: " << exPhase.id(); ExerciseManagementPhaseEditDialog *dialog = new ExerciseManagementPhaseEditDialog(this); dialog->init(m_session, m_exerciseId, exPhase.id()); const QString identifier = QLatin1String("EditExercisePhase") + QString::number(exPhase.id()); addDialog(dialog, m_menuGroup, identifier, LtsWidgets::LayoutArea::eDialogTab); } void ExerciseManagementEditDialog::onExport() { DEBUG_LOG(m_logger) << "onExport"; const QByteArray byteArray = exerciseToJson(); DEBUG_LOG(m_logger) << "Exporting: " << byteArray; int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, 0, byteArray); if (fileId > 0) DEBUG_LOG(m_logger) << "Saved file with id = " << fileId; else ERROR_LOG(m_logger) << "Could not save file in library "; } void ExerciseManagementEditDialog::onImport() { DEBUG_LOG(m_logger) << "onImport"; QByteArray content; int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, 0, JsonKeys::XMLTYPEVALUE()); if (fileId > 0) { if (!m_flmGui->isXmlType(content, JsonKeys::XMLTYPEVALUE())) { QHash texts({{LtsWidgets::Button::Yes, tr("Ok")}}); LtsWidgets::LtsMessageDialog::showMessage(this, tr("Invalid xml file content"), windowTitle(), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes, texts); } else { ExerciseModifyRequest req = jsonToExercise(content); DEBUG_LOG(m_logger) << "Importing: " << req; m_ui->pteForces->setPlainText(QString::fromStdString(req.forces())); m_ui->pteDescription->setPlainText(QString::fromStdString(req.description())); } } return; } void ExerciseManagementEditDialog::slotChangeImage() { if (!m_ui->imageEditor->pixmap().isNull()) { if (LtsWidgets::LtsMessageDialog::showMessage(this, tr("Yes - overwrite image.
" "Cancel - do not overwrite image.
"), tr("Do you really want to overwrite the current image?"), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel) != LtsWidgets::Button::Yes) return; // do nothing if not ack by user } const QStringList filter ({ tr("Images (*.jpg *.jpeg *.png)") }); QString fileName = QFileDialog::getOpenFileName(this, tr("Select new image for this exercise"), QString(), // last wd filter.join(QStringLiteral(";;"))); if (fileName.isEmpty()) return; QPixmap pixmap; if (!pixmap.load(fileName)) { LtsWidgets::LtsMessageDialog::showMessage(this, tr("Cannot load image '%1'").arg(fileName)); return; } QSize newImgSize(200, 200); QRect requestedRect(QPoint(0, 0), newImgSize); QRect pictureRect(QPoint(0, 0), pixmap.size()); if (pictureRect.contains(requestedRect)) // only shrink, don't grow (which would make the image unnecessarily bigger) pixmap = pixmap.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_ui->imageEditor->setPixmap(pixmap); m_pictureChanged = true; // TODO: let this be automatically managed by slotChangesMade(); // ! } void ExerciseManagementEditDialog::imageModifyError() { if (m_exerciseId > 0) m_ui->imageEditor->setPixmap(QPixmap()); // request exercise image to put it into the image editor / revert requestImage(); } void ExerciseManagementEditDialog::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification modType) { Q_UNUSED(modType) if (data.id() == m_exerciseId) { const bool wasModified = hasUnsavedChanges(); m_currentState = data.state(); enaDisa(); fillStateCbx(); m_ui->cbVisibleInWarehouse->setChecked(data.sendToWMS()); m_ui->cbLinkedExercise->setCurrentIndex(m_ui->cbLinkedExercise->findData(data.linkedExerciseId())); if (!wasModified) __unchange__(); } else fillRadioGroupCbx(); } void ExerciseManagementEditDialog::onExercisePhaseChanged(const ExerciseManagement::ExercisePhase &data, DataModification modType) { Q_UNUSED(modType) if (data.exerciseId() == m_exerciseId) fillActivePhaseCbx(); } void ExerciseManagementEditDialog::enaDisa() { const bool wmsChecked = m_ui->cbVisibleInWarehouse->isChecked(); const bool exerciseIsRunning = (m_currentState == ExerciseManagement::ExerciseState::Started || m_currentState == ExerciseManagement::ExerciseState::Paused); const bool exerciseIsFinished = (m_currentState == ExerciseManagement::ExerciseState::Stopped || m_currentState == ExerciseManagement::ExerciseState::Terminated); bool radioGroupAssignable = !exerciseIsFinished; if (radioGroupAssignable && exerciseIsRunning) radioGroupAssignable = m_initialRadioGroupId.isNull(); // when running, only assignable when not yet assigned if (radioGroupAssignable) radioGroupAssignable = (m_ui->cbRadioGroup->count() > 1); m_ui->cbVisibleInWarehouse->setEnabled(!exerciseIsRunning || !wmsChecked); m_ui->cbLinkedExercise->setEnabled(!exerciseIsRunning && m_exerciseId > 0); m_ui->cbRadioGroup->setEnabled(radioGroupAssignable); m_ui->cbTrainingArea->setEnabled(!exerciseIsRunning && !exerciseIsFinished); if (getExerciseFolder() <= 0) m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, true); else m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false); } void ExerciseManagementEditDialog::fillStateCbx() { LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableExerciseTerminatedToStopped = ps->value(QStringLiteral("exm"), QStringLiteral("enableExerciseTerminatedToStopped")).toBool(); // exercise states m_ui->cbState->clear(); switch (m_currentState.underlying()) { case ExerciseState::Created: m_ui->cbState->addItem(tr("Created"), ExerciseState::Created); m_ui->cbState->addItem(tr("Start"), ExerciseState::Started); break; case ExerciseState::Started: m_ui->cbState->addItem(tr("Started"), ExerciseState::Started); m_ui->cbState->addItem(tr("Pause"), ExerciseState::Paused); m_ui->cbState->addItem(tr("Stop"), ExerciseState::Stopped); break; case ExerciseState::Paused: m_ui->cbState->addItem(tr("Paused"), ExerciseState::Paused); m_ui->cbState->addItem(tr("Resume"), ExerciseState::Started); m_ui->cbState->addItem(tr("Stop"), ExerciseState::Stopped); break; case ExerciseState::Stopped: m_ui->cbState->addItem(tr("Stopped"), ExerciseState::Stopped); m_ui->cbState->addItem(tr("Terminated"), ExerciseState::Terminated); break; case ExerciseState::Terminated: m_ui->cbState->addItem(tr("Terminated"), ExerciseState::Terminated); if (enableExerciseTerminatedToStopped) m_ui->cbState->addItem(tr("Stopped"), ExerciseState::Stopped); break; case ExerciseState::Unknown: m_ui->cbState->addItem(tr("Unknown"), ExerciseState::Unknown); break; } } void ExerciseManagementEditDialog::fillTrainingAreaCbx() { // training areas int curIdxTa = m_ui->cbTrainingArea->currentIndex(); m_ui->cbTrainingArea->clear(); auto cb = [this](const AssetManagement::TrainingArea &ta) { m_ui->cbTrainingArea->addItem(QString::fromStdString(ta.name()), QVariant(ta.id())); }; m_asmDtc->trainingAreas(cb); m_ui->cbTrainingArea->model()->sort(0); if (curIdxTa >= 0) m_ui->cbTrainingArea->setCurrentIndex(curIdxTa); } void ExerciseManagementEditDialog::fillActivePhaseCbx() { // active phase int curIdxActPh = m_ui->cbActivePhase->currentIndex(); m_ui->cbActivePhase->clear(); auto cb = [this](const ExerciseManagement::ExercisePhase &phase) { if (m_exerciseId == phase.exerciseId()) m_ui->cbActivePhase->addItem(QString::fromStdString(phase.name()), QVariant(phase.id())); }; m_dtc->exercisePhases(cb); m_ui->cbActivePhase->setEnabled(m_ui->cbActivePhase->count() > 1); if (curIdxActPh >= 0) m_ui->cbActivePhase->setCurrentIndex(curIdxActPh); } void ExerciseManagementEditDialog::fillRadioGroupCbx() { // radio group const bool wasModified = hasUnsavedChanges(); int curRadioGroupId = m_ui->cbRadioGroup->currentData().toInt(); m_ui->cbRadioGroup->clear(); // const entry for no radio m_ui->cbRadioGroup->addItem(tr("No Radio Group"), QVariant(0)); // filter already used radio groups const QMap mapEx = m_dtc->exercises(); QMap mapRadioGrp = m_pdmDtc->radioGroups(); for (auto it = mapEx.begin(); it != mapEx.end(); ++it) if (it.value().id() != m_exerciseId) if (it.value().radioGroupId().is_set()) mapRadioGrp.remove(it.value().radioGroupId().get()); // add available groups except base group for (auto it = mapRadioGrp.begin(); it != mapRadioGrp.end(); ++it) { const auto &radioGroup = it.value(); if (!radioGroup.base_group()) m_ui->cbRadioGroup->addItem(m_pdmDtc->trRadioGroupName(radioGroup.id()), radioGroup.id()); } m_ui->cbTrainingArea->model()->sort(0); m_ui->cbRadioGroup->setEnabled(m_ui->cbRadioGroup->count() > 1); if (curRadioGroupId >= 0) { int newIdx = m_ui->cbRadioGroup->findData(curRadioGroupId); if (newIdx >= 0) { m_ui->cbRadioGroup->setCurrentIndex(newIdx); if (!wasModified) __unchange__(); } } } void ExerciseManagementEditDialog::fillLinkedExercisesCbx() { int curId = m_ui->cbLinkedExercise->currentData().toInt(); m_ui->cbLinkedExercise->clear(); auto cb = [this](const Exercise &ex) { if (ex.state() != ExerciseManagement::ExerciseState::Created && ex.linkedExerciseId() != m_exerciseId) return; if (ex.id() == m_exerciseId) return; m_ui->cbLinkedExercise->addItem(QString::fromStdString(ex.name()), ex.id()); }; m_dtc->exercises(cb); m_ui->cbLinkedExercise->addItem(tr("---"), 0); if (curId < 0) curId = 0; m_ui->cbLinkedExercise->setCurrentIndex(m_ui->cbLinkedExercise->findData(curId)); } void ExerciseManagementEditDialog::fillAllCbx() { fillStateCbx(); fillTrainingAreaCbx(); fillActivePhaseCbx(); fillRadioGroupCbx(); fillLinkedExercisesCbx(); } template bool ExerciseManagementEditDialog::removeElement(std::vector &vec, T elem) { for (auto it = vec.begin(); it != vec.end(); ++it) { if (*it == elem) { vec.erase(it); return true; } } return false; } bool ExerciseManagementEditDialog::apply() // TODO: clean me up ! { bool retVal = true; bool isAlreadyUnchangeable = unchangeable(); __unchange__(); if (!isAlreadyUnchangeable) setUnchangeable(true); if (m_exerciseId > 0) // edit existing exercise { ExerciseState state = static_cast(m_ui->cbState->currentData().toInt()); ExerciseManagement::Exercise data = m_dtc->exercise(m_exerciseId); if (state == ExerciseState_def::Stopped && data.state() != ExerciseState_def::Stopped) { if (LtsWidgets::LtsMessageDialog::showMessage(this, QString(), tr("Are you sure you want to stop the exercise?"), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No) != LtsWidgets::Button::Yes) retVal = false; } else if (state == ExerciseState_def::Terminated && data.state() != ExerciseState_def::Terminated) { if (LtsWidgets::LtsMessageDialog::showMessage(this, QString(), tr("Are you sure you want to terminate the exercise?"), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No) != LtsWidgets::Button::Yes) retVal = false; } if (retVal) { ExerciseModifyRequest request; // Update Data of Existing Exercise request.id(m_exerciseId); request.beginTime(m_ui->dtBegin->msecsSinceEpoch()); request.endTime(m_ui->dtEnd->msecsSinceEpoch()); request.name(m_ui->leName->text().toStdString()); request.sendToWMS(m_ui->cbVisibleInWarehouse->isChecked()); request.linkedExerciseId(m_ui->cbLinkedExercise->currentData().toInt()); request.trainingAreaId(m_ui->cbTrainingArea->currentData().toInt()); int radioGroup = m_ui->cbRadioGroup->currentData().toInt(); if (radioGroup != 0) { m_initialRadioGroupId = radioGroup; request.radioGroupId(radioGroup); } // TODO: add symbolFillType (PDM) request.symbolFillTypeId(0); request.activePhaseId(m_ui->cbActivePhase->currentData().toInt()); request.defaultTimeZone(m_ui->cbTimeZone->currentData().toChar().toLatin1()); request.forces(m_ui->pteForces->toPlainText().toStdString()); request.description(m_ui->pteDescription->toPlainText().toStdString()); request.hasHlaInterface(m_ui->cbStartHlaInterface->isChecked()); request.newState(static_cast(m_ui->cbState->currentData().toInt())); request.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); request.workstationId(LtsSessionData::LtsSessionData::getInstance()->getWorkstationId()); //ExerciseManagement::Exercise data = m_dtc->exercise(m_exerciseId); if (data.startTime() > 0) request.startTime(data.startTime()); if (data.stopTime() > 0) request.stopTime(data.stopTime()); if (!m_requestHelper->startSingleRequest([this, request]() { m_ftc->enableExerciseModify(); return m_ftc->exerciseModifyRequest(request, this); })) retVal = false; if (m_pictureChanged) { ExerciseImageModifyRequest exImgRequest(m_exerciseId, LtsUtils::fromPixmap(getPixmap())); m_requestHelper->startSingleRequest([this, exImgRequest]() { m_ftc->enableExerciseImageModify(); return m_ftc->exerciseImageModifyRequest(exImgRequest, this); }); } sendModifiedOperators(m_exerciseId); } // do nothing on failure - rh's message is enough //return m_requestHelper->startRequests(); // return success or failure // TODO: check if something has changed (constraints manager / ui requirements should also check for changes) } else // add new exercise { ExerciseCreateRequest request; request.beginTime(m_ui->dtBegin->msecsSinceEpoch()); request.endTime(m_ui->dtEnd->msecsSinceEpoch()); request.name(m_ui->leName->text().toStdString()); request.sendToWMS(m_ui->cbVisibleInWarehouse->isChecked()); request.linkedExerciseId(m_ui->cbLinkedExercise->currentData().toInt()); request.trainingAreaId(m_ui->cbTrainingArea->currentData().toInt()); int radioGroup = m_ui->cbRadioGroup->currentData().toInt(); if (radioGroup != 0) request.radioGroupId(radioGroup); // TODO: symbolFillType (PDM) request.symbolFillTypeId(0); request.defaultTimeZone(m_ui->cbTimeZone->currentData().toChar().toLatin1()); request.forces(m_ui->pteForces->toPlainText().toStdString()); request.description(m_ui->pteDescription->toPlainText().toStdString()); request.hasHlaInterface(m_ui->cbStartHlaInterface->isChecked()); // Add New Exercise if (!m_requestHelper->startSingleRequest([this, request]() { m_ftc->enableExerciseCreate(); return m_ftc->exerciseCreateRequest(request, this); })) retVal = false; int newExId = m_requestHelper->getRepliedId(); DEBUG_LOG(m_logger) << "Created new exercise with id " << newExId; if (newExId > 0) { ExerciseImageModifyRequest exImgRequest(newExId, LtsUtils::fromPixmap(getPixmap())); if (!m_requestHelper->startSingleRequest([this, exImgRequest]() { m_ftc->enableExerciseImageModify(); return m_ftc->exerciseImageModifyRequest(exImgRequest, this); })) // not a critical error - don't return false! { WARNING_LOG(m_logger) << "Unable to set default exercise picture"; LtsWidgets::LtsMessageDialog::showMessage(this, tr("Unable to set default exercise picture")); } // now we've the new exerciseId -> we can update the operators sendModifiedOperators(newExId); // create Folders if (!createFolders(newExId)) { WARNING_LOG(m_logger) << "Unable to create exercise folders in document library."; LtsWidgets::LtsMessageDialog::showMessage(this, tr("Unable to create exercise folders in document library.")); } } else retVal = false; // TODO: check if something has changed (constraints manager / ui requirements should also check for changes) } if (!isAlreadyUnchangeable) setUnchangeable(false); return retVal; } const QPixmap ExerciseManagementEditDialog::getPixmap() const { return QPixmap(m_ui->imageEditor->pixmap()); } void ExerciseManagementEditDialog::sendModifiedOperators(int thisExerciseId) { std::shared_ptr opmFtc; if (!m_session->getFtc(opmFtc)) { ERROR_LOG(m_logger) << "Cannot change add/remove Operators to/from Exercise as the OPM-FTC could not be fetched." ; return; } for (int row = 0; row < m_operatorModel->rowCount(); ++row) { const auto rowData = m_operatorModel->getRowData(row); bool found = false; OperatorManagement::User user = m_opmDtc->user(rowData.second.id()); for (auto ex : user.exerciseIds()) if (ex == thisExerciseId) found = true; if (found == rowData.first) // Nothing changed continue; if (rowData.first) user.exerciseIds().resize(user.exerciseIds().size() + 1, thisExerciseId); else removeElement(user.exerciseIds(), thisExerciseId); OperatorManagement::OperatorCreateModifyRequest req; req.id(user.id()); req.visibleExercises() = user.exerciseIds(); req.visiblePartiesByExercise() = user.visiblePartiesByExercise(); DEBUG_LOG(m_logger) << "Adding request: " << req; m_requestHelper->addRequest([this, req, opmFtc]() { DEBUG_LOG(m_logger) << "Executing request: " << req; return opmFtc->operatorCreateModifyRequest(req, this); }); } m_requestHelper->startRequests(); } bool ExerciseManagementEditDialog::createFolders(int newExId) { // exercise folder QString folderName = m_flmDtc->getExerciseFolderName(newExId); int folderId = createFolder(FileManagement::MainFolder::ExerciseDocuments, folderName); if (folderId <= 0) return false; DEBUG_LOG(m_logger) << "Exercise Folder created with id = " << folderId; // Documents sub-folder folderName = QStringLiteral("Documents") ; int subFolderId = createFolder(folderId, folderName); if (subFolderId <= 0) return false; DEBUG_LOG(m_logger) << "Sub Folder Documents created with id = " << subFolderId; // Under Documents: TODO: english names Errors... QStringList folderNames; folderNames << QStringLiteral("Fault documentation Evaluation") << QStringLiteral("Fault documentation Evaluation EXCON"); folderNames << QStringLiteral("Incidents") << QStringLiteral("Instructions"); bool ok = createExerciseFolders(subFolderId, folderNames); DEBUG_LOG(m_logger) << "Created sub-folders Documents: " << ok; // Phase documents LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableEditPhases = ps->value(QStringLiteral("exm"), QStringLiteral("enableEditPhases")).toBool(); if (enableEditPhases) { // Phase Documents folder (needs to be created separate because we need the id of new created folder) folderName = QStringLiteral("Phase Documents"); int folderPhaseDocsId = createFolder(subFolderId, folderName); DEBUG_LOG(m_logger) << "Phase Documents Folder created with id = " << folderPhaseDocsId; // active phase folder if (folderPhaseDocsId > 0) { int phId = m_dtc->exercise(newExId).activePhaseId(); const QString &phaseName = m_flmDtc->getPhaseFolderName(phId); int folderPhaseId = createFolder(folderPhaseDocsId, phaseName); DEBUG_LOG(m_logger) << "Phase Folder created with id = " << folderPhaseId; } } // Markers folderName = QStringLiteral("Markers"); subFolderId = createFolder(folderId, folderName); DEBUG_LOG(m_logger) << "Markers Folder created with id = " << subFolderId; FileManagement::PathCreateRequest req; req.parentId(folderId); req.path({"Planning", "TNE Lists"}); req.isEditable(false); req.isHidden(false); req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); bool success = m_flmFtc->pathCreateRequest(req); if (!success) ERROR_LOG(m_logger) << "Planning/TNE Lists could not be created"; // Tactical Plans req.path({"Tactical Plans", "BDA"}); success = m_flmFtc->pathCreateRequest(req); if (!success) ERROR_LOG(m_logger) << "Tactical Plans/BDA could not be created"; req.path({"Tactical Plans", "HQ Data"}); success = m_flmFtc->pathCreateRequest(req); if (!success) ERROR_LOG(m_logger) << "Tactical Plans/HQ Data could not be created"; // Reports, Snapshots folderNames.clear(); folderNames << QStringLiteral("Reports") << QStringLiteral("Snapshots"); ok = createExerciseFolders(folderId, folderNames); DEBUG_LOG(m_logger) << "Created last exercise documents: " << ok; return true; } int ExerciseManagementEditDialog::createFolder(int parentFolderId, const QString &folderName, bool isHidden) { FileManagement::Folder parentFolder = m_flmDtc->folder(parentFolderId); const std::vector &parentPath = parentFolder.path(); FileManagement::FolderModifyRequest req; req.name(folderName.toStdString()); req.path(parentPath); req.isEditable(false); req.isHidden(isHidden); req.parentId(parentFolderId); req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); m_requestHelper->startSingleRequest([this, req]() { return m_flmFtc->folderModifyRequest(req, this); }); DEBUG_LOG(m_logger) << "Created folder id = " << m_requestHelper->getRepliedId(); return m_requestHelper->getRepliedId(); } bool ExerciseManagementEditDialog::createExerciseFolders(int parentFolderId, const QStringList &folderNames) { FileManagement::Folder parentFolder = m_flmDtc->folder(parentFolderId); const std::vector &parentPath = parentFolder.path(); FileManagement::FolderModifyRequest req; req.path(parentPath); req.isEditable(false); req.isHidden(false); req.parentId(parentFolderId); req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); for ( const QString &folderName : folderNames ) { req.name(folderName.toStdString()); m_requestHelper->addRequest([this, req]() { return m_flmFtc->folderModifyRequest(req, this); }); } return m_requestHelper->startRequests(); } void ExerciseManagementEditDialog::onContextMenuRequested(const QPoint &position) { QMenu menu(m_ui->dvOperators); menu.addAction(tr("Select All"), this, &ExerciseManagementEditDialog::onSelectAllUsers); menu.addAction(tr("Unselect All"), this, &ExerciseManagementEditDialog::onUnselectAllUsers); menu.exec(m_ui->dvOperators->mapToGlobal(position)); } void ExerciseManagementEditDialog::onSelectAllUsers() { m_operatorModel->selectAll(); m_operatorModel->update(); } void ExerciseManagementEditDialog::onUnselectAllUsers() { m_operatorModel->unselectAll(); m_operatorModel->update(); } ExerciseModifyRequest ExerciseManagementEditDialog::setRequestContent() const { ExerciseModifyRequest req; req.forces(m_ui->pteForces->toPlainText().toStdString()); req.description(m_ui->pteDescription->toPlainText().toStdString()); const ExerciseModifyRequest out = req; return out; } QByteArray ExerciseManagementEditDialog::exerciseToJson() const { const ExerciseModifyRequest req = setRequestContent(); QJsonObject obj; long long startExercise = m_ui->dtBegin->msecsSinceEpoch(); obj[JsonKeys::XMLTYPE()] = JsonKeys::XMLTYPEVALUE(); obj[JsonKeys::FORCES()] = QString::fromStdString(req.forces()); obj[JsonKeys::DESCRIPTION()] = QString::fromStdString(req.description()); QJsonArray phases; for(int row = 0; row < m_phaseModel->rowCount(); ++row) { QJsonObject phase; phase.insert(JsonKeys::PID(), m_phaseModel->getRowData(row).id()); phase.insert(JsonKeys::PNAME(), QString::fromStdString(m_phaseModel->getRowData(row).name())); phase.insert(JsonKeys::PISDELETED(), m_phaseModel->getRowData(row).isDeleted()); phase.insert(JsonKeys::PISUSED(), m_phaseModel->getRowData(row).isUsed()); long long beginTime = m_phaseModel->getRowData(row).beginTime() - startExercise; long long endTime = m_phaseModel->getRowData(row).endTime() - startExercise; phase.insert(JsonKeys::PBEGINTIME(), beginTime); phase.insert(JsonKeys::PENDTIME(), endTime); phase.insert(JsonKeys::PDESCRIPTION(), QString::fromStdString(m_phaseModel->getRowData(row).description())); phase.insert(JsonKeys::PEXERCISEID(), m_phaseModel->getRowData(row).exerciseId()); phases.append(phase); } obj[JsonKeys::PHASES()] = phases; QJsonArray operatorIds; const QVector checkedIds = m_operatorModel->getCheckedIds(); for(const int id : checkedIds) operatorIds += id; obj[JsonKeys::OPERATORS()] = operatorIds; QJsonDocument doc(obj); return doc.toJson(QJsonDocument::Compact); } ExerciseModifyRequest ExerciseManagementEditDialog::jsonToExercise(const QByteArray &content) const { ExerciseModifyRequest result; // already checked that content is not empty, is parseable and it contains a proper XmlType (EXMExerciseDescription) const QJsonDocument doc(QJsonDocument::fromJson(content)); const QJsonObject obj(doc.object()); result.forces(obj[JsonKeys::FORCES()].toString().toStdString()); result.description(obj[JsonKeys::DESCRIPTION()].toString().toStdString()); // ----- Phases ----- QJsonArray phasesArray = obj[JsonKeys::PHASES()].toArray(); QVector phases; for (auto it = phasesArray.constBegin(); it != phasesArray.constEnd(); ++it) { QJsonObject phaseObj = (*it).toObject(); ExerciseManagement::ExercisePhase phase; phase.id(phaseObj[JsonKeys::PID()].toInt()); phase.name(phaseObj[JsonKeys::PNAME()].toString().toStdString()); phase.isDeleted(phaseObj[JsonKeys::PISDELETED()].toBool()); phase.isUsed(phaseObj[JsonKeys::PISUSED()].toBool()); phase.description(phaseObj[JsonKeys::PDESCRIPTION()].toString().toStdString()); // Relative to the exercise start time phase.beginTime(phaseObj[JsonKeys::PBEGINTIME()].toInt() + m_ui->dtBegin->msecsSinceEpoch()); phase.endTime(phaseObj[JsonKeys::PENDTIME()].toInt() + m_ui->dtBegin->msecsSinceEpoch()); phase.exerciseId(phaseObj[JsonKeys::PEXERCISEID()].toInt()); phases.append(phase); } m_phaseModel->setData(phases); if ( phases.size()> 0) { m_ui->dvPhases->show(); m_ui->lblPhases->show(); m_ui->btbDvPhases->show(); } // ----- Operators ----- const QJsonArray operatorArray = obj[JsonKeys::OPERATORS()].toArray(); QVector selectedIds; selectedIds.reserve(operatorArray.size()); for (const auto &ele : operatorArray) selectedIds.append(ele.toInt()); m_operatorModel->setChecked(selectedIds); return result; } void ExerciseManagementEditDialog::onCreateFolders() { DEBUG_LOG(m_logger) << "onCreateFolders"; if (m_exerciseId > 0 && getExerciseFolder() <= 0 ) { bool ok = createFolders(m_exerciseId); if (!ok) { WARNING_LOG(m_logger) << "Could not create exercise folders"; LtsWidgets::LtsMessageDialog::showMessage(this, tr("Could not create exercise folders")); } else { LtsWidgets::LtsMessageDialog::showMessage(this, tr("Exercise folders successfully created")); m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false); } } } int ExerciseManagementEditDialog::getExerciseFolder() { if (m_exerciseId <= 0) return 0; const int folderId = m_flmDtc->getExerciseFolderId(m_exerciseId); DEBUG_LOG(m_logger) << "getExerciseFolder: folderId = " << folderId; return folderId; } } // namespace ExerciseManagement