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

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

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

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

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

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

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

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

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

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

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

44 45
#include <QDebug>

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

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

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

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

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

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

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

126 127 128 129 130
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
const char* QGCApplication::_promptFlightDataSave           = "PromptFLightDataSave";
const char* QGCApplication::_promptFlightDataSaveNotArmed   = "PromptFLightDataSaveNotArmed";
const char* QGCApplication::_styleKey                       = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
131 132
const char* QGCApplication::_defaultMapPositionLatKey       = "DefaultMapPositionLat";
const char* QGCApplication::_defaultMapPositionLonKey       = "DefaultMapPositionLon";
Don Gagne's avatar
Don Gagne committed
133

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

137
// Qml Singleton factories
138

Don Gagne's avatar
Don Gagne committed
139
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
140
{
Don Gagne's avatar
Don Gagne committed
141 142
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
143 144
}

145 146 147 148 149
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

150 151
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
152 153 154 155 156
    // We create this object as a QGCTool even though it isn't int he toolbox
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
157 158
}

pixhawk's avatar
pixhawk committed
159 160 161 162 163 164 165 166 167 168
/**
 * @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
 **/

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

193
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
194
#ifndef __android__
195
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
196
#endif
dogmaphobic's avatar
dogmaphobic committed
197

198 199
#ifdef Q_OS_LINUX
#ifndef __mobile__
200 201 202 203 204 205 206 207 208 209 210 211 212 213
    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);
        }
214

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        // 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;
                }
232
            }
233
            permFile.close();
234 235 236 237 238
        }
    }
#endif
#endif

239
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
240

241
    bool fClearSettingsOptions = false; // Clear stored settings
242 243
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
244

245
    CmdLineOpt_t rgCmdLineOptions[] = {
246 247
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
248
        { "--fake-mobile",      &_fakeMobile,           NULL },
249
#ifdef QT_DEBUG
250
        { "--test-high-dpi",    &_testHighDPI,          NULL },
251
#endif
252 253
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
254

255
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
256

dogmaphobic's avatar
dogmaphobic committed
257
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
258
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
259
#else
260
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
261

262 263
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
264

265 266
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
267

268 269 270 271 272 273 274
        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 {
275
            foreach(const QString &rule, logList) {
276 277 278 279
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
280 281 282
    } 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.
283

284 285 286
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
287

288 289 290 291 292 293 294 295 296
        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;
297
            }
298
        } else {
299 300
            loggingDirectoryOk = true;
        }
301

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

320 321
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
322
#endif
323

324
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
325 326 327
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
328

Don Gagne's avatar
Don Gagne committed
329
    // Set application information
330 331 332 333 334 335 336 337 338
    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);
339

dogmaphobic's avatar
dogmaphobic committed
340
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
341 342 343 344 345
    // 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
346
    this->setApplicationVersion(versionString);
347

348 349
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
350
    QSettings settings;
351
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
352

353
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
354 355 356
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
357
#endif
358
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
359
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
360

Don Gagne's avatar
Don Gagne committed
361
    if (_runningUnitTests) {
362
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
363 364
        fClearSettingsOptions = true;
    }
365

366 367 368
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
369
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
370
    }
Gus Grubba's avatar
Gus Grubba committed
371

Don Gagne's avatar
Don Gagne committed
372 373 374
    _defaultMapPosition.setLatitude(settings.value(_defaultMapPositionLatKey, 37.803784).toDouble());
    _defaultMapPosition.setLongitude(settings.value(_defaultMapPositionLonKey, -122.462276).toDouble());

dogmaphobic's avatar
dogmaphobic committed
375 376 377 378 379 380 381 382 383
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

384 385
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
386 387

    _toolbox = new QGCToolbox(this);
388 389
}

Don Gagne's avatar
Don Gagne committed
390 391
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
392
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
393 394 395 396
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
397
#endif
398
    shutdownVideoStreaming();
399
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
400 401
}

