QGCApplication.cc 24.3 KB
Newer Older
1
/****************************************************************************
2 3 4 5 6 7 8
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
9

pixhawk's avatar
pixhawk committed
10 11 12

/**
 * @file
Don Gagne's avatar
Don Gagne committed
13
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
14 15 16 17 18 19 20 21 22 23 24 25
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>
26
#include <QStringListModel>
pixhawk's avatar
pixhawk committed
27

dogmaphobic's avatar
dogmaphobic committed
28 29 30 31
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

32 33
#include <QDebug>

34
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
35

36
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
37
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
38
#include "GAudioOutput.h"
39
#include "CmdLineOptParser.h"
40 41
#include "UDPLink.h"
#include "LinkManager.h"
42
#include "UASMessageHandler.h"
43
#include "QGCTemporaryFile.h"
44
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
45
#include "QGCMapPalette.h"
46
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
47 48
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
49
#include "CustomCommandWidgetController.h"
dogmaphobic's avatar
dogmaphobic committed
50
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
51
#include "ScreenToolsController.h"
Don Gagne's avatar
Don Gagne committed
52
#include "QGCMobileFileDialogController.h"
Don Gagne's avatar
Don Gagne committed
53
#include "RCChannelMonitorController.h"
54 55
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
56
#include "FirmwarePluginManager.h"
57 58
#include "MultiVehicleManager.h"
#include "Vehicle.h"
59
#include "MavlinkQmlSingleton.h"
60
#include "JoystickConfigController.h"
61
#include "JoystickManager.h"
62
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
63
#include "MissionManager.h"
64
#include "QGroundControlQmlGlobal.h"
65
#include "FlightMapSettings.h"
66
#include "CoordinateVector.h"
67
#include "MissionController.h"
68 69
#include "GeoFenceController.h"
#include "RallyPointController.h"
70
#include "VideoManager.h"
71 72
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
73
#include "LogDownloadController.h"
74
#include "ValuesWidgetController.h"
75
#include "AppMessages.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
76 77 78
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
79
#include "MissionCommandTree.h"
80
#include "QGCMapPolygon.h"
81
#include "ParameterManager.h"
82
#include "SettingsManager.h"
83

Gus Grubba's avatar
Gus Grubba committed
84
#ifndef NO_SERIAL_LINK
85
#include "SerialLink.h"
86 87 88
#endif

#ifndef __mobile__
89 90 91 92 93
#include "QGCFileDialog.h"
#include "QGCMessageBox.h"
#include "FirmwareUpgradeController.h"
#include "MainWindow.h"
#include "GeoTagController.h"
94
#endif
95

96
#ifdef QGC_RTLAB_ENABLED
97
#include "OpalLink.h"
98
#endif
99

Don Gagne's avatar
Don Gagne committed
100 101 102 103 104 105
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
106

dogmaphobic's avatar
dogmaphobic committed
107 108
#include "QGCMapEngine.h"

109
QGCApplication* QGCApplication::_app = NULL;
pixhawk's avatar
pixhawk committed
110

Don Gagne's avatar
Don Gagne committed
111 112
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
113
const char* QGCApplication::fenceFileExtension =        "fence";
114
const char* QGCApplication::rallyPointFileExtension =   "rally";
Don Gagne's avatar
Don Gagne committed
115 116
const char* QGCApplication::telemetryFileExtension =     "tlog";

117 118
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
Don Gagne's avatar
Don Gagne committed
119

dogmaphobic's avatar
dogmaphobic committed
120 121
const char* QGCApplication::_darkStyleFile          = ":/res/styles/style-dark.css";
const char* QGCApplication::_lightStyleFile         = ":/res/styles/style-light.css";
122

123 124 125
// Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];

126
// Qml Singleton factories
127

Don Gagne's avatar
Don Gagne committed
128
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
129
{
Don Gagne's avatar
Don Gagne committed
130 131
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
132 133
}

134 135 136 137 138
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

139 140
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
141
    // We create this object as a QGCTool even though it isn't in the toolbox
142 143 144 145
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
146 147
}

pixhawk's avatar
pixhawk committed
148 149 150 151 152 153 154 155 156 157
/**
 * @brief Constructor for the main application.
 *
 * This constructor initializes and starts the whole application. It takes standard
 * command-line parameters
 *
 * @param argc The number of command-line parameters
 * @param argv The string array of parameters
 **/

