QGCApplication.cc 29.4 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 65
#include "PX4AdvancedFlightModesController.h"
#include "PX4SimpleFlightModesController.h"
Don Gagne's avatar
Don Gagne committed
66
#include "APMFlightModesComponentController.h"
Don Gagne's avatar
Don Gagne committed
67 68
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
Don Gagne's avatar
Don Gagne committed
69
#include "APMSensorsComponentController.h"
70
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
71
#include "RadioComponentController.h"
dogmaphobic's avatar
dogmaphobic committed
72
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
73
#include "ScreenToolsController.h"
Don Gagne's avatar
Don Gagne committed
74
#include "QGCMobileFileDialogController.h"
75 76
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
77
#include "FirmwarePluginManager.h"
78
#include "MultiVehicleManager.h"
79
#include "APM/ArduCopterFirmwarePlugin.h"
80 81
#include "APM/ArduPlaneFirmwarePlugin.h"
#include "APM/ArduRoverFirmwarePlugin.h"
82
#include "APM/APMAirframeComponentController.h"
Don Gagne's avatar
Don Gagne committed
83
#include "PX4/PX4FirmwarePlugin.h"
84
#include "Vehicle.h"
85
#include "MavlinkQmlSingleton.h"
86
#include "JoystickManager.h"
87
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
88
#include "MissionManager.h"
89 90
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
91
#include "FlightMapSettings.h"
92 93
#include "QGCQGeoCoordinate.h"
#include "CoordinateVector.h"
94
#include "MainToolBarController.h"
95
#include "MissionController.h"
Don Gagne's avatar
Don Gagne committed
96
#include "MissionCommands.h"
97 98 99
#include "FlightDisplayViewController.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
100
#include "LogDownloadController.h"
101
#include "PX4AirframeLoader.h"
102
#include "ValuesWidgetController.h"
103 104 105 106 107 108

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

#ifndef __mobile__
109 110
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
111 112
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
Don Gagne's avatar
Don Gagne committed
113
    #include "MainWindow.h"
114
#endif
115

116
#ifdef QGC_RTLAB_ENABLED
117
    #include "OpalLink.h"
118
#endif
119

Don Gagne's avatar
Don Gagne committed
120 121 122 123 124 125
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
126 127

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

Don Gagne's avatar
Don Gagne committed
129 130 131 132
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
const char* QGCApplication::telemetryFileExtension =     "tlog";

133 134 135 136 137
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";
138 139 140
const char* QGCApplication::_lastKnownHomePositionLatKey    = "LastKnownHomePositionLat";
const char* QGCApplication::_lastKnownHomePositionLonKey    = "LastKnownHomePositionLon";
const char* QGCApplication::_lastKnownHomePositionAltKey    = "LastKnownHomePositionAlt";
Don Gagne's avatar
Don Gagne committed
141

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

145
// Qml Singleton factories
146

Don Gagne's avatar
Don Gagne committed
147
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
148
{
Don Gagne's avatar
Don Gagne committed
149 150
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
151 152
}

153 154 155 156 157
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

158 159
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
160
    // We create this object as a QGCTool even though it isn't in the toolbox
161 162 163 164
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
165 166
}

pixhawk's avatar
pixhawk committed
167 168 169 170 171 172 173 174 175 176
/**
 * @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
 **/

177
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
178 179 180 181
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
#else
182
    : QApplication(argc, argv)
Don Gagne's avatar
Don Gagne committed
183
#endif
184
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
185 186 187
#if defined (__mobile__)
    , _styleIsDark(false)
#else
188
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
189
#endif
dogmaphobic's avatar
dogmaphobic committed
190
    , _fakeMobile(false)
191 192 193
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
194
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
195
    , _bluetoothAvailable(false)
196
    , _lastKnownHomePosition(37.803784, -122.462276, 0.0)
pixhawk's avatar
pixhawk committed
197
{
198 199
    Q_ASSERT(_app == NULL);
    _app = this;
200

201
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
202
#ifndef __android__
203
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
204
#endif
dogmaphobic's avatar
dogmaphobic committed
205

206 207
#ifdef Q_OS_LINUX
#ifndef __mobile__
208 209 210 211 212 213 214 215 216 217 218 219 220 221
    if (!_runningUnitTests) {
        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 on Ubuntu, execute the following commands to fix most 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);
        }
