QGCApplication.cc 27.5 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 40
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QSplashScreen>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>

41 42
#include <QDebug>

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

lm's avatar
lm committed
45
#include "configuration.h"
46
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
47
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
48
#include "MainWindow.h"
pixhawk's avatar
pixhawk committed
49
#include "GAudioOutput.h"
50
#include "CmdLineOptParser.h"
Don Gagne's avatar
Don Gagne committed
51
#include "QGCMessageBox.h"
52 53 54 55
#include "MainWindow.h"
#include "UDPLink.h"
#include "QGCSingleton.h"
#include "LinkManager.h"
56
#include "HomePositionManager.h"
57
#include "UASMessageHandler.h"
58
#include "AutoPilotPluginManager.h"
59
#include "QGCTemporaryFile.h"
60
#include "QGCFileDialog.h"
61
#include "QGCPalette.h"
62
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
63 64
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
65
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
66 67 68
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
69
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
70
#include "RadioComponentController.h"
Don Gagne's avatar
Don Gagne committed
71
#include "ScreenToolsController.h"
72 73
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
74
#include "FirmwarePluginManager.h"
75
#include "MultiVehicleManager.h"
Don Gagne's avatar
Don Gagne committed
76
#include "Generic/GenericFirmwarePlugin.h"
Don Gagne's avatar
Don Gagne committed
77
#include "APM/APMFirmwarePlugin.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 86 87 88 89 90 91 92

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

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

94
#ifdef QGC_RTLAB_ENABLED
95
    #include "OpalLink.h"
96
#endif
97 98 99


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

Don Gagne's avatar
Don Gagne committed
101 102 103 104
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
105
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
106 107 108 109

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

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

114 115

// Qml Singleton factories
116

Don Gagne's avatar
Don Gagne committed
117
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
118
{
Don Gagne's avatar
Don Gagne committed
119 120
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
121 122
}

123 124 125 126 127
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

dogmaphobic's avatar
dogmaphobic committed
128
#if defined(QGC_GST_STREAMING)
129 130
#ifdef Q_OS_MAC
#ifndef __ios__
131
#ifdef QGC_INSTALL_RELEASE
dogmaphobic's avatar
dogmaphobic committed
132 133 134 135 136 137
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
138 139
#endif
#endif
140
#endif
dogmaphobic's avatar
dogmaphobic committed
141

pixhawk's avatar
pixhawk committed
142 143 144 145 146 147 148 149 150 151
/**
 * @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
 **/

152 153 154 155
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
156
	, _fakeMobile(false)
157
    , _useNewMissionEditor(false)
158 159 160
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
pixhawk's avatar
pixhawk committed
161
{
162 163
    Q_ASSERT(_app == NULL);
    _app = this;
164

165
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
166
#ifndef __android__
167
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
168
#endif
169 170 171 172 173 174 175 176 177
    
    // Parse command line options
    
    bool fClearSettingsOptions = false; // Clear stored settings
    bool fullLogging = false; // Turn on all logging
    
    CmdLineOpt_t rgCmdLineOptions[] = {
        { "--clear-settings",   &fClearSettingsOptions, QString() },
        { "--full-logging",     &fullLogging,           QString() },
178
		{ "--fake-mobile",      &_fakeMobile,           QString() },
179 180 181
#ifdef QT_DEBUG
        { "--test-high-dpi",    &_testHighDPI,          QString() },
#endif
182 183 184 185
        // Add additional command line option flags here
    };
    
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
186

dogmaphobic's avatar
dogmaphobic committed
187
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
188
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
189 190 191 192
#else
    if (fullLogging) {
        QLoggingCategory::setFilterRules(QStringLiteral("*Log=true"));
    } else {
Don Gagne's avatar
Don Gagne committed
193 194 195 196 197
        if (_runningUnitTests) {
            // We need to turn off these warnings until the firmware meta data is cleaned up
            QLoggingCategory::setFilterRules(QStringLiteral("PX4ParameterLoaderLog.warning=false"));
        }
        
198 199
        // 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.
200

201 202 203
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
204

205 206 207 208 209 210 211 212 213
        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;
214
            }
215
        } else {
216 217
            loggingDirectoryOk = true;
        }
