QGCApplication.cc 25.3 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"
98 99 100 101 102 103

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

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

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


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

118 119 120 121 122
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
123

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

127
// Qml Singleton factories
128

Don Gagne's avatar
Don Gagne committed
129
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
130
{
Don Gagne's avatar
Don Gagne committed
131 132
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
133 134
}

135 136 137 138 139
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

140 141
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
142 143 144 145 146
    // 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;
147 148
}

pixhawk's avatar
pixhawk committed
149 150 151 152 153 154 155 156 157 158
/**
 * @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
 **/

159
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
160 161 162 163
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
#else
164
    : QApplication(argc, argv)
Don Gagne's avatar
Don Gagne committed
165
#endif
166
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
167 168 169
#if defined (__mobile__)
    , _styleIsDark(false)
#else
170
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
171
#endif
dogmaphobic's avatar
dogmaphobic committed
172
    , _fakeMobile(false)
173 174 175
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
176
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
177
    , _bluetoothAvailable(false)
pixhawk's avatar
pixhawk committed
178
{
179 180
    Q_ASSERT(_app == NULL);
    _app = this;
181

182
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
183
#ifndef __android__
184
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
185
#endif
dogmaphobic's avatar
dogmaphobic committed
186

187
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
188

189
    bool fClearSettingsOptions = false; // Clear stored settings
190 191
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
192

193
    CmdLineOpt_t rgCmdLineOptions[] = {
194 195
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
196
        { "--fake-mobile",      &_fakeMobile,           NULL },
197
#ifdef QT_DEBUG
198
        { "--test-high-dpi",    &_testHighDPI,          NULL },
199
#endif
200 201
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
202

203
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
204

dogmaphobic's avatar
dogmaphobic committed
205
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
206
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
207
#else
208
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
209

210 211
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
212

213 214
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
215

216 217 218 219 220 221 222
        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 {
223
            foreach(const QString &rule, logList) {
224 225 226 227
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
228 229 230
    } 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.
231

232 233 234
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
235

236 237 238 239 240 241 242 243 244
        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;
245
            }
246
        } else {
247 248
            loggingDirectoryOk = true;
        }
249

250
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
251
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
252 253 254 255 256 257
            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";
258
                    foreach(const QString &category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
259 260 261 262
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
263
                }
264 265 266
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
267

268 269
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
270
#endif
271

272
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
273 274 275
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
276

Don Gagne's avatar
Don Gagne committed
277
    // Set application information
278 279 280 281 282 283 284 285 286
    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);
287

dogmaphobic's avatar
dogmaphobic committed
288
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
289 290 291 292 293
    // 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
294
    this->setApplicationVersion(versionString);
295

296
    // Set settings format
dogmaphobic's avatar
dogmaphobic committed
297
#if !defined(__mobile__) && !defined(__macos__)
298
    QSettings::setDefaultFormat(QSettings::IniFormat);
dogmaphobic's avatar
dogmaphobic committed
299 300 301 302 303 304 305
#else
    QString settingsLocation = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
    if(!settingsLocation.isEmpty())
    {
        QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, settingsLocation);
    }
#endif
306

307
    QSettings settings;
Don Gagne's avatar
Don Gagne committed
308 309
    qDebug() << "Settings location" << settings.fileName() << settings.isWritable();

310
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
311 312 313
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
314
#endif
315
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
316
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
317

Don Gagne's avatar
Don Gagne committed
318
    if (_runningUnitTests) {
319
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
320 321
        fClearSettingsOptions = true;
    }
322

323 324 325
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
326
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
327
    }
Gus Grubba's avatar
Gus Grubba committed
328

dogmaphobic's avatar
dogmaphobic committed
329 330 331 332 333 334 335 336 337
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

338 339
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
340 341

    _toolbox = new QGCToolbox(this);
342 343
}

Don Gagne's avatar
Don Gagne committed
344 345
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
346
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
347 348 349 350
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
351
#endif
352
    shutdownVideoStreaming();
353
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
354 355
}

