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

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

#ifndef __mobile__
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
#endif
103

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


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

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

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

Don Gagne's avatar
Don Gagne committed
121 122
const char* QGCApplication::_darkStyleFile = ":/res/styles/style-dark.css";
const char* QGCApplication::_lightStyleFile = ":/res/styles/style-light.css";
123

124 125

// Qml Singleton factories
126

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

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

138 139 140 141 142
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new QGroundControlQmlGlobal;
}

dogmaphobic's avatar
dogmaphobic committed
143
#if defined(QGC_GST_STREAMING)
144 145
#ifdef Q_OS_MAC
#ifndef __ios__
146
#ifdef QGC_INSTALL_RELEASE
dogmaphobic's avatar
dogmaphobic committed
147 148 149 150 151 152
static void qgcputenv(const QString& key, const QString& root, const QString& path)
{
    QString value = root + path;
    qputenv(key.toStdString().c_str(), QByteArray(value.toStdString().c_str()));
}
#endif
153 154
#endif
#endif
155
#endif
dogmaphobic's avatar
dogmaphobic committed
156

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

167 168 169 170
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
171
    , _fakeMobile(false)
172 173 174
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
pixhawk's avatar
pixhawk committed
175
{
176 177
    Q_ASSERT(_app == NULL);
    _app = this;
178

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

184
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
185

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

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

200
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
201

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

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

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

213 214 215 216 217 218 219 220 221 222 223 224
        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";
            }
        }
225 226 227
    } 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.
228

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

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

247
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
248
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
249 250 251 252 253 254 255 256 257 258 259
            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;
260
                }
261 262 263
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
264

265 266
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
267
#endif
268

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

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

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

293 294
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
295

296
    QSettings settings;
Don Gagne's avatar
Don Gagne committed
297 298
    qDebug() << "Settings location" << settings.fileName() << settings.isWritable();

299 300 301
#ifdef UNITTEST_BUILD
    Q_ASSERT(settings.isWritable());
#endif
302
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
303
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
304

Don Gagne's avatar
Don Gagne committed
305
    if (_runningUnitTests) {
306
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
307 308
        fClearSettingsOptions = true;
    }
309

310 311 312
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
313
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
314
    }
Gus Grubba's avatar
Gus Grubba committed
315

316 317
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
318 319
}

Don Gagne's avatar
Don Gagne committed
320 321
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
322
    _destroySingletons();
323
    shutdownVideoStreaming();
Don Gagne's avatar
Don Gagne committed
324 325
}

326
void QGCApplication::_initCommon(void)
327 328
{
    QSettings settings;
329

Don Gagne's avatar
Don Gagne committed
330
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
331

Don Gagne's avatar
Don Gagne committed
332
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
dogmaphobic's avatar
dogmaphobic committed
333

334 335 336 337 338 339 340 341
    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");
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                  1, 0, "QmlObjectListModel",     "Reference only");
342 343
    qmlRegisterUncreatableType<QGCQGeoCoordinate>   ("QGroundControl",                  1, 0, "QGCQGeoCoordinate",      "Reference only");
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
344 345 346
    qmlRegisterUncreatableType<VideoSurface>        ("QGroundControl",                  1, 0, "VideoSurface",           "Reference only");
    qmlRegisterUncreatableType<VideoReceiver>       ("QGroundControl",                  1, 0, "VideoReceiver",          "Reference only");

347 348 349 350 351 352 353
    qmlRegisterType<ParameterEditorController>      ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    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");
354
    qmlRegisterType<MainToolBarController>          ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
355
    qmlRegisterType<MissionController>              ("QGroundControl.Controllers", 1, 0, "MissionController");
356 357
    qmlRegisterType<FlightDisplayViewController>    ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");

Don Gagne's avatar
Don Gagne committed
358
#ifndef __mobile__
359 360
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
361
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
362
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
363
#endif
dogmaphobic's avatar
dogmaphobic committed
364

365
    // Register Qml Singletons
366 367 368
    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
369

370 371 372 373 374
    // 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;
375
        }
376 377 378
    } 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
379 380
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
381
    }
382

383
    if (settingsUpgraded) {
384
        settings.clear();
385
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
386 387 388
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
389
    }
390

Don Gagne's avatar
Don Gagne committed
391
    // Load saved files location and validate
392

Don Gagne's avatar
Don Gagne committed
393 394 395
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
396 397 398 399
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
dogmaphobic's avatar
dogmaphobic committed
400

401 402
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
403

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