218

219 220 221 222 223 224 225 226 227 228 229 230 231
        if (loggingDirectoryOk) {
            qDebug () << iniFileLocation;
            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;
232
                }
233 234 235
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
236
#endif
237

238
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
239 240 241
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
Don Gagne's avatar
Don Gagne committed
242
    
Don Gagne's avatar
Don Gagne committed
243
    // Set application information
244 245 246 247 248 249 250 251 252
    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);
253

254 255 256 257
    // Version string is build from component parts. Format is:
    //  vMajor.Minor.BuildNumber BuildType
    QString versionString("v%1.%2.%3 %4");
    versionString = versionString.arg(QGC_APPLICATION_VERSION_MAJOR).arg(QGC_APPLICATION_VERSION_MINOR).arg(QGC_APPLICATION_VERSION_BUILDNUMBER).arg(QGC_APPLICATION_VERSION_BUILDTYPE);
Don Gagne's avatar
Don Gagne committed
258
    this->setApplicationVersion(versionString);
259

260 261
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
262

263
    QSettings settings;
264 265 266 267
#ifdef UNITTEST_BUILD
    qDebug() << "Settings location" << settings.fileName();
    Q_ASSERT(settings.isWritable());
#endif
268
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
269
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
270

Don Gagne's avatar
Don Gagne committed
271
    if (_runningUnitTests) {
272
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
273 274
        fClearSettingsOptions = true;
    }
275

276 277 278
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
279
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
280
    }
Gus Grubba's avatar
Gus Grubba committed
281

282 283
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
Gus Grubba's avatar
Gus Grubba committed
284

285 286
}

Don Gagne's avatar
Don Gagne committed
287 288
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
289
    _destroySingletons();
290
    shutdownVideoStreaming();
Don Gagne's avatar
Don Gagne committed
291 292
}

293
void QGCApplication::_initCommon(void)
294 295
{
    QSettings settings;
296

Don Gagne's avatar
Don Gagne committed
297 298 299 300
    // Register our Qml objects
    
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
    
301 302 303 304
    qmlRegisterUncreatableType<AutoPilotPlugin>     ("QGroundControl.AutoPilotPlugin",  1, 0, "AutoPilotPlugin",    "Can only reference, cannot create");
    qmlRegisterUncreatableType<VehicleComponent>    ("QGroundControl.AutoPilotPlugin",  1, 0, "VehicleComponent",   "Can only reference, cannot create");
    qmlRegisterUncreatableType<Vehicle>             ("QGroundControl.Vehicle",          1, 0, "Vehicle",            "Can only reference, cannot create");
    qmlRegisterUncreatableType<MissionItem>         ("QGroundControl.Vehicle",          1, 0, "MissionItem",        "Can only reference, cannot create");
Don Gagne's avatar
Don Gagne committed
305
    qmlRegisterUncreatableType<MissionManager>      ("QGroundControl.Vehicle",          1, 0, "MissionManager",     "Can only reference, cannot create");
306 307 308
    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");
Don Gagne's avatar
Don Gagne committed
309
    
310 311 312 313 314 315 316 317 318
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<ParameterEditorController>      ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
    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");
Don Gagne's avatar
Don Gagne committed
319 320
    
#ifndef __mobile__
321
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
322
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
323 324
#endif
    
325 326 327
    // Register Qml Singletons
    qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>   ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
Don Gagne's avatar
Don Gagne committed
328
    
329 330 331 332 333
    // 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;
334
        }
335 336 337 338
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
339

340
    if (settingsUpgraded) {
341
        settings.clear();
342
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
343 344 345
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
346
    }
347

Don Gagne's avatar
Don Gagne committed
348
    // Load saved files location and validate
349

Don Gagne's avatar
Don Gagne committed
350 351 352
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
353 354 355 356 357 358 359
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
    
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
360

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

Don Gagne's avatar
Don Gagne committed
363
        QDir documentsDir(documentsLocation);
