QGCApplication.cc 29.9 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
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

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

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

45 46
#include <QDebug>

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

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

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

#ifndef __mobile__
116 117
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
118
    #include "FirmwareUpgradeController.h"
Don Gagne's avatar
Don Gagne committed
119
    #include "MainWindow.h"
120
#endif
121

122
#ifdef QGC_RTLAB_ENABLED
123
    #include "OpalLink.h"
124
#endif
125

Don Gagne's avatar
Don Gagne committed
126 127 128 129 130 131
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
132 133

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

Don Gagne's avatar
Don Gagne committed
135 136 137 138
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
const char* QGCApplication::telemetryFileExtension =     "tlog";

139 140 141 142 143
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";
144 145 146
const char* QGCApplication::_lastKnownHomePositionLatKey    = "LastKnownHomePositionLat";
const char* QGCApplication::_lastKnownHomePositionLonKey    = "LastKnownHomePositionLon";
const char* QGCApplication::_lastKnownHomePositionAltKey    = "LastKnownHomePositionAlt";
Don Gagne's avatar
Don Gagne committed
147

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

151
// Qml Singleton factories
152

Don Gagne's avatar
Don Gagne committed
153
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
154
{
Don Gagne's avatar
Don Gagne committed
155 156
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
157 158
}

159 160 161 162 163
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

164 165
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
166
    // We create this object as a QGCTool even though it isn't in the toolbox
167 168 169 170
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
171 172
}

pixhawk's avatar
pixhawk committed
173 174 175 176 177 178 179 180 181 182
/**
 * @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
 **/

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

207
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
208
#ifndef __android__
209
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
210
#endif
dogmaphobic's avatar
dogmaphobic committed
211

212 213
#ifdef Q_OS_LINUX
#ifndef __mobile__
214 215 216 217 218 219 220 221 222 223 224 225 226 227
    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);
        }
228

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
        // 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;
                }
246
            }
247
            permFile.close();
248 249 250 251 252
        }
    }
#endif
#endif

253
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
254

255
    bool fClearSettingsOptions = false; // Clear stored settings
256 257
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
258

259
    CmdLineOpt_t rgCmdLineOptions[] = {
260 261
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
262
        { "--fake-mobile",      &_fakeMobile,           NULL },
263
#ifdef QT_DEBUG
264
        { "--test-high-dpi",    &_testHighDPI,          NULL },
265
#endif
266 267
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
268

269
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
270

dogmaphobic's avatar
dogmaphobic committed
271
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
272
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
273
#else
274
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
275

276 277
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
278

279 280
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
281

282 283 284 285 286 287 288
        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 {
289
            foreach(const QString &rule, logList) {
290 291 292 293
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
294 295 296
    } 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.
297

298 299 300
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
301

302 303 304 305 306 307 308 309 310
        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;
311
            }
312
        } else {
313 314
            loggingDirectoryOk = true;
        }
315

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

334 335
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
336
#endif
337

338
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
339 340 341
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
342

Don Gagne's avatar
Don Gagne committed
343
    // Set application information
344 345 346 347 348 349 350 351 352
    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);
353

354
    QString versionString(GIT_TAG);
Daniel Agar's avatar
Daniel Agar committed
355 356 357 358 359
    // 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
360
    this->setApplicationVersion(versionString);
361

362 363
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
364
    QSettings settings;
365
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
366

367
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
368 369 370
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
371
#endif
372
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
373
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
374

Don Gagne's avatar
Don Gagne committed
375
    if (_runningUnitTests) {
376
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
377 378
        fClearSettingsOptions = true;
    }
379

380 381
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
382

383
        settings.clear();
384
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
385

386
        // Clear parameter cache
387 388 389
        QDir paramDir(ParameterLoader::parameterCacheDir());
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
390
    }
Gus Grubba's avatar
Gus Grubba committed
391

392 393 394
    _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
395

dogmaphobic's avatar
dogmaphobic committed
396 397 398 399 400 401 402 403 404
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

405 406
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
407 408

    _toolbox = new QGCToolbox(this);
409 410
}

Don Gagne's avatar
Don Gagne committed
411 412
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
413
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
414 415 416 417
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
418
#endif
419
    shutdownVideoStreaming();
420
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
421 422
}

