diff --git a/src/blackgui/components/ownaircraftcomponent.cpp b/src/blackgui/components/ownaircraftcomponent.cpp new file mode 100644 index 000000000..e1ac6a640 --- /dev/null +++ b/src/blackgui/components/ownaircraftcomponent.cpp @@ -0,0 +1,392 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +#include "ownaircraftcomponent.h" +#include "ui_ownaircraftcomponent.h" +#include "dbquickmappingwizard.h" + +#include "blackgui/uppercasevalidator.h" +#include "blackgui/guiapplication.h" +#include "blackcore/context/contextsimulator.h" +#include "blackcore/context/contextownaircraft.h" +#include "blackcore/context/contextnetwork.h" +#include "blackcore/webdataservices.h" +#include "blackcore/simulator.h" +#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/crashhandler.h" +#include "blackmisc/logmessage.h" +#include "blackconfig/buildconfig.h" + +#include + +using namespace BlackConfig; +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Network; +using namespace BlackCore; +using namespace BlackCore::Context; + +namespace BlackGui +{ + namespace Components + { + COwnAircraftComponent::COwnAircraftComponent(QWidget *parent) : + QFrame(parent), + ui(new Ui::COwnAircraftComponent) + { + ui->setupUi(this); + ui->selector_AircraftIcao->displayWithIcaoDescription(false); + ui->selector_AirlineIcao->displayWithIcaoDescription(false); + ui->selector_AircraftIcao->displayMode(CDbAircraftIcaoSelectorComponent::DisplayIcaoAndId); + ui->selector_AirlineIcao->displayMode(CDbAirlineIcaoSelectorComponent::DisplayVDesignatorAndId); + + // own aircraft + ui->lblp_AircraftCombinedType->setToolTips("ok", "wrong"); + ui->lblp_AirlineIcao->setToolTips("ok", "wrong"); + ui->lblp_AircraftIcao->setToolTips("ok", "wrong"); + ui->lblp_Callsign->setToolTips("ok", "wrong"); + + constexpr int MaxLength = 10; + constexpr int MinLength = 0; + CUpperCaseValidator *ucv = new CUpperCaseValidator(MinLength, MaxLength, ui->le_Callsign); + ucv->setAllowedCharacters09AZ(); + ui->le_Callsign->setMaxLength(MaxLength); + ui->le_Callsign->setValidator(ucv); + + ui->le_AircraftCombinedType->setMaxLength(3); + ui->le_AircraftCombinedType->setValidator(new CUpperCaseValidator(this)); + ui->comp_ModelStringCompleter->setSourceVisible(CAircraftModelStringCompleter::OwnModels, false); + + connect(ui->le_Callsign, &QLineEdit::editingFinished, this, &COwnAircraftComponent::validate); + connect(ui->comp_ModelStringCompleter, &CAircraftModelStringCompleter::modelStringChanged, this, &COwnAircraftComponent::onModelStringSendChanged); + connect(ui->le_AircraftCombinedType, &QLineEdit::editingFinished, this, &COwnAircraftComponent::validate); + connect(ui->selector_AircraftIcao, &CDbAircraftIcaoSelectorComponent::changedAircraftIcao, this, &COwnAircraftComponent::changedAircraftIcao, Qt::QueuedConnection); + connect(ui->selector_AirlineIcao, &CDbAirlineIcaoSelectorComponent::changedAirlineIcao, this, &COwnAircraftComponent::changedAirlineIcao, Qt::QueuedConnection); + connect(ui->pb_SimulatorLookup, &QToolButton::clicked, this, &COwnAircraftComponent::lookupOwnAircraftModel); + connect(ui->pb_MappingWizard, &QToolButton::clicked, this, &COwnAircraftComponent::mappingWizard, Qt::QueuedConnection); + + if (sGui && sGui->getIContextSimulator()) + { + connect(sGui->getIContextSimulator(), &IContextSimulator::ownAircraftModelChanged, this, &COwnAircraftComponent::onSimulatorModelChanged, Qt::QueuedConnection); + connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorStatusChanged, this, &COwnAircraftComponent::onSimulatorStatusChanged, Qt::QueuedConnection); + } + + if (sGui && sGui->getIContextOwnAircraft()) + { + this->onSimulatorModelChanged(sGui->getIContextOwnAircraft()->getOwnAircraft().getModel()); + } + } + + COwnAircraftComponent::~COwnAircraftComponent() + { } + + void COwnAircraftComponent::setUser(const CUser &user) + { + if (user.hasCallsign()) + { + ui->le_Callsign->setText(user.getCallsign().asString()); + } + else if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + ui->le_Callsign->setText("SWIFT"); + } + } + + void COwnAircraftComponent::lookupOwnAircraftModel() + { + if (!this->hasValidContexts()) { return; } + if (!sGui->getIContextSimulator()->isSimulatorAvailable()) { return; } + const CAircraftModel model(sGui->getIContextOwnAircraft()->getOwnAircraft().getModel()); + this->onSimulatorModelChanged(model); + } + + void COwnAircraftComponent::onSimulatorModelChanged(const CAircraftModel &model) + { + if (!sGui || !sGui->getIContextNetwork() || sApp->isShuttingDown()) { return; } + const bool isNetworkConnected = sGui && sGui->getIContextNetwork()->isConnected(); + if (isNetworkConnected) { return; } + + // update with latest DB data + CAircraftModel reverseModel(model); + if (sGui->hasWebDataServices()) + { + reverseModel = sGui->getWebDataServices()->getModelForModelString(model.getModelString()); + if (!reverseModel.isLoadedFromDb()) { reverseModel = model; } // reset if not found + } + + const QString modelStr(reverseModel.hasModelString() ? reverseModel.getModelString() : ""); + if (!reverseModel.hasModelString()) + { + CLogMessage(this).validationInfo(u"Invalid lookup for '%1' successful: %2") << modelStr << reverseModel.toQString(); + CLogMessage(this).validationInfo(u"Hint: Are you using the emulated driver? Set a model if so!"); + return; + } + this->setOwnModelAndIcaoValues(reverseModel); + + // open dialog for model mapping + if (m_autoPopupWizard && !reverseModel.isLoadedFromDb()) + { + this->mappingWizard(); + } + + emit this->aircraftDataChanged(); + } + + void COwnAircraftComponent::onModelStringSendChanged() + { + if (!this->hasValidContexts()) { return; } + if (!sGui->hasWebDataServices()) { return; } + const QString modelString = ui->comp_ModelStringCompleter->getModelString(); + const CAircraftModel model = sGui->getWebDataServices()->getModelForModelString(modelString); + this->setGuiIcaoValues(model, false); + } + + void COwnAircraftComponent::onSimulatorStatusChanged(int status) + { + ISimulator::SimulatorStatus s = static_cast(status); + Q_UNUSED(s); + if (!this->hasValidContexts()) { return; } + if (sGui->getIContextNetwork()->isConnected()) + { + // void + } + } + + CStatusMessageList COwnAircraftComponent::validate() const + { + CGuiAircraftValues values = this->getAircraftValuesFromGui(); + CStatusMessageList msgs; + + // fill in combined type if empty + if (ui->le_AircraftCombinedType->text().isEmpty() && values.ownAircraftIcao.isLoadedFromDb()) + { + ui->le_AircraftCombinedType->setText(values.ownAircraftIcao.getCombinedType()); + values.ownAircraftCombinedType = values.ownAircraftIcao.getCombinedType(); + } + + const bool validCombinedType = CAircraftIcaoCode::isValidCombinedType(values.ownAircraftCombinedType); + ui->lblp_AircraftCombinedType->setTicked(validCombinedType); + if (!validCombinedType) { msgs.addValidationMessage("Invalid combined type", CStatusMessage::SeverityError); } + + // airline is optional, e.g. C172 has no airline + const bool validAirlineDesignator = values.ownAirlineIcao.hasValidDesignator() || values.ownAirlineIcao.getDesignator().isEmpty(); + ui->lblp_AirlineIcao->setTicked(validAirlineDesignator); + if (!validAirlineDesignator) { msgs.addValidationMessage("Invalid airline designator", CStatusMessage::SeverityError); } + + const bool validAircraftDesignator = values.ownAircraftIcao.hasValidDesignator(); + ui->lblp_AircraftIcao->setTicked(validAircraftDesignator); + if (!validAircraftDesignator) { msgs.addValidationMessage("Invalid aircraft designator", CStatusMessage::SeverityError); } + + const bool validCallsign = CCallsign::isValidAircraftCallsign(values.ownCallsign); + ui->lblp_Callsign->setTicked(validCallsign); + if (!validCallsign) { msgs.addValidationMessage("Invalid callsign", CStatusMessage::SeverityError); } + + // model intentionally ignored + // return validCombinedType && validAirlineDesignator && validAircraftDesignator && validCallsign; + + return msgs; + } + + void COwnAircraftComponent::changedAircraftIcao(const CAircraftIcaoCode &icao) + { + if (icao.isLoadedFromDb()) + { + ui->le_AircraftCombinedType->setText(icao.getCombinedType()); + } + this->validate(); + } + + COwnAircraftComponent::CGuiAircraftValues COwnAircraftComponent::getAircraftValuesFromGui() const + { + CGuiAircraftValues values; + values.ownCallsign = CCallsign(ui->le_Callsign->text().trimmed().toUpper()); + values.ownAircraftIcao = ui->selector_AircraftIcao->getAircraftIcao(); + values.ownAirlineIcao = ui->selector_AirlineIcao->getAirlineIcao(); + values.ownAircraftCombinedType = ui->le_AircraftCombinedType->text().trimmed().toUpper(); + values.ownAircraftSimulatorModelString = ui->le_SimulatorModel->text().trimmed().toUpper(); + values.ownAircraftModelStringSend = ui->comp_ModelStringCompleter->getModelString().toUpper(); + values.ownLivery = ui->le_SendLivery->text().trimmed().toUpper(); + return values; + } + + CCallsign COwnAircraftComponent::getCallsignFromGui() const + { + const CCallsign cs(ui->le_Callsign->text().trimmed().toUpper()); + return cs; + } + + void COwnAircraftComponent::changedAirlineIcao(const CAirlineIcaoCode &icao) + { + Q_UNUSED(icao); + this->validate(); + } + + bool COwnAircraftComponent::setGuiIcaoValues(const CAircraftModel &model, bool onlyIfEmpty) + { + bool changed = false; + if (!onlyIfEmpty || !ui->selector_AircraftIcao->isSet()) + { + changed = ui->selector_AircraftIcao->setAircraftIcao(model.getAircraftIcaoCode()); + } + if (!onlyIfEmpty || !ui->selector_AirlineIcao->isSet()) + { + const bool c = ui->selector_AirlineIcao->setAirlineIcao(model.getAirlineIcaoCode()); + changed |= c; + } + if (!onlyIfEmpty || ui->le_AircraftCombinedType->text().trimmed().isEmpty()) + { + const QString combined(model.getAircraftIcaoCode().getCombinedType()); + if (ui->le_AircraftCombinedType->text() != combined) + { + ui->le_AircraftCombinedType->setText(combined); + changed = true; + } + } + const bool valid = this->validate().isSuccess(); + return valid ? changed : false; + } + + void COwnAircraftComponent::highlightModelField(const CAircraftModel &model) + { + if (!model.hasModelString()) { ui->le_SimulatorModel->setProperty("validation", "error"); } + else if (!model.isLoadedFromDb()) { ui->le_SimulatorModel->setProperty("validation", "warning"); } + else { ui->le_SimulatorModel->setProperty("validation", "ok"); } + ui->le_SimulatorModel->setStyleSheet(""); // force update + } + + bool COwnAircraftComponent::hasValidContexts() const + { + if (!sGui || !sGui->supportsContexts()) { return false; } + if (sGui->isShuttingDown()) { return false; } + if (!sGui->getIContextSimulator()) { return false; } + if (!sGui->getIContextNetwork()) { return false; } + if (!sGui->getIContextOwnAircraft()) { return false; } + return true; + } + + CAircraftModel COwnAircraftComponent::getPrefillModel() const + { + const CAircraftModel model = m_lastAircraftModel.get(); + if (model.hasAircraftDesignator()) { return model; } + return IContextOwnAircraft::getDefaultOwnAircraftModel(); + } + + void COwnAircraftComponent::setOwnModelAndIcaoValues(const CAircraftModel &ownModel) + { + if (!this->hasValidContexts()) { return; } + CAircraftModel model = ownModel; + const bool simulating = sGui->getIContextSimulator() && + (sGui->getIContextSimulator()->getSimulatorStatus() & ISimulator::Simulating); + if (simulating) + { + if (!model.hasModelString()) + { + model = sGui->getIContextOwnAircraft()->getOwnAircraft().getModel(); + } + const QString modelAndKey(model.getModelStringAndDbKey()); + + ui->le_SimulatorModel->setText(modelAndKey); + ui->le_SimulatorModel->home(false); + this->highlightModelField(model); + + const CSimulatorInfo sim = sGui->getIContextSimulator()->getSimulatorPluginInfo().getSimulator(); + ui->comp_ModelStringCompleter->setSimulator(sim); + m_lastAircraftModel.set(model); + + const CSimulatorInternals simulatorInternals = sGui->getIContextSimulator()->getSimulatorInternals(); + const QString simStr = sim.toQString() + QStringLiteral(" ") + simulatorInternals.getSimulatorVersion(); + CCrashHandler::instance()->crashAndLogInfoSimulator(simStr); + } + else + { + if (!model.hasModelString()) + { + model = this->getPrefillModel(); + } + ui->le_SimulatorModel->clear(); + this->highlightModelField(); + } + ui->le_SimulatorModel->setToolTip(model.asHtmlSummary()); + ui->comp_ModelStringCompleter->setText(model.getModelString()); + ui->le_SendLivery->setText(model.getSwiftLiveryString()); + + // reset the model + if (model.isLoadedFromDb() || (model.getAircraftIcaoCode().isLoadedFromDb() && model.getLivery().isLoadedFromDb())) + { + // full model from DB, take all values + this->setGuiIcaoValues(model, false); + } + else + { + if (sGui->getIContextSimulator()->isSimulatorAvailable()) + { + // sim. attached, but no model data from DB + ui->le_AircraftCombinedType->clear(); + ui->selector_AircraftIcao->clear(); + ui->selector_AirlineIcao->clear(); + } + } + } + + void COwnAircraftComponent::mappingWizard() + { + if (!sGui || !sGui->getIContextOwnAircraft() || sGui->isShuttingDown()) { return; } + if (!sGui->hasMinimumMappingVersion()) { return; } + + if (!m_mappingWizard) + { + m_mappingWizard.reset(new CDbQuickMappingWizard(this)); + } + + if (sGui->getIContextSimulator()->isSimulatorAvailable()) + { + // preset on model + const CAircraftModel model(sGui->getIContextOwnAircraft()->getOwnAircraft().getModel()); + m_mappingWizard->presetModel(model); + } + else + { + // preset on GUI values only + const CAircraftIcaoCode icao(ui->selector_AircraftIcao->getAircraftIcao()); + m_mappingWizard->presetAircraftIcao(icao); + } + m_mappingWizard->show(); + } + + bool COwnAircraftComponent::updateOwnAircaftIcaoValuesFromGuiValues() + { + if (!this->hasValidContexts()) { return false; } + const CSimulatedAircraft ownAircraft(sGui->getIContextOwnAircraft()->getOwnAircraft()); + const CGuiAircraftValues aircraftValues = this->getAircraftValuesFromGui(); + + CAircraftIcaoCode aircraftCode(ownAircraft.getAircraftIcaoCode()); + CAirlineIcaoCode airlineCode(ownAircraft.getAirlineIcaoCode()); + + bool changedIcaoCodes = false; + if (aircraftValues.ownAircraftIcao.hasValidDesignator() && aircraftValues.ownAircraftIcao != aircraftCode) + { + aircraftCode = aircraftValues.ownAircraftIcao; + changedIcaoCodes = true; + } + if (aircraftValues.ownAirlineIcao.hasValidDesignator() && aircraftValues.ownAirlineIcao != airlineCode) + { + airlineCode = aircraftValues.ownAirlineIcao; + changedIcaoCodes = true; + } + + if (changedIcaoCodes) + { + sGui->getIContextOwnAircraft()->updateOwnIcaoCodes(aircraftCode, airlineCode); + } + + return changedIcaoCodes; + } + + } // ns +} // ns diff --git a/src/blackgui/components/ownaircraftcomponent.h b/src/blackgui/components/ownaircraftcomponent.h new file mode 100644 index 000000000..c9daa62a3 --- /dev/null +++ b/src/blackgui/components/ownaircraftcomponent.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_COMPONENTS_OWNAIRCRAFTCOMPONENT_H +#define BLACKGUI_COMPONENTS_OWNAIRCRAFTCOMPONENT_H + +#include "blackmisc/simulation/data/lastmodel.h" +#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/aviation/airlineicaocode.h" +#include "blackmisc/network/user.h" +#include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/aviation/callsign.h" + +#include +#include + +namespace Ui { class COwnAircraftComponent; } +namespace BlackGui +{ + namespace Components + { + class CDbQuickMappingWizard; + + //! Own aircraft + class COwnAircraftComponent : public QFrame + { + Q_OBJECT + + public: + //! Constructor + explicit COwnAircraftComponent(QWidget *parent = nullptr); + + //! Destructor + virtual ~COwnAircraftComponent() override; + + //! Validate aircaft + BlackMisc::CStatusMessageList validate() const; + + //! Set a user + void setUser(const BlackMisc::Network::CUser &user); + + //! Own model and ICAO data for GUI and own aircraft + void setOwnModelAndIcaoValues(const BlackMisc::Simulation::CAircraftModel &ownModel = {}); + + //! Values from GUI + BlackMisc::Aviation::CCallsign getCallsignFromGui() const; + + //! Update own ICAO values (own aircraft from what is set in the GUI) + //! \return changed? + bool updateOwnAircaftIcaoValuesFromGuiValues(); + + signals: + //! Changed values + void aircraftDataChanged(); + + private: + //! GUI aircraft values, formatted + struct CGuiAircraftValues + { + BlackMisc::Aviation::CCallsign ownCallsign; + BlackMisc::Aviation::CAircraftIcaoCode ownAircraftIcao; + BlackMisc::Aviation::CAirlineIcaoCode ownAirlineIcao; + QString ownAircraftCombinedType; + QString ownAircraftSimulatorModelString; + QString ownAircraftModelStringSend; + QString ownLivery; + }; + + //! Launch mapping wizard + void mappingWizard(); + + //! Lookup own model + void lookupOwnAircraftModel(); + + //! Simulator model has been changed + void onSimulatorModelChanged(const BlackMisc::Simulation::CAircraftModel &model); + + //! Model string send + void onModelStringSendChanged(); + + //! Status has changed + void onSimulatorStatusChanged(int status); + + //! Values from GUI + CGuiAircraftValues getAircraftValuesFromGui() const; + + //! Set ICAO values + //! \return changed values? + bool setGuiIcaoValues(const BlackMisc::Simulation::CAircraftModel &model, bool onlyIfEmpty); + + //! Aircraft ICAO code has been changed + void changedAircraftIcao(const BlackMisc::Aviation::CAircraftIcaoCode &icao); + + //! Airline ICAO code has been changed + void changedAirlineIcao(const BlackMisc::Aviation::CAirlineIcaoCode &icao); + + //! Highlight model field according to model data + void highlightModelField(const BlackMisc::Simulation::CAircraftModel &model = {}); + + //! Has contexts? + bool hasValidContexts() const; + + //! Get a prefill model + BlackMisc::Simulation::CAircraftModel getPrefillModel() const; + + bool m_autoPopupWizard = false; //!< automatically popup wizard if mapping is needed + QScopedPointer m_mappingWizard; //!< mapping wizard + BlackMisc::CData m_lastAircraftModel { this }; //!< recently used aircraft model + QScopedPointer ui; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackgui/components/ownaircraftcomponent.ui b/src/blackgui/components/ownaircraftcomponent.ui new file mode 100644 index 000000000..3ca63420f --- /dev/null +++ b/src/blackgui/components/ownaircraftcomponent.ui @@ -0,0 +1,305 @@ + + + COwnAircraftComponent + + + + 0 + 0 + 266 + 132 + + + + Frame + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Model send + + + + + + + Livery sent + + + + + + + ICAO code e.g. B737, A320, F18 + + + Aircraft + + + + + + + ICAO code e.g. DLH, LHA, ... + + + Airline + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 75 + 0 + + + + Qt::StrongFocus + + + + + + + Callsign + + + + + + + Launch + + + Model + + + + + + + e.g. L2J + + + + + + + + + 3 + + + e.g. "L2J" + + + + + + + + 75 + 0 + + + + Qt::StrongFocus + + + + + + + + 0 + 0 + + + + + + + + + + + e.g. DEMBZ + + + Qt::ImhUppercaseOnly + + + 10 + + + e.g. DEMBZ + + + + + + + Aircraft type e.g. L2J, L1P, .... + + + Type + + + + + + + + 0 + 20 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + unique model identifier + + + + + + + + 35 + 16777215 + + + + refresh + + + + :/diagona/icons/diagona/icons/arrow-circle-225.png:/diagona/icons/diagona/icons/arrow-circle-225.png + + + + + + + + 35 + 16777215 + + + + quick mapping wizard + + + + :/own/icons/own/swift3D/sw3DGreen-24.png:/own/icons/own/swift3D/sw3DGreen-24.png + + + + + + + + + + livery string send + + + + + + + + BlackGui::CTickLabel + QLabel +
blackgui/ticklabel.h
+
+ + BlackGui::Components::CDbAircraftIcaoSelectorComponent + QFrame +
blackgui/components/dbaircrafticaoselectorcomponent.h
+ 1 +
+ + BlackGui::Components::CDbAirlineIcaoSelectorComponent + QFrame +
blackgui/components/dbairlineicaoselectorcomponent.h
+ 1 +
+ + BlackGui::Components::CAircraftModelStringCompleter + QFrame +
blackgui/components/aircraftmodelstringcompleter.h
+ 1 +
+
+ + le_SimulatorModel + pb_SimulatorLookup + pb_MappingWizard + le_SendLivery + le_Callsign + selector_AircraftIcao + le_AircraftCombinedType + selector_AirlineIcao + + + + + +