QGCApplication.cc 25.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
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>

40 41
#include <QDebug>

42
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
43

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
#include "MainWindow.h"
#include "UDPLink.h"
#include "LinkManager.h"
53
#include "HomePositionManager.h"
54
#include "UASMessageHandler.h"
55
#include "AutoPilotPluginManager.h"
56
#include "QGCTemporaryFile.h"
57
#include "QGCFileDialog.h"
58
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
59
#include "QGCMapPalette.h"
60
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
61 62
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
63
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
64
#include "FlightModesComponentController.h"
Don Gagne's avatar
Don Gagne committed
65
#include "APMFlightModesComponentController.h"
Don Gagne's avatar
Don Gagne committed
66 67
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
68
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
69
#include "RadioComponentController.h"
Don Gagne's avatar
Don Gagne committed
70
#include "ScreenToolsController.h"
71 72
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
73
#include "FirmwarePluginManager.h"
74
#include "MultiVehicleManager.h"
Don Gagne's avatar
Don Gagne committed
75
#include "Generic/GenericFirmwarePlugin.h"
76
#include "APM/ArduCopterFirmwarePlugin.h"
77 78
#include "APM/ArduPlaneFirmwarePlugin.h"
#include "APM/ArduRoverFirmwarePlugin.h"
Don Gagne's avatar
Don Gagne committed
79
#include "PX4/PX4FirmwarePlugin.h"
80
#include "Vehicle.h"
81
#include "MavlinkQmlSingleton.h"
82
#include "JoystickManager.h"
83
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
84
#include "MissionManager.h"
85 86
#include "QGroundControlQmlGlobal.h"
#include "HomePositionManager.h"
87
#include "FlightMapSettings.h"
88 89
#include "QGCQGeoCoordinate.h"
#include "CoordinateVector.h"
90
#include "MainToolBarController.h"
91
#include "MissionController.h"
Don Gagne's avatar
Don Gagne committed
92
#include "MissionCommands.h"
93 94 95
#include "FlightDisplayViewController.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
96 97 98 99 100 101 102 103 104

#ifndef __ios__
    #include "SerialLink.h"
#endif

#ifndef __mobile__
    #include "FirmwareUpgradeController.h"
    #include "JoystickConfigController.h"
#endif
105

106
#ifdef QGC_RTLAB_ENABLED
107
    #include "OpalLink.h"
108
#endif
109 110 111


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

113 114 115 116 117 118
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey          = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave           = "PromptFLightDataSave";
const char* QGCApplication::_promptFlightDataSaveNotArmed   = "PromptFLightDataSaveNotArmed";
const char* QGCApplication::_styleKey                       = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
119

dogmaphobic's avatar
dogmaphobic committed
120 121 122
const char* QGCApplication::_defaultSavedFileDirectoryName      = "QGroundControl";
const char* QGCApplication::_savedFileMavlinkLogDirectoryName   = "FlightData";
const char* QGCApplication::_savedFileParameterDirectoryName    = "SavedParameters";
pixhawk's avatar
pixhawk committed
123

dogmaphobic's avatar
dogmaphobic committed
124 125
const char* QGCApplication::_darkStyleFile          = ":/res/styles/style-dark.css";
const char* QGCApplication::_lightStyleFile         = ":/res/styles/style-light.css";
126

127 128

// Qml Singleton factories
129

Don Gagne's avatar
Don Gagne committed
130
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
131
{
Don Gagne's avatar
Don Gagne committed
132 133
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
134 135
}

136 137 138 139 140
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

141 142
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
143
    return new QGroundControlQmlGlobal(qgcApp()->toolbox());
144 145
}

pixhawk's avatar
pixhawk committed
146 147 148 149 150 151 152 153 154 155
/**
 * @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
 **/

156 157 158
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
159 160 161
#if defined (__mobile__)
    , _styleIsDark(false)
#else
162
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
163
#endif
dogmaphobic's avatar
dogmaphobic committed
164
    , _fakeMobile(false)
165 166 167
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
168
    , _toolbox(NULL)
pixhawk's avatar
pixhawk committed
169
{
170 171
    Q_ASSERT(_app == NULL);
    _app = this;
172

173
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
174
#ifndef __android__
175
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
176
#endif
dogmaphobic's avatar
dogmaphobic committed
177

178
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
179

180
    bool fClearSettingsOptions = false; // Clear stored settings
181 182
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
183

184
    CmdLineOpt_t rgCmdLineOptions[] = {
185 186
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
187
        { "--fake-mobile",      &_fakeMobile,           NULL },
188
#ifdef QT_DEBUG
189
        { "--test-high-dpi",    &_testHighDPI,          NULL },
190
#endif
191 192
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
193

194
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
195

dogmaphobic's avatar
dogmaphobic committed
196
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
197
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
198
#else
199
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
200

201 202
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
203

204 205
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
206

207 208 209 210 211 212 213 214 215 216 217 218
        if (logList[0] == "full") {
            filterRules += "*Log.debug=true\n";
            for(int i=1; i<logList.count(); i++) {
                filterRules += logList[i];
                filterRules += ".debug=false\n";
            }
        } else {
            foreach(QString rule, logList) {
                filterRules += rule;
                filterRules += ".debug=true\n";
            }
        }
219 220 221
    } 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.
222

223 224 225
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
226

227 228 229 230 231 232 233 234 235
        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;
236
            }
