QGCApplication.cc 26.1 KB
Newer Older
1
 /*=====================================================================
2

Don Gagne's avatar
Don Gagne committed
3
 QGroundControl Open Source Ground Control Station
4

Don Gagne's avatar
Don Gagne committed
5
 (c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6

Don Gagne's avatar
Don Gagne committed
7
 This file is part of the QGROUNDCONTROL project
8

Don Gagne's avatar
Don Gagne committed
9 10 11 12
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
13

Don Gagne's avatar
Don Gagne committed
14 15 16 17
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
18

Don Gagne's avatar
Don Gagne committed
19 20
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
21

Don Gagne's avatar
Don Gagne committed
22
 ======================================================================*/
pixhawk's avatar
pixhawk committed
23 24 25

/**
 * @file
Don Gagne's avatar
Don Gagne committed
26
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
27 28 29 30 31 32 33 34 35 36 37 38 39
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>

dogmaphobic's avatar
dogmaphobic committed
40 41 42 43
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

44 45
#include <QDebug>

46
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
47

48
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
49
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
50
#include "GAudioOutput.h"
51
#include "CmdLineOptParser.h"
52 53
#include "UDPLink.h"
#include "LinkManager.h"
54
#include "HomePositionManager.h"
55
#include "UASMessageHandler.h"
56
#include "AutoPilotPluginManager.h"
57
#include "QGCTemporaryFile.h"
58
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
59
#include "QGCMapPalette.h"
60
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
61 62
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
63
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
64
#include "FlightModesComponentController.h"
Don Gagne's avatar
Don Gagne committed
65
#include "APMFlightModesComponentController.h"
Don Gagne's avatar
Don Gagne committed
66 67
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
Don Gagne's avatar
Don Gagne committed
68
#include "APMSensorsComponentController.h"
69
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
70
#include "RadioComponentController.h"
dogmaphobic's avatar
dogmaphobic committed
71
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
72
#include "ScreenToolsController.h"
73 74
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
75
#include "FirmwarePluginManager.h"
76
#include "MultiVehicleManager.h"
Don Gagne's avatar
Don Gagne committed
77
#include "Generic/GenericFirmwarePlugin.h"
78
#include "APM/ArduCopterFirmwarePlugin.h"
79 80
#include "APM/ArduPlaneFirmwarePlugin.h"
#include "APM/ArduRoverFirmwarePlugin.h"
81
#include "APM/APMAirframeComponentController.h"
Don Gagne's avatar
Don Gagne committed
82
#include "PX4/PX4FirmwarePlugin.h"
83
#include "Vehicle.h"
84
#include "MavlinkQmlSingleton.h"
85
#include "JoystickManager.h"
86
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
87
#include "MissionManager.h"
88 89
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
90
#include "FlightMapSettings.h"
91 92
#include "QGCQGeoCoordinate.h"
#include "CoordinateVector.h"
93
#include "MainToolBarController.h"
94
#include "MissionController.h"
Don Gagne's avatar
Don Gagne committed
95
#include "MissionCommands.h"
96 97 98
#include "FlightDisplayViewController.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
99
#include "LogDownloadController.h"
100 101 102 103 104 105

#ifndef __ios__
    #include "SerialLink.h"
#endif

#ifndef __mobile__
106 107
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
108 109
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
Don Gagne's avatar
Don Gagne committed
110
    #include "MainWindow.h"
111
#endif
112

113
#ifdef QGC_RTLAB_ENABLED
114
    #include "OpalLink.h"
115
#endif
116 117 118


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

120 121 122 123 124
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
const char* QGCApplication::_promptFlightDataSave           = "PromptFLightDataSave";
const char* QGCApplication::_promptFlightDataSaveNotArmed   = "PromptFLightDataSaveNotArmed";
const char* QGCApplication::_styleKey                       = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
125 126
const char* QGCApplication::_defaultMapPositionLatKey       = "DefaultMapPositionLat";
const char* QGCApplication::_defaultMapPositionLonKey       = "DefaultMapPositionLon";
Don Gagne's avatar
Don Gagne committed
127

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

131
// Qml Singleton factories
132

Don Gagne's avatar
Don Gagne committed
133
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
134
{
Don Gagne's avatar
Don Gagne committed
135 136
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
137 138
}

139 140 141 142 143
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

144 145
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
146 147 148 149 150
    // We create this object as a QGCTool even though it isn't int he toolbox
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
151 152
}

pixhawk's avatar
pixhawk committed
153 154 155 156 157 158 159 160 161 162
/**
 * @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
 **/

