swift
swiftguistdinit.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 
4 #include <QAction>
5 #include <QHBoxLayout>
6 #include <QPointer>
7 #include <QScopedPointer>
8 #include <QStackedWidget>
9 #include <QStatusBar>
10 #include <QString>
11 #include <QTimer>
12 #include <QVBoxLayout>
13 
14 #include "swiftguistd.h"
15 #include "ui_swiftguistd.h"
16 
17 #include "config/buildconfig.h"
21 #include "core/webdataservices.h"
36 #include "gui/dockwidgetinfobar.h"
37 #include "gui/guiapplication.h"
38 #include "gui/managedstatusbar.h"
40 #include "gui/stylesheetutility.h"
41 #include "misc/loghandler.h"
42 #include "misc/logmessage.h"
43 #include "misc/logpattern.h"
46 #include "misc/slot.h"
47 #include "misc/statusmessage.h"
48 #include "sound/audioutilities.h"
49 
50 using namespace swift::config;
51 using namespace swift::core;
52 using namespace swift::core::context;
53 using namespace swift::misc;
54 using namespace swift::misc::aviation;
55 using namespace swift::misc::network;
56 using namespace swift::misc::input;
57 using namespace swift::gui;
58 using namespace swift::gui::components;
59 
60 void SwiftGuiStd::init()
61 {
62  // POST(!) GUI init
63  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Missing sGui");
64  Q_ASSERT_X(sGui->getWebDataServices(), Q_FUNC_INFO, "Missing web services");
65  Q_ASSERT_X(sGui->supportsContexts(), Q_FUNC_INFO, "Missing contexts");
66 
67  if (m_init) { return; }
68 
69  ui->dw_InfoBarStatus->initialFloating();
70 
71  this->setVisible(false); // hide all, so no flashing windows during init
72  m_mwaStatusBar = &m_statusBar;
73  m_mwaOverlayFrame = ui->fr_CentralFrameInside;
74  m_mwaLogComponent = ui->comp_MainInfoArea->getLogComponent();
76 
77  // log messages
78  m_logHistoryForOverlay.setFilter(CLogPattern().withSeverityAtOrAbove(CStatusMessage::SeverityError));
79  m_logHistoryForLogButtons.setFilter(CLogPattern().withSeverityAtOrAbove(SeverityWarning));
80  connect(&m_logHistoryForOverlay, &CLogHistoryReplica::elementAdded, this, [this](const CStatusMessage &message) {
82  if (!message.getCategories().contains(CLogCategories::validation()))
83  {
84  ui->fr_CentralFrameInside->showOverlayMessage(message);
85  }
86  });
87  connect(&m_logHistoryForLogButtons, &CLogHistoryReplica::elementAdded, this, [this](const CStatusMessage &message) {
88  if (message.getSeverity() == CStatusMessage::SeverityError) { m_statusBar.showErrorButton(); }
89  else if (message.getSeverity() == CStatusMessage::SeverityWarning) { m_statusBar.showWarningButton(); }
90  });
91  m_logHistoryForOverlay.initialize(sApp->getDataLinkDBus());
92  m_logHistoryForLogButtons.initialize(sApp->getDataLinkDBus());
93 
94  // style
95  this->initStyleSheet();
96 
97  // with frameless window, we shift menu and statusbar into central widget
98  // http://stackoverflow.com/questions/18316710/frameless-and-transparent-window-qt5
99  if (this->isFrameless())
100  {
101  // wrap menu in layout, add button to menu bar and insert on top
102  QHBoxLayout *menuBarLayout = this->addFramelessCloseButton(ui->mb_MainMenuBar);
103  ui->vl_CentralWidgetOutside->insertLayout(0, menuBarLayout, 0);
104 
105  // now insert the dock widget info bar into the widget
106  ui->vl_CentralWidgetOutside->insertWidget(1, ui->dw_InfoBarStatus);
107 
108  // move the status bar into the frame
109  // (otherwise it is dangling outside the frame as it belongs to the window)
110  ui->sb_MainStatusBar->setParent(ui->wi_CentralWidgetOutside);
111  ui->vl_CentralWidgetOutside->addWidget(ui->sb_MainStatusBar, 0);
112 
113  // grip
114  this->addFramelessSizeGripToStatusBar(ui->sb_MainStatusBar);
115  }
116 
117  // timers
118  m_timerContextWatchdog.setObjectName(this->objectName().append(":m_timerContextWatchdog"));
119 
120  // info bar and status bar
121  m_statusBar.initStatusBar(ui->sb_MainStatusBar);
122  connect(&m_statusBar, &CManagedStatusBar::requestLogPage, ui->comp_MainInfoArea,
123  &CMainInfoAreaComponent::displayLog);
124  ui->dw_InfoBarStatus->allowStatusBar(false);
125  ui->dw_InfoBarStatus->setPreferredSizeWhenFloating(ui->dw_InfoBarStatus->size()); // set floating size
126 
127  // navigator
128  m_navigator->addAction(this->getToggleWindowVisibilityAction(m_navigator.data()));
129  m_navigator->addActions(ui->comp_MainInfoArea->getInfoAreaToggleFloatingActions(
130  m_navigator.data())); // here we add the actions for the main windows
131  m_navigator->addAction(this->getWindowNormalAction(m_navigator.data()));
132  m_navigator->addAction(this->getWindowMinimizeAction(m_navigator.data()));
133  m_navigator->addAction(this->getToggleStayOnTopAction(m_navigator.data()));
134  m_navigator->buildNavigator(1);
135 
136  // wire GUI signals
137  this->initGuiSignals();
138 
139  // signal / slots contexts / timers
140  Q_ASSERT_X(sGui->getIContextNetwork(), Q_FUNC_INFO, "Missing network context");
141  Q_ASSERT_X(sGui->getIContextSimulator(), Q_FUNC_INFO, "Missing simulator context");
142 
143  bool s = connect(sGui->getIContextNetwork(), &IContextNetwork::connectionStatusChanged, this,
144  &SwiftGuiStd::onConnectionStatusChanged, Qt::QueuedConnection);
145  Q_ASSERT(s);
146  s = connect(sGui->getIContextNetwork(), &IContextNetwork::kicked, this, &SwiftGuiStd::onKickedFromNetwork,
147  Qt::QueuedConnection);
148  Q_ASSERT(s);
149  s = connect(sGui->getIContextSimulator(), &IContextSimulator::validatedModelSet, this,
150  &SwiftGuiStd::onValidatedModelSet, Qt::QueuedConnection);
151  Q_ASSERT(s);
152  s = connect(&m_timerContextWatchdog, &QTimer::timeout, this, &SwiftGuiStd::handleTimerBasedUpdates);
153  Q_ASSERT(s);
154 
155  if (sGui->getIContextAudio())
156  {
157  s = connect(sGui->getIContextAudio(), &IContextAudio::voiceClientFailure, this,
158  &SwiftGuiStd::onAudioClientFailure, Qt::QueuedConnection);
159  Q_ASSERT(s);
160  }
161  Q_UNUSED(s)
162 
163  // check if DB data have been loaded
164  // only check once, so data can be loaded and
165  connectOnce(sGui->getWebDataServices(), &CWebDataServices::sharedInfoObjectsRead, this,
166  &SwiftGuiStd::checkDbDataLoaded, Qt::QueuedConnection);
167 
168  // start timers, update timers will be started when network is connected
169  m_timerContextWatchdog.start(2500);
170 
171  // init availability
172  this->setContextAvailability();
173 
174  // data
175  this->initialContextDataReads();
176 
177  // start screen and complete menu
178  this->setMainPageToInfoArea();
179  this->initMenus();
180 
181  // info
182  connect(ui->comp_InfoBarStatus, &CInfoBarStatusComponent::transponderModeChanged, ui->dw_InfoBarStatus,
183  &CDockWidgetInfoBar::reloadStyleSheet, Qt::QueuedConnection);
184 
185  // do this as last statement, so it can be used as flag
186  // whether init has been completed
187  this->setVisible(true);
188 
189  // more checks
190  QPointer<SwiftGuiStd> myself(this);
191  QTimer::singleShot(5000, this, [=] {
192  if (!myself) { return; }
193  this->verifyPrerequisites();
194  });
195 
196  // trigger version check
197  sGui->triggerNewVersionCheck(10 * 1000);
198 
199  // done
200  m_init = true;
201 }
202 
203 void SwiftGuiStd::initStyleSheet()
204 {
205  if (!sGui || sGui->isShuttingDown()) { return; }
206  const QString s = sGui->getStyleSheetUtility().styles({ CStyleSheetUtility::fileNameFonts(),
207  CStyleSheetUtility::fileNameStandardWidget(),
208  CStyleSheetUtility::fileNameSwiftStandardGui() });
209  this->setStyleSheet("");
210  this->setStyleSheet(s);
211 }
212 
213 void SwiftGuiStd::initGuiSignals()
214 {
215  // Remark: With new style, only methods of same signature can be connected
216  // This is why we still have some "old" SIGNAL/SLOT connections here
217 
218  // main window
219  connect(ui->sw_MainMiddle, &QStackedWidget::currentChanged, this, &SwiftGuiStd::onCurrentMainWidgetChanged);
220 
221  // main keypad
222  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::selectedMainInfoAreaDockWidget, this,
223  &SwiftGuiStd::setMainPageInfoArea);
224  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::connectPressed, this, &SwiftGuiStd::loginRequested);
225  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::changedOpacity, this,
226  &SwiftGuiStd::onChangedWindowOpacity);
227  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::identPressed,
228  ui->comp_MainInfoArea->getCockpitComponent(), &CCockpitComponent::setSelectedTransponderModeStateIdent);
229  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::textEntered,
230  ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::handleGlobalCommandLineText);
231  connect(ui->comp_MainKeypadArea, &CMainKeypadAreaComponent::audioPressed, ui->comp_MainInfoArea,
232  &CMainInfoAreaComponent::selectAudioTab);
233  connect(ui->comp_MainInfoArea, &CMainInfoAreaComponent::changedInfoAreaStatus, ui->comp_MainKeypadArea,
234  &CMainKeypadAreaComponent::onMainInfoAreaChanged);
235 
236  // text component
237  connect(ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::textMessageTabSelected, this,
238  &SwiftGuiStd::focusInTextMessageEntryField, Qt::QueuedConnection);
239 
240  // audio
241  connect(ui->comp_MainInfoArea->getAtcStationComponent(), &CAtcStationComponent::requestAudioWidget,
242  ui->comp_MainInfoArea, &CMainInfoAreaComponent::selectAudioTab);
243 
244  // menu
245  connect(ui->menu_TestLocationsEDDF, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
246  connect(ui->menu_TestLocationsEDDM, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
247  connect(ui->menu_TestLocationsEDNX, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
248  connect(ui->menu_TestLocationsEDRY, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
249  connect(ui->menu_TestLocationsLOWW, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
250 
251  connect(ui->menu_WindowToggleNavigator, &QAction::triggered, m_navigator.data(),
252  &CNavigatorDialog::toggleNavigatorVisibility);
253  connect(ui->menu_WindowFont, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
254  connect(ui->menu_WindowMinimize, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
255  connect(ui->menu_WindowToggleOnTop, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
256  connect(ui->menu_InternalsPage, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
257  connect(ui->menu_AutoPublish, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
258  connect(ui->menu_ToggleIncognito, &QAction::triggered, this, &SwiftGuiStd::onMenuClicked);
259  connect(ui->menu_ModelBrowser, &QAction::triggered, this, &SwiftGuiStd::startModelBrowser, Qt::QueuedConnection);
260  connect(ui->menu_AfvMap, &QAction::triggered, this, &SwiftGuiStd::startAFVMap, Qt::QueuedConnection);
261 
262  connect(m_navigator.data(), &CNavigatorDialog::navigatorClosed, this, &SwiftGuiStd::onNavigatorClosed,
263  Qt::QueuedConnection);
264  m_navigator->setMainWindow(this);
265 
266  // settings (GUI component), styles
267  connect(ui->comp_MainInfoArea->getSettingsComponent(), &CSettingsComponent::changedWindowsOpacity, this,
268  &SwiftGuiStd::onChangedWindowOpacity);
269  connect(sGui, &CGuiApplication::styleSheetsChanged, this, &SwiftGuiStd::onStyleSheetsChanged, Qt::QueuedConnection);
270 
271  // login
272  connect(ui->comp_Login, &CLoginComponent::loginOrLogoffCancelled, this, &SwiftGuiStd::setMainPageToInfoArea);
273  connect(ui->comp_Login, &CLoginComponent::loginOrLogoffSuccessful, this, &SwiftGuiStd::setMainPageToInfoArea);
274  connect(ui->comp_Login, &CLoginComponent::loginOrLogoffSuccessful, ui->comp_MainInfoArea->getFlightPlanComponent(),
275  &CFlightPlanComponent::loginDataSet);
276  connect(ui->comp_Login, &CLoginComponent::loginDataChangedDigest, ui->comp_MainInfoArea->getFlightPlanComponent(),
277  &CFlightPlanComponent::loginDataSet);
278  connect(ui->comp_Login, &CLoginComponent::requestNetworkSettings, this, &SwiftGuiStd::displayNetworkSettings);
279  connect(ui->comp_Login, &CLoginComponent::requestLoginPage, [this]() {
280  if (!sApp || sApp->isShuttingDown()) { return; }
281  ui->sw_MainMiddle->setCurrentIndex(MainPageLogin);
282  });
283  connect(this, &SwiftGuiStd::currentMainInfoAreaChanged, ui->comp_Login, &CLoginComponent::mainInfoAreaChanged,
284  Qt::QueuedConnection);
285 
286  // text messages
287  connect(ui->comp_MainInfoArea->getAtcStationComponent(), &CAtcStationComponent::requestTextMessageWidget,
288  ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::showCorrespondingTab,
289  Qt::QueuedConnection);
290  connect(ui->comp_MainInfoArea->getMappingComponent(), &CMappingComponent::requestTextMessageWidget,
291  ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::showCorrespondingTab,
292  Qt::QueuedConnection);
293  connect(ui->comp_MainInfoArea->getAircraftComponent(), &CAircraftComponent::requestTextMessageWidget,
294  ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::showCorrespondingTab,
295  Qt::QueuedConnection);
296  connect(ui->comp_MainInfoArea->getUserComponent(), &CUserComponent::requestTextMessageWidget,
297  ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::showCorrespondingTab,
298  Qt::QueuedConnection);
299 
300  // command line / text messages
301  // here we display SUP messages and such in a central window
302  ui->fr_CentralFrameInside->activateTextMessages(true);
303  connect(ui->comp_MainInfoArea->getTextMessageComponent(), &CTextMessageComponent::displayInInfoWindow, this,
304  &SwiftGuiStd::onShowOverlayVariant, Qt::QueuedConnection);
305  connect(ui->comp_MainInfoArea->getAtcStationComponent(), &CAtcStationComponent::requestTextMessageEntryTab, this,
306  &SwiftGuiStd::onShowOverlayInlineTextMessageTab, Qt::QueuedConnection);
307  connect(ui->comp_MainInfoArea->getAtcStationComponent(), &CAtcStationComponent::requestTextMessageEntryCallsign,
308  this, &SwiftGuiStd::onShowOverlayInlineTextMessageCallsign, Qt::QueuedConnection);
309  connect(ui->comp_MainInfoArea->getCockpitComponent(), &CCockpitComponent::requestTextMessageEntryTab, this,
310  &SwiftGuiStd::onShowOverlayInlineTextMessageTab, Qt::QueuedConnection);
311  connect(ui->comp_MainInfoArea->getCockpitComponent(), &CCockpitComponent::requestTextMessageEntryCallsign, this,
312  &SwiftGuiStd::onShowOverlayInlineTextMessageCallsign, Qt::QueuedConnection);
313 
314  // interpolation and validation
315  connect(ui->comp_MainInfoArea->getMappingComponent(), &CMappingComponent::requestValidationDialog, this,
316  &SwiftGuiStd::displayValidationDialog);
317 
318  // on top
319  connect(sGui, &CGuiApplication::alwaysOnTop, this, &SwiftGuiStd::onToggledWindowsOnTop, Qt::QueuedConnection);
320 
321  // main info area
322  connect(ui->comp_MainInfoArea, &CMainInfoAreaComponent::changedWholeInfoAreaFloating, this,
323  &SwiftGuiStd::onChangedMainInfoAreaFloating, Qt::QueuedConnection);
324 }
325 
326 void SwiftGuiStd::initialContextDataReads()
327 {
328  this->setContextAvailability();
329  if (!m_coreAvailable)
330  {
331  CLogMessage(this).error(u"No initial data read as network context is not available");
332  return;
333  }
334 
335  this->reloadOwnAircraft(); // init read, independent of traffic network
336  CLogMessage(this).info(u"Initial data read");
337 }
338 
339 void SwiftGuiStd::stopAllTimers(bool disconnectSignalSlots)
340 {
341  m_timerContextWatchdog.stop();
342  if (!disconnectSignalSlots) { return; }
343  this->disconnect(&m_timerContextWatchdog);
344 }
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
void currentMainInfoAreaChanged(const QWidget *currentWidget)
Main info area has changed.
swift::misc::shared_state::CDataLinkDBus * getDataLinkDBus()
Transport mechanism for sharing state between applications.
const context::IContextAudio * getIContextAudio() const
Direct access to contexts if a CCoreFacade has been initialized.
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.
void triggerNewVersionCheck(int delayedMs)
Trigger new version check.
const CStyleSheetUtility & getStyleSheetUtility() const
Style sheet handling.
void initMainApplicationWidget(QWidget *mainWidget)
Init the main application window based on information in this application.
QString styles(const QStringList &fileNames) const
Multiple styles concatenated.
Class for emitting a log message.
Definition: logmessage.h:27
Value class for matching log messages based on their categories.
Definition: logpattern.h:49
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.
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
Streamable status message, e.g.
StatusSeverity getSeverity() const
Message severity.
const CLogCategoryList & getCategories() const
Message categories.
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
GUI related classes.
Free functions in swift::misc.
QMetaObject::Connection connectOnce(T *sender, F signal, U *receiver, G &&slot, Qt::ConnectionType type=Qt::AutoConnection)
Wrapper around QObject::connect which disconnects after the signal has been emitted once.
Definition: slot.h:27
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Definition: threadutils.h:30