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

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

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

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

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

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

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

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

/**
 * @file
Don Gagne's avatar
Don Gagne committed
26
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
27 28 29 30 31 32 33 34 35 36 37 38 39 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>

lm's avatar
lm committed
43
#include "configuration.h"
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
#include "MainWindow.h"
#include "UDPLink.h"
dogmaphobic's avatar
dogmaphobic committed
52
#ifndef __ios__
53
#include "SerialLink.h"
dogmaphobic's avatar
dogmaphobic committed
54
#endif
55 56 57
#include "QGCSingleton.h"
#include "LinkManager.h"
#include "UASManager.h"
58
#include "UASMessageHandler.h"
59
#include "AutoPilotPluginManager.h"
60
#include "QGCTemporaryFile.h"
61
#include "QGCFileDialog.h"
62
#include "QGCPalette.h"
63
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
64 65
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
66
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
67 68 69
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
70
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
71
#include "RadioComponentController.h"
dogmaphobic's avatar
dogmaphobic committed
72
#ifndef __mobile__
73 74 75 76
#include "FirmwareUpgradeController.h"
#endif
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
pixhawk's avatar
pixhawk committed
77

78
#include "ScreenTools.h"
79
#include "MavManager.h"
80

81
#ifdef QGC_RTLAB_ENABLED
82 83
#include "OpalLink.h"
#endif
84 85 86


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

Don Gagne's avatar
Don Gagne committed
88 89 90 91
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
92
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
93 94 95 96

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

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

101 102 103 104 105 106 107 108 109 110 111 112
/**
 * @brief ScreenTools creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
 **/

static QObject* screenToolsSingletonFactory(QQmlEngine*, QJSEngine*)
{
    ScreenTools* screenTools = new ScreenTools;
    return screenTools;
}

113 114 115 116 117 118 119 120 121
/**
 * @brief MavManager creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
**/

static QObject* mavManagerSingletonFactory(QQmlEngine*, QJSEngine*)
{
    MavManager* mavManager = new MavManager;
122
    qgcApp()->setMavManager(mavManager);
123 124 125
    return mavManager;
}

pixhawk's avatar
pixhawk committed
126 127 128 129 130 131 132 133 134 135
/**
 * @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
 **/

136

137 138 139 140 141
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
    , _pMavManager(NULL)
pixhawk's avatar
pixhawk committed
142
{
143 144
    Q_ASSERT(_app == NULL);
    _app = this;
145

146
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
147
#ifndef __android__
148
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
149
#endif
150 151 152 153 154 155 156 157 158 159 160 161 162
    
    // 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);
163

dogmaphobic's avatar
dogmaphobic committed
164
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
165
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
166 167 168 169 170 171
#else
    if (fullLogging) {
        QLoggingCategory::setFilterRules(QStringLiteral("*Log=true"));
    } 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.
172

173 174 175
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
176

177 178 179 180 181 182 183 184 185
        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;
186
            }
187
        } else {
188 189
            loggingDirectoryOk = true;
        }
190

191 192 193 194 195 196 197 198 199 200 201 202 203
        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;
204
                }
205 206 207
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
208
#endif
209

210
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
211 212 213
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
Don Gagne's avatar
Don Gagne committed
214
    
Don Gagne's avatar
Don Gagne committed
215
    // Set application information
216 217 218 219 220 221 222 223 224
    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);
225

Don Gagne's avatar
Don Gagne committed
226 227 228 229 230
    // 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);
231

232 233
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
234

235
    QSettings settings;
236 237 238 239
#ifdef UNITTEST_BUILD
    qDebug() << "Settings location" << settings.fileName();
    Q_ASSERT(settings.isWritable());
#endif
240
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
241
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
242

Don Gagne's avatar
Don Gagne committed
243
    if (_runningUnitTests) {
244
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
245 246
        fClearSettingsOptions = true;
    }
247

248 249 250
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
251
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
252
    }
253 254
}

Don Gagne's avatar
Don Gagne committed
255 256
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
257
    _destroySingletons();
Don Gagne's avatar
Don Gagne committed
258 259
}

260
void QGCApplication::_initCommon(void)
261 262
{
    QSettings settings;
263

264 265 266 267 268
    // 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;
269
        }
270 271 272 273
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
274

275
    if (settingsUpgraded) {
276
        settings.clear();
277
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
278 279 280
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
281
    }
282

283 284
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
285

Don Gagne's avatar
Don Gagne committed
286
    // Load saved files location and validate
287

Don Gagne's avatar
Don Gagne committed
288 289 290
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
291 292 293 294 295 296 297
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
    
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
298

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

Don Gagne's avatar
Don Gagne committed
301 302
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
303

304
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
305
        Q_UNUSED(pathCreated);
306 307
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
308
    }
309

Don Gagne's avatar
Don Gagne committed
310 311 312 313 314
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
315
    qDebug() << "Saved files location" << savedFilesLocation;
316 317 318 319
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);

    // Load application font
    QFontDatabase fontDatabase = QFontDatabase();
Don Gagne's avatar
Don Gagne committed
320
    const QString fontFileName = ":/res/fonts/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
321 322 323 324 325 326
    //const QString fontFamilyName = "Bitstream Vera Sans";
    if(!QFile::exists(fontFileName)) printf("ERROR! font file: %s DOES NOT EXIST!\n", fontFileName.toStdString().c_str());
    fontDatabase.addApplicationFont(fontFileName);
    // Avoid Using setFont(). In the Qt docu you can read the following:
    //     "Warning: Do not use this function in conjunction with Qt Style Sheets."
    // setFont(fontDatabase.font(fontFamilyName, "Roman", 12));
327
    