402
void QGCApplication::_initCommon(void)
403 404
{
    QSettings settings;
405

Don Gagne's avatar
Don Gagne committed
406
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
407

Don Gagne's avatar
Don Gagne committed
408 409
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
410

Don Gagne's avatar
Don Gagne committed
411 412 413 414 415 416 417
    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");

418 419 420 421 422 423 424
    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");
425

dogmaphobic's avatar
dogmaphobic committed
426
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
427 428
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
429
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
430
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
431
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
432 433 434
    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
435
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
436 437 438 439
    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");
440

Don Gagne's avatar
Don Gagne committed
441
#ifndef __mobile__
442 443
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
444
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
445
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
446
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
447
#endif
dogmaphobic's avatar
dogmaphobic committed
448

449
    // Register Qml Singletons
450 451 452
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
dogmaphobic's avatar
dogmaphobic committed
453

454 455 456 457 458
    // 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;
459
        }
460 461 462
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
Don Gagne's avatar
Don Gagne committed
463 464
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
465
    }
466

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

Don Gagne's avatar
Don Gagne committed
474
    settings.sync();
475 476 477 478 479
}

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

481 482
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
483

484
    // Exit main application when last window is closed
485
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
486

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

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

    // Load known link configurations
504
    toolbox()->linkManager()->loadLinkConfigurationList();
505

506
    return true;
pixhawk's avatar
pixhawk committed
507 508
}

509
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
510
{
511
    return true;
pixhawk's avatar
pixhawk committed
512 513
}

Don Gagne's avatar
Don Gagne committed
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
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;
529

Don Gagne's avatar
Don Gagne committed
530 531 532
    return settings.value(_promptFlightDataSave, true).toBool();
}

533 534 535 536 537 538 539
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
540 541 542 543 544 545
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

546 547 548 549 550 551
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
552 553 554
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
555 556 557 558
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

559 560
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
561 562
    Q_UNUSED(title);
    showMessage(msg);
563 564 565 566
}

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

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
577 578 579 580
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
581
    QGCMessageBox::critical(title, msg);
582
#endif
583 584
}

585
#ifndef __mobile__
586 587
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
588 589 590 591 592 593
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
594
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
595 596
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
597

598 599 600
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
601
                // if the file cannot be removed, prompt user and ask new path
602
                saveError = true;
603
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
604 605 606
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
607
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
608 609 610
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
611 612
    QFile::remove(tempLogfile);
}
613
#endif
614 615 616 617

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

619 620 621 622 623 624 625 626
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
627
#ifndef __mobile__
628 629
    bool success = true;
    QString styles;
630

631 632 633 634 635 636 637 638 639
    // 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;
    }
640

641 642 643 644 645 646 647 648 649 650 651
    if (success && !_styleIsDark) {
        qDebug() << "LOADING LIGHT";
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
            qDebug() << "Unable to load slave light sheet:";
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
652

653
    setStyleSheet(styles);
654

655 656 657 658
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
659
#endif
660

661
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
662
}
Don Gagne's avatar
Don Gagne committed
663

Don Gagne's avatar
Don Gagne committed
664
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
665
{
Don Gagne's avatar
Don Gagne committed
666 667
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
668
}
669

Don Gagne's avatar
Don Gagne committed
670 671
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
672
{
Don Gagne's avatar
Don Gagne committed
673
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
674

Don Gagne's avatar
Don Gagne committed
675
    QString params;
676
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
677 678
        if (params.isEmpty()) {
            params += name;
679
        } else {
Don Gagne's avatar
Don Gagne committed
680
            params += QString(", %1").arg(name);
681 682
        }
    }
Don Gagne's avatar
Don Gagne committed
683
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
684

685
    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));
686 687
}

Don Gagne's avatar
Don Gagne committed
688 689 690 691 692
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
693 694 695 696 697 698 699 700 701 702
    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
703 704 705 706
#endif
}


707
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
708
{
709 710 711 712 713
    QObject* rootQmlObject = _rootQmlObject();

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

715 716 717 718 719 720 721 722 723
        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
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
}

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

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

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

741
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
742
{
743
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
}


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
768
}
Don Gagne's avatar
Don Gagne committed
769 770 771 772 773 774 775 776 777

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

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