QGCApplication.cc 23.7 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"
Don Gagne's avatar
Don Gagne committed
69
#include "RadioComponentController.h"
pixhawk's avatar
pixhawk committed
70

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

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


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

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

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

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

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

106 107 108 109 110 111 112 113 114 115 116 117
/**
 * @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
118 119 120 121 122 123 124 125 126 127
/**
 * @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
 **/

128

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

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

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

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

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

169 170
    if (loggingDirectoryOk) {
        qDebug () << iniFileLocation;
Don Gagne's avatar
Don Gagne committed
171
        if (!iniFileLocation.exists(qtLoggingFile)) {
172 173 174 175
            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
176
                out << "*Log.debug=false\n";
177
                foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
Don Gagne's avatar
Don Gagne committed
178
                    out << category << ".debug=false\n";
179 180 181
                }
            } else {
                qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
182 183 184 185
            }
        }
    }
#endif
Don Gagne's avatar
Don Gagne committed
186
#endif
187

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

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

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

213
    // Parse command line options
214

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342
    _createSingletons();
343

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

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

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

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

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

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

382
    return true;
pixhawk's avatar
pixhawk committed
383 384
}

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

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

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

418
    tempFile.remove();
419

Don Gagne's avatar
Don Gagne committed
420 421 422 423 424 425
    return true;
}

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

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

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

Don Gagne's avatar
Don Gagne committed
435
    location = parentDir.filePath(_savedFileParameterDirectoryName);
436

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

Don Gagne's avatar
Don Gagne committed
445 446 447 448 449 450 451
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
453
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
454

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

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

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

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

493 494 495
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
496

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

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

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

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

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

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

526 527
}

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

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

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

Don Gagne's avatar
Don Gagne committed
547
    // Take down singletons in reverse order of creation
548

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

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

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

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

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

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

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

629 630 631 632 633 634 635 636 637 638 639
    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
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
    
    // 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);
655
                adjustedLine = QString("font-size: %1pt;").arg(ScreenTools::adjustFontPointSize_s(match.captured(1).toDouble()));
Don Gagne's avatar
Don Gagne committed
656 657 658 659 660 661 662 663
                //qDebug() << "adjusted:" << adjustedLine;
            } else {
                adjustedLine = line;
            }
            
            dpiAdjustedStyles += adjustedLine;
        }
    }
664

Don Gagne's avatar
Don Gagne committed
665 666
    if (!dpiAdjustedStyles.isEmpty()) {
        setStyleSheet(dpiAdjustedStyles);
667
    }
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
    
    QGCMessageBox::critical("Missing Parameters",
                            QString("Parameters missing from firmware: %1.\n\n"
Don Gagne's avatar
Don Gagne committed
703
                                    "You should quit QGroundControl immediately and update your firmware.").arg(params));
704
}