237
        } else {
238 239
            loggingDirectoryOk = true;
        }
240

241
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
242
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
243 244 245 246 247 248 249 250 251 252 253
            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;
254
                }
255 256 257
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
258

259 260
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
261
#endif
262

263
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
264 265 266
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
267

Don Gagne's avatar
Don Gagne committed
268
    // Set application information
269 270 271 272 273 274 275 276 277
    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);
278

dogmaphobic's avatar
dogmaphobic committed
279
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
280 281 282 283 284
    // stable versions are on tags (v1.2.3)
    // development versions are full git describe versions (v1.2.3-18-g879e8b3)
    if (versionString.length() > 8) {
        versionString.append(" (Development)");
    }
Don Gagne's avatar
Don Gagne committed
285
    this->setApplicationVersion(versionString);
286

287
    // Set settings format
dogmaphobic's avatar
dogmaphobic committed
288
#if !defined(__mobile__) && !defined(__macos__)
289
    QSettings::setDefaultFormat(QSettings::IniFormat);
dogmaphobic's avatar
dogmaphobic committed
290 291 292 293 294 295 296
#else
    QString settingsLocation = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
    if(!settingsLocation.isEmpty())
    {
        QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, settingsLocation);
    }
#endif
297

298
    QSettings settings;
Don Gagne's avatar
Don Gagne committed
299 300
    qDebug() << "Settings location" << settings.fileName() << settings.isWritable();

301 302 303
#ifdef UNITTEST_BUILD
    Q_ASSERT(settings.isWritable());
#endif
304
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
305
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
306

Don Gagne's avatar
Don Gagne committed
307
    if (_runningUnitTests) {
308
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
309 310
        fClearSettingsOptions = true;
    }
311

312 313 314
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
315
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
316
    }
Gus Grubba's avatar
Gus Grubba committed
317

318 319
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
320 321

    _toolbox = new QGCToolbox(this);
322 323
}

Don Gagne's avatar
Don Gagne committed
324 325
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
326 327 328 329
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
330
    shutdownVideoStreaming();
331
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
332 333
}

334
void QGCApplication::_initCommon(void)
335 336
{
    QSettings settings;
337

Don Gagne's avatar
Don Gagne committed
338
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
339

Don Gagne's avatar
Don Gagne committed
340 341
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
342

Don Gagne's avatar
Don Gagne committed
343 344 345 346 347 348 349
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
    qmlRegisterUncreatableType<MissionCommands>     ("QGroundControl",                  1, 0, "MissionCommands",        "Reference only");
    qmlRegisterUncreatableType<QGCQGeoCoordinate>   ("QGroundControl",                  1, 0, "QGCQGeoCoordinate",      "Reference only");
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                  1, 0, "QmlObjectListModel",     "Reference only");
    qmlRegisterUncreatableType<VideoReceiver>       ("QGroundControl",                  1, 0, "VideoReceiver",          "Reference only");
    qmlRegisterUncreatableType<VideoSurface>        ("QGroundControl",                  1, 0, "VideoSurface",           "Reference only");

350 351 352 353 354 355 356
    qmlRegisterUncreatableType<AutoPilotPlugin>     ("QGroundControl.AutoPilotPlugin",  1, 0, "AutoPilotPlugin",        "Reference only");
    qmlRegisterUncreatableType<VehicleComponent>    ("QGroundControl.AutoPilotPlugin",  1, 0, "VehicleComponent",       "Reference only");
    qmlRegisterUncreatableType<Vehicle>             ("QGroundControl.Vehicle",          1, 0, "Vehicle",                "Reference only");
    qmlRegisterUncreatableType<MissionItem>         ("QGroundControl.Vehicle",          1, 0, "MissionItem",            "Reference only");
    qmlRegisterUncreatableType<MissionManager>      ("QGroundControl.Vehicle",          1, 0, "MissionManager",         "Reference only");
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",  1, 0, "JoystickManager",        "Reference only");
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",  1, 0, "Joystick",               "Reference only");
357

Don Gagne's avatar
Don Gagne committed
358 359 360 361 362 363 364 365 366 367 368
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    qmlRegisterType<APMFlightModesComponentController>  ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
    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");
    qmlRegisterType<MainToolBarController>              ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
    qmlRegisterType<FlightDisplayViewController>        ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");
369

Don Gagne's avatar
Don Gagne committed
370
#ifndef __mobile__
371 372
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
373
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
374
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
375
#endif
dogmaphobic's avatar
dogmaphobic committed
376

377
    // Register Qml Singletons
378 379 380
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
dogmaphobic's avatar
dogmaphobic committed
381

382 383 384 385 386
    // 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;
387
        }