364 365 366
        if (!documentsDir.exists()) {
            qWarning() << "Documents directory doesn't exist" << documentsDir.absolutePath();
        }
367

368
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
369
        Q_UNUSED(pathCreated);
370 371
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
372
    }
373

Don Gagne's avatar
Don Gagne committed
374 375 376 377 378
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
379
    qDebug() << "Saved files location" << savedFilesLocation;
380 381 382 383 384 385
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
}

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

387
    _createSingletons();
388

389 390 391
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
    
392 393 394
    // Temp hack for new mission editor
    _useNewMissionEditor = settings.value("UseNewMissionEditor", false).toBool();
    
395
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
396
    QPixmap splashImage(":/res/SplashScreen");
397
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
398 399
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
400
    splashScreen->show();
lm's avatar
lm committed
401
    processEvents();
402
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
403 404 405
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
406
    // Start the user interface
407
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
408
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
409
    Q_CHECK_PTR(mainWindow);
410

411 412 413 414 415
    // 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()) {
416 417 418
        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."));
419 420
        mainWindow->showSettings();
    }
421

lm's avatar
lm committed
422
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
423 424
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
425

426
    // Now that main window is up check for lost log files
427 428
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
429 430 431 432

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

433
    return true;
pixhawk's avatar
pixhawk committed
434 435
}

436
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
437
{
438
    return true;
pixhawk's avatar
pixhawk committed
439 440
}

Don Gagne's avatar
Don Gagne committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
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
462

Don Gagne's avatar
Don Gagne committed
463
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
464
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
465 466 467
    if (!tempFile.open()) {
        return false;
    }
468

469
    tempFile.remove();
470

Don Gagne's avatar
Don Gagne committed
471 472 473 474 475 476
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
478 479 480 481 482 483 484
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
486
    location = parentDir.filePath(_savedFileParameterDirectoryName);
487

Don Gagne's avatar
Don Gagne committed
488 489 490 491 492 493 494
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
495

Don Gagne's avatar
Don Gagne committed
496 497 498 499 500 501 502
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
504
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
505

Don Gagne's avatar
Don Gagne committed
506 507 508 509 510 511 512
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
513

Don Gagne's avatar
Don Gagne committed
514 515 516 517 518 519
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
521 522 523 524 525 526 527 528 529 530 531 532
    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)
{
533 534 535 536 537 538 539 540 541
    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
542
    // The order here is important since the singletons reference each other
543

Don Gagne's avatar
Don Gagne committed
544 545 546 547 548 549 550
    // No dependencies
    FirmwarePlugin* firmwarePlugin = GenericFirmwarePlugin::_createSingleton();
    Q_UNUSED(firmwarePlugin);
    Q_ASSERT(firmwarePlugin);
    
    // No dependencies
    firmwarePlugin = PX4FirmwarePlugin::_createSingleton();
Don Gagne's avatar
Don Gagne committed
551
    firmwarePlugin = APMFirmwarePlugin::_createSingleton();
Don Gagne's avatar
Don Gagne committed
552 553 554 555 556 557
    
    // No dependencies
    FirmwarePluginManager* firmwarePluginManager = FirmwarePluginManager::_createSingleton();
    Q_UNUSED(firmwarePluginManager);
    Q_ASSERT(firmwarePluginManager);
    
558 559 560 561 562
    // No dependencies
    MultiVehicleManager* multiVehicleManager = MultiVehicleManager::_createSingleton();
    Q_UNUSED(multiVehicleManager);
    Q_ASSERT(multiVehicleManager);
    
563 564 565 566 567
    // No dependencies
    JoystickManager* joystickManager = JoystickManager::_createSingleton();
    Q_UNUSED(joystickManager);
    Q_ASSERT(joystickManager);
    
Don Gagne's avatar
Don Gagne committed
568
    // No dependencies
569 570 571
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
572

Don Gagne's avatar
Don Gagne committed
573
    // No dependencies
Don Gagne's avatar
Don Gagne committed
574
    LinkManager* linkManager = LinkManager::_createSingleton();
575 576 577
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
578
    // Needs LinkManager
579
    HomePositionManager* uasManager = HomePositionManager::_createSingleton();
580 581 582
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

583
    // Need HomePositionManager
Don Gagne's avatar
Don Gagne committed
584
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
585 586
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
587

588
    // Need HomePositionManager
589
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
590 591 592
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

593
    // Needs HomePositionManager
Don Gagne's avatar
Don Gagne committed
594
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
595 596
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
597

598 599 600 601
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
602

603 604
}