Don Gagne's avatar
Don Gagne committed
406
        QDir documentsDir(documentsLocation);
407 408 409
        if (!documentsDir.exists()) {
            qWarning() << "Documents directory doesn't exist" << documentsDir.absolutePath();
        }
410

411
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
412
        Q_UNUSED(pathCreated);
413 414
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
415
    }
416

Don Gagne's avatar
Don Gagne committed
417 418 419 420 421
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
422
    qDebug() << "Saved files location" << savedFilesLocation;
423
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
Don Gagne's avatar
Don Gagne committed
424 425

    settings.sync();
426 427 428 429 430
}

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

432
    _createSingletons();
433

434 435 436
#ifdef __mobile__
    _styleIsDark = false;
#else
437
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
438
#endif
439
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
440

441 442 443
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
444
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
445
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
446
    Q_CHECK_PTR(mainWindow);
447

448 449 450 451 452
    // 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()) {
453 454 455
        QGCMessageBox::warning(
            tr("Bad save location"),
            tr("The location to save files to is invalid, or cannot be written to. Please provide a new one."));
456 457
        mainWindow->showSettings();
    }
458

Don Gagne's avatar
Don Gagne committed
459
#ifndef __mobile__
460
    // Now that main window is up check for lost log files
461 462
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
463
#endif
464 465 466 467

    // Load known link configurations
    LinkManager::instance()->loadLinkConfigurationList();

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

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

Don Gagne's avatar
Don Gagne committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
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
497

Don Gagne's avatar
Don Gagne committed
498
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
499
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
500 501 502
    if (!tempFile.open()) {
        return false;
    }
503

504
    tempFile.remove();
505

Don Gagne's avatar
Don Gagne committed
506 507 508 509 510 511
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
513 514 515 516 517 518 519
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
521
    location = parentDir.filePath(_savedFileParameterDirectoryName);
522

Don Gagne's avatar
Don Gagne committed
523 524 525 526 527 528 529
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
530

Don Gagne's avatar
Don Gagne committed
531 532 533 534 535 536 537
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
539
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
540

Don Gagne's avatar
Don Gagne committed
541 542 543 544 545 546 547
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
548

Don Gagne's avatar
Don Gagne committed
549 550 551 552 553 554
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
556 557 558 559 560 561 562 563 564 565 566 567
    return settings.value(_promptFlightDataSave, true).toBool();
}

void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
568 569 570 571 572 573 574 575 576
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

/// @brief We create all the non-ui based singletons here instead of allowing them to be created randomly
///         by calls to instance. The reason being that depending on boot sequence the singleton may end
///         up being creating on something other than the main thread.
void QGCApplication::_createSingletons(void)
{
Don Gagne's avatar
Don Gagne committed
577
    // The order here is important since the singletons reference each other
578

579 580 581 582
    // No dependencies
    FlightMapSettings* flightMapSettings = FlightMapSettings::_createSingleton();
    Q_UNUSED(flightMapSettings);
    Q_ASSERT(flightMapSettings);
dogmaphobic's avatar
dogmaphobic committed
583

584 585 586 587 588
    // No dependencies
    HomePositionManager* homePositionManager = HomePositionManager::_createSingleton();
    Q_UNUSED(homePositionManager);
    Q_ASSERT(homePositionManager);

Don Gagne's avatar
Don Gagne committed
589 590 591 592
    // No dependencies
    FirmwarePlugin* firmwarePlugin = GenericFirmwarePlugin::_createSingleton();
    Q_UNUSED(firmwarePlugin);
    Q_ASSERT(firmwarePlugin);
dogmaphobic's avatar
dogmaphobic committed
593

Don Gagne's avatar
Don Gagne committed
594 595
    // No dependencies
    firmwarePlugin = PX4FirmwarePlugin::_createSingleton();
596
    firmwarePlugin = ArduCopterFirmwarePlugin::_createSingleton();
597 598
    firmwarePlugin = ArduPlaneFirmwarePlugin::_createSingleton();
    firmwarePlugin = ArduRoverFirmwarePlugin::_createSingleton();
dogmaphobic's avatar
dogmaphobic committed
599

Don Gagne's avatar
Don Gagne committed
600 601 602 603
    // No dependencies
    FirmwarePluginManager* firmwarePluginManager = FirmwarePluginManager::_createSingleton();
    Q_UNUSED(firmwarePluginManager);
    Q_ASSERT(firmwarePluginManager);
dogmaphobic's avatar
dogmaphobic committed
604

605 606 607 608
    // No dependencies
    MultiVehicleManager* multiVehicleManager = MultiVehicleManager::_createSingleton();
    Q_UNUSED(multiVehicleManager);
    Q_ASSERT(multiVehicleManager);
dogmaphobic's avatar
dogmaphobic committed
609

610 611 612 613
    // No dependencies
    JoystickManager* joystickManager = JoystickManager::_createSingleton();
    Q_UNUSED(joystickManager);
    Q_ASSERT(joystickManager);
dogmaphobic's avatar
dogmaphobic committed
614

Don Gagne's avatar
Don Gagne committed
615
    // No dependencies
616 617 618
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
619

Don Gagne's avatar
Don Gagne committed
620
    // No dependencies
Don Gagne's avatar
Don Gagne committed
621
    LinkManager* linkManager = LinkManager::_createSingleton();
622 623 624
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

625
    // Need MultiVehicleManager
Don Gagne's avatar
Don Gagne committed
626
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
627 628
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
629

630
    // Need MultiVehicleManager
631
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
632 633 634
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

635
    // Needs MultiVehicleManager
Don Gagne's avatar
Don Gagne committed
636
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
637 638
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
639

640 641 642 643
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
644

645 646
}

