QGCApplication.cc 22 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 "ScreenTools.h"
62
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
63 64
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
65
#include "CustomCommandWidgetController.h"
pixhawk's avatar
pixhawk committed
66

67
#ifdef QGC_RTLAB_ENABLED
68 69
#include "OpalLink.h"
#endif
70 71 72


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

Don Gagne's avatar
Don Gagne committed
74 75 76 77
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
78
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
79 80 81 82

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

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

87 88 89 90 91 92 93 94 95 96 97 98
/**
 * @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;
}

pixhawk's avatar
pixhawk committed
99 100 101 102 103 104 105 106 107 108
/**
 * @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
 **/

109

Don Gagne's avatar
Don Gagne committed
110 111
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) :
    QApplication(argc, argv),
112 113
    _runningUnitTests(unitTesting),
    _styleIsDark(true)
pixhawk's avatar
pixhawk committed
114
{
115 116
    Q_ASSERT(_app == NULL);
    _app = this;
117

118
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
119
#ifndef __android__
120
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
121
#endif
122

Don Gagne's avatar
Don Gagne committed
123 124 125 126 127
#ifdef __android__
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
#endif
    
#ifndef __android__
128 129 130
#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.
131

Don Gagne's avatar
Don Gagne committed
132 133
    static const char* qtProjectDir = "QtProject";
    static const char* qtLoggingFile = "qtlogging.ini";
134
    bool loggingDirectoryOk = false;
135

136
    QDir iniFileLocation(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
Don Gagne's avatar
Don Gagne committed
137 138 139
    if (!iniFileLocation.cd(qtProjectDir)) {
        if (!iniFileLocation.mkdir(qtProjectDir)) {
            qDebug() << "Unable to create qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);
140
        } else {
Don Gagne's avatar
Don Gagne committed
141 142
            if (!iniFileLocation.cd(qtProjectDir)) {
                qDebug() << "Unable to access qtlogging.ini directory" << iniFileLocation.filePath(qtProjectDir);;
143 144 145 146 147 148
            }
            loggingDirectoryOk = true;
        }
    } else {
        loggingDirectoryOk = true;
    }
149

150 151
    if (loggingDirectoryOk) {
        qDebug () << iniFileLocation;
Don Gagne's avatar
Don Gagne committed
152
        if (!iniFileLocation.exists(qtLoggingFile)) {
153 154 155 156
            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
157
                out << "*Log.debug=false\n";
158
                foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
Don Gagne's avatar
Don Gagne committed
159
                    out << category << ".debug=false\n";
160 161 162
                }
            } else {
                qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
163 164 165 166
            }
        }
    }
#endif
Don Gagne's avatar
Don Gagne committed
167
#endif
168

Don Gagne's avatar
Don Gagne committed
169
    
Don Gagne's avatar
Don Gagne committed
170
    // Set application information
171 172 173 174 175 176 177 178 179
    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);
180

Don Gagne's avatar
Don Gagne committed
181 182 183 184 185
    // 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);
186

187 188
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
189

190
    // Parse command line options
191

192
    bool fClearSettingsOptions = false; // Clear stored settings
193

194
    CmdLineOpt_t rgCmdLineOptions[] = {
Don Gagne's avatar
Don Gagne committed
195
        { "--clear-settings",   &fClearSettingsOptions, QString() },
196 197
        // Add additional command line option flags here
    };
198

199
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
200

201
    QSettings settings;
202

203
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
204
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
205

Don Gagne's avatar
Don Gagne committed
206
    if (_runningUnitTests) {
207
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
208 209
        fClearSettingsOptions = true;
    }
210

211 212 213
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
214
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
215
    }
216 217
}

Don Gagne's avatar
Don Gagne committed
218 219
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
220
    _destroySingletons();
Don Gagne's avatar
Don Gagne committed
221 222
}

223
void QGCApplication::_initCommon(void)
224 225
{
    QSettings settings;
226

227 228 229 230 231
    // 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;
232
        }
233 234 235 236
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
237

238
    if (settingsUpgraded) {
239
        settings.clear();
240
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
241 242 243
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
244
    }
245

246 247
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
248

Don Gagne's avatar
Don Gagne committed
249
    // Load saved files location and validate
250

Don Gagne's avatar
Don Gagne committed
251 252 253 254 255
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
    } else {
        // No location set. Create a default one in Documents standard location.
256

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

Don Gagne's avatar
Don Gagne committed
259 260
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
261

262
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
263
        Q_UNUSED(pathCreated);
264 265
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
266
    }
267

Don Gagne's avatar
Don Gagne committed
268 269 270 271 272
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
273 274 275 276
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);

    // Load application font
    QFontDatabase fontDatabase = QFontDatabase();
Don Gagne's avatar
Don Gagne committed
277
    const QString fontFileName = ":/res/fonts/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
278 279 280 281 282 283
    //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));
284
    
Don Gagne's avatar
Don Gagne committed
285
    // Register our Qml objects
286
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
Don Gagne's avatar
Don Gagne committed
287 288
	qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
	qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
Don Gagne's avatar
Don Gagne committed
289 290
    qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");

291 292
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenTools>("QGroundControl.ScreenTools", 1, 0, "ScreenTools", screenToolsSingletonFactory);
293 294 295 296 297
}

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

299
    _createSingletons();
300

301
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
302
    QPixmap splashImage(":/res/SplashScreen");
303
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
304 305
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
306
    splashScreen->show();