356
void QGCApplication::_initCommon(void)
357 358
{
    QSettings settings;
359

Don Gagne's avatar
Don Gagne committed
360
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
361

Don Gagne's avatar
Don Gagne committed
362 363
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
364

Don Gagne's avatar
Don Gagne committed
365 366 367 368 369 370 371
    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");

372 373 374 375 376 377 378
    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");
379

380
    qmlRegisterType<ParameterEditorController>      ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
381 382
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
383
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
384
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
385
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
386 387 388 389 390 391 392
    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");
393

Don Gagne's avatar
Don Gagne committed
394
#ifndef __mobile__
395 396
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
397
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
398
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
399
#endif
dogmaphobic's avatar
dogmaphobic committed
400

401
    // Register Qml Singletons
402 403 404
    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
405

406 407 408 409 410
    // 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;
411
        }
412 413 414
    } 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
415 416
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
417
    }
418

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

Don Gagne's avatar
Don Gagne committed
426
    settings.sync();
427 428 429 430 431
}

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

433 434
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
435

436 437 438
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

Don Gagne's avatar
Don Gagne committed
439 440 441 442 443 444 445
#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
446
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
447
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
448
    Q_CHECK_PTR(mainWindow);
449

450
    // Now that main window is up check for lost log files
451
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
452
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
453
#endif
454 455

    // Load known link configurations
456
    toolbox()->linkManager()->loadLinkConfigurationList();
457

458
    return true;
pixhawk's avatar
pixhawk committed
459 460
}

461
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
462
{
463
    return true;
pixhawk's avatar
pixhawk committed
464 465
}

Don Gagne's avatar
Don Gagne committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
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;
481

Don Gagne's avatar
Don Gagne committed
482 483 484
    return settings.value(_promptFlightDataSave, true).toBool();
}

485 486 487 488 489 490 491
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
492 493 494 495 496 497
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

498 499 500 501 502 503
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
504 505 506
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
507 508 509 510
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

511 512
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
513 514
    Q_UNUSED(title);
    showMessage(msg);
515 516 517 518
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
519 520 521 522
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
523
    QGCMessageBox::warning(title, msg);
524
#endif
525 526 527 528
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
529 530 531 532
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
533
    QGCMessageBox::critical(title, msg);
534
#endif
535 536
}

537
#ifndef __mobile__
538 539
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
540 541 542 543 544 545
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
546
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
547 548
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
549

550 551 552
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
553
                // if the file cannot be removed, prompt user and ask new path
554
                saveError = true;
555
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
556 557 558
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
559
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
560 561 562
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
563 564
    QFile::remove(tempLogfile);
}
565
#endif
566 567 568 569

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

571 572 573 574 575 576 577 578
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
579
#ifndef __mobile__
580 581
    bool success = true;
    QString styles;
582

583 584 585 586 587 588 589 590 591
    // 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;
    }
592

593 594 595 596 597 598 599 600 601 602 603
    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
604

605
    setStyleSheet(styles);
606

607 608 609 610
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
611
#endif
612

613
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
614
}
Don Gagne's avatar
Don Gagne committed
615

Don Gagne's avatar
Don Gagne committed
616
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
617
{
Don Gagne's avatar
Don Gagne committed
618 619
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
620
}
621

Don Gagne's avatar
Don Gagne committed
622 623
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
624
{
Don Gagne's avatar
Don Gagne committed
625
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
626

Don Gagne's avatar
Don Gagne committed
627
    QString params;
628
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
629 630
        if (params.isEmpty()) {
            params += name;
631
        } else {
Don Gagne's avatar
Don Gagne committed
632
            params += QString(", %1").arg(name);
633 634
        }
    }
Don Gagne's avatar
Don Gagne committed
635
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
636

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

Don Gagne's avatar
Don Gagne committed
640 641 642 643 644
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
645 646 647 648 649 650 651 652 653 654
    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
655 656 657 658
#endif
}


659
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
660
{
661 662 663 664 665
    QObject* rootQmlObject = _rootQmlObject();

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

667 668 669 670 671 672 673 674 675
        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
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
}

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

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

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

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


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
720
}