222

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
        // 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;
                }
240
            }
241
            permFile.close();
242 243 244 245 246
        }
    }
#endif
#endif

247
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
248

249
    bool fClearSettingsOptions = false; // Clear stored settings
250 251
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
252

253
    CmdLineOpt_t rgCmdLineOptions[] = {
254 255
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
256
        { "--fake-mobile",      &_fakeMobile,           NULL },
257
#ifdef QT_DEBUG
258
        { "--test-high-dpi",    &_testHighDPI,          NULL },
259
#endif
260 261
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
262

263
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
264

dogmaphobic's avatar
dogmaphobic committed
265
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
266
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
267
#else
268
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
269

270 271
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
272

273 274
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
275

276 277 278 279 280 281 282
        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 {
283
            foreach(const QString &rule, logList) {
284 285 286 287
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
288 289 290
    } 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.
291

292 293 294
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
295

296 297 298 299 300 301 302 303 304
        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;
305
            }
306
        } else {
307 308
            loggingDirectoryOk = true;
        }
309

310
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
311
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
312 313 314 315 316 317
            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";
318
                    foreach(const QString &category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
319 320 321 322
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
323
                }
324 325 326
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
327

328 329
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
330
#endif
331

332
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
333 334 335
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
336

Don Gagne's avatar
Don Gagne committed
337
    // Set application information
338 339 340 341 342 343 344 345 346
    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);
347

348
    QString versionString(GIT_TAG);
Daniel Agar's avatar
Daniel Agar committed
349 350 351 352 353
    // 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
354
    this->setApplicationVersion(versionString);
355

356 357
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
358
    QSettings settings;
359
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
360

361
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
362 363 364
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
365
#endif
366
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
367
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
368

Don Gagne's avatar
Don Gagne committed
369
    if (_runningUnitTests) {
370
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
371 372
        fClearSettingsOptions = true;
    }
373

374 375
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
376

377
        settings.clear();
378
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
379

380
        // Clear parameter cache
381 382 383
        QDir paramDir(ParameterLoader::parameterCacheDir());
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
384
    }
Gus Grubba's avatar
Gus Grubba committed
385

386 387 388
    _lastKnownHomePosition.setLatitude(settings.value(_lastKnownHomePositionLatKey, 37.803784).toDouble());
    _lastKnownHomePosition.setLongitude(settings.value(_lastKnownHomePositionLonKey, -122.462276).toDouble());
    _lastKnownHomePosition.setAltitude(settings.value(_lastKnownHomePositionAltKey, 0.0).toDouble());
Don Gagne's avatar
Don Gagne committed
389

dogmaphobic's avatar
dogmaphobic committed
390 391 392 393 394 395 396 397 398
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

399 400
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
401 402

    _toolbox = new QGCToolbox(this);
403 404
}

Don Gagne's avatar
Don Gagne committed
405 406
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
407
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
408 409 410 411
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
412
#endif
413
    shutdownVideoStreaming();
414
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
415 416
}

417
void QGCApplication::_initCommon(void)
418 419
{
    QSettings settings;
420

Don Gagne's avatar
Don Gagne committed
421
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
422

Don Gagne's avatar
Don Gagne committed
423 424
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
425

Don Gagne's avatar
Don Gagne committed
426 427 428 429 430 431 432
    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");

433 434 435 436 437 438 439
    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");
440

dogmaphobic's avatar
dogmaphobic committed
441
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
442
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
Don Gagne's avatar
Don Gagne committed
443 444
    qmlRegisterType<PX4AdvancedFlightModesController>   ("QGroundControl.Controllers", 1, 0, "PX4AdvancedFlightModesController");
    qmlRegisterType<PX4SimpleFlightModesController>     ("QGroundControl.Controllers", 1, 0, "PX4SimpleFlightModesController");
445
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
446
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
447
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
448 449 450
    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
451
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
452 453 454 455
    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");
456
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
457
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
458

Don Gagne's avatar
Don Gagne committed
459
#ifndef __mobile__
460 461
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
462
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
463
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
464
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
465
#endif
dogmaphobic's avatar
dogmaphobic committed
466

467
    // Register Qml Singletons
468 469 470
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
471 472 473 474 475
}

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