158
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
159 160 161
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
162
    #else
163
    : QApplication(argc, argv)
164
    #endif
165
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
166
    , _fakeMobile(false)
167
    , _settingsUpgraded(false)
168
    #ifdef QT_DEBUG
169
    , _testHighDPI(false)
170
    #endif
171
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
172
    , _bluetoothAvailable(false)
pixhawk's avatar
pixhawk committed
173
{
174 175
    Q_ASSERT(_app == NULL);
    _app = this;
176

177
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
178
#ifndef __android__
179
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
180
#endif
dogmaphobic's avatar
dogmaphobic committed
181

182 183 184
    // Setup for network proxy support
    QNetworkProxyFactory::setUseSystemConfiguration(true);

185 186
#ifdef Q_OS_LINUX
#ifndef __mobile__
187 188 189
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
190 191 192
            msgBox.setInformativeText(tr("You are running %1 as root. "
                                      "You should not do this since it will cause other issues with %1. "
                                      "%1 will now exit. "
193 194
                                      "If you are having serial port issues on Ubuntu, execute the following commands to fix most issues:\n"
                                      "sudo usermod -a -G dialout $USER\n"
195
                                      "sudo apt-get remove modemmanager").arg(qgcApp()->applicationName()));
196 197 198 199 200
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.exec();
            _exit(0);
        }
201

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
        // Determine if we have the correct permissions to access USB serial devices
        QFile permFile("/etc/group");
        if(permFile.open(QIODevice::ReadOnly)) {
            while(!permFile.atEnd()) {
                QString line = permFile.readLine();
                if (line.contains("dialout") && !line.contains(getenv("USER"))) {
                    QMessageBox msgBox;
                    msgBox.setInformativeText("The current user does not have the correct permissions to access serial devices. "
                                              "You should also remove modemmanager since it also interferes. "
                                              "If you are using Ubuntu, execute the following commands to fix these issues:\n"
                                              "sudo usermod -a -G dialout $USER\n"
                                              "sudo apt-get remove modemmanager");
                    msgBox.setStandardButtons(QMessageBox::Ok);
                    msgBox.setDefaultButton(QMessageBox::Ok);
                    msgBox.exec();
                    break;
                }
219
            }
220
            permFile.close();
221 222 223 224 225
        }
    }
#endif
#endif

226
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
227

228
    bool fClearSettingsOptions = false; // Clear stored settings
229 230
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
231

232
    CmdLineOpt_t rgCmdLineOptions[] = {
233 234
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
235
        { "--fake-mobile",      &_fakeMobile,           NULL },
236
    #ifdef QT_DEBUG
237
        { "--test-high-dpi",    &_testHighDPI,          NULL },
238
    #endif
239 240
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
241

242
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
243

244
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
245 246 247
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
248

Don Gagne's avatar
Don Gagne committed
249
    // Set application information
250 251 252 253 254 255 256 257 258
    if (_runningUnitTests) {
        // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
        // name. Also we want to run unit tests with clean settings every time.
        setApplicationName(QString("%1_unittest").arg(QGC_APPLICATION_NAME));
    } else {
        setApplicationName(QGC_APPLICATION_NAME);
    }
    setOrganizationName(QGC_ORG_NAME);
    setOrganizationDomain(QGC_ORG_DOMAIN);
259

Daniel Agar's avatar
Daniel Agar committed
260
    this->setApplicationVersion(QString(GIT_VERSION));
261

262 263
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
264
    QSettings settings;
265
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
266

267
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
268 269 270
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
271
#endif
272
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
273
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
274

Don Gagne's avatar
Don Gagne committed
275
    if (_runningUnitTests) {
276
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
277 278
        fClearSettingsOptions = true;
    }
279

280 281 282
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
283

284
        // Clear parameter cache
285
        QDir paramDir(ParameterManager::parameterCacheDir());
286 287
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
288
    } else {
289
        // Determine if upgrade message for settings version bump is required. Check and clear must happen before toolbox is started since
290 291 292
        // that will write some settings.
        if (settings.contains(_settingsVersionKey)) {
            if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
293
                settings.clear();
294 295 296 297
                _settingsUpgraded = true;
            }
        } else if (settings.allKeys().count()) {
            // Settings version key is missing and there are settings. This is an upgrade scenario.
298
            settings.clear();
299 300
            _settingsUpgraded = true;
        }
