QGCApplication.cc 27.7 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"
Don Gagne's avatar
Don Gagne committed
71
#include "ScreenToolsController.h"
72 73
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
74
#include "FirmwarePluginManager.h"
75
#include "MultiVehicleManager.h"
Don Gagne's avatar
Don Gagne committed
76
#include "Generic/GenericFirmwarePlugin.h"
77
#include "APM/ArduCopterFirmwarePlugin.h"
78 79
#include "APM/ArduPlaneFirmwarePlugin.h"
#include "APM/ArduRoverFirmwarePlugin.h"
80
#include "APM/APMAirframeComponentController.h"
Don Gagne's avatar
Don Gagne committed
81
#include "PX4/PX4FirmwarePlugin.h"
82
#include "Vehicle.h"
83
#include "MavlinkQmlSingleton.h"
84
#include "JoystickManager.h"
85
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
86
#include "MissionManager.h"
87 88
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
89
#include "FlightMapSettings.h"
90 91
#include "QGCQGeoCoordinate.h"
#include "CoordinateVector.h"
92
#include "MainToolBarController.h"
93
#include "MissionController.h"
Don Gagne's avatar
Don Gagne committed
94
#include "MissionCommands.h"
95 96 97
#include "FlightDisplayViewController.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
98
#include "LogDownloadController.h"
99 100 101 102 103 104

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

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

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


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

119 120 121 122 123
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
124 125
const char* QGCApplication::_defaultMapPositionLatKey       = "DefaultMapPositionLat";
const char* QGCApplication::_defaultMapPositionLonKey       = "DefaultMapPositionLon";
Don Gagne's avatar
Don Gagne committed
126

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

130
// Qml Singleton factories
131

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

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

143 144
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
145 146 147 148 149
    // 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;
150 151
}

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

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

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

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
#ifdef Q_OS_LINUX
#ifndef __mobile__
    if (getuid() == 0) {
        QMessageBox msgBox;
        msgBox.setInformativeText("You are runnning QGroundControl as root. "
                                  "You should not do this since it will cause other issues with QGroundControl. "
                                  "QGroundControl will now exit. "
                                  "If you are having serial port issues, execute the following commands to fix most serial port issues:\n"
                                  "sudo usermod -a -G dialout $USER\n"
                                  "sudo apt-get remove modemmanager");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
        _exit(0);
    }

    // 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. "
                                          "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;
            }
        }
        permFile.close();
    }
#endif
#endif

230
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
231

232
    bool fClearSettingsOptions = false; // Clear stored settings
233 234
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
235

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

246
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
247

dogmaphobic's avatar
dogmaphobic committed
248
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
249
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
250
#else
251
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
252

253 254
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
255

256 257
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
258

259 260 261 262 263 264 265
        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 {
266
            foreach(const QString &rule, logList) {
267 268 269 270
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
271 272 273
    } 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.
274

275 276 277
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
278

279 280 281 282 283 284 285 286 287
        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;
288
            }
289
        } else {
290 291
            loggingDirectoryOk = true;
        }
292

293
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
294
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
295 296 297 298 299 300
            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";
301
                    foreach(const QString &category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
302 303 304 305
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
306
                }
307 308 309
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
310

311 312
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
313
#endif
314

315
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
316 317 318
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
319

Don Gagne's avatar
Don Gagne committed
320
    // Set application information
321 322 323 324 325 326 327 328 329
    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);
330

dogmaphobic's avatar
dogmaphobic committed
331
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
332 333 334 335 336
    // 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
337
    this->setApplicationVersion(versionString);
338

339 340
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
341
    QSettings settings;
342
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
343

344
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
345 346 347
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
348
#endif
349
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
350
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
351

Don Gagne's avatar
Don Gagne committed
352
    if (_runningUnitTests) {
353
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
354 355
        fClearSettingsOptions = true;
    }
356

357 358 359
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
360
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
361
    }
Gus Grubba's avatar
Gus Grubba committed
362

Don Gagne's avatar
Don Gagne committed
363 364 365
    _defaultMapPosition.setLatitude(settings.value(_defaultMapPositionLatKey, 37.803784).toDouble());
    _defaultMapPosition.setLongitude(settings.value(_defaultMapPositionLonKey, -122.462276).toDouble());

dogmaphobic's avatar
dogmaphobic committed
366 367 368 369 370 371 372 373 374
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

375 376
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
377 378

    _toolbox = new QGCToolbox(this);
379 380
}

Don Gagne's avatar
Don Gagne committed
381 382
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
383
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
384 385 386 387
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
388
#endif
389
    shutdownVideoStreaming();
390
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
391 392
}

