QGCApplication.cc 24 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 170 171
#else
    if (fullLogging) {
        QLoggingCategory::setFilterRules(QStringLiteral("*Log=true"));
    } else {
        // First thing we want to do is set up the qtlogging.ini file. If it doesn't already exist we copy
        // it to the correct location. This way default debug builds will have logging turned off.
172

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

177 178 179 180 181 182 183 184 185
        QDir iniFileLocation(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
        if (!iniFileLocation.cd(qtProjectDir)) {
            if (!iniFileLocation.mkdir(qtProjectDir)) {
                qDebug() << "Unable to create qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);
            } else {
                if (!iniFileLocation.cd(qtProjectDir)) {
                    qDebug() << "Unable to access qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);;
                }
                loggingDirectoryOk = true;
186
            }
187
        } else {
188 189
            loggingDirectoryOk = true;
        }
190

191 192 193 194 195 196 197 198 199 200 201 202 203
        if (loggingDirectoryOk) {
            qDebug () << iniFileLocation;
            if (!iniFileLocation.exists(qtLoggingFile)) {
                QFile loggingFile(iniFileLocation.filePath(qtLoggingFile));
                if (loggingFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
                    QTextStream out(&loggingFile);
                    out << "[Rules]\n";
                    out << "*Log.debug=false\n";
                    foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
204
                }
205 206 207
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
208
#endif
209

210
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
211 212 213
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
Don Gagne's avatar
Don Gagne committed
214
    
Don Gagne's avatar
Don Gagne committed
215
    // Set application information
216 217 218 219 220 221 222 223 224
    if (_runningUnitTests) {
        // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
        // name. Also we want to run unit tests with clean settings every time.
        setApplicationName(QString("%1_unittest").arg(QGC_APPLICATION_NAME));
    } else {
        setApplicationName(QGC_APPLICATION_NAME);
    }
    setOrganizationName(QGC_ORG_NAME);
    setOrganizationDomain(QGC_ORG_DOMAIN);
225

Don Gagne's avatar
Don Gagne committed
226 227 228 229 230
    // Version string is build from component parts. Format is:
    //  vMajor.Minor.BuildNumber BuildType
    QString versionString("v%1.%2.%3 %4");
    versionString = versionString.arg(QGC_APPLICATION_VERSION_MAJOR).arg(QGC_APPLICATION_VERSION_MINOR).arg(QGC_APPLICATION_VERSION_BUILDNUMBER).arg(QGC_APPLICATION_VERSION_BUILDTYPE);
    this->setApplicationVersion(versionString);
231

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

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

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

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

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

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

Don Gagne's avatar
Don Gagne committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    // 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");
291 292 293 294 295
    // 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;
296
        }
297 298 299 300
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
301

302
    if (settingsUpgraded) {
303
        settings.clear();
304
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
305 306 307
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
308
    }
309

310 311
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
312

Don Gagne's avatar
Don Gagne committed
313
    // Load saved files location and validate
314

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

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

Don Gagne's avatar
Don Gagne committed
328 329
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
330

331
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
332
        Q_UNUSED(pathCreated);
333 334
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
335
    }
336

Don Gagne's avatar
Don Gagne committed
337 338 339 340 341
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
342
    qDebug() << "Saved files location" << savedFilesLocation;
343 344 345 346 347 348
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
}

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

350
    _createSingletons();
351

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

pixhawk's avatar
pixhawk committed
363
    // Start the user interface
364
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
365
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
366
    Q_CHECK_PTR(mainWindow);
367

368 369 370 371 372
    // 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()) {
373 374 375
        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."));
376 377
        mainWindow->showSettings();
    }
378

lm's avatar
lm committed
379
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
380 381
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
382

383
    // Now that main window is up check for lost log files
384 385
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
386 387 388 389

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

390
    return true;
pixhawk's avatar
pixhawk committed
391 392
}

393
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
394
{
395
    return true;
pixhawk's avatar
pixhawk committed
396 397
}

Don Gagne's avatar
Don Gagne committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
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
419

Don Gagne's avatar
Don Gagne committed
420
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
421
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
422 423 424
    if (!tempFile.open()) {
        return false;
    }
425

426
    tempFile.remove();
427

Don Gagne's avatar
Don Gagne committed
428 429 430 431 432 433
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
435 436 437 438 439 440 441
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
443
    location = parentDir.filePath(_savedFileParameterDirectoryName);
444

Don Gagne's avatar
Don Gagne committed
445 446 447 448 449 450 451
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
452

Don Gagne's avatar
Don Gagne committed
453 454 455 456 457 458 459
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
461
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
462

Don Gagne's avatar
Don Gagne committed
463 464 465 466 467 468 469
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
470

Don Gagne's avatar
Don Gagne committed
471 472 473 474 475 476
    return location;
}

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

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

501 502 503
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
504

Don Gagne's avatar
Don Gagne committed
505
    LinkManager* linkManager = LinkManager::_createSingleton();
506 507 508
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
509 510
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
511 512 513
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
514 515
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
516 517
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
518

519
    // Need UASManager
520
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
521 522 523
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
524 525
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
526 527
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
528

529 530 531 532
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
533

534 535
}

Don Gagne's avatar
Don Gagne committed
536
void QGCApplication::_destroySingletons(void)
537
{
538 539 540
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
541

542 543 544 545 546 547 548 549 550
    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();
    }
551

552 553
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
554

Don Gagne's avatar
Don Gagne committed
555
    // Take down singletons in reverse order of creation
556

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

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

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

613 614 615 616 617 618 619 620 621 622
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

624 625
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
626

627 628 629 630 631 632 633 634 635
    // 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;
    }
636

637 638 639 640 641 642 643 644 645 646 647
    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
648
    
649
    // Now that we have the styles loaded we need to adjust the font sizes.
Don Gagne's avatar
Don Gagne committed
650 651 652 653 654 655
    
    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;
656 657 658 659 660

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

662 663 664 665
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
666

667
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
668

669 670 671
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
672

Don Gagne's avatar
Don Gagne committed
673
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
674
{
Don Gagne's avatar
Don Gagne committed
675 676
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
677
}
678

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