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

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 52 53 54 55
#include "MainWindow.h"
#include "UDPLink.h"
#include "SerialLink.h"
#include "QGCSingleton.h"
#include "LinkManager.h"
#include "UASManager.h"
56
#include "UASMessageHandler.h"
57
#include "AutoPilotPluginManager.h"
58
#include "QGCTemporaryFile.h"
59
#include "QGCFileDialog.h"
60
#include "QGCPalette.h"
61
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
62 63
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
64
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
65 66 67
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
68
#include "PowerComponentController.h"
pixhawk's avatar
pixhawk committed
69

70
#include "ScreenTools.h"
71
#include "MavManager.h"
72

73
#ifdef QGC_RTLAB_ENABLED
74 75
#include "OpalLink.h"
#endif
76 77 78


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

Don Gagne's avatar
Don Gagne committed
80 81 82 83
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
84
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
85 86 87 88

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

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

93 94 95 96 97 98 99 100 101 102 103 104
/**
 * @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;
}

105 106 107 108 109 110 111 112 113 114 115 116
/**
 * @brief MavManager creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
**/

static QObject* mavManagerSingletonFactory(QQmlEngine*, QJSEngine*)
{
    MavManager* mavManager = new MavManager;
    return mavManager;
}

pixhawk's avatar
pixhawk committed
117 118 119 120 121 122 123 124 125 126
/**
 * @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
 **/

127

Don Gagne's avatar
Don Gagne committed
128 129
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) :
    QApplication(argc, argv),
130 131
    _runningUnitTests(unitTesting),
    _styleIsDark(true)
pixhawk's avatar
pixhawk committed
132
{
133 134
    Q_ASSERT(_app == NULL);
    _app = this;
135

136
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
137
#ifndef __android__
138
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
139
#endif
140

Don Gagne's avatar
Don Gagne committed
141 142 143 144 145
#ifdef __android__
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
#endif
    
#ifndef __android__
146 147 148
#ifdef QT_DEBUG
    // 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.
149

Don Gagne's avatar
Don Gagne committed
150 151
    static const char* qtProjectDir = "QtProject";
    static const char* qtLoggingFile = "qtlogging.ini";
152
    bool loggingDirectoryOk = false;
153

154
    QDir iniFileLocation(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
Don Gagne's avatar
Don Gagne committed
155 156 157
    if (!iniFileLocation.cd(qtProjectDir)) {
        if (!iniFileLocation.mkdir(qtProjectDir)) {
            qDebug() << "Unable to create qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);
158
        } else {
Don Gagne's avatar
Don Gagne committed
159 160
            if (!iniFileLocation.cd(qtProjectDir)) {
                qDebug() << "Unable to access qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);;
161 162 163 164 165 166
            }
            loggingDirectoryOk = true;
        }
    } else {
        loggingDirectoryOk = true;
    }
167

168 169
    if (loggingDirectoryOk) {
        qDebug () << iniFileLocation;
Don Gagne's avatar
Don Gagne committed
170
        if (!iniFileLocation.exists(qtLoggingFile)) {
171 172 173 174
            QFile loggingFile(iniFileLocation.filePath(qtLoggingFile));
            if (loggingFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
                QTextStream out(&loggingFile);
                out << "[Rules]\n";
Don Gagne's avatar
Don Gagne committed
175
                out << "*Log.debug=false\n";
176
                foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
Don Gagne's avatar
Don Gagne committed
177
                    out << category << ".debug=false\n";
178 179 180
                }
            } else {
                qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
181 182 183 184
            }
        }
    }
#endif
Don Gagne's avatar
Don Gagne committed
185
#endif
186

187 188 189 190
    // Set up timer for delayed missing fact display
    _missingFactDelayedDisplayTimer.setSingleShot(true);
    _missingFactDelayedDisplayTimer.setInterval(_missingFactDelayedDisplayTimerTimeout);
    connect(&_missingFactDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingFactsDisplay);
Don Gagne's avatar
Don Gagne committed
191
    
Don Gagne's avatar
Don Gagne committed
192
    // Set application information
193 194 195 196 197 198 199 200 201
    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);
202

Don Gagne's avatar
Don Gagne committed
203 204 205 206 207
    // 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);
