QGCApplication.cc 27.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>

40 41
#include <QDebug>

42
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
43

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

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

#ifndef __mobile__
98 99
    #include "QGCFileDialog.h"
    #include "QGCMessageBox.h"
100 101
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
Don Gagne's avatar
Don Gagne committed
102
    #include "MainWindow.h"
103
#endif
104

105
#ifdef QGC_RTLAB_ENABLED
106
    #include "OpalLink.h"
107
#endif
108 109 110


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

112 113 114 115 116 117
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey          = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave           = "PromptFLightDataSave";
const char* QGCApplication::_promptFlightDataSaveNotArmed   = "PromptFLightDataSaveNotArmed";
const char* QGCApplication::_styleKey                       = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
118

dogmaphobic's avatar
dogmaphobic committed
119 120 121
const char* QGCApplication::_defaultSavedFileDirectoryName      = "QGroundControl";
const char* QGCApplication::_savedFileMavlinkLogDirectoryName   = "FlightData";
const char* QGCApplication::_savedFileParameterDirectoryName    = "SavedParameters";
pixhawk's avatar
pixhawk committed
122

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

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
    return new QGroundControlQmlGlobal(qgcApp()->toolbox());
143 144
}

pixhawk's avatar
pixhawk committed
145 146 147 148 149 150 151 152 153 154
/**
 * @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
 **/

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

177
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
178
#ifndef __android__
179
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
180
#endif
dogmaphobic's avatar
dogmaphobic committed
181

182
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
183

184
    bool fClearSettingsOptions = false; // Clear stored settings
185 186
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
187

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

198
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
199

dogmaphobic's avatar
dogmaphobic committed
200
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
201
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
202
#else
203
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
204

205 206
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
207

208 209
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
210

211 212 213 214 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 {
            foreach(QString rule, logList) {
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
223 224 225
    } 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.
226

227 228 229
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
230

231 232 233 234 235 236 237 238 239
        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;
240
            }
241
        } else {
242 243
            loggingDirectoryOk = true;
        }
244

245
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
246
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
247 248 249 250 251 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";
                    foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
258
                }
259 260 261
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
262

263 264
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
265
#endif
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

dogmaphobic's avatar
dogmaphobic committed
283
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
284 285 286 287 288
    // 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
289
    this->setApplicationVersion(versionString);
290

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

302
    QSettings settings;
Don Gagne's avatar
Don Gagne committed
303 304
    qDebug() << "Settings location" << settings.fileName() << settings.isWritable();

305 306 307
#ifdef UNITTEST_BUILD
    Q_ASSERT(settings.isWritable());
#endif
308
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
309
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
310

Don Gagne's avatar
Don Gagne committed
311
    if (_runningUnitTests) {
312
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
313 314
        fClearSettingsOptions = true;
    }
315

316 317 318
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
319
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
320
    }
Gus Grubba's avatar
Gus Grubba committed
321

322 323
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
324 325

    _toolbox = new QGCToolbox(this);
326 327
}

Don Gagne's avatar
Don Gagne committed
328 329
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
330
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
331 332 333 334
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
335
#endif
336
    shutdownVideoStreaming();
337
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
338 339
}

340
void QGCApplication::_initCommon(void)
341 342
{
    QSettings settings;
343

Don Gagne's avatar
Don Gagne committed
344
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
345

Don Gagne's avatar
Don Gagne committed
346 347
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
348

Don Gagne's avatar
Don Gagne committed
349 350 351 352 353 354 355
    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");

356 357 358 359 360 361 362
    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");
363

Don Gagne's avatar
Don Gagne committed
364 365 366 367 368 369 370 371 372 373 374
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    qmlRegisterType<FlightModesComponentController>     ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
    qmlRegisterType<AirframeComponentController>        ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
    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");
375

Don Gagne's avatar
Don Gagne committed
376
#ifndef __mobile__
377 378
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
379
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
380
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
381
#endif
dogmaphobic's avatar
dogmaphobic committed
382

383
    // Register Qml Singletons
384 385 386
    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
387

388 389 390 391 392
    // 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;
393
        }
394 395 396
    } 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
397 398
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
399
    }
400

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

Don Gagne's avatar
Don Gagne committed
408
    // Load saved files location and validate
409

Don Gagne's avatar
Don Gagne committed
410 411 412
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
413 414 415 416
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
dogmaphobic's avatar
dogmaphobic committed
417

418 419
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
420

Don Gagne's avatar
Don Gagne committed
421
        QString documentsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
422

Don Gagne's avatar
Don Gagne committed
423
        QDir documentsDir(documentsLocation);
424 425 426
        if (!documentsDir.exists()) {
            qWarning() << "Documents directory doesn't exist" << documentsDir.absolutePath();
        }
427

428
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
429
        Q_UNUSED(pathCreated);
430 431
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
432
    }
433

Don Gagne's avatar
Don Gagne committed
434 435 436 437 438
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
439
    qDebug() << "Saved files location" << savedFilesLocation;
440
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
Don Gagne's avatar
Don Gagne committed
441 442

    settings.sync();
443 444 445 446 447
}

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

449 450
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
451

452 453 454
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

Don Gagne's avatar
Don Gagne committed
455 456 457 458 459 460 461
#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
462
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
463
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
464
    Q_CHECK_PTR(mainWindow);
465

