swift
ownaircraftcomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "ownaircraftcomponent.h"
5 
6 #include <QPushButton>
7 
8 #include "ui_ownaircraftcomponent.h"
9 
10 #include "config/buildconfig.h"
14 #include "core/simulator.h"
15 #include "core/webdataservices.h"
16 #include "gui/guiapplication.h"
17 #include "gui/uppercasevalidator.h"
18 #include "misc/crashhandler.h"
19 #include "misc/logmessage.h"
21 
22 using namespace swift::config;
23 using namespace swift::misc;
24 using namespace swift::misc::aviation;
25 using namespace swift::misc::simulation;
26 using namespace swift::misc::network;
27 using namespace swift::core;
28 using namespace swift::core::context;
29 
30 namespace swift::gui::components
31 {
32  COwnAircraftComponent::COwnAircraftComponent(QWidget *parent) : QFrame(parent), ui(new Ui::COwnAircraftComponent)
33  {
34  ui->setupUi(this);
35  ui->selector_AircraftIcao->displayWithIcaoDescription(false);
36  ui->selector_AirlineIcao->displayWithIcaoDescription(false);
37  ui->selector_AircraftIcao->displayMode(CDbAircraftIcaoSelectorComponent::DisplayIcaoAndId);
38  ui->selector_AirlineIcao->displayMode(CDbAirlineIcaoSelectorComponent::DisplayVDesignatorAndId);
39 
40  ui->cb_Model->setChecked(true);
41  ui->cb_Livery->setChecked(true);
42 
43  // own aircraft
44  ui->lblp_AircraftCombinedType->setToolTips("ok", "wrong");
45  ui->lblp_AirlineIcao->setToolTips("ok", "wrong");
46  ui->lblp_AircraftIcao->setToolTips("ok", "wrong");
47  ui->lblp_Callsign->setToolTips("ok", "wrong");
48 
49  constexpr int MaxLength = 10;
50  constexpr int MinLength = 0;
51  CUpperCaseValidator *ucv = new CUpperCaseValidator(MinLength, MaxLength, ui->le_Callsign);
53  ui->le_Callsign->setMaxLength(MaxLength);
54  ui->le_Callsign->setValidator(ucv);
55 
56  ui->le_AircraftCombinedType->setMaxLength(3);
57  ui->le_AircraftCombinedType->setValidator(new CUpperCaseValidator(this));
58  ui->comp_ModelStringCompleter->setSourceVisible(CAircraftModelStringCompleter::OwnModels, false);
59  ui->comp_ModelStringCompleter->selectSource(CAircraftModelStringCompleter::ModelSet);
60 
61  connect(ui->le_Callsign, &QLineEdit::editingFinished, this, &COwnAircraftComponent::validate);
62  connect(ui->comp_ModelStringCompleter, &CAircraftModelStringCompleter::modelStringChanged, this,
63  &COwnAircraftComponent::onModelStringSendChanged);
64  connect(ui->le_AircraftCombinedType, &QLineEdit::editingFinished, this, &COwnAircraftComponent::validate);
65  connect(ui->selector_AircraftIcao, &CDbAircraftIcaoSelectorComponent::changedAircraftIcao, this,
66  &COwnAircraftComponent::changedAircraftIcao, Qt::QueuedConnection);
67  connect(ui->selector_AirlineIcao, &CDbAirlineIcaoSelectorComponent::changedAirlineIcao, this,
68  &COwnAircraftComponent::changedAirlineIcao, Qt::QueuedConnection);
69  connect(ui->pb_Clear, &QPushButton::clicked, this, &COwnAircraftComponent::clearLivery, Qt::QueuedConnection);
70 
71  if (sGui && sGui->getIContextSimulator())
72  {
73  connect(sGui->getIContextSimulator(), &IContextSimulator::ownAircraftModelChanged, this,
74  &COwnAircraftComponent::onSimulatorModelChanged, Qt::QueuedConnection);
75  connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorStatusChanged, this,
76  &COwnAircraftComponent::onSimulatorStatusChanged, Qt::QueuedConnection);
77  }
78 
80  {
81  this->onSimulatorModelChanged(sGui->getIContextOwnAircraft()->getOwnAircraft().getModel());
82  }
83  }
84 
86 
88  {
89  if (user.hasCallsign()) { ui->le_Callsign->setText(user.getCallsign().asString()); }
90  else if (CBuildConfig::isLocalDeveloperDebugBuild()) { ui->le_Callsign->setText("SWIFT"); }
91  this->validate();
92  }
93 
94  void COwnAircraftComponent::onSimulatorModelChanged(const CAircraftModel &model)
95  {
96  if (!sGui || !sGui->getIContextNetwork() || sApp->isShuttingDown()) { return; }
97  const bool isNetworkConnected = sGui && sGui->getIContextNetwork()->isConnected();
98  if (isNetworkConnected) { return; }
99 
100  // update with latest DB data
101  CAircraftModel reverseModel(model);
102  if (sGui->hasWebDataServices())
103  {
104  reverseModel = sGui->getWebDataServices()->getModelForModelString(model.getModelString());
105  if (!reverseModel.isLoadedFromDb()) { reverseModel = model; } // reset if not found
106  }
107 
108  const QString modelStr(reverseModel.hasModelString() ? reverseModel.getModelString() : "<unknown>");
109  if (!reverseModel.hasModelString())
110  {
111  CLogMessage(this).validationInfo(u"Invalid lookup for '%1' successful: %2")
112  << modelStr << reverseModel.toQString();
113  CLogMessage(this).validationInfo(u"Hint: Are you using the emulated driver? Set a model if so!");
114  return;
115  }
116  this->setOwnModelAndIcaoValues(reverseModel);
117 
118  emit this->aircraftDataChanged();
119  }
120 
121  void COwnAircraftComponent::onModelStringSendChanged()
122  {
123  if (!this->hasValidContexts()) { return; }
124  if (!sGui->hasWebDataServices()) { return; }
125  const QString modelString = ui->comp_ModelStringCompleter->getModelString();
126  if (modelString.isEmpty()) { return; } // keep last values
127  const CAircraftModel model = sGui->getWebDataServices()->getModelForModelString(modelString);
128  this->setGuiIcaoValues(model, false);
129  }
130 
131  void COwnAircraftComponent::onSimulatorStatusChanged(int status)
132  {
133  ISimulator::SimulatorStatus s = static_cast<ISimulator::SimulatorStatus>(status);
134  Q_UNUSED(s);
135  if (!this->hasValidContexts()) { return; }
137  {
138  // void
139  }
140  }
141 
143  {
145  CStatusMessageList msgs;
146 
147  // fill in combined type if empty
148  if (ui->le_AircraftCombinedType->text().isEmpty() && values.ownAircraftIcao.isLoadedFromDb())
149  {
150  ui->le_AircraftCombinedType->setText(values.ownAircraftIcao.getCombinedType());
152  }
153 
154  const bool validCombinedType = CAircraftIcaoCode::isValidCombinedType(values.ownAircraftCombinedType);
155  ui->lblp_AircraftCombinedType->setTicked(validCombinedType);
156  if (!validCombinedType) { msgs.addValidationMessage("Invalid combined type", CStatusMessage::SeverityError); }
157 
158  // airline is optional, e.g. C172 has no airline
159  const bool validAirlineDesignator =
160  values.ownAirlineIcao.hasValidDesignator() || values.ownAirlineIcao.getDesignator().isEmpty();
161  ui->lblp_AirlineIcao->setTicked(validAirlineDesignator);
162  if (!validAirlineDesignator)
163  {
164  msgs.addValidationMessage("Invalid airline designator", CStatusMessage::SeverityError);
165  }
166 
167  const bool validAircraftDesignator = values.ownAircraftIcao.hasValidDesignator();
168  ui->lblp_AircraftIcao->setTicked(validAircraftDesignator);
169  if (!validAircraftDesignator)
170  {
171  msgs.addValidationMessage("Invalid aircraft designator", CStatusMessage::SeverityError);
172  }
173 
174  const bool validCallsign = CCallsign::isValidAircraftCallsign(values.ownCallsign);
175  ui->lblp_Callsign->setTicked(validCallsign);
176  if (!validCallsign) { msgs.addValidationMessage("Invalid callsign", CStatusMessage::SeverityError); }
177 
178  // model intentionally ignored
179  // return validCombinedType && validAirlineDesignator && validAircraftDesignator && validCallsign;
180 
181  return msgs;
182  }
183 
184  void COwnAircraftComponent::changedAircraftIcao(const CAircraftIcaoCode &icao)
185  {
186  if (icao.isLoadedFromDb()) { ui->le_AircraftCombinedType->setText(icao.getCombinedType()); }
187  this->validate();
188  }
189 
191  {
192  CGuiAircraftValues values;
193  values.ownCallsign = CCallsign(ui->le_Callsign->text().trimmed().toUpper());
194  values.ownAircraftIcao = ui->selector_AircraftIcao->getAircraftIcao();
195  values.ownAirlineIcao = ui->selector_AirlineIcao->getAirlineIcao();
196  values.ownAircraftCombinedType = ui->le_AircraftCombinedType->text().trimmed().toUpper();
197  values.ownAircraftSimulatorModelString = ui->le_SimulatorModel->text().trimmed().toUpper();
198  values.ownAircraftModelStringSend = ui->comp_ModelStringCompleter->getModelString().toUpper();
199  values.ownLiverySend = ui->le_SendLivery->text().trimmed().toUpper();
200  values.useLivery = ui->cb_Livery->isChecked();
201  values.useModelString = ui->cb_Model->isChecked();
202  return values;
203  }
204 
206  {
207  const CCallsign cs(ui->le_Callsign->text().trimmed().toUpper());
208  return cs;
209  }
210 
211  void COwnAircraftComponent::changedAirlineIcao(const CAirlineIcaoCode &icao)
212  {
213  Q_UNUSED(icao);
214  this->validate();
215  }
216 
217  bool COwnAircraftComponent::setGuiIcaoValues(const CAircraftModel &model, bool onlyIfEmpty)
218  {
219  bool changed = false;
220  if (!onlyIfEmpty || !ui->selector_AircraftIcao->isSet())
221  {
222  changed = ui->selector_AircraftIcao->setAircraftIcao(model.getAircraftIcaoCode());
223  }
224  if (!onlyIfEmpty || !ui->selector_AirlineIcao->isSet())
225  {
226  const bool c = ui->selector_AirlineIcao->setAirlineIcao(model.getAirlineIcaoCode());
227  changed |= c;
228  }
229  if (!onlyIfEmpty || ui->le_AircraftCombinedType->text().trimmed().isEmpty())
230  {
231  const QString combined(model.getAircraftIcaoCode().getCombinedType());
232  if (ui->le_AircraftCombinedType->text() != combined)
233  {
234  ui->le_AircraftCombinedType->setText(combined);
235  changed = true;
236  }
237  }
238  if (!onlyIfEmpty || ui->le_SendLivery->text().trimmed().isEmpty())
239  {
240  const QString ls(model.getSwiftLiveryString());
241  if (ui->le_SendLivery->text() != ls)
242  {
243  ui->le_SendLivery->setText(ls);
244  changed = true;
245  }
246  }
247 
248  const bool valid = this->validate().isSuccess();
249  return valid ? changed : false;
250  }
251 
252  void COwnAircraftComponent::highlightModelField(const CAircraftModel &model)
253  {
254  if (!model.hasModelString()) { ui->le_SimulatorModel->setProperty("validation", "error"); }
255  else if (!model.isLoadedFromDb()) { ui->le_SimulatorModel->setProperty("validation", "warning"); }
256  else { ui->le_SimulatorModel->setProperty("validation", "ok"); }
257  ui->le_SimulatorModel->setStyleSheet(""); // force update
258  }
259 
260  bool COwnAircraftComponent::hasValidContexts() const
261  {
262  if (!sGui || !sGui->supportsContexts()) { return false; }
263  if (sGui->isShuttingDown()) { return false; }
264  if (!sGui->getIContextSimulator()) { return false; }
265  if (!sGui->getIContextNetwork()) { return false; }
266  if (!sGui->getIContextOwnAircraft()) { return false; }
267  return true;
268  }
269 
270  void COwnAircraftComponent::clearLivery() { ui->le_SendLivery->clear(); }
271 
272  CAircraftModel COwnAircraftComponent::getPrefillModel() const
273  {
274  const CAircraftModel model = m_lastAircraftModel.get();
275  if (model.hasAircraftDesignator()) { return model; }
276  return IContextOwnAircraft::getDefaultOwnAircraftModel();
277  }
278 
280  {
281  if (!this->hasValidContexts()) { return; }
282  CAircraftModel model = ownModel;
283  const bool simulating = sGui->getIContextSimulator() &&
284  (sGui->getIContextSimulator()->getSimulatorStatus() & ISimulator::Simulating);
285  if (simulating)
286  {
287  if (!model.hasModelString()) { model = sGui->getIContextOwnAircraft()->getOwnAircraft().getModel(); }
288  const QString modelAndKey(model.getModelStringAndDbKey());
289 
290  ui->le_SimulatorModel->setText(modelAndKey);
291  ui->le_SimulatorModel->home(false);
292  this->highlightModelField(model);
293 
295  ui->comp_ModelStringCompleter->setSimulator(sim);
296  m_lastAircraftModel.set(model);
297 
298  const CSimulatorInternals simulatorInternals = sGui->getIContextSimulator()->getSimulatorInternals();
299  const QString simStr = sim.toQString() + QStringLiteral(" ") + simulatorInternals.getSimulatorVersion();
300  CCrashHandler::instance()->crashAndLogInfoSimulator(simStr);
301  }
302  else
303  {
304  if (!model.hasModelString()) { model = this->getPrefillModel(); }
305  ui->le_SimulatorModel->clear();
306  this->highlightModelField();
307  }
308  ui->le_SimulatorModel->setToolTip(model.asHtmlSummary());
309  ui->comp_ModelStringCompleter->setText(model.getModelString());
310  ui->le_SendLivery->setText(model.getSwiftLiveryString());
311 
312  // reset the model
313  if (model.isLoadedFromDb() ||
315  {
316  // full model from DB, take all values
317  this->setGuiIcaoValues(model, false);
318  }
319  else
320  {
322  {
323  // sim. attached, but no model data from DB
324  ui->le_AircraftCombinedType->clear();
325  ui->selector_AircraftIcao->clear();
326  ui->selector_AirlineIcao->clear();
327  }
328  }
329  }
330 
332  {
333  if (!this->hasValidContexts()) { return false; }
335  const CGuiAircraftValues aircraftValues = this->getAircraftValuesFromGui();
336 
337  CAircraftIcaoCode aircraftCode(ownAircraft.getAircraftIcaoCode());
338  CAirlineIcaoCode airlineCode(ownAircraft.getAirlineIcaoCode());
339 
340  bool changedIcaoCodes = false;
341  if (aircraftValues.ownAircraftIcao.hasValidDesignator() && aircraftValues.ownAircraftIcao != aircraftCode)
342  {
343  aircraftCode = aircraftValues.ownAircraftIcao;
344  changedIcaoCodes = true;
345  }
346  if (aircraftValues.ownAirlineIcao.hasValidDesignator() && aircraftValues.ownAirlineIcao != airlineCode)
347  {
348  airlineCode = aircraftValues.ownAirlineIcao;
349  changedIcaoCodes = true;
350  }
351 
352  if (changedIcaoCodes) { sGui->getIContextOwnAircraft()->updateOwnIcaoCodes(aircraftCode, airlineCode); }
353 
354  return changedIcaoCodes;
355  }
356 
357 } // namespace swift::gui::components
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
const context::IContextOwnAircraft * getIContextOwnAircraft() const
Direct access to contexts if a CCoreFacade has been initialized.
bool hasWebDataServices() const
Web data services available?
const context::IContextNetwork * getIContextNetwork() const
Direct access to contexts if a CCoreFacade has been initialized.
bool isShuttingDown() const
Is application shutting down?
const context::IContextSimulator * getIContextSimulator() const
Direct access to contexts if a CCoreFacade has been initialized.
bool supportsContexts(bool ignoreShutdownTest=false) const
Supports contexts.
CWebDataServices * getWebDataServices() const
Get the web data services.
swift::misc::simulation::CAircraftModel getModelForModelString(const QString &modelString) const
Model for model string if any.
virtual bool isConnected() const =0
Network connected?
virtual bool updateOwnIcaoCodes(const swift::misc::aviation::CAircraftIcaoCode &aircraftIcaoCode, const swift::misc::aviation::CAirlineIcaoCode &airlineIcaoCode)=0
Set ICAO data.
virtual swift::misc::simulation::CSimulatedAircraft getOwnAircraft() const =0
Get own aircraft.
virtual swift::misc::simulation::CSimulatorPluginInfo getSimulatorPluginInfo() const =0
Simulator info, currently loaded plugin.
bool isSimulatorAvailable() const
Simulator avialable (driver available)?
virtual swift::misc::simulation::CSimulatorInternals getSimulatorInternals() const =0
Simulator setup.
virtual ISimulator::SimulatorStatus getSimulatorStatus() const =0
Simulator combined status.
void setAllowedCharacters09AZ()
Set the allowed characters as 0-9 and A-Z.
void changedAircraftIcao(const swift::misc::aviation::CAircraftIcaoCode &icao)
ICAO was changed.
void changedAirlineIcao(const swift::misc::aviation::CAirlineIcaoCode &icao)
ICAO was changed.
swift::misc::CStatusMessageList validate() const
Validate aircaft.
void setUser(const swift::misc::network::CUser &user)
Set a user.
bool updateOwnAircaftIcaoValuesFromGuiValues()
Update own ICAO values (own aircraft from what is set in the GUI)
void setOwnModelAndIcaoValues(const swift::misc::simulation::CAircraftModel &ownModel={})
Own model and ICAO data for GUI and own aircraft.
swift::misc::aviation::CCallsign getCallsignFromGui() const
Values from GUI.
CGuiAircraftValues getAircraftValuesFromGui() const
Values from GUI.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
CStatusMessage set(const typename Trait::type &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
Definition: datacache.h:350
Class for emitting a log message.
Definition: logmessage.h:27
Derived & validationInfo(const char16_t(&format)[N])
Set the severity to info, providing a format string, and adding the validation category.
Status messages, e.g. from Core -> GUI.
void addValidationMessage(const QString &validationText, CStatusMessage::StatusSeverity severity)
Convenience function to push back a validation message.
bool isSuccess() const
All messages are marked as success.
Value object for ICAO classification.
bool hasValidDesignator() const
Valid aircraft designator?
const QString & getCombinedType() const
Get type, e.g. "L2J".
Value object for ICAO classification.
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasValidDesignator() const
Airline designator available?
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
bool isLoadedFromDb() const
Loaded from DB.
Definition: datastore.cpp:49
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
Value object encapsulating information of a user.
Definition: user.h:28
const aviation::CCallsign & getCallsign() const
Get associated callsign.
Definition: user.h:140
bool hasCallsign() const
Has associated callsign?
Definition: user.h:89
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
QString getSwiftLiveryString(bool aircraftIcao=true, bool livery=true, bool model=true) const
swift livery string (to be sent via network)
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code.
const aviation::CLivery & getLivery() const
Get livery.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
bool hasAircraftDesignator() const
Has aircraft designator?
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Aircraft ICAO code.
QString getModelStringAndDbKey() const
Model string and DB key (if available)
QString asHtmlSummary(const QString &separator="<br>") const
As a brief HTML summary (e.g. used in tooltips)
bool hasModelString() const
Non empty model string?
Comprehensive information of an aircraft.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code if any.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
Simulator internals for flight simulators. Those are obtained from a running simulator and represent ...
QString getSimulatorVersion() const
Simulator version info, something like "10.3.2".
const QString & getSimulator() const
Simulator.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
High level reusable GUI components.
Definition: aboutdialog.cpp:13
Free functions in swift::misc.
swift::misc::aviation::CAirlineIcaoCode ownAirlineIcao
ICAO airline.
swift::misc::aviation::CCallsign ownCallsign
own aircraft callsign
swift::misc::aviation::CAircraftIcaoCode ownAircraftIcao
ICAO aircraft.