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 "JoystickManager.h"
89
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
90
#include "MissionManager.h"
91 92
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
93
#include "FlightMapSettings.h"
94 95
#include "QGCQGeoCoordinate.h"
#include "CoordinateVector.h"
96
#include "MainToolBarController.h"
97
#include "MissionController.h"
Don Gagne's avatar
Don Gagne committed
98
#include "MissionCommands.h"
99 100 101
#include "FlightDisplayViewController.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
102
#include "LogDownloadController.h"
103
#include "PX4AirframeLoader.h"
104
#include "ValuesWidgetController.h"
105
#include "AppMessages.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
106 107 108
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
109 110 111 112 113 114

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

#ifndef __mobile__
115 116
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
117 118
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.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

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");
471
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
472
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
473
#endif
dogmaphobic's avatar
dogmaphobic committed
474

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

678
    setStyleSheet(styles);
679

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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