208

209 210
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
211

212
    // Parse command line options
213

214
    bool fClearSettingsOptions = false; // Clear stored settings
215

216
    CmdLineOpt_t rgCmdLineOptions[] = {
Don Gagne's avatar
Don Gagne committed
217
        { "--clear-settings",   &fClearSettingsOptions, QString() },
218 219
        // Add additional command line option flags here
    };
220

221
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
222

223
    QSettings settings;
224 225 226 227
#ifdef UNITTEST_BUILD
    qDebug() << "Settings location" << settings.fileName();
    Q_ASSERT(settings.isWritable());
#endif
228
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
229
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
230

Don Gagne's avatar
Don Gagne committed
231
    if (_runningUnitTests) {
232
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
233 234
        fClearSettingsOptions = true;
    }
235

236 237 238
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
239
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
240
    }
241 242
}

Don Gagne's avatar
Don Gagne committed
243 244
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
245
    _destroySingletons();
Don Gagne's avatar
Don Gagne committed
246 247
}

248
void QGCApplication::_initCommon(void)
249 250
{
    QSettings settings;
251

252 253 254 255 256
    // 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;
257
        }
258 259 260 261
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
262

263
    if (settingsUpgraded) {
264
        settings.clear();
265
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
266 267 268
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
269
    }
270

271 272
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
273

Don Gagne's avatar
Don Gagne committed
274
    // Load saved files location and validate
275

Don Gagne's avatar
Don Gagne committed
276 277 278
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
279 280 281 282 283 284 285
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
    
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
286

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

Don Gagne's avatar
Don Gagne committed
289 290
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
291

292
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
293
        Q_UNUSED(pathCreated);
294 295
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
296
    }
297

Don Gagne's avatar
Don Gagne committed
298 299 300 301 302
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
303
    qDebug() << "Saved files location" << savedFilesLocation;
304 305 306 307
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);

    // Load application font
    QFontDatabase fontDatabase = QFontDatabase();
Don Gagne's avatar
Don Gagne committed
308
    const QString fontFileName = ":/res/fonts/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
309 310 311 312 313 314
    //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));
315
    
Don Gagne's avatar
Don Gagne committed
316
    // Register our Qml objects
Don Gagne's avatar
Don Gagne committed
317
    
318
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
Don Gagne's avatar
Don Gagne committed
319
    
Don Gagne's avatar
Don Gagne committed
320 321
	qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
	qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
322
    qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
Don Gagne's avatar
Don Gagne committed
323 324 325
    qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
    qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
    qmlRegisterType<SensorsComponentController>("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
326
    qmlRegisterType<PowerComponentController>("QGroundControl.Controllers", 1, 0, "PowerComponentController");
Don Gagne's avatar
Don Gagne committed
327
    
328 329
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenTools>("QGroundControl.ScreenTools", 1, 0, "ScreenTools", screenToolsSingletonFactory);
330
    qmlRegisterSingletonType<MavManager>("QGroundControl.MavManager", 1, 0, "MavManager", mavManagerSingletonFactory);
Don Gagne's avatar
Don Gagne committed
331
    
332 333
    //-- Register Waypoint Interface
    qmlRegisterInterface<Waypoint>("Waypoint");
334 335 336 337 338
}

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

340
    _createSingletons();
341

342
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
343
    QPixmap splashImage(":/res/SplashScreen");
344
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
345 346
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
347
    splashScreen->show();
lm's avatar
lm committed
348
    processEvents();
349
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
350 351 352
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
353
    // Start the user interface
354
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
355
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
356
    Q_CHECK_PTR(mainWindow);
357

358 359 360 361 362
    // 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()) {
363 364 365
        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."));
366 367
        mainWindow->showSettings();
    }
368

lm's avatar
lm committed
369
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
370 371
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
372

373 374 375
    // Now that main window is upcheck for lost log files
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
376 377 378 379

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

380
    return true;
pixhawk's avatar
pixhawk committed
381 382
}

383
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
384
{
385
    return true;
pixhawk's avatar
pixhawk committed
386 387
}

Don Gagne's avatar
Don Gagne committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
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
409

Don Gagne's avatar
Don Gagne committed
410
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
411
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
412 413 414
    if (!tempFile.open()) {
        return false;
    }