301
    }
302
    settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
Gus Grubba's avatar
Gus Grubba committed
303

304 305 306
    // Set up our logging filters
    QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);

dogmaphobic's avatar
dogmaphobic committed
307 308 309 310 311 312 313 314 315
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

316 317
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
318 319

    _toolbox = new QGCToolbox(this);
320
    _toolbox->setChildToolboxes();
321 322
}

Don Gagne's avatar
Don Gagne committed
323
void QGCApplication::_shutdown(void)
Don Gagne's avatar
Don Gagne committed
324
{
Don Gagne's avatar
Don Gagne committed
325 326 327 328
    // This code is specifically not in the destructor since the application object may not be available in the destructor.
    // This cause problems for deleting object like settings which are in the toolbox which may have qml references. By
    // moving them here and having main.cc call this prior to deleting the app object we make sure app object is still
    // around while these things are shutting down.
Don Gagne's avatar
Don Gagne committed
329
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
330 331 332 333
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
334
#endif
335
    shutdownVideoStreaming();
336
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
337 338
}

Don Gagne's avatar
Don Gagne committed
339 340 341 342 343
QGCApplication::~QGCApplication()
{
    // Place shutdown code in _shutdown
}

344
void QGCApplication::_initCommon(void)
345 346
{
    QSettings settings;
347

Don Gagne's avatar
Don Gagne committed
348
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
349

Don Gagne's avatar
Don Gagne committed
350 351
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
352

Don Gagne's avatar
Don Gagne committed
353 354 355 356
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                  1, 0, "QmlObjectListModel",     "Reference only");
    qmlRegisterUncreatableType<VideoReceiver>       ("QGroundControl",                  1, 0, "VideoReceiver",          "Reference only");
    qmlRegisterUncreatableType<VideoSurface>        ("QGroundControl",                  1, 0, "VideoSurface",           "Reference only");
357
    qmlRegisterUncreatableType<MissionCommandTree>  ("QGroundControl",                  1, 0, "MissionCommandTree",     "Reference only");
Don Gagne's avatar
Don Gagne committed
358

359 360 361 362 363
    qmlRegisterUncreatableType<AutoPilotPlugin>     ("QGroundControl.AutoPilotPlugin",      1, 0, "AutoPilotPlugin",        "Reference only");
    qmlRegisterUncreatableType<VehicleComponent>    ("QGroundControl.AutoPilotPlugin",      1, 0, "VehicleComponent",       "Reference only");
    qmlRegisterUncreatableType<Vehicle>             ("QGroundControl.Vehicle",              1, 0, "Vehicle",                "Reference only");
    qmlRegisterUncreatableType<MissionItem>         ("QGroundControl.Vehicle",              1, 0, "MissionItem",            "Reference only");
    qmlRegisterUncreatableType<MissionManager>      ("QGroundControl.Vehicle",              1, 0, "MissionManager",         "Reference only");
364
    qmlRegisterUncreatableType<ParameterManager>    ("QGroundControl.Vehicle",              1, 0, "ParameterManager",       "Reference only");
365 366 367
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",      1, 0, "JoystickManager",        "Reference only");
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",      1, 0, "Joystick",               "Reference only");
    qmlRegisterUncreatableType<QGCPositionManager>  ("QGroundControl.QGCPositionManager",   1, 0, "QGCPositionManager",     "Reference only");
368
    qmlRegisterUncreatableType<QGCMapPolygon>       ("QGroundControl.FlightMap",            1, 0, "QGCMapPolygon",          "Reference only");
369

dogmaphobic's avatar
dogmaphobic committed
370
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
dogmaphobic's avatar
dogmaphobic committed
371
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
372 373
    qmlRegisterType<ScreenToolsController>              ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
374 375
    qmlRegisterType<GeoFenceController>                 ("QGroundControl.Controllers", 1, 0, "GeoFenceController");
    qmlRegisterType<RallyPointController>               ("QGroundControl.Controllers", 1, 0, "RallyPointController");