Don Gagne's avatar
Don Gagne committed
605
void QGCApplication::_destroySingletons(void)
606
{
607 608 609
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
610

611 612 613 614 615 616 617
    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);
618

Don Gagne's avatar
Don Gagne committed
619
    // Take down singletons in reverse order of creation
620

621
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
622
    FactSystem::_deleteSingleton();
623
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
624
    AutoPilotPluginManager::_deleteSingleton();
625
    HomePositionManager::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
626
    LinkManager::_deleteSingleton();
627
    GAudioOutput::_deleteSingleton();
628
    JoystickManager::_deleteSingleton();
629
    MultiVehicleManager::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
630 631 632
    FirmwarePluginManager::_deleteSingleton();
    GenericFirmwarePlugin::_deleteSingleton();
    PX4FirmwarePlugin::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
633
    APMFirmwarePlugin::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
634
}
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

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)
{
653 654 655 656 657 658 659 660 661 662 663 664 665
    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");
    
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
666
                // if the file cannot be removed, prompt user and ask new path
667
                saveError = true;
668
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
669 670 671
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
672
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
673 674 675
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
676 677
    QFile::remove(tempLogfile);
}
678 679 680 681

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

683 684 685 686 687 688 689 690 691 692
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

694 695
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
696

697 698 699 700 701 702 703 704 705
    // 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;
    }
706

707 708 709 710 711 712 713 714 715 716 717
    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;
        }
    }
Don Gagne's avatar
Don Gagne committed
718
    
719
    // Now that we have the styles loaded we need to adjust the font sizes.
Don Gagne's avatar
Don Gagne committed
720 721 722 723 724 725
    
    QString fSmall  = QString("%1px;").arg(ScreenToolsController::smallFontPixelSize_s());
    QString fNormal = QString("%1px;").arg(ScreenToolsController::defaultFontPixelSize_s());
    QString fLarge  = QString("%1px;").arg(ScreenToolsController::largeFontPixelSize_s());
    
    qDebug() << fSmall << fNormal << fLarge;
726 727 728 729 730

    styles.replace("FONT_SMALL",  fSmall);
    styles.replace("FONT_NORMAL", fNormal);
    styles.replace("FONT_LARGE",  fLarge);
    setStyleSheet(styles);
731

732 733 734 735
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
736

737
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
738

739 740 741
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
742

Don Gagne's avatar
Don Gagne committed
743
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
744
{
Don Gagne's avatar
Don Gagne committed
745 746
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
747
}
748

Don Gagne's avatar
Don Gagne committed
749 750
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
751
{
Don Gagne's avatar
Don Gagne committed
752
    Q_ASSERT(_missingParams.count());
753
    
Don Gagne's avatar
Don Gagne committed
754 755 756 757
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
758
        } else {
Don Gagne's avatar
Don Gagne committed
759
            params += QString(", %1").arg(name);
760 761
        }
    }
Don Gagne's avatar
Don Gagne committed
762
    _missingParams.clear();
763
    
764 765 766 767 768 769
    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
770 771 772 773 774 775 776 777 778
void QGCApplication::showToolBarMessage(const QString& message)
{
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        mainWindow->getMainToolBar()->showToolBarMessage(message);
    } else {
        QGCMessageBox::information("", message);
    }
}
779 780 781 782 783 784 785 786

void QGCApplication::setUseNewMissionEditor(bool use)
{
    // Temp hack for new mission editor
    QSettings settings;
    
    settings.setValue("UseNewMissionEditor", use);
}