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

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

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

103
#ifdef QGC_RTLAB_ENABLED
104
    #include "OpalLink.h"
105
#endif
106 107 108


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

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

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

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

123 124

// Qml Singleton factories
125

Don Gagne's avatar
Don Gagne committed
126
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
127
{
Don Gagne's avatar
Don Gagne committed
128 129
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
130 131
}

132 133 134 135 136
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

137 138
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
139
    return new QGroundControlQmlGlobal(qgcApp()->toolbox());
140 141
}

dogmaphobic's avatar
dogmaphobic committed
142
#if defined(QGC_GST_STREAMING)
143 144
#ifdef Q_OS_MAC
#ifndef __ios__
145
#ifdef QGC_INSTALL_RELEASE
dogmaphobic's avatar
dogmaphobic committed
146 147 148 149 150 151
static void qgcputenv(const QString& key, const QString& root, const QString& path)
{
    QString value = root + path;
    qputenv(key.toStdString().c_str(), QByteArray(value.toStdString().c_str()));
}
#endif
152 153
#endif
#endif
154
#endif
dogmaphobic's avatar
dogmaphobic committed
155

pixhawk's avatar
pixhawk committed
156 157 158 159 160 161 162 163 164 165
/**
 * @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
 **/

166 167 168 169
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
dogmaphobic's avatar
dogmaphobic committed
170
    , _fakeMobile(false)
171 172 173
#ifdef QT_DEBUG
    , _testHighDPI(false)
#endif
174
    , _toolbox(NULL)
pixhawk's avatar
pixhawk committed
175
{
176 177
    Q_ASSERT(_app == NULL);
    _app = this;
178

179
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
180
#ifndef __android__
181
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
182
#endif
dogmaphobic's avatar
dogmaphobic committed
183

184
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
185

186
    bool fClearSettingsOptions = false; // Clear stored settings
187 188
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
189

190
    CmdLineOpt_t rgCmdLineOptions[] = {
191 192
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
193
        { "--fake-mobile",      &_fakeMobile,           NULL },
194
#ifdef QT_DEBUG
195
        { "--test-high-dpi",    &_testHighDPI,          NULL },
196
#endif
197 198
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
199

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

dogmaphobic's avatar
dogmaphobic committed
202
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
203
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
204
#else
205
    QString filterRules;
dogmaphobic's avatar
dogmaphobic committed
206

207 208
    // Turn off bogus ssl warning
    filterRules += "qt.network.ssl.warning=false\n";
dogmaphobic's avatar
dogmaphobic committed
209

210 211
    if (logging) {
        QStringList logList = loggingOptions.split(",");
dogmaphobic's avatar
dogmaphobic committed
212

213 214 215 216 217 218 219 220 221 222 223 224
        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";
            }
        }
225 226 227
    } 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.
228

229 230 231
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
232

233 234 235 236 237 238 239 240 241
        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;
242
            }
243
        } else {
244 245
            loggingDirectoryOk = true;
        }
246

247
        if (loggingDirectoryOk) {
Don Gagne's avatar
Don Gagne committed
248
            qDebug () << "Logging ini file directory" << iniFileLocation.absolutePath();
249 250 251 252 253 254 255 256 257 258 259
            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;
260
                }
261 262 263
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
264

265 266
    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
Don Gagne's avatar
Don Gagne committed
267
#endif
268

269
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
270 271 272
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
273

Don Gagne's avatar
Don Gagne committed
274
    // Set application information
275 276 277 278 279 280 281 282 283
    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);
284

dogmaphobic's avatar
dogmaphobic committed
285
    QString versionString(GIT_VERSION);
Daniel Agar's avatar
Daniel Agar committed
286 287 288 289 290
    // 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
291
    this->setApplicationVersion(versionString);
292

293 294
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
295

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

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

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

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

316 317
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
318 319

    _toolbox = new QGCToolbox(this);
320 321
}

