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

Gus Grubba's avatar
Gus Grubba committed
43 44 45 46 47 48 49 50
#if defined(QGC_GST_STREAMING)
#include <videoitem.h>
#include <videosurface.h>
G_BEGIN_DECLS
GST_PLUGIN_STATIC_DECLARE(QTVIDEOSINK_NAME);
G_END_DECLS
#endif

lm's avatar
lm committed
51
#include "configuration.h"
52
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
53
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
54
#include "MainWindow.h"
pixhawk's avatar
pixhawk committed
55
#include "GAudioOutput.h"
56
#include "CmdLineOptParser.h"
Don Gagne's avatar
Don Gagne committed
57
#include "QGCMessageBox.h"
58 59
#include "MainWindow.h"
#include "UDPLink.h"
dogmaphobic's avatar
dogmaphobic committed
60
#ifndef __ios__
61
#include "SerialLink.h"
dogmaphobic's avatar
dogmaphobic committed
62
#endif
63 64 65
#include "QGCSingleton.h"
#include "LinkManager.h"
#include "UASManager.h"
66
#include "UASMessageHandler.h"
67
#include "AutoPilotPluginManager.h"
68
#include "QGCTemporaryFile.h"
69
#include "QGCFileDialog.h"
70
#include "QGCPalette.h"
71
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
72 73
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
74
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
75 76 77
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
78
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
79
#include "RadioComponentController.h"
Don Gagne's avatar
Don Gagne committed
80
#include "ScreenToolsController.h"
dogmaphobic's avatar
dogmaphobic committed
81
#ifndef __mobile__
82 83 84 85
#include "FirmwareUpgradeController.h"
#endif
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
pixhawk's avatar
pixhawk committed
86

87
#include "MavManager.h"
88

89
#ifdef QGC_RTLAB_ENABLED
90 91
#include "OpalLink.h"
#endif
92 93 94


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

Don Gagne's avatar
Don Gagne committed
96 97 98 99
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
100
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
101 102 103 104

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

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

109 110 111 112 113 114
/**
 * @brief ScreenTools creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
 **/

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

121 122 123 124 125 126 127 128 129
/**
 * @brief MavManager creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
**/

static QObject* mavManagerSingletonFactory(QQmlEngine*, QJSEngine*)
{
    MavManager* mavManager = new MavManager;
130
    qgcApp()->setMavManager(mavManager);
131 132 133
    return mavManager;
}

pixhawk's avatar
pixhawk committed
134 135 136 137 138 139 140 141 142 143
/**
 * @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
 **/

144

145 146 147 148 149
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
    , _pMavManager(NULL)
pixhawk's avatar
pixhawk committed
150
{
151 152
    Q_ASSERT(_app == NULL);
    _app = this;
153

154
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
155
#ifndef __android__
156
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
157
#endif
158 159 160 161 162 163 164 165 166 167 168 169 170
    
    // 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() },
        // Add additional command line option flags here
    };
    
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
171

dogmaphobic's avatar
dogmaphobic committed
172
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
173
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
174 175 176 177
#else
    if (fullLogging) {
        QLoggingCategory::setFilterRules(QStringLiteral("*Log=true"));
    } else {
Don Gagne's avatar
Don Gagne committed
178 179 180 181 182
        if (_runningUnitTests) {
            // We need to turn off these warnings until the firmware meta data is cleaned up
            QLoggingCategory::setFilterRules(QStringLiteral("PX4ParameterLoaderLog.warning=false"));
        }
        
183 184
        // 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.
185

186 187 188
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
189

190 191 192 193 194 195 196 197 198
        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;
199
            }
200
        } else {
201 202
            loggingDirectoryOk = true;
        }
203

204 205 206 207 208 209 210 211 212 213 214 215 216
        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;
217
                }
218 219 220
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
221
#endif
222

223
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
224 225 226
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
Don Gagne's avatar
Don Gagne committed
227
    