Don Gagne's avatar
Don Gagne committed
647
void QGCApplication::_destroySingletons(void)
648
{
649 650 651
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
652
    }
653

654 655 656 657 658 659 660
    if (LinkManager::instance(true /* nullOk */)) {
        // This will close/delete all connections
        LinkManager::instance()->_shutdown();
    }

    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
661

Don Gagne's avatar
Don Gagne committed
662
    // Take down singletons in reverse order of creation
663

664
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
665
    FactSystem::_deleteSingleton();
666
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
667 668
    AutoPilotPluginManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
669
    GAudioOutput::_deleteSingleton();
670
    JoystickManager::_deleteSingleton();
671
    MultiVehicleManager::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
672 673 674
    FirmwarePluginManager::_deleteSingleton();
    GenericFirmwarePlugin::_deleteSingleton();
    PX4FirmwarePlugin::_deleteSingleton();
675
    ArduCopterFirmwarePlugin::_deleteSingleton();
676 677
    ArduPlaneFirmwarePlugin::_deleteSingleton();
    ArduRoverFirmwarePlugin::_deleteSingleton();
678 679
    HomePositionManager::_deleteSingleton();
    FlightMapSettings::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
680
}
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
    QGCMessageBox::information(title, msg);
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
    QGCMessageBox::warning(title, msg);
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
    QGCMessageBox::critical(title, msg);
}

void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile)
{
699 700 701 702 703 704 705 706 707
    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
708

709 710 711
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
712
                // if the file cannot be removed, prompt user and ask new path
713
                saveError = true;
714
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
715 716 717
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
718
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
719 720 721
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
722 723
    QFile::remove(tempLogfile);
}
724 725 726 727

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

729 730 731 732 733 734 735 736 737 738
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

void QGCApplication::_loadCurrentStyle(void)
{
    bool success = true;
    QString styles;
739

740 741
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
742

743 744 745 746 747 748 749 750 751
    // 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;
    }
752

753 754 755 756 757 758 759 760 761 762 763
    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
764

765
    setStyleSheet(styles);
766

767 768 769 770
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
771

772
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
773

774 775 776
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
777

Don Gagne's avatar
Don Gagne committed
778
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
779
{
Don Gagne's avatar
Don Gagne committed
780 781
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
782
}
783

Don Gagne's avatar
Don Gagne committed
784 785
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
786
{
Don Gagne's avatar
Don Gagne committed
787
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
788

Don Gagne's avatar
Don Gagne committed
789 790 791 792
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
793
        } else {
Don Gagne's avatar
Don Gagne committed
794
            params += QString(", %1").arg(name);
795 796
        }
    }
Don Gagne's avatar
Don Gagne committed
797
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
798

799 800 801 802 803 804
    QGCMessageBox::critical(
        "Missing Parameters",
        QString("Parameters missing from firmware: %1.\n\n"
                "You should quit QGroundControl immediately and update your firmware.").arg(params));
}

Don Gagne's avatar
Don Gagne committed
805 806 807 808
void QGCApplication::showToolBarMessage(const QString& message)
{
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
809
        mainWindow->showToolbarMessage(message);
Don Gagne's avatar
Don Gagne committed
810 811 812 813
    } else {
        QGCMessageBox::information("", message);
    }
}