163
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
164 165 166 167
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
#else
168
    : QApplication(argc, argv)
Don Gagne's avatar
Don Gagne committed
169
#endif
170
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
171 172 173
#if defined (__mobile__)
    , _styleIsDark(false)
#else
174
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
175
#endif
dogmaphobic's avatar
dogmaphobic committed
176
    , _fakeMobile(false)
177 178 179
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
180
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
181
    , _bluetoothAvailable(false)
Don Gagne's avatar
Don Gagne committed
182
    , _defaultMapPosition(37.803784, -122.462276)
pixhawk's avatar
pixhawk committed
183
{
184 185
    Q_ASSERT(_app == NULL);
    _app = this;
186

187
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
188
#ifndef __android__
189
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
190
#endif
dogmaphobic's avatar
dogmaphobic committed
191

192
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
193

194
    bool fClearSettingsOptions = false; // Clear stored settings
195 196
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
197

198
    CmdLineOpt_t rgCmdLineOptions[] = {
199 200
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
201
        { "--fake-mobile",      &_fakeMobile,           NULL },
202
#ifdef QT_DEBUG
203
        { "--test-high-dpi",    &_testHighDPI,          NULL },
204
#endif
205 206
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
207

208
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
209

dogmaphobic's avatar
dogmaphobic committed
210
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
211
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
212
#else
213
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
214

215 216
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
217

218 219
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
220

221 222 223 224 225 226 227
        if (logList[0] == "full") {
            filterRules += "*Log.debug=true\n";
            for(int i=1; i<logList.count(); i++) {
                filterRules += logList[i];
                filterRules += ".debug=false\n";
            }
        } else {
228
            foreach(const QString &rule, logList) {
229 230 231 232
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
233 234 235
    } else {
        // First thing we want to do is set up the qtlogging.ini file. If it doesn't already exist we copy
        // it to the correct location. This way default debug builds will have logging turned off.
236

237 238 239
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
240

241 242 243 244 245 246 247 248 249
        QDir iniFileLocation(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
        if (!iniFileLocation.cd(qtProjectDir)) {
            if (!iniFileLocation.mkdir(qtProjectDir)) {
                qDebug() << "Unable to create qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);
            } else {
                if (!iniFileLocation.cd(qtProjectDir)) {
                    qDebug() << "Unable to access qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);;
                }
                loggingDirectoryOk = true;
250
            }
251
        } else {
252 253
            loggingDirectoryOk = true;
        }
254

255
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
256
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
257 258 259 260 261 262
            if (!iniFileLocation.exists(qtLoggingFile)) {
                QFile loggingFile(iniFileLocation.filePath(qtLoggingFile));
                if (loggingFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
                    QTextStream out(&loggingFile);
                    out << "[Rules]\n";
                    out << "*Log.debug=false\n";
263
                    foreach(const QString &category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
264 265 266 267
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
268
                }
269 270 271
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
272

273 274
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
275
#endif
276

277
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
278 279 280
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
281

Don Gagne's avatar
Don Gagne committed
282
    // Set application information
283 284 285 286 287 288 289 290 291
    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);
292

dogmaphobic's avatar
dogmaphobic committed
293
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
294 295 296 297 298
    // stable versions are on tags (v1.2.3)
    // development versions are full git describe versions (v1.2.3-18-g879e8b3)
    if (versionString.length() > 8) {
        versionString.append(" (Development)");
    }
Don Gagne's avatar
Don Gagne committed
299
    this->setApplicationVersion(versionString);
300

301 302
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
303
    QSettings settings;
304
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
305

306
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
307 308 309
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
310
#endif
311
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
312
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
313

Don Gagne's avatar
Don Gagne committed
314
    if (_runningUnitTests) {
315
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
316 317
        fClearSettingsOptions = true;
    }
318

319 320 321
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
322
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
323
    }
Gus Grubba's avatar
Gus Grubba committed
324

Don Gagne's avatar
Don Gagne committed
325 326 327
    _defaultMapPosition.setLatitude(settings.value(_defaultMapPositionLatKey, 37.803784).toDouble());
    _defaultMapPosition.setLongitude(settings.value(_defaultMapPositionLonKey, -122.462276).toDouble());

dogmaphobic's avatar
dogmaphobic committed
328 329 330 331 332 333 334 335 336
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

337 338
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
339 340

    _toolbox = new QGCToolbox(this);
341 342
}

Don Gagne's avatar
Don Gagne committed
343 344
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
345
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
346 347 348 349
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
350
#endif
351
    shutdownVideoStreaming();
352
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
353 354
}

355
void QGCApplication::_initCommon(void)
356 357
{
    QSettings settings;
358

Don Gagne's avatar
Don Gagne committed
359
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
360

Don Gagne's avatar
Don Gagne committed
361 362
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
363

Don Gagne's avatar
Don Gagne committed
364 365 366 367 368 369 370
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
    qmlRegisterUncreatableType<MissionCommands>     ("QGroundControl",                  1, 0, "MissionCommands",        "Reference only");
    qmlRegisterUncreatableType<QGCQGeoCoordinate>   ("QGroundControl",                  1, 0, "QGCQGeoCoordinate",      "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");

371 372 373 374 375 376 377
    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");
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",  1, 0, "JoystickManager",        "Reference only");
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",  1, 0, "Joystick",               "Reference only");
378

dogmaphobic's avatar
dogmaphobic committed
379
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
380 381
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
382
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
383
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
384
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
385 386 387
    qmlRegisterType<SensorsComponentController>         ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>           ("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>           ("QGroundControl.Controllers", 1, 0, "RadioComponentController");
dogmaphobic's avatar
dogmaphobic committed
388
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
389 390 391 392
    qmlRegisterType<ScreenToolsController>              ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    qmlRegisterType<MainToolBarController>              ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
    qmlRegisterType<FlightDisplayViewController>        ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");
393

Don Gagne's avatar
Don Gagne committed
394
#ifndef __mobile__
395 396
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
397
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
398
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
399
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
400
#endif
dogmaphobic's avatar
dogmaphobic committed
401

402
    // Register Qml Singletons
403 404 405
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
dogmaphobic's avatar
dogmaphobic committed
406

407 408 409 410 411
    // Show user an upgrade message if the settings version has been bumped up
    bool settingsUpgraded = false;
    if (settings.contains(_settingsVersionKey)) {
        if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
            settingsUpgraded = true;
412
        }
413 414 415
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
Don Gagne's avatar
Don Gagne committed
416 417
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
418
    }
419

420
    if (settingsUpgraded) {
421
        settings.clear();
422
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
423 424
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
425
    }
426

Don Gagne's avatar
Don Gagne committed
427
    settings.sync();
428 429 430 431 432
}

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

434 435
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
436

437
    // Exit main application when last window is closed
438
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
439

Don Gagne's avatar
Don Gagne committed
440 441 442 443 444 445 446
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("multiVehicleManager", toolbox()->multiVehicleManager());
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
447
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
448
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
449
    Q_CHECK_PTR(mainWindow);
450

451
    // Now that main window is up check for lost log files
452
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
453
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
454
#endif
455 456

    // Load known link configurations
457
    toolbox()->linkManager()->loadLinkConfigurationList();
458

459
    return true;
pixhawk's avatar
pixhawk committed
460 461
}

462
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
463
{
464
    return true;
pixhawk's avatar
pixhawk committed
465 466
}

Don Gagne's avatar
Don Gagne committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
void QGCApplication::deleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.setValue(_deleteAllSettingsKey, true);
}

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

bool QGCApplication::promptFlightDataSave(void)
{
    QSettings settings;
482

Don Gagne's avatar
Don Gagne committed
483 484 485
    return settings.value(_promptFlightDataSave, true).toBool();
}

486 487 488 489 490 491 492
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

    return settings.value(_promptFlightDataSaveNotArmed, false).toBool();
}

