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

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

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

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

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

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

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

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

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

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

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

45 46
#include <QDebug>

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

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

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

#ifndef __mobile__
111 112
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
113 114
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
Don Gagne's avatar
Don Gagne committed
115
    #include "MainWindow.h"
116
#endif
117

118
#ifdef QGC_RTLAB_ENABLED
119
    #include "OpalLink.h"
120
#endif
121

Don Gagne's avatar
Don Gagne committed
122 123 124 125 126 127
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
128 129

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

Don Gagne's avatar
Don Gagne committed
131 132 133 134
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
const char* QGCApplication::telemetryFileExtension =     "tlog";

135 136 137 138 139
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";
140 141 142
const char* QGCApplication::_lastKnownHomePositionLatKey    = "LastKnownHomePositionLat";
const char* QGCApplication::_lastKnownHomePositionLonKey    = "LastKnownHomePositionLon";
const char* QGCApplication::_lastKnownHomePositionAltKey    = "LastKnownHomePositionAlt";
Don Gagne's avatar
Don Gagne committed
143

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

147
// Qml Singleton factories
148

Don Gagne's avatar
Don Gagne committed
149
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
150
{
Don Gagne's avatar
Don Gagne committed
151 152
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
153 154
}

155 156 157 158 159
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

160 161
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
162
    // We create this object as a QGCTool even though it isn't in the toolbox
163 164 165 166
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
167 168
}

pixhawk's avatar
pixhawk committed
169 170 171 172 173 174 175 176 177 178
/**
 * @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
 **/

179
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
180 181 182 183
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
#else
184
    : QApplication(argc, argv)
Don Gagne's avatar
Don Gagne committed
185
#endif
186
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
187 188 189
#if defined (__mobile__)
    , _styleIsDark(false)
#else
190
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
191
#endif
dogmaphobic's avatar
dogmaphobic committed
192
    , _fakeMobile(false)
193 194 195
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
196
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
197
    , _bluetoothAvailable(false)
198
    , _lastKnownHomePosition(37.803784, -122.462276, 0.0)
pixhawk's avatar
pixhawk committed
199
{
200 201
    Q_ASSERT(_app == NULL);
    _app = this;
202

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

208 209
#ifdef Q_OS_LINUX
#ifndef __mobile__
210 211 212 213 214 215 216 217 218 219 220 221 222 223
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
            msgBox.setInformativeText("You are runnning QGroundControl as root. "
                                      "You should not do this since it will cause other issues with QGroundControl. "
                                      "QGroundControl will now exit. "
                                      "If you are having serial port issues on Ubuntu, execute the following commands to fix most issues:\n"
                                      "sudo usermod -a -G dialout $USER\n"
                                      "sudo apt-get remove modemmanager");
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.exec();
            _exit(0);
        }
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

dogmaphobic's avatar
dogmaphobic committed
267
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
268
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
269
#else
270
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
271

272 273
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
274

275 276
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
277

278 279 280 281 282 283 284
        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 {
285
            foreach(const QString &rule, logList) {
286 287 288 289
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
290 291 292
    } 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.
293

294 295 296
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
297

298 299 300 301 302 303 304 305 306
        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;
307
            }
308
        } else {
309 310
            loggingDirectoryOk = true;
        }
311

312
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
313
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
314 315 316 317 318 319
            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";
320
                    foreach(const QString &category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
321 322 323 324
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
325
                }
326 327 328
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
329

330 331
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
332
#endif
333

334
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
335 336 337
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
338

Don Gagne's avatar
Don Gagne committed
339
    // Set application information
340 341 342 343 344 345 346 347 348
    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);
349

350
    QString versionString(GIT_TAG);
Daniel Agar's avatar
Daniel Agar committed
351 352 353 354 355
    // 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
356
    this->setApplicationVersion(versionString);
357

358 359
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
360
    QSettings settings;
361
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
362

363
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
364 365 366
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
367
#endif
368
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
369
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
370

Don Gagne's avatar
Don Gagne committed
371
    if (_runningUnitTests) {
372
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
373 374
        fClearSettingsOptions = true;
    }
375

376 377
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
378

379
        settings.clear();
380
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
381

382
        // Clear parameter cache
383 384 385
        QDir paramDir(ParameterLoader::parameterCacheDir());
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
386
    }
Gus Grubba's avatar
Gus Grubba committed
387

388 389 390
    _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
391

dogmaphobic's avatar
dogmaphobic committed
392 393 394 395 396 397 398 399 400
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

401 402
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
403 404

    _toolbox = new QGCToolbox(this);
405 406
}

Don Gagne's avatar
Don Gagne committed
407 408
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
409
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
410 411 412 413
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
414
#endif
415
    shutdownVideoStreaming();
416
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
417 418
}