376
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
377
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
Don Gagne's avatar
Don Gagne committed
378
    qmlRegisterType<RCChannelMonitorController>         ("QGroundControl.Controllers", 1, 0, "RCChannelMonitorController");
379
    qmlRegisterType<JoystickConfigController>           ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
380
    qmlRegisterType<LogDownloadController>              ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
381
#ifndef __mobile__
382 383
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
384
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
Don Gagne's avatar
Don Gagne committed
385
    qmlRegisterType<GeoTagController>               ("QGroundControl.Controllers", 1, 0, "GeoTagController");
Don Gagne's avatar
Don Gagne committed
386
#endif
dogmaphobic's avatar
dogmaphobic committed
387

388
    // Register Qml Singletons
389 390 391
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
392 393 394 395 396
}

bool QGCApplication::_initForNormalAppBoot(void)
{
    QSettings settings;
397

398
    _loadCurrentStyleSheet();
Lorenz Meier's avatar
Lorenz Meier committed
399

400
    // Exit main application when last window is closed
401
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
402

Don Gagne's avatar
Don Gagne committed
403 404 405 406
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
407
    _qmlAppEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel());
Don Gagne's avatar
Don Gagne committed
408 409
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
410
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
411
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
412
    Q_CHECK_PTR(mainWindow);
413

414
    // Now that main window is up check for lost log files
415
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
416
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
417
#endif
418 419

    // Load known link configurations
420
    toolbox()->linkManager()->loadLinkConfigurationList();
421

Jacob Walser's avatar
Jacob Walser committed
422 423
    // Probe for joysticks
    toolbox()->joystickManager()->init();
424

425
    if (_settingsUpgraded) {
Don Gagne's avatar
Don Gagne committed
426 427 428 429
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
    }

430 431 432
    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

dogmaphobic's avatar
dogmaphobic committed
433 434 435 436 437
    if (getQGCMapEngine()->wasCacheReset()) {
        showMessage("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset.");
    }

Don Gagne's avatar
Don Gagne committed
438
    settings.sync();
439
    return true;
pixhawk's avatar
pixhawk committed
440 441
}

442
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
443
{
444
    return true;
pixhawk's avatar
pixhawk committed
445 446
}

Don Gagne's avatar
Don Gagne committed
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
void QGCApplication::deleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.setValue(_deleteAllSettingsKey, true);
}

void QGCApplication::clearDeleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.remove(_deleteAllSettingsKey);
}

/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
462 463 464 465
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

466 467
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
468 469
    Q_UNUSED(title);
    showMessage(msg);
470 471 472 473
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
474 475 476 477
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
478
    QGCMessageBox::warning(title, msg);
479
#endif
480 481 482 483
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
484 485 486 487
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
488
    QGCMessageBox::critical(title, msg);
489
#endif
490 491
}

492
#ifndef __mobile__
493
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
494
{
495 496 497 498 499 500
    // The vehicle is gone now and we are shutting down so we need to use a message box for errors to hold shutdown and show the error
    if (_checkTelemetrySavePath(true /* useMessageBox */)) {

        QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath()->rawValue().toString();
        QDir saveDir(saveDirPath);

501 502
        QString nameFormat("%1%2.tlog");
        QString dtFormat("yyyy-MM-dd hh-mm-ss");
503 504 505 506 507 508 509 510 511 512 513

        int tryIndex = 1;
        QString saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat)).arg("");
        while (saveDir.exists(saveFileName)) {
            saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat)).arg(QStringLiteral(".%1").arg(tryIndex++));
        }
        QString saveFilePath = saveDir.absoluteFilePath(saveFileName);

        QFile tempFile(tempLogfile);
        if (!tempFile.copy(saveFilePath)) {
            QGCMessageBox::warning(tr("Telemetry save error"), tr("Unable to save telemetry log. Error copying telemetry to '%1': '%2'.").arg(saveFilePath).arg(tempFile.errorString()));
514
        }
515 516
    }

517 518
    QFile::remove(tempLogfile);
}
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

void QGCApplication::checkTelemetrySavePathOnMainThread(void)
{
    // This is called with an active vehicle so don't pop message boxes which holds ui thread
    _checkTelemetrySavePath(false /* useMessageBox */);
}

