swift
settingssimulatorcomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <algorithm>
7 
8 #include <QApplication>
9 #include <QCheckBox>
10 #include <QLabel>
11 #include <QLineEdit>
12 #include <QPushButton>
13 #include <QSpinBox>
14 #include <QStringList>
15 #include <Qt>
16 #include <QtGlobal>
17 
18 #include "ui_settingssimulatorcomponent.h"
19 
23 #include "gui/guiapplication.h"
24 #include "gui/led.h"
25 #include "gui/pluginconfig.h"
26 #include "gui/pluginconfigwindow.h"
28 #include "gui/pluginselector.h"
29 #include "misc/iterator.h"
30 #include "misc/logmessage.h"
31 #include "misc/pq/length.h"
32 #include "misc/pq/time.h"
33 #include "misc/pq/units.h"
36 #include "misc/statusmessage.h"
37 #include "misc/verify.h"
38 
39 using namespace swift::misc;
40 using namespace swift::misc::physical_quantities;
41 using namespace swift::misc::aviation;
42 using namespace swift::misc::simulation;
43 using namespace swift::misc::simulation::settings;
44 using namespace swift::core;
45 using namespace swift::core::context;
46 
47 namespace swift::gui::components
48 {
49  CSettingsSimulatorComponent::CSettingsSimulatorComponent(QWidget *parent)
50  : QFrame(parent), ui(new Ui::CSettingsSimulatorComponent), m_plugins(new CPluginManagerSimulator(this))
51  {
52  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Missing sGui");
53  Q_ASSERT_X(sGui->getIContextSimulator(), Q_FUNC_INFO, "Missing context");
54 
55  m_plugins->collectPlugins();
56  ui->setupUi(this);
57  const CLedWidget::LedShape shape = CLedWidget::Circle;
58  ui->led_RestrictedRendering->setValues(CLedWidget::Yellow, CLedWidget::Black, shape, "Limited", "Unlimited",
59  14);
60  ui->led_RenderingEnabled->setValues(CLedWidget::Yellow, CLedWidget::Black, shape, "Rendering enabled",
61  "No aircraft will be rendered", 14);
62 
63  ui->le_MaxAircraft->setValidator(new QIntValidator(ui->le_MaxAircraft));
64  ui->le_MaxDistance->setValidator(new QIntValidator(ui->le_MaxDistance));
65 
66  // connects
67  connect(ui->pluginSelector_EnabledSimulators, &CPluginSelector::pluginStateChanged, this,
68  &CSettingsSimulatorComponent::pluginStateChanged);
69  connect(ui->pluginSelector_EnabledSimulators, &CPluginSelector::pluginConfigRequested, this,
70  &CSettingsSimulatorComponent::showPluginConfig);
71 
72  connect(ui->pb_Reload, &QCheckBox::pressed, this, &CSettingsSimulatorComponent::onReload, Qt::QueuedConnection);
73  connect(ui->pb_ApplyMaxAircraft, &QCheckBox::pressed, this,
74  &CSettingsSimulatorComponent::onApplyMaxRenderedAircraft, Qt::QueuedConnection);
75  connect(ui->pb_ApplyMaxDistance, &QCheckBox::pressed, this,
76  &CSettingsSimulatorComponent::onApplyMaxRenderedDistance, Qt::QueuedConnection);
77  connect(ui->pb_ApplyComSync, &QCheckBox::pressed, this, &CSettingsSimulatorComponent::onApplyComSync,
78  Qt::QueuedConnection);
79  connect(ui->pb_ApplyCGSource, &QCheckBox::pressed, this, &CSettingsSimulatorComponent::onApplyCGSource,
80  Qt::QueuedConnection);
81  connect(ui->pb_ApplyRecordOwnAircraftGnd, &QCheckBox::pressed, this,
82  &CSettingsSimulatorComponent::onApplyRecordGnd, Qt::QueuedConnection);
83 
84  connect(ui->pb_ClearRestrictedRendering, &QCheckBox::pressed, this,
85  &CSettingsSimulatorComponent::clearRestricedRendering);
86  connect(ui->pb_DisableRendering, &QCheckBox::pressed, this,
87  &CSettingsSimulatorComponent::onApplyDisableRendering);
88  connect(ui->pb_Check, &QCheckBox::pressed, this, &CSettingsSimulatorComponent::checkSimulatorPlugins);
89  connect(ui->le_MaxAircraft, &QLineEdit::editingFinished, this,
90  &CSettingsSimulatorComponent::onApplyMaxRenderedAircraft);
91  connect(ui->le_MaxDistance, &QLineEdit::editingFinished, this,
92  &CSettingsSimulatorComponent::onApplyMaxRenderedDistance);
93  connect(ui->le_MaxAircraft, &QLineEdit::returnPressed, this,
94  &CSettingsSimulatorComponent::onApplyMaxRenderedAircraft);
95  connect(ui->le_MaxDistance, &QLineEdit::returnPressed, this,
96  &CSettingsSimulatorComponent::onApplyMaxRenderedDistance);
97 
98  // list all available simulators
99  const CSimulatorPluginInfoList plugins = CSettingsSimulatorComponent::getAvailablePlugins();
100  for (const auto &p : plugins)
101  {
102  const QString config = m_plugins->getPluginConfigId(p.getIdentifier());
103  ui->pluginSelector_EnabledSimulators->addPlugin(p.getIdentifier(), p.getName(), !config.isEmpty(), false);
104  }
105 
106  const int h = qRound(1.3 * plugins.size() * CGuiUtility::fontMetricsLazyDog43Chars().height());
107  ui->pluginSelector_EnabledSimulators->setMinimumHeight(h);
108 
109  // config
110  this->reloadPluginConfig(plugins);
111 
112  // init
113  if (sGui && sGui->getIContextSimulator())
114  {
115  this->simulatorPluginChanged(sGui->getIContextSimulator()->getSimulatorPluginInfo());
116  connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorPluginChanged, this,
117  &CSettingsSimulatorComponent::simulatorPluginChanged, Qt::QueuedConnection);
118  connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorSettingsChanged, this,
119  &CSettingsSimulatorComponent::onReload, Qt::QueuedConnection);
120  }
121  }
122 
124 
125  void CSettingsSimulatorComponent::setGuiValues()
126  {
127  if (!sGui || !sGui->getIContextSimulator() || sGui->isShuttingDown()) { return; }
130 
131  // COM unit
132  ui->pb_ApplyComSync->setEnabled(m_pluginLoaded);
133  ui->cb_ComSync->setEnabled(m_pluginLoaded);
134 
135  // CG
136  ui->comp_CGSourceSelector->setEnabled(m_pluginLoaded);
137 
138  // record GND
139  ui->pb_ApplyRecordOwnAircraftGnd->setEnabled(m_pluginLoaded);
140  ui->cb_RecordOwnGndPositions->setEnabled(m_pluginLoaded);
141  ui->le_RecordOwnGndPositionsRadius->setEnabled(m_pluginLoaded);
142 
143  // led
144  ui->led_RestrictedRendering->setOn(m_pluginLoaded ? setup.isRenderingRestricted() : false);
145  ui->lbl_RestrictionText->setText(m_pluginLoaded ? setup.getRenderRestrictionText() : "");
146 
147  ui->le_MaxDistance->setEnabled(m_pluginLoaded);
148  ui->le_MaxAircraft->setEnabled(m_pluginLoaded);
149  ui->pb_ApplyMaxAircraft->setEnabled(m_pluginLoaded);
150  ui->pb_ApplyMaxDistance->setEnabled(m_pluginLoaded);
151  ui->pb_ClearRestrictedRendering->setEnabled((m_pluginLoaded));
152  ui->pb_DisableRendering->setEnabled(m_pluginLoaded);
153  ui->pb_Check->setEnabled(!m_pluginLoaded);
154  ui->pb_ApplyCGSource->setEnabled(m_pluginLoaded);
155  ui->pb_Reload->setEnabled(m_pluginLoaded);
156 
157  if (m_pluginLoaded)
158  {
160 
161  // settings
162  const CSimulatorSettings settings = sim->getSimulatorSettings();
163  ui->cb_ComSync->setChecked(settings.isComIntegrated());
164 
165  // CG
166  ui->comp_CGSourceSelector->setValue(settings);
167 
168  // record
169  ui->le_RecordOwnGndPositionsRadius->setText(
170  settings.getRecordedGndRadius().valueRoundedWithUnit(CLengthUnit::m(), 1, false, true));
171  ui->cb_RecordOwnGndPositions->setChecked(settings.isRecordOwnAircraftGnd());
172 
173  // rendering
174  const int maxAircraft = setup.getMaxRenderedAircraft();
175  ui->le_MaxAircraft->setText(setup.isMaxAircraftRestricted() ? QString::number(maxAircraft) : "");
176 
177  const CLength maxDistance(setup.getMaxRenderedDistance());
178  ui->le_MaxDistance->setText(
179  setup.isMaxDistanceRestricted() ? QString::number(maxDistance.valueInteger(CLengthUnit::NM())) : "");
180  ui->led_RenderingEnabled->setOn(setup.isRenderingEnabled());
181  }
182  else { ui->led_RenderingEnabled->setOn(false); }
183  }
184 
185  CSimulatorPluginInfoList CSettingsSimulatorComponent::getAvailablePlugins()
186  {
187  if (!sGui || !sGui->getIContextSimulator()) { return {}; }
189  }
190 
191  void CSettingsSimulatorComponent::pluginStateChanged(const QString &identifier, bool enabled)
192  {
193  Q_ASSERT(sGui && sGui->getIContextSimulator());
194 
195  const CSimulatorPluginInfoList simDrivers(getAvailablePlugins());
196  const CSimulatorPluginInfo selected = simDrivers.findByIdentifier(identifier);
197  if (selected.isUnspecified())
198  {
199  CLogMessage(this).error(u"Simulator plugin does not exist: '%1'") << identifier;
200  return;
201  }
202 
203  QStringList e = m_enabledSimulators.getThreadLocal(); // from setting
204  if (enabled != e.contains(selected.getIdentifier()))
205  {
206  if (enabled)
207  {
208  e << selected.getIdentifier(); // add enabled plugin
209  }
210  else { e.removeAll(selected.getIdentifier()); }
211  const CStatusMessage msg = m_enabledSimulators.set(e); // change setting
212  if (msg.isWarningOrAbove()) { CLogMessage::preformatted(msg); }
213  }
214 
215  // changing of GUI state will be done via received signal
216  }
217 
218  void CSettingsSimulatorComponent::onApplyMaxRenderedAircraft()
219  {
220  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
221 
222  // get initial aircraft to render
225  const int noRequested =
226  ui->le_MaxAircraft->text().isEmpty() ? setup.InfiniteAircraft() : ui->le_MaxAircraft->text().toInt();
227  const int oldValue = setup.getMaxRenderedAircraft();
228  if (oldValue == noRequested) { return; }
229 
230  // set value
231  setup.setMaxRenderedAircraft(noRequested);
233 
234  // re-read real value
236  const int noRendered = setup.getMaxRenderedAircraft();
237  if (noRequested == noRendered) { CLogMessage(this).info(u"Max.rendered aircraft: %1") << noRendered; }
238  else
239  {
240  CLogMessage(this).info(u"Max.rendered aircraft: %1, requested: %2") << noRendered << noRequested;
241  ui->le_MaxAircraft->setText(QString::number(noRendered));
242  }
243  this->setGuiValues();
244  }
245 
246  void CSettingsSimulatorComponent::onApplyMaxRenderedDistance()
247  {
248  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
249 
250  // get initial aircraft to render
253  CLength newDistance(0, nullptr);
254  if (!ui->le_MaxDistance->text().isEmpty())
255  {
256  newDistance = CLength(ui->le_MaxDistance->text().toInt(), CLengthUnit::NM());
257  }
258 
259  CLength currentDistance(setup.getMaxRenderedDistance());
260  if (currentDistance == newDistance) { return; }
261 
262  CLogMessage(this).info(u"Max.distance requested: %1") << newDistance.valueRoundedWithUnit(2, true);
263  setup.setMaxRenderedDistance(newDistance);
265  this->setGuiValues();
266  }
267 
268  void CSettingsSimulatorComponent::onApplyDisableRendering()
269  {
270  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
271 
274  setup.disableRendering();
276  this->setGuiValues();
277  }
278 
279  CSimulatorSettings CSettingsSimulatorComponent::getSimulatorSettings(bool &ok)
280  {
281  ok = false;
282  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return {}; }
284  const CSimulatorInfo simulator = sim->getSimulatorPluginInfo().getSimulatorInfo();
285  if (!simulator.isSingleSimulator()) { return {}; }
286  ok = true;
287  const CSimulatorSettings settings = sim->getSimulatorSettings();
288  return settings;
289  }
290 
291  void CSettingsSimulatorComponent::setSimulatorSettings(CSimulatorSettings &settings)
292  {
293  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
295  const CSimulatorInfo simulator = sim->getSimulatorPluginInfo().getSimulatorInfo();
296  sim->setSimulatorSettings(settings, simulator);
297  }
298 
299  void CSettingsSimulatorComponent::onApplyComSync()
300  {
301  bool ok = false;
302  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
303  if (!ok || !settings.setComIntegrated(ui->cb_ComSync->isChecked())) { return; }
304  this->setSimulatorSettings(settings);
305  }
306 
307  void CSettingsSimulatorComponent::onApplyCGSource()
308  {
309  bool ok = false;
310  const CSimulatorSettings::CGSource source = ui->comp_CGSourceSelector->getValue();
311  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
312  if (!ok || !settings.setCGSource(source)) { return; }
313  this->setSimulatorSettings(settings);
314  }
315 
316  void CSettingsSimulatorComponent::onApplyRecordGnd()
317  {
318  bool ok = false;
319  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
320  if (!ok) { return; }
321 
322  // get value, automatically add default unit if unit is missing
323  CLength radius = CLength::null();
324  QString radiusString = ui->le_RecordOwnGndPositionsRadius->text().trimmed();
325  if (!radiusString.isEmpty())
326  {
327  if (!CMeasurementUnit::endWithValidUnitSymbol<CLengthUnit>(radiusString)) { radiusString += "m"; }
328  radius.parseFromString(radiusString);
329  }
330  ui->le_RecordOwnGndPositionsRadius->setText(radius.valueRoundedWithUnit(1));
331 
332  const bool c1 = settings.setRecordOwnAircraftGnd(ui->cb_RecordOwnGndPositions->isChecked());
333  const bool c2 = settings.setRecordedGndRadius(radius);
334  if (!c1 && !c2) { return; }
335  this->setSimulatorSettings(settings);
336  }
337 
338  void CSettingsSimulatorComponent::onReload() { this->setGuiValues(); }
339 
340  void CSettingsSimulatorComponent::onEnabledSimulatorsChanged()
341  {
342  this->reloadPluginConfig(CSettingsSimulatorComponent::getAvailablePlugins());
343  }
344 
345  void CSettingsSimulatorComponent::clearRestricedRendering()
346  {
347  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
348 
352  this->setGuiValues();
353  }
354 
355  void CSettingsSimulatorComponent::simulatorPluginChanged(const CSimulatorPluginInfo &info)
356  {
357  // I intentionally do not set the selected plugin combobox here
358  // as this would cause undesired roundtrips
359 
360  // other GUI values
361  if (!info.isUnspecified())
362  {
363  m_pluginLoaded = true;
364  ui->lbl_PluginInfo->setText("Connected to: " % info.getName());
365  }
366  else
367  {
368  m_pluginLoaded = false;
369  ui->lbl_PluginInfo->setText("No connection to simulator");
370  }
371  this->setGuiValues();
372  }
373 
374  void CSettingsSimulatorComponent::showPluginConfig(const QString &identifier)
375  {
376  const CSimulatorPluginInfoList simDrivers(getAvailablePlugins());
377  const CSimulatorPluginInfo selected = simDrivers.findByIdentifier(identifier);
378 
379  const QString configId = m_plugins->getPluginConfigId(selected.getIdentifier());
380  IPluginConfig *config = m_plugins->getPluginById<IPluginConfig>(configId);
381  SWIFT_VERIFY_X(config, Q_FUNC_INFO, "Missing config");
382  if (!config) { return; }
383 
384  CPluginConfigWindow *window = config->createConfigWindow(qApp->activeWindow());
385  SWIFT_VERIFY_X(window, Q_FUNC_INFO, "Missing window");
386  if (!window) { return; }
387 
388  window->setAttribute(Qt::WA_DeleteOnClose);
389  window->show();
390  }
391 
392  void CSettingsSimulatorComponent::reloadPluginConfig(const CSimulatorPluginInfoList &plugins)
393  {
394  // list all available simulators
395  const auto enabledSimulators = m_enabledSimulators.getThreadLocal();
396  for (const auto &p : plugins)
397  {
398  ui->pluginSelector_EnabledSimulators->setEnabled(p.getIdentifier(),
399  enabledSimulators.contains(p.getIdentifier()));
400  }
401  }
402 
403  void CSettingsSimulatorComponent::checkSimulatorPlugins()
404  {
405  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
406  if (sGui->getIContextSimulator()->isSimulatorAvailable()) { return; } // already available
408  }
409 } // namespace swift::gui::components
bool isShuttingDown() const
Is application shutting down?
const context::IContextSimulator * getIContextSimulator() const
Direct access to contexts if a CCoreFacade has been initialized.
Manages plugins for the simulator context.
virtual void collectPlugins()
Looks for all available plugins.
T * getPluginById(const QString &identifier)
Loads the given plugin (if necessary), casts it to the desired type and returns its instance....
Definition: pluginmanager.h:42
QString getPluginConfigId(const QString &identifier)
If the plugin specifies its config plugin, its identifier can be obtained using this method....
virtual swift::misc::simulation::CSimulatorPluginInfo getSimulatorPluginInfo() const =0
Simulator info, currently loaded plugin.
virtual void setInterpolationAndRenderingSetupGlobal(const swift::misc::simulation::CInterpolationAndRenderingSetupGlobal &setup)=0
Set the global setup.
virtual int checkListeners()=0
Check all listeners enabled if simulator is connected.
bool isSimulatorAvailable() const
Simulator avialable (driver available)?
virtual swift::misc::simulation::settings::CSimulatorSettings getSimulatorSettings() const =0
Get the current simulator settings.
virtual swift::misc::simulation::CInterpolationAndRenderingSetupGlobal getInterpolationAndRenderingSetupGlobal() const =0
The global setup.
virtual bool setSimulatorSettings(const swift::misc::simulation::settings::CSimulatorSettings &settings, const swift::misc::simulation::CSimulatorInfo &simulator)=0
Set settings for give simulator.
virtual swift::misc::simulation::CSimulatorPluginInfoList getAvailableSimulatorPlugins() const =0
Return list of available simulator plugins.
static QSizeF fontMetricsLazyDog43Chars(bool withRatio=false)
43 characters width/height "the quick brown ..."
Definition: guiutility.cpp:755
LedShape
Shapes.
Definition: led.h:51
void pluginStateChanged(const QString &identifier, bool enabled)
Emitted when user enables/disables the particular plugin.
void pluginConfigRequested(const QString &identifier)
Emitted when user clicks the "Settings" button.
CStatusMessage set(const T &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
Definition: valuecache.h:411
const T & getThreadLocal() const
Read the current value.
Definition: valuecache.h:400
Class for emitting a log message.
Definition: logmessage.h:27
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
Streamable status message, e.g.
bool isWarningOrAbove() const
Warning or above.
Physical unit length (length)
Definition: length.h:18
void parseFromString(const QString &value)
Parse value from string.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
bool setMaxRenderedDistance(const physical_quantities::CLength &distance)
Max.distance for rendering.
QString getRenderRestrictionText() const
Text describing the restrictions.
physical_quantities::CLength getMaxRenderedDistance() const
Max.distance for rendering.
bool setMaxRenderedAircraft(int maxRenderedAircraft)
Max.number of aircraft rendered.
bool isRenderingRestricted() const
Rendering enabled, but restricted.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool isSingleSimulator() const
Single simulator selected.
const QString & getIdentifier() const
Identifier.
const CSimulatorInfo & getSimulatorInfo() const
Simulator info object.
bool isUnspecified() const
Unspecified simulator.
Value object encapsulating a list of SimulatorInfo objects.
Settings for simulator Driver independent parts (such as directories), also used in model loaders.
swift::misc::physical_quantities::CLength getRecordedGndRadius() const
Record GND values with radius.
CGSource
Where we get the CG (aka vertical offset) from.
bool isRecordOwnAircraftGnd() const
Record GND values (of own aircraft)
bool setRecordedGndRadius(const swift::misc::physical_quantities::CLength &radius)
Record GND values with radius.
bool setRecordOwnAircraftGnd(bool record)
Record GND values (of own aircraft)
bool setComIntegrated(bool integrated)
COM unit integration.
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.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26