419
void QGCApplication::_initCommon(void)
420 421
{
    QSettings settings;
422

Don Gagne's avatar
Don Gagne committed
423
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
424

Don Gagne's avatar
Don Gagne committed
425 426
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
427

Don Gagne's avatar
Don Gagne committed
428 429 430 431 432 433 434
    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");

435 436 437 438 439 440 441
    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");
442

dogmaphobic's avatar
dogmaphobic committed
443
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
444
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
Don Gagne's avatar
Don Gagne committed
445 446
    qmlRegisterType<PX4AdvancedFlightModesController>   ("QGroundControl.Controllers", 1, 0, "PX4AdvancedFlightModesController");
    qmlRegisterType<PX4SimpleFlightModesController>     ("QGroundControl.Controllers", 1, 0, "PX4SimpleFlightModesController");
447
    qmlRegisterType<APMAirframeComponentController>     ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
Don Gagne's avatar
Don Gagne committed
448
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
Don Gagne's avatar
Don Gagne committed
449
    qmlRegisterType<APMSensorsComponentController>      ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
Don Gagne's avatar
Don Gagne committed
450 451 452
    qmlRegisterType<SensorsComponentController>         ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>           ("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>           ("QGroundControl.Controllers", 1, 0, "RadioComponentController");
dogmaphobic's avatar
dogmaphobic committed
453
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
454 455 456 457
    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");
458
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
459
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
460

Don Gagne's avatar
Don Gagne committed
461
#ifndef __mobile__
462 463
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
464
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
465
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
dogmaphobic's avatar
dogmaphobic committed
466
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
467
#endif
dogmaphobic's avatar
dogmaphobic committed
468

469
    // Register Qml Singletons
470 471 472
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
473 474 475 476 477
}

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

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

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

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

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

    // Load known link configurations
502
    toolbox()->linkManager()->loadLinkConfigurationList();
503

Don Gagne's avatar
Don Gagne committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    // Show user an upgrade message if the settings version has been bumped up
    bool settingsUpgraded = false;
    if (settings.contains(_settingsVersionKey)) {
        if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
            settingsUpgraded = true;
        }
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
    }

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

    settings.sync();

526
    return true;
pixhawk's avatar
pixhawk committed
527 528
}

529
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
530
{
531
    return true;
pixhawk's avatar
pixhawk committed
532 533
}

Don Gagne's avatar
Don Gagne committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
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;
549

Don Gagne's avatar
Don Gagne committed
550 551 552
    return settings.value(_promptFlightDataSave, true).toBool();
}

553 554 555 556 557 558 559
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
560 561 562 563 564 565
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

566 567 568 569 570 571
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
572 573 574
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
575 576 577 578
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

579 580
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
581 582
    Q_UNUSED(title);
    showMessage(msg);
583 584 585 586
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
587 588 589 590
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
591
    QGCMessageBox::warning(title, msg);
592
#endif
593 594 595 596
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
597 598 599 600
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
601
    QGCMessageBox::critical(title, msg);
602
#endif
603 604
}

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

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

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

639 640 641 642 643 644 645 646
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
647
#ifndef __mobile__
648 649
    bool success = true;
    QString styles;
650

651 652 653 654 655 656 657 658 659
    // 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;
    }
660

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

672
    setStyleSheet(styles);
673

674 675 676 677
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
678
#endif
679

680
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
681
}
Don Gagne's avatar
Don Gagne committed
682

Don Gagne's avatar
Don Gagne committed
683
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
684
{
Don Gagne's avatar
Don Gagne committed
685 686
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
687
}
688

Don Gagne's avatar
Don Gagne committed
689 690
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
691
{
Don Gagne's avatar
Don Gagne committed
692
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
693

Don Gagne's avatar
Don Gagne committed
694
    QString params;
695
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
696 697
        if (params.isEmpty()) {
            params += name;
698
        } else {
Don Gagne's avatar
Don Gagne committed
699
            params += QString(", %1").arg(name);
700 701
        }
    }
Don Gagne's avatar
Don Gagne committed
702
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
703

704
    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));
705 706
}

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


726
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
727
{
Don Gagne's avatar
Don Gagne committed
728 729 730 731 732
    // 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;
    }

733 734 735 736 737
    QObject* rootQmlObject = _rootQmlObject();

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

739 740 741 742 743 744 745 746 747
        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
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
}

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

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

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

765
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
766
{
767
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
}


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
792
}
Don Gagne's avatar
Don Gagne committed
793

794
void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition)
Don Gagne's avatar
Don Gagne committed
795 796 797
{
    QSettings settings;

798 799 800 801
    settings.setValue(_lastKnownHomePositionLatKey, lastKnownHomePosition.latitude());
    settings.setValue(_lastKnownHomePositionLonKey, lastKnownHomePosition.longitude());
    settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude());
    _lastKnownHomePosition = lastKnownHomePosition;
Don Gagne's avatar
Don Gagne committed
802
}