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,
79  connect(ui->pb_ApplyCGSource, &QCheckBox::pressed, this, &CSettingsSimulatorComponent::onApplyCGSource,
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 = ui->le_MaxAircraft->text().isEmpty() ?
226  CInterpolationAndRenderingSetupGlobal::InfiniteAircraft() :
227  ui->le_MaxAircraft->text().toInt();
228  const int oldValue = setup.getMaxRenderedAircraft();
229  if (oldValue == noRequested) { return; }
230 
231  // set value
232  setup.setMaxRenderedAircraft(noRequested);
234 
235  // re-read real value
237  const int noRendered = setup.getMaxRenderedAircraft();
238  if (noRequested == noRendered) { CLogMessage(this).info(u"Max.rendered aircraft: %1") << noRendered; }
239  else
240  {
241  CLogMessage(this).info(u"Max.rendered aircraft: %1, requested: %2") << noRendered << noRequested;
242  ui->le_MaxAircraft->setText(QString::number(noRendered));
243  }
244  this->setGuiValues();
245  }
246 
247  void CSettingsSimulatorComponent::onApplyMaxRenderedDistance()
248  {
249  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
250 
251  // get initial aircraft to render
254  CLength newDistance(0, nullptr);
255  if (!ui->le_MaxDistance->text().isEmpty())
256  {
257  newDistance = CLength(ui->le_MaxDistance->text().toInt(), CLengthUnit::NM());
258  }
259 
260  CLength currentDistance(setup.getMaxRenderedDistance());
261  if (currentDistance == newDistance) { return; }
262 
263  CLogMessage(this).info(u"Max.distance requested: %1") << newDistance.valueRoundedWithUnit(2, true);
264  setup.setMaxRenderedDistance(newDistance);
266  this->setGuiValues();
267  }
268 
269  void CSettingsSimulatorComponent::onApplyDisableRendering()
270  {
271  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
272 
275  setup.disableRendering();
277  this->setGuiValues();
278  }
279 
280  CSimulatorSettings CSettingsSimulatorComponent::getSimulatorSettings(bool &ok)
281  {
282  ok = false;
283  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return {}; }
285  const CSimulatorInfo simulator = sim->getSimulatorPluginInfo().getSimulatorInfo();
286  if (!simulator.isSingleSimulator()) { return {}; }
287  ok = true;
288  const CSimulatorSettings settings = sim->getSimulatorSettings();
289  return settings;
290  }
291 
292  void CSettingsSimulatorComponent::setSimulatorSettings(CSimulatorSettings &settings)
293  {
294  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
296  const CSimulatorInfo simulator = sim->getSimulatorPluginInfo().getSimulatorInfo();
297  sim->setSimulatorSettings(settings, simulator);
298  }
299 
300  void CSettingsSimulatorComponent::onApplyComSync()
301  {
302  bool ok = false;
303  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
304  if (!ok || !settings.setComIntegrated(ui->cb_ComSync->isChecked())) { return; }
305  setSimulatorSettings(settings);
306  }
307 
308  void CSettingsSimulatorComponent::onApplyCGSource()
309  {
310  bool ok = false;
311  const CSimulatorSettings::CGSource source = ui->comp_CGSourceSelector->getValue();
312  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
313  if (!ok || !settings.setCGSource(source)) { return; }
314  setSimulatorSettings(settings);
315  }
316 
317  void CSettingsSimulatorComponent::onApplyRecordGnd()
318  {
319  bool ok = false;
320  CSimulatorSettings settings = CSettingsSimulatorComponent::getSimulatorSettings(ok);
321  if (!ok) { return; }
322 
323  // get value, automatically add default unit if unit is missing
324  CLength radius = CLength::null();
325  QString radiusString = ui->le_RecordOwnGndPositionsRadius->text().trimmed();
326  if (!radiusString.isEmpty())
327  {
328  if (!CMeasurementUnit::endWithValidUnitSymbol<CLengthUnit>(radiusString)) { radiusString += "m"; }
329  radius.parseFromString(radiusString);
330  }
331  ui->le_RecordOwnGndPositionsRadius->setText(radius.valueRoundedWithUnit(1));
332 
333  const bool c1 = settings.setRecordOwnAircraftGnd(ui->cb_RecordOwnGndPositions->isChecked());
334  const bool c2 = settings.setRecordedGndRadius(radius);
335  if (!c1 && !c2) { return; }
336  setSimulatorSettings(settings);
337  }
338 
339  void CSettingsSimulatorComponent::onReload() { this->setGuiValues(); }
340 
341  void CSettingsSimulatorComponent::onEnabledSimulatorsChanged()
342  {
343  this->reloadPluginConfig(CSettingsSimulatorComponent::getAvailablePlugins());
344  }
345 
346  void CSettingsSimulatorComponent::clearRestricedRendering()
347  {
348  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
349 
353  this->setGuiValues();
354  }
355 
356  void CSettingsSimulatorComponent::simulatorPluginChanged(const CSimulatorPluginInfo &info)
357  {
358  // I intentionally do not set the selected plugin combobox here
359  // as this would cause undesired roundtrips
360 
361  // other GUI values
362  if (!info.isUnspecified())
363  {
364  m_pluginLoaded = true;
365  ui->lbl_PluginInfo->setText("Connected to: " % info.getName());
366  }
367  else
368  {
369  m_pluginLoaded = false;
370  ui->lbl_PluginInfo->setText("No connection to simulator");
371  }
372  this->setGuiValues();
373  }
374 
375  void CSettingsSimulatorComponent::showPluginConfig(const QString &identifier)
376  {
377  const CSimulatorPluginInfoList simDrivers(getAvailablePlugins());
378  const CSimulatorPluginInfo selected = simDrivers.findByIdentifier(identifier);
379 
380  const QString configId = m_plugins->getPluginConfigId(selected.getIdentifier());
381  auto *config = m_plugins->getPluginById<IPluginConfig>(configId);
382  SWIFT_VERIFY_X(config, Q_FUNC_INFO, "Missing config");
383  if (!config) { return; }
384 
385  CPluginConfigWindow *window = config->createConfigWindow(qApp->activeWindow());
386  SWIFT_VERIFY_X(window, Q_FUNC_INFO, "Missing window");
387  if (!window) { return; }
388 
390  window->show();
391  }
392 
393  void CSettingsSimulatorComponent::reloadPluginConfig(const CSimulatorPluginInfoList &plugins)
394  {
395  // list all available simulators
396  const auto enabledSimulators = m_enabledSimulators.getThreadLocal();
397  for (const auto &p : plugins)
398  {
399  ui->pluginSelector_EnabledSimulators->setEnabled(p.getIdentifier(),
400  enabledSimulators.contains(p.getIdentifier()));
401  }
402  }
403 
404  void CSettingsSimulatorComponent::checkSimulatorPlugins()
405  {
406  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
407  if (sGui->getIContextSimulator()->isSimulatorAvailable()) { return; } // already available
409  }
410 } // 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.
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:746
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:14
Free functions in swift::misc.
void editingFinished()
void returnPressed()
qsizetype removeAll(const AT &t)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qreal height() const const
bool isEmpty() const const
QString number(double n, char format, int precision)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QueuedConnection
WA_DeleteOnClose
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void show()
QWidget * window() const const
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26