Don Gagne's avatar
Don Gagne committed
328
    // Register our Qml objects
Don Gagne's avatar
Don Gagne committed
329
    
330
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
Don Gagne's avatar
Don Gagne committed
331
    
332 333 334
    qmlRegisterUncreatableType<AutoPilotPlugin>("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Can only reference, cannot create");
    qmlRegisterUncreatableType<VehicleComponent>("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Can only reference, cannot create");
    
Don Gagne's avatar
Don Gagne committed
335 336
	qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
	qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
337
    qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
Don Gagne's avatar
Don Gagne committed
338 339 340
    qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
    qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
    qmlRegisterType<SensorsComponentController>("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
341
    qmlRegisterType<PowerComponentController>("QGroundControl.Controllers", 1, 0, "PowerComponentController");
Don Gagne's avatar
Don Gagne committed
342
    qmlRegisterType<RadioComponentController>("QGroundControl.Controllers", 1, 0, "RadioComponentController");
dogmaphobic's avatar
dogmaphobic committed
343
#ifndef __mobile__
344 345 346
    qmlRegisterType<FirmwareUpgradeController>("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
#endif

347 348
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenTools>("QGroundControl.ScreenTools", 1, 0, "ScreenTools", screenToolsSingletonFactory);
349
    qmlRegisterSingletonType<MavManager>("QGroundControl.MavManager", 1, 0, "MavManager", mavManagerSingletonFactory);
350

351 352
    //-- Register Waypoint Interface
    qmlRegisterInterface<Waypoint>("Waypoint");
353 354 355 356 357
}

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

359
    _createSingletons();
360

361
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
362
    QPixmap splashImage(":/res/SplashScreen");
363
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
364 365
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
366
    splashScreen->show();
lm's avatar
lm committed
367
    processEvents();
368
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
369 370 371
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
372
    // Start the user interface
373
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
374
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
375
    Q_CHECK_PTR(mainWindow);
376

377 378 379 380 381
    // 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()) {
382 383 384
        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."));
385 386
        mainWindow->showSettings();
    }
387

lm's avatar
lm committed
388
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
389 390
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
391

392
    // Now that main window is up check for lost log files
393 394
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
395 396 397 398

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

399
    return true;
pixhawk's avatar
pixhawk committed
400 401
}

402
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
403
{
404
    return true;
pixhawk's avatar
pixhawk committed
405 406
}

Don Gagne's avatar
Don Gagne committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
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
428

Don Gagne's avatar
Don Gagne committed
429
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
430
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
431 432 433
    if (!tempFile.open()) {
        return false;
    }
434

435
    tempFile.remove();
436

Don Gagne's avatar
Don Gagne committed
437 438 439 440 441 442
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
444 445 446 447 448 449 450
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
452
    location = parentDir.filePath(_savedFileParameterDirectoryName);
453

Don Gagne's avatar
Don Gagne committed
454 455 456 457 458 459 460
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
461

Don Gagne's avatar
Don Gagne committed
462 463 464 465 466 467 468
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
470
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
471

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

Don Gagne's avatar
Don Gagne committed
480 481 482 483 484 485
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
487 488 489 490 491 492 493 494 495 496 497 498
    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)
{
499 500 501 502 503 504 505 506 507
    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
508
    // The order here is important since the singletons reference each other
509

510 511 512
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
513

Don Gagne's avatar
Don Gagne committed
514
    LinkManager* linkManager = LinkManager::_createSingleton();
515 516 517
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
518 519
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
520 521 522
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
523 524
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
525 526
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
527

528
    // Need UASManager
529
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
530 531 532
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
533 534
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
535 536
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
537

538 539 540 541
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
542

543 544
}

Don Gagne's avatar
Don Gagne committed
545
void QGCApplication::_destroySingletons(void)
546
{
547 548 549
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
550

551 552 553 554 555 556 557 558 559
    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();
    }
560

561 562
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
563

Don Gagne's avatar
Don Gagne committed
564
    // Take down singletons in reverse order of creation
565

566
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
567
    FactSystem::_deleteSingleton();
568
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
569 570 571
    AutoPilotPluginManager::_deleteSingleton();
    UASManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
572
    GAudioOutput::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
573
}
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591

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)
{
592 593 594 595 596 597 598 599 600 601 602 603 604
    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)){
605
                // if the file cannot be removed, prompt user and ask new path
606
                saveError = true;
607
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
608 609 610
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
611
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
612 613 614
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
615 616
    QFile::remove(tempLogfile);
}
617 618 619 620

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

622 623 624 625 626 627 628 629 630 631
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

633 634
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
635

636 637 638 639 640 641 642 643 644
    // 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;
    }
645

646 647 648 649 650 651 652 653 654 655 656
    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
657
    
658
    // Now that we have the styles loaded we need to adjust the font sizes.
659

660 661 662 663 664 665 666 667
    QString fSmall  = QString("%1px;").arg(ScreenTools::font10_s());
    QString fNormal = QString("%1px;").arg(ScreenTools::defaultFontPizelSize_s());
    QString fLarge  = QString("%1px;").arg(ScreenTools::largeFontPixelSize_s());

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

669 670 671 672
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
673

674
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
675

676 677 678
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
679

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

Don Gagne's avatar
Don Gagne committed
686 687
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
688
{
Don Gagne's avatar
Don Gagne committed
689
    Q_ASSERT(_missingParams.count());
690
    
Don Gagne's avatar
Don Gagne committed
691 692 693 694
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
695
        } else {
Don Gagne's avatar
Don Gagne committed
696
            params += QString(", %1").arg(name);
697 698
        }
    }
Don Gagne's avatar
Don Gagne committed
699
    _missingParams.clear();
700
    
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
    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
716
}