QGCApplication.cc 26.7 KB
Newer Older
1 2 3 4 5 6 7 8
 /****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
9

pixhawk's avatar
pixhawk committed
10 11 12

/**
 * @file
Don Gagne's avatar
Don Gagne committed
13
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
14 15 16 17 18 19 20 21 22 23 24 25
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

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

dogmaphobic's avatar
dogmaphobic committed
28 29 30 31
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

32 33
#include <QDebug>

34
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
35

36
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
37
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
38
#include "GAudioOutput.h"
39
#include "CmdLineOptParser.h"
40 41
#include "UDPLink.h"
#include "LinkManager.h"
42
#include "HomePositionManager.h"
43
#include "UASMessageHandler.h"
44
#include "QGCTemporaryFile.h"
45
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
46
#include "QGCMapPalette.h"
47
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
48 49
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
50
#include "CustomCommandWidgetController.h"
dogmaphobic's avatar
dogmaphobic committed
51
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
52
#include "ScreenToolsController.h"
Don Gagne's avatar
Don Gagne committed
53
#include "QGCMobileFileDialogController.h"
Don Gagne's avatar
Don Gagne committed
54
#include "RCChannelMonitorController.h"
55 56
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
57
#include "FirmwarePluginManager.h"
58 59
#include "MultiVehicleManager.h"
#include "Vehicle.h"
60
#include "MavlinkQmlSingleton.h"
61
#include "JoystickConfigController.h"
62
#include "JoystickManager.h"
63
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
64
#include "MissionManager.h"
65 66
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
67
#include "FlightMapSettings.h"
68
#include "CoordinateVector.h"
69
#include "MainToolBarController.h"
70
#include "MissionController.h"
71 72
#include "GeoFenceController.h"
#include "RallyPointController.h"
73
#include "VideoManager.h"
74 75
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
76
#include "LogDownloadController.h"
77
#include "ValuesWidgetController.h"
78
#include "AppMessages.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
79 80 81
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
82
#include "MissionCommandTree.h"
83
#include "QGCMapPolygon.h"
84
#include "ParameterManager.h"
85

Gus Grubba's avatar
Gus Grubba committed
86 87 88 89
#if defined(QGC_CUSTOM_BUILD)
#include CUSTOMHEADER
#endif

90 91 92 93 94
#ifndef __ios__
    #include "SerialLink.h"
#endif

#ifndef __mobile__
95 96
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
97
    #include "FirmwareUpgradeController.h"
Don Gagne's avatar
Don Gagne committed
98
    #include "MainWindow.h"
Don Gagne's avatar
Don Gagne committed
99
    #include "GeoTagController.h"
100
#endif
101

102
#ifdef QGC_RTLAB_ENABLED
103
    #include "OpalLink.h"
104
#endif
105

Don Gagne's avatar
Don Gagne committed
106 107 108 109 110 111
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
112

dogmaphobic's avatar
dogmaphobic committed
113 114
#include "QGCMapEngine.h"

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

Don Gagne's avatar
Don Gagne committed
117 118
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
119
const char* QGCApplication::fenceFileExtension =        "fence";
120
const char* QGCApplication::rallyPointFileExtension =   "rally";
Don Gagne's avatar
Don Gagne committed
121 122
const char* QGCApplication::telemetryFileExtension =     "tlog";

123 124 125 126 127
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";
128 129 130
const char* QGCApplication::_lastKnownHomePositionLatKey    = "LastKnownHomePositionLat";
const char* QGCApplication::_lastKnownHomePositionLonKey    = "LastKnownHomePositionLon";
const char* QGCApplication::_lastKnownHomePositionAltKey    = "LastKnownHomePositionAlt";
Don Gagne's avatar
Don Gagne committed
131

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

135 136 137
// Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];

138
// Qml Singleton factories
139

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

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

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

    return qmlGlobal;
158 159
}

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

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

Gus Grubba's avatar
Gus Grubba committed
197 198 199
    //-- Scan and load plugins
    _scanAndLoadPlugins();

200
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
201
#ifndef __android__
202
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
203
#endif
dogmaphobic's avatar
dogmaphobic committed
204

205 206 207
    // Setup for network proxy support
    QNetworkProxyFactory::setUseSystemConfiguration(true);

208 209
#ifdef Q_OS_LINUX
#ifndef __mobile__
210 211 212
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
213
            msgBox.setInformativeText("You are running QGroundControl as root. "
214 215 216 217 218 219 220 221 222 223
                                      "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);
        }
224

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

249
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
250

251
    bool fClearSettingsOptions = false; // Clear stored settings
252 253
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
254

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

265
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
266

267
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
268 269 270
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
271

Don Gagne's avatar
Don Gagne committed
272
    // Set application information
273 274 275 276 277 278 279 280 281
    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);
282

Daniel Agar's avatar
Daniel Agar committed
283
    this->setApplicationVersion(QString(GIT_VERSION));
284

285 286
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
287
    QSettings settings;
288
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
289

290
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
291 292 293
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
294
#endif
295
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
296
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
297

Don Gagne's avatar
Don Gagne committed
298
    if (_runningUnitTests) {
299
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
300 301
        fClearSettingsOptions = true;
    }
302

303 304
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
305

306
        settings.clear();
307
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
308

309
        // Clear parameter cache
310
        QDir paramDir(ParameterManager::parameterCacheDir());
311 312
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
313 314 315 316 317 318 319 320 321 322 323 324 325
    } else {
        // Determine if upgrade message for settings version bump is required. Check must happen before toolbox is started since
        // that will write some settings.
        if (settings.contains(_settingsVersionKey)) {
            if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
                _settingsUpgraded = true;
            }
        } else if (settings.allKeys().count()) {
            // Settings version key is missing and there are settings. This is an upgrade scenario.
            _settingsUpgraded = true;
        } else {
            settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
        }
326
    }
Gus Grubba's avatar
Gus Grubba committed
327

328 329 330
    // Set up our logging filters
    QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);

331 332 333
    _lastKnownHomePosition.setLatitude(settings.value(_lastKnownHomePositionLatKey, 37.803784).toDouble());
    _lastKnownHomePosition.setLongitude(settings.value(_lastKnownHomePositionLonKey, -122.462276).toDouble());
    _lastKnownHomePosition.setAltitude(settings.value(_lastKnownHomePositionAltKey, 0.0).toDouble());
Don Gagne's avatar
Don Gagne committed
334

dogmaphobic's avatar
dogmaphobic committed
335 336 337 338 339 340 341 342 343
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

344 345
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
346 347

    _toolbox = new QGCToolbox(this);
348
    _toolbox->setChildToolboxes();
349 350
}

Don Gagne's avatar
Don Gagne committed
351 352
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
353
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
354 355 356 357
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
358
#endif
359
    shutdownVideoStreaming();
360
    delete _toolbox;
Gus Grubba's avatar
Gus Grubba committed
361 362 363
    if(_pCorePlugin) {
        delete _pCorePlugin;
    }
Don Gagne's avatar
Don Gagne committed
364 365
}

366
void QGCApplication::_initCommon(void)
367 368
{
    QSettings settings;
369

Don Gagne's avatar
Don Gagne committed
370
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
371

Don Gagne's avatar
Don Gagne committed
372 373
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
374

Don Gagne's avatar
Don Gagne committed
375 376 377 378
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "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");
379
    qmlRegisterUncreatableType<MissionCommandTree>  ("QGroundControl",                  1, 0, "MissionCommandTree",     "Reference only");
Don Gagne's avatar
Don Gagne committed
380

381 382 383 384 385
    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");
386
    qmlRegisterUncreatableType<ParameterManager>    ("QGroundControl.Vehicle",              1, 0, "ParameterManager",       "Reference only");
387 388 389
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",      1, 0, "JoystickManager",        "Reference only");
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",      1, 0, "Joystick",               "Reference only");
    qmlRegisterUncreatableType<QGCPositionManager>  ("QGroundControl.QGCPositionManager",   1, 0, "QGCPositionManager",     "Reference only");
390
    qmlRegisterUncreatableType<QGCMapPolygon>       ("QGroundControl.FlightMap",            1, 0, "QGCMapPolygon",          "Reference only");
391

dogmaphobic's avatar
dogmaphobic committed
392
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
dogmaphobic's avatar
dogmaphobic committed
393
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
394 395 396
    qmlRegisterType<ScreenToolsController>              ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    qmlRegisterType<MainToolBarController>              ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
397 398
    qmlRegisterType<GeoFenceController>                 ("QGroundControl.Controllers", 1, 0, "GeoFenceController");
    qmlRegisterType<RallyPointController>               ("QGroundControl.Controllers", 1, 0, "RallyPointController");
399
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
400
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
Don Gagne's avatar
Don Gagne committed
401
    qmlRegisterType<RCChannelMonitorController>         ("QGroundControl.Controllers", 1, 0, "RCChannelMonitorController");
402
    qmlRegisterType<JoystickConfigController>           ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
403
#ifndef __mobile__
404 405
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
406
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
dogmaphobic's avatar
dogmaphobic committed
407
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
408
    qmlRegisterType<GeoTagController>               ("QGroundControl.Controllers", 1, 0, "GeoTagController");
Don Gagne's avatar
Don Gagne committed
409
#endif
dogmaphobic's avatar
dogmaphobic committed
410

411
    // Register Qml Singletons
412 413 414
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
415 416 417 418 419
}

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

421 422
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
423

424
    // Exit main application when last window is closed
425
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
426

Don Gagne's avatar
Don Gagne committed
427 428 429 430
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
431
    _qmlAppEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel());
Don Gagne's avatar
Don Gagne committed
432 433
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
434
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
435
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
436
    Q_CHECK_PTR(mainWindow);
437

438
    // Now that main window is up check for lost log files
439
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
440
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
441
#endif
442 443

    // Load known link configurations
444
    toolbox()->linkManager()->loadLinkConfigurationList();
445

446
    if (_settingsUpgraded) {
Don Gagne's avatar
Don Gagne committed
447 448 449 450 451 452
        settings.clear();
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
    }

dogmaphobic's avatar
dogmaphobic committed
453 454 455 456 457
    if (getQGCMapEngine()->wasCacheReset()) {
        showMessage("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset.");
    }

Don Gagne's avatar
Don Gagne committed
458 459
    settings.sync();

Gus Grubba's avatar
Gus Grubba committed
460 461 462 463 464 465 466
    //-- Initialize Core Plugin (if any)
    if(_pCorePlugin) {
        if(!_pCorePlugin->init(this)) {
            return false;
        }
    }

467
    return true;
pixhawk's avatar
pixhawk committed
468 469
}

470
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
471
{
472
    return true;
pixhawk's avatar
pixhawk committed
473 474
}

Don Gagne's avatar
Don Gagne committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
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;
490

Don Gagne's avatar
Don Gagne committed
491 492 493
    return settings.value(_promptFlightDataSave, true).toBool();
}

494 495 496 497 498 499 500
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
501 502 503 504 505 506
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

507 508 509 510 511 512
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
513 514 515
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
516 517 518 519
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

520 521
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
522 523
    Q_UNUSED(title);
    showMessage(msg);
524 525 526 527
}

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

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
538 539 540 541
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
542
    QGCMessageBox::critical(title, msg);
543
#endif
544 545
}

546
#ifndef __mobile__
547 548
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
549 550 551 552 553 554
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
555
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
556 557
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
558

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

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

580 581 582 583 584 585
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

Gus Grubba's avatar
Gus Grubba committed
586
void QGCApplication::_loadCurrentStyle()
587
{
Don Gagne's avatar
Don Gagne committed
588
#ifndef __mobile__
589 590
    bool success = true;
    QString styles;
591

592 593 594 595 596 597 598 599 600
    // 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;
    }
601

602 603 604 605 606 607
    if (success && !_styleIsDark) {
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
608
            qWarning() << "Unable to load slave light sheet:";
609 610 611
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
612

613
    setStyleSheet(styles);
614

615 616 617 618
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
619
#endif
620

621
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
622
}
Don Gagne's avatar
Don Gagne committed
623

Don Gagne's avatar
Don Gagne committed
624
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
625
{
Don Gagne's avatar
Don Gagne committed
626 627
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
628
}
629

Don Gagne's avatar
Don Gagne committed
630 631
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
632
{
Don Gagne's avatar
Don Gagne committed
633
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
634

Don Gagne's avatar
Don Gagne committed
635
    QString params;
636
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
637 638
        if (params.isEmpty()) {
            params += name;
639
        } else {
Don Gagne's avatar
Don Gagne committed
640
            params += QString(", %1").arg(name);
641 642
        }
    }
Don Gagne's avatar
Don Gagne committed
643
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
644

645
    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));
646 647
}

Gus Grubba's avatar
Gus Grubba committed
648
QObject* QGCApplication::_rootQmlObject()
Don Gagne's avatar
Don Gagne committed
649 650 651 652
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
653 654 655 656 657 658 659 660 661 662
    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
663 664 665 666
#endif
}


667
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
668
{
Don Gagne's avatar
Don Gagne committed
669 670 671 672 673
    // Special case hack for ArduPilot prearm messages. These show up in the center of the map, so no need for popup.
    if (message.contains("PreArm:")) {
        return;
    }

674 675 676 677 678
    QObject* rootQmlObject = _rootQmlObject();

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

680 681 682 683 684 685 686 687 688
        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
689 690 691 692 693 694 695
}

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

696
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
697
{
698
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
699 700 701
}


702
void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition)
Don Gagne's avatar
Don Gagne committed
703 704 705
{
    QSettings settings;

706 707 708 709
    settings.setValue(_lastKnownHomePositionLatKey, lastKnownHomePosition.latitude());
    settings.setValue(_lastKnownHomePositionLonKey, lastKnownHomePosition.longitude());
    settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude());
    _lastKnownHomePosition = lastKnownHomePosition;
Don Gagne's avatar
Don Gagne committed
710
}
Gus Grubba's avatar
Gus Grubba committed
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

IQGCOptions* QGCApplication::qgcOptions()
{
    return _pQGCOptions;
}

void QGCApplication::_scanAndLoadPlugins()
{
#if defined (QGC_DYNAMIC_PLUGIN)
    //-- Look for plugins (Dynamic)
    QString filter = "*.core.so";
    QString path = QCoreApplication::applicationDirPath();
    QDirIterator it(path, QStringList() << filter, QDir::Files);
    while(it.hasNext()) {
        QString pluginFile = it.next();
        QPluginLoader loader(pluginFile);
        QObject *plugin = loader.instance();
        if(plugin) {
            _pCorePlugin = qobject_cast<IQGCCorePlugin*>(plugin);
            if(_pCorePlugin) {
                _pQGCOptions = _pCorePlugin->uiOptions();
                return;
            }
        } else {
            qWarning() << "Plugin" << pluginFile << " not loaded:" << loader.errorString();
        }
    }
#elif defined (QGC_CUSTOM_BUILD)
    //-- Create custom plugin (Static)
    _pCorePlugin = (IQGCCorePlugin*) new CUSTOMCLASS(this);
    if(_pCorePlugin) {
        _pQGCOptions = _pCorePlugin->uiOptions();
        return;
    }
#endif
    //-- No plugins found, use default options
    _pQGCOptions = new IQGCOptions;
}