423
void QGCApplication::_initCommon(void)
424 425
{
    QSettings settings;
426

Don Gagne's avatar
Don Gagne committed
427
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
428

Don Gagne's avatar
Don Gagne committed
429 430
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
431

Don Gagne's avatar
Don Gagne committed
432 433 434 435 436 437 438
    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");

439 440 441 442 443 444 445
    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");
Jimmy Johnson's avatar
Jimmy Johnson committed
446
    qmlRegisterUncreatableType<QGCPositionManager>  ("QGroundControl.QGCPositionManager",  1, 0, "QGCPositionManager",  "Reference only");
447

dogmaphobic's avatar
dogmaphobic committed
448
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
449
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
Don Gagne's avatar
Don Gagne committed
450 451
    qmlRegisterType<PX4AdvancedFlightModesController>   ("QGroundControl.Controllers", 1, 0, "PX4AdvancedFlightModesController");
    qmlRegisterType<PX4SimpleFlightModesController>     ("QGroundControl.Controllers", 1, 0, "PX4SimpleFlightModesController");
452
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
453
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
454
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
455 456 457
    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
458
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
459 460 461 462
    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");
463
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
464
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
Don Gagne's avatar
Don Gagne committed
465
    qmlRegisterType<RCChannelMonitorController>         ("QGroundControl.Controllers", 1, 0, "RCChannelMonitorController");
466
    qmlRegisterType<JoystickConfigController>           ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
467
#ifndef __mobile__
468 469
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
470
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
dogmaphobic's avatar
dogmaphobic committed
471
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
472
#endif
dogmaphobic's avatar
dogmaphobic committed
473

474
    // Register Qml Singletons
475 476 477
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
478 479 480 481 482
}

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

484 485
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
486

487
    // Exit main application when last window is closed
488
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
489

Don Gagne's avatar
Don Gagne committed
490 491 492 493
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
494
    _qmlAppEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel());
Don Gagne's avatar
Don Gagne committed
495 496
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
497
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
498
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
499
    Q_CHECK_PTR(mainWindow);
500

501
    // Now that main window is up check for lost log files
502
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
503
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
504
#endif
505 506

    // Load known link configurations
507
    toolbox()->linkManager()->loadLinkConfigurationList();
508

Don Gagne's avatar
Don Gagne committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
    // 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();

531
    return true;
pixhawk's avatar
pixhawk committed
532 533
}

534
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
535
{
536
    return true;
pixhawk's avatar
pixhawk committed
537 538
}

Don Gagne's avatar
Don Gagne committed
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
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;
554

Don Gagne's avatar
Don Gagne committed
555 556 557
    return settings.value(_promptFlightDataSave, true).toBool();
}

558 559 560 561 562 563 564
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
565 566 567 568 569 570
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

571 572 573 574 575 576
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
577 578 579
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
580 581 582 583
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

584 585
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
586 587
    Q_UNUSED(title);
    showMessage(msg);
588 589 590 591
}

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

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
602 603 604 605
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
606
    QGCMessageBox::critical(title, msg);
607
#endif
608 609
}

610
#ifndef __mobile__
611 612
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
613 614 615 616 617 618
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
619
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
620 621
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
622

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

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

644 645 646 647 648 649 650 651
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
652
#ifndef __mobile__
653 654
    bool success = true;
    QString styles;
655

656 657 658 659 660 661 662 663 664
    // 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;
    }
665

666 667 668 669 670 671
    if (success && !_styleIsDark) {
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
672
            qWarning() << "Unable to load slave light sheet:";
673 674 675
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
676

677
    setStyleSheet(styles);
678

679 680 681 682
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
683
#endif
684

685
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
686
}
Don Gagne's avatar
Don Gagne committed
687

Don Gagne's avatar
Don Gagne committed
688
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
689
{
Don Gagne's avatar
Don Gagne committed
690 691
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
692
}
693

Don Gagne's avatar
Don Gagne committed
694 695
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
696
{
Don Gagne's avatar
Don Gagne committed
697
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
698

Don Gagne's avatar
Don Gagne committed
699
    QString params;
700
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
701 702
        if (params.isEmpty()) {
            params += name;
703
        } else {
Don Gagne's avatar
Don Gagne committed
704
            params += QString(", %1").arg(name);
705 706
        }
    }
Don Gagne's avatar
Don Gagne committed
707
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
708

709
    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));
710 711
}

Don Gagne's avatar
Don Gagne committed
712 713 714 715 716
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
717 718 719 720 721 722 723 724 725 726
    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
727 728 729 730
#endif
}


731
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
732
{
Don Gagne's avatar
Don Gagne committed
733 734 735 736 737
    // 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;
    }

738 739 740 741 742
    QObject* rootQmlObject = _rootQmlObject();

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

744 745 746 747 748 749 750 751 752
        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
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
}

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

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

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

770
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
771
{
772
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
}


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
797
}
Don Gagne's avatar
Don Gagne committed
798

799
void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition)
Don Gagne's avatar
Don Gagne committed
800 801 802
{
    QSettings settings;

803 804 805 806
    settings.setValue(_lastKnownHomePositionLatKey, lastKnownHomePosition.latitude());
    settings.setValue(_lastKnownHomePositionLonKey, lastKnownHomePosition.longitude());
    settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude());
    _lastKnownHomePosition = lastKnownHomePosition;
Don Gagne's avatar
Don Gagne committed
807
}