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

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

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

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

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

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

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

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

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

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

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

44 45
#include <QDebug>

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

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

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

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

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

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

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

125 126 127 128 129
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
130 131
const char* QGCApplication::_defaultMapPositionLatKey       = "DefaultMapPositionLat";
const char* QGCApplication::_defaultMapPositionLonKey       = "DefaultMapPositionLon";
Don Gagne's avatar
Don Gagne committed
132

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

136
// Qml Singleton factories
137

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

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

149 150
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
151 152 153 154 155
    // 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;
156 157
}

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

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

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

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

    // Determine if we have the correct permissions to access USB serial devices
    QFile permFile("/etc/group");
    if(permFile.open(QIODevice::ReadOnly)) {
        while(!permFile.atEnd()) {
            QString line = permFile.readLine();
            if (line.contains("dialout") && !line.contains(getenv("USER"))) {
                QMessageBox msgBox;
                msgBox.setInformativeText("The current user does not have the correct permissions to access serial devices. "
                                          "You should also remove modemmanager since it also interferes. "
                                          "Execute the following commands to fix these issues:\n"
                                          "sudo usermod -a -G dialout $USER\n"
                                          "sudo apt-get remove modemmanager");
                msgBox.setStandardButtons(QMessageBox::Ok);
                msgBox.setDefaultButton(QMessageBox::Ok);
                msgBox.exec();
                break;
            }
        }
        permFile.close();
    }
#endif
#endif

236
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
237

238
    bool fClearSettingsOptions = false; // Clear stored settings
239 240
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
241

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

252
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
253

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

259 260
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
261

262 263
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
264

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

281 282 283
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
284

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

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

317 318
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
319
#endif
320

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

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

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

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

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

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

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

Don Gagne's avatar
Don Gagne committed
369 370 371
    _defaultMapPosition.setLatitude(settings.value(_defaultMapPositionLatKey, 37.803784).toDouble());
    _defaultMapPosition.setLongitude(settings.value(_defaultMapPositionLonKey, -122.462276).toDouble());

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

381 382
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
383 384

    _toolbox = new QGCToolbox(this);
385 386
}

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

399
void QGCApplication::_initCommon(void)
400 401
{
    QSettings settings;
402

Don Gagne's avatar
Don Gagne committed
403
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
404

Don Gagne's avatar
Don Gagne committed
405 406
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
407

Don Gagne's avatar
Don Gagne committed
408 409 410 411 412 413 414
    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");

415 416 417 418 419 420 421
    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");
422

dogmaphobic's avatar
dogmaphobic committed
423
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
424 425
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
426
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
427
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
428
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
429 430 431 432 433 434 435
    qmlRegisterType<SensorsComponentController>         ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>           ("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>           ("QGroundControl.Controllers", 1, 0, "RadioComponentController");
    qmlRegisterType<ScreenToolsController>              ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    qmlRegisterType<MainToolBarController>              ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
    qmlRegisterType<FlightDisplayViewController>        ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");
436

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

445
    // Register Qml Singletons
446 447 448
    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
449

450 451 452 453 454
    // 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;
455
        }
456 457 458
    } 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
459 460
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
461
    }
462

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

Don Gagne's avatar
Don Gagne committed
470
    settings.sync();
471 472 473 474 475
}

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

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

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

Don Gagne's avatar
Don Gagne committed
483 484 485 486 487 488 489
#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
490
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
491
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
492
    Q_CHECK_PTR(mainWindow);
493

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

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

502
    return true;
pixhawk's avatar
pixhawk committed
503 504
}

505
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
506
{
507
    return true;
pixhawk's avatar
pixhawk committed
508 509
}

Don Gagne's avatar
Don Gagne committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
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;
525

Don Gagne's avatar
Don Gagne committed
526 527 528
    return settings.value(_promptFlightDataSave, true).toBool();
}

529 530 531 532 533 534 535
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
536 537 538 539 540 541
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

542 543 544 545 546 547
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
548 549 550
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
551 552 553 554
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

555 556
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
557 558
    Q_UNUSED(title);
    showMessage(msg);
559 560 561 562
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
563 564 565 566
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
567
    QGCMessageBox::warning(title, msg);
568
#endif
569 570 571 572
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
573 574 575 576
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
577
    QGCMessageBox::critical(title, msg);
578
#endif
579 580
}

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

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

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

615 616 617 618 619 620 621 622
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
623
#ifndef __mobile__
624 625
    bool success = true;
    QString styles;
626

627 628 629 630 631 632 633 634 635
    // 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;
    }
636

637 638 639 640 641 642 643 644 645 646 647
    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
648

649
    setStyleSheet(styles);
650

651 652 653 654
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
655
#endif
656

657
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
658
}
Don Gagne's avatar
Don Gagne committed
659

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

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

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

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

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


703
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
704
{
705 706 707 708 709
    QObject* rootQmlObject = _rootQmlObject();

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

711 712 713 714 715 716 717 718 719
        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
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
}

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

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

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

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


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
764
}
Don Gagne's avatar
Don Gagne committed
765 766 767 768 769 770 771 772 773

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

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