393
void QGCApplication::_initCommon(void)
394 395
{
    QSettings settings;
396

Don Gagne's avatar
Don Gagne committed
397
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
398

Don Gagne's avatar
Don Gagne committed
399 400
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
401

Don Gagne's avatar
Don Gagne committed
402 403 404 405 406 407 408
    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");

409 410 411 412 413 414 415
    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");
416

dogmaphobic's avatar
dogmaphobic committed
417
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
418 419
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
420
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
421
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
422
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
423 424 425 426 427 428 429
    qmlRegisterType<SensorsComponentController>         ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>           ("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>           ("QGroundControl.Controllers", 1, 0, "RadioComponentController");
    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");
430

Don Gagne's avatar
Don Gagne committed
431
#ifndef __mobile__
432 433
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
434
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
435
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
436
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
437
#endif
dogmaphobic's avatar
dogmaphobic committed
438

439
    // Register Qml Singletons
440 441 442
    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
443

444 445 446 447 448
    // 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;
449
        }
450 451 452
    } 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
453 454
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
455
    }
456

457
    if (settingsUpgraded) {
458
        settings.clear();
459
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
460 461
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
462
    }
463

Don Gagne's avatar
Don Gagne committed
464
    settings.sync();
465 466 467 468 469
}

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

471 472
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
473

474
    // Exit main application when last window is closed
475
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
476

Don Gagne's avatar
Don Gagne committed
477 478 479 480 481 482 483
#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
484
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
485
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
486
    Q_CHECK_PTR(mainWindow);
487

488
    // Now that main window is up check for lost log files
489
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
490
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
491
#endif
492 493

    // Load known link configurations
494
    toolbox()->linkManager()->loadLinkConfigurationList();
495

496
    return true;
pixhawk's avatar
pixhawk committed
497 498
}

499
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
500
{
501
    return true;
pixhawk's avatar
pixhawk committed
502 503
}

Don Gagne's avatar
Don Gagne committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
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;
519

Don Gagne's avatar
Don Gagne committed
520 521 522
    return settings.value(_promptFlightDataSave, true).toBool();
}

523 524 525 526 527 528 529
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
530 531 532 533 534 535
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

536 537 538 539 540 541
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
542 543 544
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
545 546 547 548
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

549 550
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
551 552
    Q_UNUSED(title);
    showMessage(msg);
553 554 555 556
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
557 558 559 560
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
561
    QGCMessageBox::warning(title, msg);
562
#endif
563 564 565 566
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
567 568 569 570
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
571
    QGCMessageBox::critical(title, msg);
572
#endif
573 574
}

575
#ifndef __mobile__
576 577
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
578 579 580 581 582 583
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
584
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
585 586
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
587

588 589 590
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
591
                // if the file cannot be removed, prompt user and ask new path
592
                saveError = true;
593
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
594 595 596
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
597
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
598 599 600
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
601 602
    QFile::remove(tempLogfile);
}
603
#endif
604 605 606 607

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

609 610 611 612 613 614 615 616
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
617
#ifndef __mobile__
618 619
    bool success = true;
    QString styles;
620

621 622 623 624 625 626 627 628 629
    // 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;
    }
630

631 632 633 634 635 636 637 638 639 640 641
    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
642

643
    setStyleSheet(styles);
644

645 646 647 648
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
649
#endif
650

651
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
652
}
Don Gagne's avatar
Don Gagne committed
653

Don Gagne's avatar
Don Gagne committed
654
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
655
{
Don Gagne's avatar
Don Gagne committed
656 657
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
658
}
659

Don Gagne's avatar
Don Gagne committed
660 661
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
662
{
Don Gagne's avatar
Don Gagne committed
663
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
664

Don Gagne's avatar
Don Gagne committed
665
    QString params;
666
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
667 668
        if (params.isEmpty()) {
            params += name;
669
        } else {
Don Gagne's avatar
Don Gagne committed
670
            params += QString(", %1").arg(name);
671 672
        }
    }
Don Gagne's avatar
Don Gagne committed
673
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
674

Don Gagne's avatar
Don Gagne committed
675
    showMessage(QString("Parameters missing from firmware: %1.\n\nYou should quit QGroundControl immediately and update your firmware.").arg(params));
676 677
}

Don Gagne's avatar
Don Gagne committed
678 679 680 681 682
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
683 684 685 686 687 688 689 690 691 692
    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
693 694 695 696
#endif
}


697
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
698
{
699 700 701 702 703
    QObject* rootQmlObject = _rootQmlObject();

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

705 706 707 708 709 710 711 712 713
        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
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
}

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

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

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

731
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
732
{
733
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
}


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
758
}
Don Gagne's avatar
Don Gagne committed
759 760 761 762 763 764 765 766 767

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

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