Don Gagne's avatar
Don Gagne committed
322 323
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
324 325 326 327 328
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }

329
    shutdownVideoStreaming();
330
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
331 332
}

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

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

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

341 342 343 344 345 346 347 348
    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");
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                  1, 0, "QmlObjectListModel",     "Reference only");
349 350
    qmlRegisterUncreatableType<QGCQGeoCoordinate>   ("QGroundControl",                  1, 0, "QGCQGeoCoordinate",      "Reference only");
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
351 352 353
    qmlRegisterUncreatableType<VideoSurface>        ("QGroundControl",                  1, 0, "VideoSurface",           "Reference only");
    qmlRegisterUncreatableType<VideoReceiver>       ("QGroundControl",                  1, 0, "VideoReceiver",          "Reference only");

354 355 356 357 358 359 360
    qmlRegisterType<ParameterEditorController>      ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    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");
361
    qmlRegisterType<MainToolBarController>          ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
362
    qmlRegisterType<MissionController>              ("QGroundControl.Controllers", 1, 0, "MissionController");
363 364
    qmlRegisterType<FlightDisplayViewController>    ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");

Don Gagne's avatar
Don Gagne committed
365
#ifndef __mobile__
366 367
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
368
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
369
    qmlRegisterType<JoystickConfigController>       ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
370
#endif
dogmaphobic's avatar
dogmaphobic committed
371

372
    // Register Qml Singletons
373 374 375
    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
376

377 378 379 380 381
    // 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;
382
        }
383 384 385
    } 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
386 387
    } else {
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
388
    }
389

390
    if (settingsUpgraded) {
391
        settings.clear();
392
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
393 394 395
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
396
    }
397

Don Gagne's avatar
Don Gagne committed
398
    // Load saved files location and validate
399

Don Gagne's avatar
Don Gagne committed
400 401 402
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
403 404 405 406
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
dogmaphobic's avatar
dogmaphobic committed
407

408 409
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
410

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

Don Gagne's avatar
Don Gagne committed
413
        QDir documentsDir(documentsLocation);
414 415 416
        if (!documentsDir.exists()) {
            qWarning() << "Documents directory doesn't exist" << documentsDir.absolutePath();
        }
417

418
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
419
        Q_UNUSED(pathCreated);
420 421
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
422
    }
423

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

    settings.sync();
433 434 435 436 437
}

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

439 440 441
#ifdef __mobile__
    _styleIsDark = false;
#else
442
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
443
#endif
444
    _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 564 565 566 567 568 569 570 571 572
    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)
{
573 574 575 576
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
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)
{
594 595 596 597 598 599 600 601 602
    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
603

604 605 606
        if (!saveFilename.isEmpty()) {
            // if file exsits already, try to remove it first to overwrite it
            if(QFile::exists(saveFilename) && !QFile::remove(saveFilename)){
607
                // if the file cannot be removed, prompt user and ask new path
608
                saveError = true;
609
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
610 611 612
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
613
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
614 615 616
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
617 618
    QFile::remove(tempLogfile);
}
619 620 621 622

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

624 625 626 627 628 629 630 631 632 633
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

635 636
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
637

638 639 640 641 642 643 644 645 646
    // 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;
    }
647

648 649 650 651 652 653 654 655 656 657 658
    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
659

660
    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());
dogmaphobic's avatar
dogmaphobic committed
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();
dogmaphobic's avatar
dogmaphobic committed
693

694 695 696 697 698 699
    QGCMessageBox::critical(
        "Missing Parameters",
        QString("Parameters missing from firmware: %1.\n\n"
                "You should quit QGroundControl immediately and update your firmware.").arg(params));
}

Don Gagne's avatar
Don Gagne committed
700 701 702 703
void QGCApplication::showToolBarMessage(const QString& message)
{
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
704
        mainWindow->showToolbarMessage(message);
Don Gagne's avatar
Don Gagne committed
705 706 707 708
    } else {
        QGCMessageBox::information("", message);
    }
}