QGCApplication.cc 24.2 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"
Don Gagne's avatar
Don Gagne committed
72
#include "ScreenToolsController.h"
dogmaphobic's avatar
dogmaphobic committed
73
#ifndef __mobile__
74 75 76 77
#include "FirmwareUpgradeController.h"
#endif
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
pixhawk's avatar
pixhawk committed
78

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
/**
 * @brief ScreenTools creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
 **/

Don Gagne's avatar
Don Gagne committed
107
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
108
{
Don Gagne's avatar
Don Gagne committed
109 110
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
111 112
}

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

178 179 180
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
181

182 183 184 185 186 187 188 189 190
        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;
191
            }
192
        } else {
193 194
            loggingDirectoryOk = true;
        }
195

196 197 198 199 200 201 202 203 204 205 206 207 208
        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;
209
                }
210 211 212
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
213
#endif
214

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

Don Gagne's avatar
Don Gagne committed
231 232 233 234 235
    // 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);
236

237 238
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
239

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

Don Gagne's avatar
Don Gagne committed
248
    if (_runningUnitTests) {
249
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
250 251
        fClearSettingsOptions = true;
    }
252

253 254 255
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
256
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
257
    }
258 259
}

Don Gagne's avatar
Don Gagne committed
260 261
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
262
    _destroySingletons();
Don Gagne's avatar
Don Gagne committed
263 264
}

265
void QGCApplication::_initCommon(void)
266 267
{
    QSettings settings;
268

Don Gagne's avatar
Don Gagne committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    // Register our Qml objects
    
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
    
    qmlRegisterUncreatableType<AutoPilotPlugin>("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Can only reference, cannot create");
    qmlRegisterUncreatableType<VehicleComponent>("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Can only reference, cannot create");
    
    qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
    qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
    qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
    qmlRegisterType<SensorsComponentController>("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>("QGroundControl.Controllers", 1, 0, "RadioComponentController");
    qmlRegisterType<ScreenToolsController>("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    
#ifndef __mobile__
    qmlRegisterType<FirmwareUpgradeController>("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
#endif
    
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenToolsController>("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavManager>("QGroundControl.MavManager", 1, 0, "MavManager", mavManagerSingletonFactory);
    
    //-- Register Waypoint Interface
    qmlRegisterInterface<Waypoint>("Waypoint");
296 297 298 299 300
    // 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;
301
        }
302 303 304 305
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
306

307
    if (settingsUpgraded) {
308
        settings.clear();
309
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
310 311 312
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
313
    }
314

315 316
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
317

Don Gagne's avatar
Don Gagne committed
318
    // Load saved files location and validate
319

Don Gagne's avatar
Don Gagne committed
320 321 322
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
323 324 325 326 327 328 329
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
    
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
330

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

Don Gagne's avatar
Don Gagne committed
333 334
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
335

336
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
337
        Q_UNUSED(pathCreated);
338 339
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
340
    }
341

Don Gagne's avatar
Don Gagne committed
342 343 344 345 346
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
347
    qDebug() << "Saved files location" << savedFilesLocation;
348 349 350 351 352 353
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
}

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

355
    _createSingletons();
356

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

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

373 374 375 376 377
    // 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()) {
378 379 380
        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."));
381 382
        mainWindow->showSettings();
    }
383

lm's avatar
lm committed
384
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
385 386
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
387

388
    // Now that main window is up check for lost log files
389 390
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
391 392 393 394

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

395
    return true;
pixhawk's avatar
pixhawk committed
396 397
}

398
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
399
{
400
    return true;
pixhawk's avatar
pixhawk committed
401 402
}

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

Don Gagne's avatar
Don Gagne committed
425
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
426
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
427 428 429
    if (!tempFile.open()) {
        return false;
    }
430

431
    tempFile.remove();
432

Don Gagne's avatar
Don Gagne committed
433 434 435 436 437 438
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
440 441 442 443 444 445 446
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
448
    location = parentDir.filePath(_savedFileParameterDirectoryName);
449

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

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

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

Don Gagne's avatar
Don Gagne committed
466
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
467

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

Don Gagne's avatar
Don Gagne committed
476 477 478 479 480 481
    return location;
}

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

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

506 507 508
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
509

Don Gagne's avatar
Don Gagne committed
510
    LinkManager* linkManager = LinkManager::_createSingleton();
511 512 513
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

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

Don Gagne's avatar
Don Gagne committed
519 520
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
521 522
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
523

524
    // Need UASManager
525
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
526 527 528
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
529 530
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
531 532
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
533

534 535 536 537
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
538

539 540
}

Don Gagne's avatar
Don Gagne committed
541
void QGCApplication::_destroySingletons(void)
542
{
543 544 545
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
546

547 548 549 550 551 552 553 554 555
    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();
    }
556

557 558
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
559

Don Gagne's avatar
Don Gagne committed
560
    // Take down singletons in reverse order of creation
561

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

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

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

618 619 620 621 622 623 624 625 626 627
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

629 630
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
631

632 633 634 635 636 637 638 639 640
    // 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;
    }
641

642 643 644 645 646 647 648 649 650 651 652
    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
653
    
654
    // Now that we have the styles loaded we need to adjust the font sizes.
Don Gagne's avatar
Don Gagne committed
655 656 657 658 659 660
    
    QString fSmall  = QString("%1px;").arg(ScreenToolsController::smallFontPixelSize_s());
    QString fNormal = QString("%1px;").arg(ScreenToolsController::defaultFontPixelSize_s());
    QString fLarge  = QString("%1px;").arg(ScreenToolsController::largeFontPixelSize_s());
    
    qDebug() << fSmall << fNormal << fLarge;
661 662 663 664 665

    styles.replace("FONT_SMALL",  fSmall);
    styles.replace("FONT_NORMAL", fNormal);
    styles.replace("FONT_LARGE",  fLarge);
    setStyleSheet(styles);
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

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

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