477 478
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
479

480
    // Exit main application when last window is closed
481
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
482

Don Gagne's avatar
Don Gagne committed
483 484 485 486 487 488
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
489
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
490
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
491
    Q_CHECK_PTR(mainWindow);
492

493
    // Now that main window is up check for lost log files
494
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
495
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
496
#endif
497 498

    // Load known link configurations
499
    toolbox()->linkManager()->loadLinkConfigurationList();
500

Don Gagne's avatar
Don Gagne committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    // 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;
        }
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
    }

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

    settings.sync();

523
    return true;
pixhawk's avatar
pixhawk committed
524 525
}

526
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
527
{
528
    return true;
pixhawk's avatar
pixhawk committed
529 530
}

Don Gagne's avatar
Don Gagne committed
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
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;
546

Don Gagne's avatar
Don Gagne committed
547 548 549
    return settings.value(_promptFlightDataSave, true).toBool();
}

550 551 552 553 554 555 556
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
557 558 559 560 561 562
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

563 564 565 566 567 568
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
569 570 571
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
572 573 574 575
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

576 577
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
578 579
    Q_UNUSED(title);
    showMessage(msg);
580 581 582 583
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
584 585 586 587
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
588
    QGCMessageBox::warning(title, msg);
589
#endif
590 591 592 593
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
594 595 596 597
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
598
    QGCMessageBox::critical(title, msg);
599
#endif
600 601
}

602
#ifndef __mobile__
603 604
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
605 606 607 608 609 610
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
611
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
612 613
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
614

615 616 617
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
618
                // if the file cannot be removed, prompt user and ask new path
619
                saveError = true;
620
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
621 622 623
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
624
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
625 626 627
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
628 629
    QFile::remove(tempLogfile);
}
630
#endif
631 632 633 634

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

636 637 638 639 640 641 642 643
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
644
#ifndef __mobile__
645 646
    bool success = true;
    QString styles;
647

648 649 650 651 652 653 654 655 656
    // 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;
    }
657

658 659 660 661 662 663
    if (success && !_styleIsDark) {
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
664
            qWarning() << "Unable to load slave light sheet:";
665 666 667
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
668

669
    setStyleSheet(styles);
670

671 672 673 674
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
675
#endif
676

677
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
678
}
Don Gagne's avatar
Don Gagne committed
679

Don Gagne's avatar
Don Gagne committed
680
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
681
{
Don Gagne's avatar
Don Gagne committed
682 683
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
684
}
685

Don Gagne's avatar
Don Gagne committed
686 687
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
688
{
Don Gagne's avatar
Don Gagne committed
689
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
690

Don Gagne's avatar
Don Gagne committed
691
    QString params;
692
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
693 694
        if (params.isEmpty()) {
            params += name;
695
        } else {
Don Gagne's avatar
Don Gagne committed
696
            params += QString(", %1").arg(name);
697 698
        }
    }
Don Gagne's avatar
Don Gagne committed
699
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
700

701
    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));
702 703
}

Don Gagne's avatar
Don Gagne committed
704 705 706 707 708
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
709 710 711 712 713 714 715 716 717 718
    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
719 720 721 722
#endif
}


723
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
724
{
Don Gagne's avatar
Don Gagne committed
725 726 727 728 729
    // 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;
    }

730 731 732 733 734
    QObject* rootQmlObject = _rootQmlObject();

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

736 737 738 739 740 741 742 743 744
        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
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
}

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

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

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

762
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
763
{
764
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
}


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
789
}
Don Gagne's avatar
Don Gagne committed
790

791
void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition)
Don Gagne's avatar
Don Gagne committed
792 793 794
{
    QSettings settings;

795 796 797 798
    settings.setValue(_lastKnownHomePositionLatKey, lastKnownHomePosition.latitude());
    settings.setValue(_lastKnownHomePositionLonKey, lastKnownHomePosition.longitude());
    settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude());
    _lastKnownHomePosition = lastKnownHomePosition;
Don Gagne's avatar
Don Gagne committed
799
}