Don Gagne's avatar
Don Gagne committed
493 494 495 496 497 498
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

499 500 501 502 503 504
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
505 506 507
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
508 509 510 511
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

512 513
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
514 515
    Q_UNUSED(title);
    showMessage(msg);
516 517 518 519
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
520 521 522 523
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
524
    QGCMessageBox::warning(title, msg);
525
#endif
526 527 528 529
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
530 531 532 533
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
534
    QGCMessageBox::critical(title, msg);
535
#endif
536 537
}

538
#ifndef __mobile__
539 540
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
541 542 543 544 545 546
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
547
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
548 549
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
550

551 552 553
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
554
                // if the file cannot be removed, prompt user and ask new path
555
                saveError = true;
556
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
557 558 559
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
560
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
561 562 563
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
564 565
    QFile::remove(tempLogfile);
}
566
#endif
567 568 569 570

void QGCApplication::setStyle(bool styleIsDark)
{
    QSettings settings;
571

572 573 574 575 576 577 578 579
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
580
#ifndef __mobile__
581 582
    bool success = true;
    QString styles;
583

584 585 586 587 588 589 590 591 592
    // 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;
    }
593

594 595 596 597 598 599 600 601 602 603 604
    if (success && !_styleIsDark) {
        qDebug() << "LOADING LIGHT";
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
            qDebug() << "Unable to load slave light sheet:";
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
605

606
    setStyleSheet(styles);
607

608 609 610 611
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
612
#endif
613

614
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
615
}
Don Gagne's avatar
Don Gagne committed
616