Don Gagne's avatar
Don Gagne committed
228
    // Set application information
229 230 231 232 233 234 235 236 237
    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);
238

Don Gagne's avatar
Don Gagne committed
239 240 241 242 243
    // 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);
    this->setApplicationVersion(versionString);
244

245 246
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
247

248
    QSettings settings;
249 250 251 252
#ifdef UNITTEST_BUILD
    qDebug() << "Settings location" << settings.fileName();
    Q_ASSERT(settings.isWritable());
#endif
253
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
254
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
255

Don Gagne's avatar
Don Gagne committed
256
    if (_runningUnitTests) {
257
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
258 259
        fClearSettingsOptions = true;
    }
260

261 262 263
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
264
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
265
    }
Gus Grubba's avatar
Gus Grubba committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279

#if defined(QGC_GST_STREAMING)
    //----------------------------------------------------------------
    //-- Video Streaming
    qmlRegisterType<VideoItem>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
    qmlRegisterUncreatableType<VideoSurface>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QLatin1String("VideoSurface from QML is not supported"));
    GError* error = NULL;
    if (!gst_init_check(&argc, &argv, &error)) {
        qCritical() << "gst_init_check() failed: " << error->message;
        g_error_free(error);
    }
    GST_PLUGIN_STATIC_REGISTER(QTVIDEOSINK_NAME);
#endif

280 281
}

Don Gagne's avatar
Don Gagne committed
282 283
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
284
    _destroySingletons();
Gus Grubba's avatar
Gus Grubba committed
285 286 287
#if defined(QGC_GST_STREAMING)
    gst_deinit();
#endif
Don Gagne's avatar
Don Gagne committed
288 289
}

290
void QGCApplication::_initCommon(void)
291 292
{
    QSettings settings;
293

Don Gagne's avatar
Don Gagne committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
    // Register our Qml objects
    
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
    
    qmlRegisterUncreatableType<AutoPilotPlugin>("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Can only reference, cannot create");
    qmlRegisterUncreatableType<VehicleComponent>("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Can only reference, cannot create");
    
    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");
    
#ifndef __mobile__
    qmlRegisterType<FirmwareUpgradeController>("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
#endif
    
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenToolsController>("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavManager>("QGroundControl.MavManager", 1, 0, "MavManager", mavManagerSingletonFactory);
    
    //-- Register Waypoint Interface
    qmlRegisterInterface<Waypoint>("Waypoint");
321 322 323 324 325
    // 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;
326
        }
327 328 329 330
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
331

332
    if (settingsUpgraded) {
333
        settings.clear();
334
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
335 336 337
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
338
    }
339

340 341
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
342

Don Gagne's avatar
Don Gagne committed
343
    // Load saved files location and validate
344

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

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

Don Gagne's avatar
Don Gagne committed
358 359
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
360

361
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
362
        Q_UNUSED(pathCreated);
363 364
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
365
    }
366

Don Gagne's avatar
Don Gagne committed
367 368 369 370 371
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
372
    qDebug() << "Saved files location" << savedFilesLocation;
373 374 375 376 377 378
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
}

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

380
    _createSingletons();
381

382
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
383
    QPixmap splashImage(":/res/SplashScreen");
384
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
385 386
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
387
    splashScreen->show();
lm's avatar
lm committed
388
    processEvents();
389
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
390 391 392
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
393
    // Start the user interface
394
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
395
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
396
    Q_CHECK_PTR(mainWindow);
397

398 399 400 401 402
    // 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()) {
403 404 405
        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."));
406 407
        mainWindow->showSettings();
    }
408

lm's avatar
lm committed
409
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
410 411
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
412

413
    // Now that main window is up check for lost log files
414 415
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
416 417 418 419

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

420
    return true;
pixhawk's avatar
pixhawk committed
421 422
}

423
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
424
{
425
    return true;
pixhawk's avatar
pixhawk committed
426 427
}

Don Gagne's avatar
Don Gagne committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
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
449