lm's avatar
lm committed
307
    processEvents();
308
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
309 310 311
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
312
    // Start the user interface
313
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
314
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
315
    Q_CHECK_PTR(mainWindow);
316

317 318 319 320 321
    // 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()) {
322 323 324
        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."));
325 326
        mainWindow->showSettings();
    }
327

lm's avatar
lm committed
328
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
329 330
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
331

332 333 334
    // Now that main window is upcheck for lost log files
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
335 336 337 338

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

339
    return true;
pixhawk's avatar
pixhawk committed
340 341
}

342
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
343
{
344
    return true;
pixhawk's avatar
pixhawk committed
345 346
}

Don Gagne's avatar
Don Gagne committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
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
368

Don Gagne's avatar
Don Gagne committed
369
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
370
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
371 372 373
    if (!tempFile.open()) {
        return false;
    }
374

375
    tempFile.remove();
376

Don Gagne's avatar
Don Gagne committed
377 378 379 380 381 382
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
384 385 386 387 388 389 390 391
    Q_ASSERT(settings.contains(_savedFilesLocationKey));
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
393
    location = parentDir.filePath(_savedFileParameterDirectoryName);
394

Don Gagne's avatar
Don Gagne committed
395 396 397 398 399 400 401
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
402

Don Gagne's avatar
Don Gagne committed
403 404 405 406 407 408 409
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
411
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
412

Don Gagne's avatar
Don Gagne committed
413 414 415 416 417 418 419
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
420

Don Gagne's avatar
Don Gagne committed
421 422 423 424 425 426
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
428 429 430 431 432 433 434 435 436 437 438 439
    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)
{
440 441 442 443 444 445 446 447 448
    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
449
    // The order here is important since the singletons reference each other
450

451 452 453
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
454

Don Gagne's avatar
Don Gagne committed
455
    LinkManager* linkManager = LinkManager::_createSingleton();
456 457 458
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
459 460
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
461 462 463
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
464 465
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
466 467
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
468

469
    // Need UASManager
470
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
471 472 473
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
474 475
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
476 477
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
478

479 480 481 482
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
483

484 485
}

Don Gagne's avatar
Don Gagne committed
486
void QGCApplication::_destroySingletons(void)
487
{
488 489 490
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
491

492 493 494 495 496 497 498 499 500
    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();
    }
501

502 503
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
504

Don Gagne's avatar
Don Gagne committed
505
    // Take down singletons in reverse order of creation
506

507
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
508
    FactSystem::_deleteSingleton();
509
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
510 511 512
    AutoPilotPluginManager::_deleteSingleton();
    UASManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
513
    GAudioOutput::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
514
}
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532

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)
{
533 534 535 536 537 538 539 540 541 542 543 544 545
    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)){
546
                // if the file cannot be removed, prompt user and ask new path
547
                saveError = true;
548
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
549 550 551
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
552
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
553 554 555
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
556 557
    QFile::remove(tempLogfile);
}
558 559 560 561

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

563 564 565 566 567 568 569 570 571 572
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

574 575
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
576

577 578 579 580 581 582 583 584 585
    // 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;
    }
586

587 588 589 590 591 592 593 594 595 596 597
    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
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
    
    // 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);
613
                adjustedLine = QString("font-size: %1pt;").arg(ScreenTools::adjustFontPointSize_s(match.captured(1).toDouble()));
Don Gagne's avatar
Don Gagne committed
614 615 616 617 618 619 620 621
                //qDebug() << "adjusted:" << adjustedLine;
            } else {
                adjustedLine = line;
            }
            
            dpiAdjustedStyles += adjustedLine;
        }
    }
622

Don Gagne's avatar
Don Gagne committed
623 624
    if (!dpiAdjustedStyles.isEmpty()) {
        setStyleSheet(dpiAdjustedStyles);
625
    }
626

627 628 629 630
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
631

632
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
633

634 635 636
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
637 638 639 640 641 642 643 644 645 646

void QGCApplication::reconnectAfterWait(int waitSeconds)
{
    LinkManager* linkManager = LinkManager::instance();
    Q_ASSERT(linkManager);
    
    Q_ASSERT(linkManager->getLinks().count() == 1);
    LinkInterface* link = linkManager->getLinks()[0];
    
    // Save the link configuration so we can restart the link laster
Don Gagne's avatar
Don Gagne committed
647
    _reconnectLinkConfig = LinkConfiguration::duplicateSettings(linkManager->getLinks()[0]->getLinkConfiguration());
Don Gagne's avatar
Don Gagne committed
648 649 650 651 652 653 654 655 656
    
    // Disconnect and wait
    
    linkManager->disconnectLink(link);
    QTimer::singleShot(waitSeconds * 1000, this, &QGCApplication::_reconnect);
}

void QGCApplication::_reconnect(void)
{
Don Gagne's avatar
Don Gagne committed
657 658
    Q_ASSERT(_reconnectLinkConfig);
    
Don Gagne's avatar
Don Gagne committed
659 660
    qgcApp()->restoreOverrideCursor();
    LinkManager::instance()->createConnectedLink(_reconnectLinkConfig);
Don Gagne's avatar
Don Gagne committed
661
    _reconnectLinkConfig = NULL;
Don Gagne's avatar
Don Gagne committed
662
}
663 664 665 666 667 668

void QGCApplication::panicShutdown(const QString& panicMessage)
{
    QGCMessageBox::critical("Panic Shutdown", panicMessage);
    ::exit(0);
}