388 389 390
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
Don Gagne's avatar
Don Gagne committed
391 392
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
393
    }
394

395
    if (settingsUpgraded) {
396
        settings.clear();
397
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
398 399
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
400
    }
401

Don Gagne's avatar
Don Gagne committed
402
    // Load saved files location and validate
403

Don Gagne's avatar
Don Gagne committed
404 405 406
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
407 408 409 410
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
dogmaphobic's avatar
dogmaphobic committed
411

412 413
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
414

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

Don Gagne's avatar
Don Gagne committed
417
        QDir documentsDir(documentsLocation);
418 419 420
        if (!documentsDir.exists()) {
            qWarning() << "Documents directory doesn't exist" << documentsDir.absolutePath();
        }
421

422
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
423
        Q_UNUSED(pathCreated);
424 425
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
426
    }
427

Don Gagne's avatar
Don Gagne committed
428 429 430 431 432
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
433
    qDebug() << "Saved files location" << savedFilesLocation;
434
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
Don Gagne's avatar
Don Gagne committed
435 436

    settings.sync();
437 438 439 440 441
}

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

443 444
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
Lorenz Meier's avatar
Lorenz Meier committed
445

446 447 448
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
449
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
450
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
451
    Q_CHECK_PTR(mainWindow);
452

453 454 455 456 457
    // 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()) {
458 459 460
        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."));
461 462
        mainWindow->showSettings();
    }
463

Don Gagne's avatar
Don Gagne committed
464
#ifndef __mobile__
465
    // Now that main window is up check for lost log files
466
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
467
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
468
#endif
469 470

    // Load known link configurations
471
    toolbox()->linkManager()->loadLinkConfigurationList();
472

473
    return true;
pixhawk's avatar
pixhawk committed
474 475
}

476
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
477
{
478
    return true;
pixhawk's avatar
pixhawk committed
479 480
}

Don Gagne's avatar
Don Gagne committed
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
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
502

Don Gagne's avatar
Don Gagne committed
503
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
504
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
505 506 507
    if (!tempFile.open()) {
        return false;
    }
508

509
    tempFile.remove();
510

Don Gagne's avatar
Don Gagne committed
511 512 513 514 515 516
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
518 519 520 521 522 523 524
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
526
    location = parentDir.filePath(_savedFileParameterDirectoryName);
527

Don Gagne's avatar
Don Gagne committed
528 529 530 531 532 533 534
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
535

Don Gagne's avatar
Don Gagne committed
536 537 538 539 540 541 542
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
544
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
545

Don Gagne's avatar
Don Gagne committed
546 547 548 549 550 551 552
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
553

Don Gagne's avatar
Don Gagne committed
554 555 556 557 558 559
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
561 562 563
    return settings.value(_promptFlightDataSave, true).toBool();
}

564 565 566 567 568 569 570
bool QGCApplication::promptFlightDataSaveNotArmed(void)
{
    QSettings settings;

    return settings.value(_promptFlightDataSaveNotArmed, false).toBool();
}

Don Gagne's avatar
Don Gagne committed
571 572 573 574 575 576
void QGCApplication::setPromptFlightDataSave(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSave, promptForSave);
}

577 578 579 580 581 582
void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave)
{
    QSettings settings;
    settings.setValue(_promptFlightDataSaveNotArmed, promptForSave);
}

Don Gagne's avatar
Don Gagne committed
583 584 585
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
586 587 588 589
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

590 591
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
592 593
    Q_UNUSED(title);
    showMessage(msg);
594 595 596 597 598 599 600 601 602 603 604 605 606 607
}

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)
{
608 609 610 611 612 613 614 615 616
    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");
dogmaphobic's avatar
dogmaphobic committed
617

618 619 620
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
621
                // if the file cannot be removed, prompt user and ask new path
622
                saveError = true;
623
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
624 625 626
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
627
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
628 629 630
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
631 632
    QFile::remove(tempLogfile);
}
633 634 635 636

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

638 639 640 641 642 643 644 645 646 647
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

649 650
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
651

652 653 654 655 656 657 658 659 660
    // 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;
    }
661

662 663 664 665 666 667 668 669 670 671 672
    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;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
673

674
    setStyleSheet(styles);
675

676 677 678 679
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
680

681
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
682

683 684 685
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
686

Don Gagne's avatar
Don Gagne committed
687
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
688
{
Don Gagne's avatar
Don Gagne committed
689 690
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
691
}
692

Don Gagne's avatar
Don Gagne committed
693 694
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
695
{
Don Gagne's avatar
Don Gagne committed
696
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
697

Don Gagne's avatar
Don Gagne committed
698 699 700 701
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
702
        } else {
Don Gagne's avatar
Don Gagne committed
703
            params += QString(", %1").arg(name);
704 705
        }
    }
Don Gagne's avatar
Don Gagne committed
706
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
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));
}

714
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
715 716 717
{
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
718
        mainWindow->showMessage(message);
Don Gagne's avatar
Don Gagne committed
719 720 721 722
    } else {
        QGCMessageBox::information("", message);
    }
}