5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "simulatormainwindow.h"
22 #include "ui_simulatormainwindow.h"
25 #include "debugoutput.h"
26 #include "radiooutputswidget.h"
27 #include "simulatorwidget.h"
28 #include "simulatorinterface.h"
29 #include "telemetrysimu.h"
30 #include "trainersimu.h"
32 #include "joystickdialog.h"
37 #include <QMessageBox>
39 extern AppData g
; // ensure what "g" means
41 const quint16
SimulatorMainWindow::m_savedUiStateVersion
= 2;
43 SimulatorMainWindow::SimulatorMainWindow(QWidget
*parent
, const QString
& firmwareId
, quint8 flags
, Qt::WindowFlags wflags
) :
44 QMainWindow(parent
, wflags
),
45 ui(new Ui::SimulatorMainWindow
),
46 m_simulatorWidget(NULL
),
47 m_consoleWidget(NULL
),
48 m_outputsWidget(NULL
),
49 m_simulatorDockWidget(NULL
),
50 m_consoleDockWidget(NULL
),
51 m_telemetryDockWidget(NULL
),
52 m_trainerDockWidget(NULL
),
53 m_outputsDockWidget(NULL
),
54 m_simulatorId(firmwareId
),
56 m_radioProfileId(g
.sessionId()),
57 m_radioSizeConstraint(Qt::Horizontal
| Qt::Vertical
),
59 m_showRadioDocked(true),
62 if (m_simulatorId
.isEmpty()) {
63 m_simulatorId
= SimulatorLoader::findSimulatorByFirmwareName(getCurrentFirmware()->getId());
65 m_simulator
= SimulatorLoader::loadSimulator(m_simulatorId
);
67 m_exitStatusMsg
= tr("ERROR: Failed to create simulator interface, possibly missing or bad library.");
68 m_exitStatusCode
= -1;
72 m_simulator
->moveToThread(&simuThread
);
77 setCorner(Qt::TopLeftCorner
, Qt::LeftDockWidgetArea
);
78 setCorner(Qt::BottomLeftCorner
, Qt::LeftDockWidgetArea
);
79 setCorner(Qt::TopRightCorner
, Qt::RightDockWidgetArea
);
80 setCorner(Qt::BottomRightCorner
, Qt::RightDockWidgetArea
);
82 m_simulatorWidget
= new SimulatorWidget(this, m_simulator
, flags
);
83 setWindowTitle(m_simulatorWidget
->windowTitle());
85 toggleRadioDocked(true);
88 ui
->actionReloadLua
->setIcon(SimulatorIcon("reload_script"));
89 ui
->actionReloadRadioData
->setIcon(SimulatorIcon("restart"));
90 ui
->actionJoystickSettings
->setIcon(SimulatorIcon("joystick_settings"));
91 ui
->actionScreenshot
->setIcon(SimulatorIcon("camera"));
92 ui
->actionShowKeymap
->setIcon(SimulatorIcon("info"));
93 ui
->actionToggleMenuBar
->setIcon(ui
->toolBar
->toggleViewAction()->icon());
94 ui
->actionFixedRadioWidth
->setIcon(ui
->toolBar
->toggleViewAction()->icon());
95 ui
->actionFixedRadioHeight
->setIcon(ui
->toolBar
->toggleViewAction()->icon());
96 ui
->actionDockRadio
->setIcon(ui
->toolBar
->toggleViewAction()->icon());
98 ui
->toolBar
->toggleViewAction()->setShortcut(tr("Alt+T"));
99 ui
->toolBar
->setIconSize(SimulatorIcon::toolbarIconSize(g
.iconSize()));
100 ui
->toolBar
->insertSeparator(ui
->actionReloadLua
);
102 // add these to this window directly to maintain shorcuts when menubar is hidden
103 addAction(ui
->toolBar
->toggleViewAction());
104 addAction(ui
->actionToggleMenuBar
);
106 ui
->menuView
->insertSeparator(ui
->actionToggleMenuBar
);
107 ui
->menuView
->insertAction(ui
->actionToggleMenuBar
, ui
->toolBar
->toggleViewAction());
109 // Hide some actions based on simulator capabilities.
110 if(!m_simulator
->getCapability(SimulatorInterface::CAP_LUA
))
111 ui
->actionReloadLua
->setDisabled(true);
112 if(!m_simulator
->getCapability(SimulatorInterface::CAP_TELEM_FRSKY_SPORT
))
113 m_telemetryDockWidget
->toggleViewAction()->setDisabled(true);
115 ui
->actionJoystickSettings
->setDisabled(true);
120 setStyleSheet(SimulatorStyle::styleSheet());
122 connect(ui
->actionShowKeymap
, &QAction::triggered
, this, &SimulatorMainWindow::showHelp
);
123 connect(ui
->actionJoystickSettings
, &QAction::triggered
, this, &SimulatorMainWindow::openJoystickDialog
);
124 connect(ui
->actionToggleMenuBar
, &QAction::toggled
, this, &SimulatorMainWindow::showMenuBar
);
125 connect(ui
->actionFixedRadioWidth
, &QAction::toggled
, this, &SimulatorMainWindow::showRadioFixedWidth
);
126 connect(ui
->actionFixedRadioHeight
, &QAction::toggled
, this, &SimulatorMainWindow::showRadioFixedHeight
);
127 connect(ui
->actionDockRadio
, &QAction::toggled
, this, &SimulatorMainWindow::showRadioDocked
);
128 connect(ui
->actionReloadRadioData
, &QAction::triggered
, this, &SimulatorMainWindow::simulatorRestart
);
130 connect(ui
->actionReloadLua
, &QAction::triggered
, m_simulator
, &SimulatorInterface::setLuaStateReloadPermanentScripts
);
132 if (m_outputsWidget
) {
133 connect(this, &SimulatorMainWindow::simulatorStart
, m_outputsWidget
, &RadioOutputsWidget::start
);
134 connect(this, &SimulatorMainWindow::simulatorRestart
, m_outputsWidget
, &RadioOutputsWidget::restart
);
137 if (m_simulatorWidget
) {
138 connect(this, &SimulatorMainWindow::simulatorStart
, m_simulatorWidget
, &SimulatorWidget::start
);
139 connect(this, &SimulatorMainWindow::simulatorRestart
, m_simulatorWidget
, &SimulatorWidget::restart
);
140 connect(ui
->actionScreenshot
, &QAction::triggered
, m_simulatorWidget
, &SimulatorWidget::captureScreenshot
);
141 connect(m_simulatorWidget
, &SimulatorWidget::windowTitleChanged
, this, &SimulatorMainWindow::setWindowTitle
);
147 SimulatorMainWindow::~SimulatorMainWindow()
149 if (m_telemetryDockWidget
)
150 delete m_telemetryDockWidget
;
151 if (m_trainerDockWidget
)
152 delete m_trainerDockWidget
;
153 if (m_outputsDockWidget
)
154 delete m_outputsDockWidget
;
155 if (m_simulatorDockWidget
)
156 delete m_simulatorDockWidget
;
157 else if (m_simulatorWidget
)
158 delete m_simulatorWidget
;
159 if (m_consoleDockWidget
)
160 delete m_consoleDockWidget
;
169 SimulatorLoader::unloadSimulator(m_simulatorId
);
172 void SimulatorMainWindow::closeEvent(QCloseEvent
*)
177 void SimulatorMainWindow::show()
183 // for whatever reason, w/out this workaround any floating docks may appear and get "stuck" behind other windows, eg. Terminal or Companion.
190 void SimulatorMainWindow::changeEvent(QEvent
*e
)
192 QMainWindow::changeEvent(e
);
194 case QEvent::LanguageChange
:
195 ui
->retranslateUi(this);
202 QMenu
* SimulatorMainWindow::createPopupMenu()
204 QMenu
* menu
= QMainWindow::createPopupMenu();
206 menu
->addActions(ui
->menuView
->actions());
210 void SimulatorMainWindow::saveUiState()
213 QDataStream
stream(&state
, QIODevice::WriteOnly
);
214 stream
<< m_savedUiStateVersion
<< saveState(m_savedUiStateVersion
)
215 << m_showMenubar
<< m_showRadioDocked
<< m_radioSizeConstraint
;
217 SimulatorOptions opts
= g
.profile
[m_radioProfileId
].simulatorOptions();
218 opts
.windowState
= state
;
219 opts
.windowGeometry
= saveGeometry();
220 g
.profile
[m_radioProfileId
].simulatorOptions(opts
);
223 void SimulatorMainWindow::restoreUiState()
226 QByteArray windowState
;
227 QByteArray state
= g
.profile
[m_radioProfileId
].simulatorOptions().windowState
;
228 QDataStream
stream(state
);
231 if (ver
&& ver
<= m_savedUiStateVersion
) {
232 stream
>> windowState
>> m_showMenubar
>> m_showRadioDocked
;
234 stream
>> m_radioSizeConstraint
;
237 toggleRadioDocked(m_showRadioDocked
);
238 setRadioSizePolicy(m_radioSizeConstraint
);
239 toggleMenuBar(m_showMenubar
);
240 restoreGeometry(g
.profile
[m_radioProfileId
].simulatorOptions().windowGeometry
);
241 restoreState(windowState
, m_savedUiStateVersion
);
244 int SimulatorMainWindow::getExitStatus(QString
* msg
)
247 *msg
= m_exitStatusMsg
;
248 return m_exitStatusCode
;
251 bool SimulatorMainWindow::setRadioData(RadioData
* radioData
)
253 return m_simulatorWidget
->setRadioData(radioData
);
256 bool SimulatorMainWindow::useTempDataPath(bool deleteOnClose
)
258 return m_simulatorWidget
->useTempDataPath(deleteOnClose
);
261 bool SimulatorMainWindow::setOptions(SimulatorOptions
& options
, bool withSave
)
263 return m_simulatorWidget
->setOptions(options
, withSave
);
266 void SimulatorMainWindow::start()
268 emit
simulatorStart();
271 void SimulatorMainWindow::createDockWidgets()
273 if (!m_outputsDockWidget
) {
274 SimulatorIcon
icon("radio_outputs");
275 m_outputsDockWidget
= new QDockWidget(tr("Radio Outputs"), this);
276 m_outputsWidget
= new RadioOutputsWidget(m_simulator
, getCurrentFirmware(), this);
277 m_outputsDockWidget
->setWidget(m_outputsWidget
);
278 m_outputsDockWidget
->setObjectName("OUTPUTS");
279 addTool(m_outputsDockWidget
, Qt::RightDockWidgetArea
, icon
, QKeySequence(tr("F2")));
282 if (!m_telemetryDockWidget
) {
283 SimulatorIcon
icon("telemetry");
284 m_telemetryDockWidget
= new QDockWidget(tr("Telemetry Simulator"), this);
285 TelemetrySimulator
* telem
= new TelemetrySimulator(this, m_simulator
);
286 m_telemetryDockWidget
->setWidget(telem
);
287 m_telemetryDockWidget
->setObjectName("TELEMETRY_SIMULATOR");
288 addTool(m_telemetryDockWidget
, Qt::LeftDockWidgetArea
, icon
, QKeySequence(tr("F4")));
291 if (!m_trainerDockWidget
) {
292 SimulatorIcon
icon("trainer");
293 m_trainerDockWidget
= new QDockWidget(tr("Trainer Simulator"), this);
294 TrainerSimulator
* trainer
= new TrainerSimulator(this, m_simulator
);
295 m_trainerDockWidget
->setWidget(trainer
);
296 m_trainerDockWidget
->setObjectName("TRAINER_SIMULATOR");
297 addTool(m_trainerDockWidget
, Qt::TopDockWidgetArea
, icon
, QKeySequence(tr("F5")));
300 if (!m_consoleDockWidget
) {
301 SimulatorIcon
icon("console");
302 m_consoleDockWidget
= new QDockWidget(tr("Debug Output"), this);
303 m_consoleWidget
= new DebugOutput(this, m_simulator
);
304 m_consoleDockWidget
->setWidget(m_consoleWidget
);
305 m_consoleDockWidget
->setObjectName("CONSOLE");
306 addTool(m_consoleDockWidget
, Qt::RightDockWidgetArea
, icon
, QKeySequence(tr("F6")));
310 void SimulatorMainWindow::addTool(QDockWidget
* widget
, Qt::DockWidgetArea area
, QIcon icon
, QKeySequence shortcut
)
312 QAction
* tempAction
= widget
->toggleViewAction();
313 tempAction
->setIcon(icon
);
314 tempAction
->setShortcut(shortcut
);
315 ui
->menuView
->insertAction(ui
->actionToggleMenuBar
, tempAction
);
316 ui
->toolBar
->insertAction(ui
->actionReloadLua
, tempAction
);
317 widget
->setWindowIcon(icon
);
318 widget
->widget()->setWindowIcon(icon
);
319 addDockWidget(area
, widget
);
321 widget
->setFloating(true);
323 // Upon subsequent launches of application, any previously un-shown floating widgets get
324 // positioned at screen location (0,0 - frameGeometry.topLeft) which is awkward at best.
325 // This ensures newly shown floating widgets don't get stuck in top left corner.
326 connect(widget
, &QDockWidget::visibilityChanged
, [this, widget
](bool visible
) {
327 if (visible
&& widget
->isFloating() && widget
->geometry().topLeft() == QPoint(0,0)) {
328 // position top left corner in middle of this parent window.
329 QPoint
newPos(pos() + (geometry().bottomRight() - geometry().topLeft()) / 2);
330 widget
->move(newPos
);
335 void SimulatorMainWindow::showMenuBar(bool show
)
337 if (m_showMenubar
!= show
)
341 void SimulatorMainWindow::toggleMenuBar(bool show
)
343 ui
->menubar
->setVisible(show
);
344 m_showMenubar
= show
;
345 if (ui
->actionToggleMenuBar
->isChecked() != show
)
346 ui
->actionToggleMenuBar
->setChecked(show
);
349 void SimulatorMainWindow::showRadioFixedSize(Qt::Orientation orientation
, bool fixed
)
351 int fix
= m_radioSizeConstraint
;
355 fix
&= ~(orientation
);
357 if (m_radioSizeConstraint
!= fix
)
358 setRadioSizePolicy(fix
);
361 void SimulatorMainWindow::showRadioFixedWidth(bool fixed
)
363 showRadioFixedSize(Qt::Horizontal
, fixed
);
366 void SimulatorMainWindow::showRadioFixedHeight(bool fixed
)
368 showRadioFixedSize(Qt::Vertical
, fixed
);
371 void SimulatorMainWindow::setRadioSizePolicy(int fixType
)
374 sp
.setHorizontalPolicy((fixType
& Qt::Horizontal
) ? QSizePolicy::Maximum
: QSizePolicy::Preferred
);
375 sp
.setVerticalPolicy((fixType
& Qt::Vertical
) ? QSizePolicy::Maximum
: QSizePolicy::Preferred
);
376 m_simulatorWidget
->setSizePolicy(sp
);
378 m_radioSizeConstraint
= fixType
;
380 if (ui
->actionFixedRadioWidth
->isChecked() != bool(fixType
& Qt::Horizontal
))
381 ui
->actionFixedRadioWidth
->setChecked((fixType
& Qt::Horizontal
));
382 if (ui
->actionFixedRadioHeight
->isChecked() != bool(fixType
& Qt::Vertical
))
383 ui
->actionFixedRadioHeight
->setChecked((fixType
& Qt::Vertical
));
386 void SimulatorMainWindow::showRadioDocked(bool dock
)
388 if (m_showRadioDocked
!= dock
)
389 toggleRadioDocked(dock
);
392 void SimulatorMainWindow::toggleRadioDocked(bool dock
)
394 if (!m_simulatorWidget
)
398 if (m_simulatorDockWidget
) {
399 m_simulatorDockWidget
->setWidget(0);
400 m_simulatorDockWidget
->deleteLater();
401 m_simulatorDockWidget
= NULL
;
404 QWidget
* w
= takeCentralWidget();
405 if (w
&& w
!= m_simulatorWidget
)
407 setCentralWidget(m_simulatorWidget
);
408 setRadioSizePolicy(m_radioSizeConstraint
);
409 ui
->actionFixedRadioWidth
->setEnabled(true);
410 ui
->actionFixedRadioHeight
->setEnabled(true);
411 m_simulatorWidget
->show();
415 if (m_simulatorDockWidget
) {
416 m_simulatorDockWidget
->deleteLater();
417 m_simulatorDockWidget
= NULL
;
421 QLabel
* dummy
= new QLabel("");
422 dummy
->setFixedSize(0, 0);
423 dummy
->setEnabled(false);
424 setCentralWidget(dummy
);
426 m_simulatorDockWidget
= new QDockWidget(m_simulatorWidget
->windowTitle(), this);
427 m_simulatorDockWidget
->setObjectName("RADIO_SIMULATOR");
428 m_simulatorDockWidget
->setWidget(m_simulatorWidget
);
429 m_simulatorDockWidget
->setFeatures(QDockWidget::DockWidgetFloatable
);
430 m_simulatorDockWidget
->setAllowedAreas(Qt::BottomDockWidgetArea
);
431 addDockWidget(Qt::BottomDockWidgetArea
, m_simulatorDockWidget
);
432 m_simulatorDockWidget
->setFloating(true);
433 m_simulatorWidget
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::Preferred
);
434 ui
->actionFixedRadioWidth
->setDisabled(true);
435 ui
->actionFixedRadioHeight
->setDisabled(true);
436 restoreDockWidget(m_simulatorDockWidget
);
437 if (!m_simulatorDockWidget
->isVisible())
438 m_simulatorDockWidget
->show();
439 if (m_simulatorDockWidget
->geometry().topLeft() == QPoint(0,0) && this->isVisible()) {
440 // default position top left corner in middle of this parent window.
441 QPoint
newPos(pos() + (geometry().bottomRight() - geometry().topLeft()) / 2);
442 m_simulatorDockWidget
->move(newPos
);
445 connect(m_simulatorWidget
, &SimulatorWidget::windowTitleChanged
, m_simulatorDockWidget
, &QDockWidget::setWindowTitle
);
446 connect(m_simulatorDockWidget
, &QDockWidget::topLevelChanged
, [this](bool top
) {
447 showRadioDocked(!top
);
451 m_showRadioDocked
= dock
;
453 if (ui
->actionDockRadio
->isChecked() != dock
)
454 ui
->actionDockRadio
->setChecked(dock
);
458 void SimulatorMainWindow::openJoystickDialog(bool)
461 joystickDialog
* jd
= new joystickDialog(this);
462 if (jd
->exec() == QDialog::Accepted
&& m_simulatorWidget
)
463 m_simulatorWidget
->setupJoysticks();
468 void SimulatorMainWindow::showHelp(bool show
)
470 QString helpText
= ""
472 " td { text-align: center; vertical-align: middle; font-size: large; padding: 0 1em; white-space: nowrap; }"
473 " th { background-color: palette(alternate-base); }"
474 " img { vertical-align: text-top; }"
476 helpText
+= tr("<b>Simulator Controls:</b>");
477 helpText
+= "<table cellspacing=4 cellpadding=0>";
478 helpText
+= tr("<tr><th>Key/Mouse</th><th>Action</th></tr>", "note: must match html layout of each table row (keyTemplate).");
480 QString keyTemplate
= tr("<tr><td><kbd>%1</kbd></td><td>%2</td></tr>", "note: must match html layout of help text table header.");
482 // Add our own help text (if any)
483 foreach (pair
, m_keymapHelp
)
484 helpText
+= keyTemplate
.arg(pair
.first
, pair
.second
);
485 // Add any radio-specific help text from simulator widget
486 foreach (pair
, m_simulatorWidget
->getKeymapHelp())
487 helpText
+= keyTemplate
.arg(pair
.first
, pair
.second
);
489 helpText
+= "</table>";
491 QMessageBox
* msgBox
= new QMessageBox(this);
492 msgBox
->setObjectName("SimulatorHelpText");
493 msgBox
->setAttribute(Qt::WA_DeleteOnClose
);
494 msgBox
->setWindowFlags(msgBox
->windowFlags() | Qt::WindowStaysOnTopHint
);
495 msgBox
->setStandardButtons( QMessageBox::Ok
);
496 msgBox
->setWindowTitle(tr("Simulator Help"));
497 msgBox
->setTextFormat(Qt::RichText
);
498 msgBox
->setText(helpText
);
499 msgBox
->setModal(false);