Don Gagne's avatar
Don Gagne committed
617
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
618
{
Don Gagne's avatar
Don Gagne committed
619 620
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
621
}
622

Don Gagne's avatar
Don Gagne committed
623 624
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
625
{
Don Gagne's avatar
Don Gagne committed
626
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
627

Don Gagne's avatar
Don Gagne committed
628
    QString params;
629
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
630 631
        if (params.isEmpty()) {
            params += name;
632
        } else {
Don Gagne's avatar
Don Gagne committed
633
            params += QString(", %1").arg(name);
634 635
        }
    }
Don Gagne's avatar
Don Gagne committed
636
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
637

638
    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));
639 640
}

Don Gagne's avatar
Don Gagne committed
641 642 643 644 645
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
646 647 648 649 650 651 652 653 654 655
    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
656 657 658 659
#endif
}


660
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
661
{
662 663 664 665 666
    QObject* rootQmlObject = _rootQmlObject();

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

668 669 670 671 672 673 674 675 676
        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
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
}

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

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

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

694
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
695
{
696
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
}


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

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

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

void QGCApplication::_showSetupVehicleComponent(VehicleComponent* vehicleComponent)
{
    QVariant varReturn;
    QVariant varComponent = QVariant::fromValue(vehicleComponent);

    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupVehicleComponent", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varComponent));
Don Gagne's avatar
Don Gagne committed
721
}
Don Gagne's avatar
Don Gagne committed
722 723 724 725 726 727 728 729 730

void QGCApplication::setDefaultMapPosition(QGeoCoordinate& defaultMapPosition)
{
    QSettings settings;

    settings.setValue(_defaultMapPositionLatKey, defaultMapPosition.latitude());
    settings.setValue(_defaultMapPositionLonKey, defaultMapPosition.longitude());
    _defaultMapPosition = defaultMapPosition;
}