Don Gagne's avatar
Don Gagne committed
450
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
451
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
452 453 454
    if (!tempFile.open()) {
        return false;
    }
455

456
    tempFile.remove();
457

Don Gagne's avatar
Don Gagne committed
458 459 460 461 462 463
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
465 466 467 468 469 470 471
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
473
    location = parentDir.filePath(_savedFileParameterDirectoryName);
474

Don Gagne's avatar
Don Gagne committed
475 476 477 478 479 480 481
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
482

Don Gagne's avatar
Don Gagne committed
483 484 485 486 487 488 489
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
491
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
492

Don Gagne's avatar
Don Gagne committed
493 494 495 496 497 498 499
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
500

Don Gagne's avatar
Don Gagne committed
501 502 503 504 505 506
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
508 509 510 511 512 513 514 515 516 517 518 519
    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)
{
520 521 522 523 524 525 526 527 528
    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
529
    // The order here is important since the singletons reference each other
530

531 532 533
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
534

Don Gagne's avatar
Don Gagne committed
535
    LinkManager* linkManager = LinkManager::_createSingleton();
536 537 538
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
539 540
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
541 542 543
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
544 545
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
546 547
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
548

549
    // Need UASManager
550
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
551 552 553
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
554 555
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
556 557
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
558

559 560 561 562
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
563

564 565
}

Don Gagne's avatar
Don Gagne committed
566
void QGCApplication::_destroySingletons(void)
567
{
568 569 570
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
571

572 573 574 575 576 577 578 579 580
    if (LinkManager::instance(true /* nullOk */)) {
        // This will close/delete all connections
        LinkManager::instance()->_shutdown();
    }

    if (UASManager::instance(true /* nullOk */)) {
        // This will delete all uas from the system
        UASManager::instance()->_shutdown();
    }
581

582 583
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
584

Don Gagne's avatar
Don Gagne committed
585
    // Take down singletons in reverse order of creation
586

587
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
588
    FactSystem::_deleteSingleton();
589
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
590 591 592
    AutoPilotPluginManager::_deleteSingleton();
    UASManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
593
    GAudioOutput::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
594
}
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612

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)
{
613 614 615 616 617 618 619 620 621 622 623 624 625
    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)){
626
                // if the file cannot be removed, prompt user and ask new path
627
                saveError = true;
628
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
629 630 631
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
632
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
633 634 635
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
636 637
    QFile::remove(tempLogfile);
}
638 639 640 641

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

643 644 645 646 647 648 649 650 651 652
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

654 655
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
656

657 658 659 660 661 662 663 664 665
    // 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;
    }
666

667 668 669 670 671 672 673 674 675 676 677
    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
678
    
679
    // Now that we have the styles loaded we need to adjust the font sizes.
Don Gagne's avatar
Don Gagne committed
680 681 682 683 684 685
    
    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;
686 687 688 689 690

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

692 693 694 695
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
696

697
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
698

699 700 701
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
702

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

Don Gagne's avatar
Don Gagne committed
709 710
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
711
{
Don Gagne's avatar
Don Gagne committed
712
    Q_ASSERT(_missingParams.count());
713
    
Don Gagne's avatar
Don Gagne committed
714 715 716 717
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
718
        } else {
Don Gagne's avatar
Don Gagne committed
719
            params += QString(", %1").arg(name);
720 721
        }
    }
Don Gagne's avatar
Don Gagne committed
722
    _missingParams.clear();
723
    
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
    QGCMessageBox::critical(
        "Missing Parameters",
        QString("Parameters missing from firmware: %1.\n\n"
                "You should quit QGroundControl immediately and update your firmware.").arg(params));
}

void QGCApplication::setMavManager(MavManager* pMgr)
{
    if(!_pMavManager)
        _pMavManager = pMgr;
}

MavManager* QGCApplication::getMavManager()
{
    return _pMavManager;
dogmaphobic's avatar
dogmaphobic committed
739
}