415

416
    tempFile.remove();
417

Don Gagne's avatar
Don Gagne committed
418 419 420 421 422 423
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
425 426 427 428 429 430 431
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
433
    location = parentDir.filePath(_savedFileParameterDirectoryName);
434

Don Gagne's avatar
Don Gagne committed
435 436 437 438 439 440 441
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
442

Don Gagne's avatar
Don Gagne committed
443 444 445 446 447 448 449
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
451
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
452

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

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

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

Don Gagne's avatar
Don Gagne committed
468 469 470 471 472 473 474 475 476 477 478 479
    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)
{
480 481 482 483 484 485 486 487 488
    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
489
    // The order here is important since the singletons reference each other
490

491 492 493
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
494

Don Gagne's avatar
Don Gagne committed
495
    LinkManager* linkManager = LinkManager::_createSingleton();
496 497 498
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
499 500
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
501 502 503
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
504 505
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
506 507
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
508

509
    // Need UASManager
510
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
511 512 513
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
514 515
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
516 517
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
518

519 520 521 522
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
523

524 525
}

Don Gagne's avatar
Don Gagne committed
526
void QGCApplication::_destroySingletons(void)
527
{
528 529 530
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
531

532 533 534 535 536 537 538 539 540
    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();
    }
541

542 543
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
544

Don Gagne's avatar
Don Gagne committed
545
    // Take down singletons in reverse order of creation
546

547
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
548
    FactSystem::_deleteSingleton();
549
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
550 551 552
    AutoPilotPluginManager::_deleteSingleton();
    UASManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
553
    GAudioOutput::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
554
}
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572

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)
{
573 574 575 576 577 578 579 580 581 582 583 584 585
    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)){
586
                // if the file cannot be removed, prompt user and ask new path
587
                saveError = true;
588
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
589 590 591
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
592
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
593 594 595
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
596 597
    QFile::remove(tempLogfile);
}
598 599 600 601

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

603 604 605 606 607 608 609 610 611 612
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

614 615
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
616

617 618 619 620 621 622 623 624 625
    // 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;
    }
626

627 628 629 630 631 632 633 634 635 636 637
    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
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
    
    // Now that we have the styles loaded we need to dpi adjust the font point sizes
    
    QString dpiAdjustedStyles;
    if (success) {
        QTextStream styleStream(&styles, QIODevice::ReadOnly);
        QRegularExpression regex("font-size:.+(\\d\\d)pt;");
        
        while (!styleStream.atEnd()) {
            QString adjustedLine;
            QString line = styleStream.readLine();
            
            QRegularExpressionMatch match = regex.match(line);
            if (match.hasMatch()) {
                //qDebug() << "found:" << line << match.captured(1);
653
                adjustedLine = QString("font-size: %1pt;").arg(ScreenTools::adjustFontPointSize_s(match.captured(1).toDouble()));
Don Gagne's avatar
Don Gagne committed
654 655 656 657 658 659 660 661
                //qDebug() << "adjusted:" << adjustedLine;
            } else {
                adjustedLine = line;
            }
            
            dpiAdjustedStyles += adjustedLine;
        }
    }
662

Don Gagne's avatar
Don Gagne committed
663 664
    if (!dpiAdjustedStyles.isEmpty()) {
        setStyleSheet(dpiAdjustedStyles);
665
    }
666

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

672
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
673

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

678
void QGCApplication::reportMissingFact(const QString& name)
679
{
680 681
    _missingFacts += name;
    _missingFactDelayedDisplayTimer.start();
682
}
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702

/// Called when the delay timer fires to show the missing facts warning
void QGCApplication::_missingFactsDisplay(void)
{
    Q_ASSERT(_missingFacts.count());
    
    QString facts;
    foreach (QString fact, _missingFacts) {
        if (facts.isEmpty()) {
            facts += fact;
        } else {
            facts += QString(", %1").arg(fact);
        }
    }
    _missingFacts.clear();
    
    QGCMessageBox::critical("Missing Parameters",
                            QString("Parameters missing from firmware: %1.\n\n"
                                    "You should quit QGroundControl immediately and update your firmware.").arg(facts));
}