466 467 468 469 470
    // If we made it this far and we still don't have a location. Either the specfied location was invalid
    // or we coudn't create a default location. Either way, we need to let the user know and prompt for a new
    /// settings.
    QString savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
    if (savedFilesLocation.isEmpty()) {
Don Gagne's avatar
Don Gagne committed
471
        showMessage("The location to save files to is invalid, or cannot be written to. Please provide a new one.");
472
    }
473

474
    // Now that main window is up check for lost log files
475
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
476
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
477
#endif
478 479

    // Load known link configurations
480
    toolbox()->linkManager()->loadLinkConfigurationList();
481

482
    return true;
pixhawk's avatar
pixhawk committed
483 484
}

485
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
486
{
487
    return true;
pixhawk's avatar
pixhawk committed
488 489
}

Don Gagne's avatar
Don Gagne committed
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
void QGCApplication::deleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.setValue(_deleteAllSettingsKey, true);
}

void QGCApplication::clearDeleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.remove(_deleteAllSettingsKey);
}

void QGCApplication::setSavedFilesLocation(QString& location)
{
    QSettings settings;
    settings.setValue(_savedFilesLocationKey, location);
}

bool QGCApplication::validatePossibleSavedFilesLocation(QString& location)
{
    // Make sure we can write to the directory
511

Don Gagne's avatar
Don Gagne committed
512
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
513
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
514 515 516
    if (!tempFile.open()) {
        return false;
    }
517

518
    tempFile.remove();
519

Don Gagne's avatar
Don Gagne committed
520 521 522 523 524 525
    return true;
}

QString QGCApplication::savedFilesLocation(void)
{
    QSettings settings;
526

Don Gagne's avatar
Don Gagne committed
527 528 529 530 531 532 533
    return settings.value(_savedFilesLocationKey).toString();
}

QString QGCApplication::savedParameterFilesLocation(void)
{
    QString location;
    QDir    parentDir(savedFilesLocation());
534

Don Gagne's avatar
Don Gagne committed
535
    location = parentDir.filePath(_savedFileParameterDirectoryName);
536

Don Gagne's avatar
Don Gagne committed
537 538 539 540 541 542 543
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
544

Don Gagne's avatar
Don Gagne committed
545 546 547 548 549 550 551
    return location;
}

QString QGCApplication::mavlinkLogFilesLocation(void)
{
    QString location;
    QDir    parentDir(savedFilesLocation());
552

Don Gagne's avatar
Don Gagne committed
553
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
554

Don Gagne's avatar
Don Gagne committed
555 556 557 558 559 560 561
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
562

Don Gagne's avatar
Don Gagne committed
563 564 565 566 567 568
    return location;
}

bool QGCApplication::promptFlightDataSave(void)
{
    QSettings settings;
569

Don Gagne's avatar
Don Gagne committed
570 571 572
    return settings.value(_promptFlightDataSave, true).toBool();
}

573 574 575 576 577 578 579
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

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

Don Gagne's avatar
Don Gagne committed
580 581 582 583 584 585
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

586 587 588 589 590 591
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
592 593 594
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
595 596 597 598
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

599 600
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
601 602
    Q_UNUSED(title);
    showMessage(msg);
603 604 605 606
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
607 608 609 610
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
611
    QGCMessageBox::warning(title, msg);
612
#endif
613 614 615 616
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
617 618 619 620
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
621
    QGCMessageBox::critical(title, msg);
622
#endif
623 624
}

625
#ifndef __mobile__
626 627
void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
628 629 630 631 632 633 634 635 636
    bool saveError;
    do{
        saveError = false;
        QString saveFilename = QGCFileDialog::getSaveFileName(
            MainWindow::instance(),
            tr("Save Flight Data Log"),
            qgcApp()->mavlinkLogFilesLocation(),
            tr("Flight Data Log Files (*.mavlink)"),
            "mavlink");
dogmaphobic's avatar
dogmaphobic committed
637

638 639 640
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
641
                // if the file cannot be removed, prompt user and ask new path
642
                saveError = true;
643
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
644 645 646
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
647
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
648 649 650
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
651 652
    QFile::remove(tempLogfile);
}
653
#endif
654 655 656 657

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

659 660 661 662 663 664 665 666
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
Don Gagne's avatar
Don Gagne committed
667
#ifndef __mobile__
668 669
    bool success = true;
    QString styles;
670

671 672 673 674 675 676 677 678 679
    // 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;
    }
680

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

693
    setStyleSheet(styles);
694

695 696 697 698
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
699
#endif
700

701
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
702
}
Don Gagne's avatar
Don Gagne committed
703

Don Gagne's avatar
Don Gagne committed
704
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
705
{
Don Gagne's avatar
Don Gagne committed
706 707
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
708
}
709

Don Gagne's avatar
Don Gagne committed
710 711
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
712
{
Don Gagne's avatar
Don Gagne committed
713
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
714

Don Gagne's avatar
Don Gagne committed
715 716 717 718
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
719
        } else {
Don Gagne's avatar
Don Gagne committed
720
            params += QString(", %1").arg(name);
721 722
        }
    }
Don Gagne's avatar
Don Gagne committed
723
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
724

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

Don Gagne's avatar
Don Gagne committed
728 729 730 731 732 733 734 735 736 737
QObject* QGCApplication::_rootQmlObject(void)
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
    return MainWindow::instance()->rootQmlObject();
#endif
}


738
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
739
{
Don Gagne's avatar
Don Gagne committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
    QVariant varReturn;
    QVariant varMessage = QVariant::fromValue(message);

    QMetaObject::invokeMethod(_rootQmlObject(), "showMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
}

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