bool QGCApplication::_checkTelemetrySavePath(bool useMessageBox)
{
    QString errorTitle = tr("Telemetry save error");

    QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath()->rawValue().toString();
    if (saveDirPath.isEmpty()) {
        QString error = tr("Unable to save telemetry log. Telemetry save directory is not set.");
        if (useMessageBox) {
            QGCMessageBox::warning(errorTitle, error);
        } else {
            showMessage(error);
        }
        return false;
    }

    QDir saveDir(saveDirPath);
    if (!saveDir.exists()) {
        QString error = tr("Unable to save telemetry log. Telemetry save directory \"%1\" does not exist.").arg(saveDirPath);
        if (useMessageBox) {
            QGCMessageBox::warning(errorTitle, error);
        } else {
            showMessage(error);
        }
        return false;
    }

    return true;
}
554
#endif
555

556
void QGCApplication::_loadCurrentStyleSheet(void)
557
{
Don Gagne's avatar
Don Gagne committed
558
#ifndef __mobile__
559 560
    bool success = true;
    QString styles;
561

562 563 564 565 566 567 568 569 570
    // The dark style sheet is the master. Any other selected style sheet just overrides
    // the colors of the master sheet.
    QFile masterStyleSheet(_darkStyleFile);
    if (masterStyleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
        styles = masterStyleSheet.readAll();
    } else {
        qDebug() << "Unable to load master dark style sheet";
        success = false;
    }
571

572
    if (success && !_toolbox->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()) {
573 574 575 576 577
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
578
            qWarning() << "Unable to load slave light sheet:";
579 580 581
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
582

583
    setStyleSheet(styles);
584

585 586 587 588
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
589
#endif
590
}
Don Gagne's avatar
Don Gagne committed
591

Don Gagne's avatar
Don Gagne committed
592
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
593
{
Don Gagne's avatar
Don Gagne committed
594 595
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
596
}
597

Don Gagne's avatar
Don Gagne committed
598 599
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
600
{
Don Gagne's avatar
Don Gagne committed
601
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
602

Don Gagne's avatar
Don Gagne committed
603
    QString params;
604
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
605 606
        if (params.isEmpty()) {
            params += name;
607
        } else {
Don Gagne's avatar
Don Gagne committed
608
            params += QString(", %1").arg(name);
609 610
        }
    }
Don Gagne's avatar
Don Gagne committed
611
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
612

613
    showMessage(QString("Parameters missing from firmware: %1. You may be running an older version of firmware QGC does not work correctly with or your firmware has a bug in it.").arg(params));
614 615
}

Gus Grubba's avatar
Gus Grubba committed
616
QObject* QGCApplication::_rootQmlObject()
Don Gagne's avatar
Don Gagne committed
617 618 619 620
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
621 622 623 624 625 626 627 628 629 630
    MainWindow * mainWindow = MainWindow::instance();
    if (mainWindow) {
        return mainWindow->rootQmlObject();
    } else if (runningUnitTests()){
        // Unit test can run without a main window
        return NULL;
    } else {
        qWarning() << "Why is MainWindow missing?";
        return NULL;
    }
Don Gagne's avatar
Don Gagne committed
631 632 633 634
#endif
}


635
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
636
{
Don Gagne's avatar
Don Gagne committed
637 638 639 640 641
    // Special case hack for ArduPilot prearm messages. These show up in the center of the map, so no need for popup.
    if (message.contains("PreArm:")) {
        return;
    }

642 643 644 645 646
    QObject* rootQmlObject = _rootQmlObject();

    if (rootQmlObject) {
        QVariant varReturn;
        QVariant varMessage = QVariant::fromValue(message);
Don Gagne's avatar
Don Gagne committed
647

648 649 650 651 652 653 654 655 656
        QMetaObject::invokeMethod(_rootQmlObject(), "showMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
#ifndef __mobile__
    } else if (runningUnitTests()){
        // Unit test can run without a main window which will lead to no root qml object. Use QGCMessageBox instead
        QGCMessageBox::information("Unit Test", message);
#endif
    } else {
        qWarning() << "Internal error";
    }
Don Gagne's avatar
Don Gagne committed
657 658 659 660 661 662 663
}

void QGCApplication::showSetupView(void)
{
    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
}

664
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
665
{
666
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
667
}