QGCApplication.cc 27.5 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>

Gus Grubba's avatar
Gus Grubba committed
43 44
#include <VideoItem.h>
#include <VideoSurface.h>
45
#if defined(QGC_GST_STREAMING)
Gus Grubba's avatar
Gus Grubba committed
46 47 48 49 50
G_BEGIN_DECLS
GST_PLUGIN_STATIC_DECLARE(QTVIDEOSINK_NAME);
G_END_DECLS
#endif

lm's avatar
lm committed
51
#include "configuration.h"
52
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
53
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
54
#include "MainWindow.h"
pixhawk's avatar
pixhawk committed
55
#include "GAudioOutput.h"
56
#include "CmdLineOptParser.h"
Don Gagne's avatar
Don Gagne committed
57
#include "QGCMessageBox.h"
58 59
#include "MainWindow.h"
#include "UDPLink.h"
dogmaphobic's avatar
dogmaphobic committed
60
#ifndef __ios__
61
#include "SerialLink.h"
dogmaphobic's avatar
dogmaphobic committed
62
#endif
63 64 65
#include "QGCSingleton.h"
#include "LinkManager.h"
#include "UASManager.h"
66
#include "UASMessageHandler.h"
67
#include "AutoPilotPluginManager.h"
68
#include "QGCTemporaryFile.h"
69
#include "QGCFileDialog.h"
70
#include "QGCPalette.h"
71
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
72 73
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
74
#include "CustomCommandWidgetController.h"
Don Gagne's avatar
Don Gagne committed
75 76 77
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "SensorsComponentController.h"
78
#include "PowerComponentController.h"
Don Gagne's avatar
Don Gagne committed
79
#include "RadioComponentController.h"
Don Gagne's avatar
Don Gagne committed
80
#include "ScreenToolsController.h"
dogmaphobic's avatar
dogmaphobic committed
81
#ifndef __mobile__
82 83 84 85
#include "FirmwareUpgradeController.h"
#endif
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
86
#include "MavManager.h"
Don Gagne's avatar
Don Gagne committed
87 88 89
#include "FirmwarePluginManager.h"
#include "Generic/GenericFirmwarePlugin.h"
#include "PX4/PX4FirmwarePlugin.h"
90

91
#ifdef QGC_RTLAB_ENABLED
92 93
#include "OpalLink.h"
#endif
94 95 96


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

Don Gagne's avatar
Don Gagne committed
98 99 100 101
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_savedFilesLocationKey = "SavedFilesLocation";
const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave";
102
const char* QGCApplication::_styleKey = "StyleIsDark";
Don Gagne's avatar
Don Gagne committed
103 104 105 106

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

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

111 112 113 114 115 116
/**
 * @brief ScreenTools creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
 **/

Don Gagne's avatar
Don Gagne committed
117
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
118
{
Don Gagne's avatar
Don Gagne committed
119 120
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
121 122
}

123 124 125 126 127 128 129 130 131
/**
 * @brief MavManager creation callback
 *
 * This is called by the QtQuick engine for creating the singleton
**/

static QObject* mavManagerSingletonFactory(QQmlEngine*, QJSEngine*)
{
    MavManager* mavManager = new MavManager;
132
    qgcApp()->setMavManager(mavManager);
133 134 135
    return mavManager;
}

dogmaphobic's avatar
dogmaphobic committed
136
#if defined(QGC_GST_STREAMING)
137 138
#ifdef Q_OS_MAC
#ifndef __ios__
139
#ifdef QGC_INSTALL_RELEASE
dogmaphobic's avatar
dogmaphobic committed
140 141 142 143 144 145
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
146 147
#endif
#endif
148
#endif
dogmaphobic's avatar
dogmaphobic committed
149

pixhawk's avatar
pixhawk committed
150 151 152 153 154 155 156 157 158 159
/**
 * @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
 **/

160 161 162 163 164
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication(argc, argv)
    , _runningUnitTests(unitTesting)
    , _styleIsDark(true)
    , _pMavManager(NULL)
165
	, _fakeMobile(false)
pixhawk's avatar
pixhawk committed
166
{
167 168
    Q_ASSERT(_app == NULL);
    _app = this;
169

170
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
171
#ifndef __android__
172
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
173
#endif
174 175 176 177 178 179 180 181 182
    
    // Parse command line options
    
    bool fClearSettingsOptions = false; // Clear stored settings
    bool fullLogging = false; // Turn on all logging
    
    CmdLineOpt_t rgCmdLineOptions[] = {
        { "--clear-settings",   &fClearSettingsOptions, QString() },
        { "--full-logging",     &fullLogging,           QString() },
183
		{ "--fake-mobile",      &_fakeMobile,           QString() },
184 185 186 187
        // Add additional command line option flags here
    };
    
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
188

dogmaphobic's avatar
dogmaphobic committed
189
#ifdef __mobile__
Don Gagne's avatar
Don Gagne committed
190
    QLoggingCategory::setFilterRules(QStringLiteral("*Log.debug=false"));
191 192 193 194
#else
    if (fullLogging) {
        QLoggingCategory::setFilterRules(QStringLiteral("*Log=true"));
    } else {
Don Gagne's avatar
Don Gagne committed
195 196 197 198 199
        if (_runningUnitTests) {
            // We need to turn off these warnings until the firmware meta data is cleaned up
            QLoggingCategory::setFilterRules(QStringLiteral("PX4ParameterLoaderLog.warning=false"));
        }
        
200 201
        // 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.
202

203 204 205
        static const char* qtProjectDir = "QtProject";
        static const char* qtLoggingFile = "qtlogging.ini";
        bool loggingDirectoryOk = false;
206

207 208 209 210 211 212 213 214 215
        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;
216
            }
217
        } else {
218 219
            loggingDirectoryOk = true;
        }
220

221 222 223 224 225 226 227 228 229 230 231 232 233
        if (loggingDirectoryOk) {
            qDebug () << iniFileLocation;
            if (!iniFileLocation.exists(qtLoggingFile)) {
                QFile loggingFile(iniFileLocation.filePath(qtLoggingFile));
                if (loggingFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
                    QTextStream out(&loggingFile);
                    out << "[Rules]\n";
                    out << "*Log.debug=false\n";
                    foreach(QString category, QGCLoggingCategoryRegister::instance()->registeredCategories()) {
                        out << category << ".debug=false\n";
                    }
                } else {
                    qDebug() << "Unable to create logging file" << QString(qtLoggingFile) << "in" << iniFileLocation;
234
                }
235 236 237
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
238
#endif
239

240
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
241 242 243
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
Don Gagne's avatar
Don Gagne committed
244
    
Don Gagne's avatar
Don Gagne committed
245
    // Set application information
246 247 248 249 250 251 252 253 254
    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);
255

Don Gagne's avatar
Don Gagne committed
256 257 258 259 260
    // 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);
261

262 263
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
264

265
    QSettings settings;
266 267 268 269
#ifdef UNITTEST_BUILD
    qDebug() << "Settings location" << settings.fileName();
    Q_ASSERT(settings.isWritable());
#endif
270
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
271
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
272

Don Gagne's avatar
Don Gagne committed
273
    if (_runningUnitTests) {
274
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
275 276
        fClearSettingsOptions = true;
    }
277

278 279 280
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
281
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
282
    }
Gus Grubba's avatar
Gus Grubba committed
283 284 285

    //----------------------------------------------------------------
    //-- Video Streaming
286 287 288
#if defined(QGC_GST_STREAMING)
#ifdef Q_OS_MAC
#ifndef __ios__
289
#ifdef QGC_INSTALL_RELEASE
290 291 292 293 294 295 296 297 298 299 300 301 302 303
    QString currentDir = QCoreApplication::applicationDirPath();
    qgcputenv("GST_PLUGIN_SCANNER",           currentDir, "/gst-plugin-scanner");
    qgcputenv("GTK_PATH",                     currentDir, "/../Frameworks/GStreamer.framework/Versions/Current");
    qgcputenv("GIO_EXTRA_MODULES",            currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gio/modules");
    qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0",   currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0");
    qgcputenv("GST_PLUGIN_SYSTEM_PATH",       currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0");
    qgcputenv("GST_PLUGIN_PATH_1_0",          currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0");
    qgcputenv("GST_PLUGIN_PATH",              currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0");
//    QStringList env = QProcessEnvironment::systemEnvironment().keys();
//    foreach(QString key, env) {
//        qDebug() << key << QProcessEnvironment::systemEnvironment().value(key);
//    }
#endif
#endif
304
#endif
305 306
#endif

Gus Grubba's avatar
Gus Grubba committed
307 308
    qmlRegisterType<VideoItem>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
    qmlRegisterUncreatableType<VideoSurface>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QLatin1String("VideoSurface from QML is not supported"));
309

310
#if defined(QGC_GST_STREAMING)
Gus Grubba's avatar
Gus Grubba committed
311 312 313 314 315 316 317 318
    GError* error = NULL;
    if (!gst_init_check(&argc, &argv, &error)) {
        qCritical() << "gst_init_check() failed: " << error->message;
        g_error_free(error);
    }
    GST_PLUGIN_STATIC_REGISTER(QTVIDEOSINK_NAME);
#endif

319 320
}

Don Gagne's avatar
Don Gagne committed
321 322
QGCApplication::~QGCApplication()
{
Don Gagne's avatar
Don Gagne committed
323
    _destroySingletons();
Gus Grubba's avatar
Gus Grubba committed
324 325 326
#if defined(QGC_GST_STREAMING)
    gst_deinit();
#endif
Don Gagne's avatar
Don Gagne committed
327 328
}

329
void QGCApplication::_initCommon(void)
330 331
{
    QSettings settings;
332

Don Gagne's avatar
Don Gagne committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    // Register our Qml objects
    
    qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
    
    qmlRegisterUncreatableType<AutoPilotPlugin>("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Can only reference, cannot create");
    qmlRegisterUncreatableType<VehicleComponent>("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Can only reference, cannot create");
    
    qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
    qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
    qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
    qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
    qmlRegisterType<SensorsComponentController>("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
    qmlRegisterType<PowerComponentController>("QGroundControl.Controllers", 1, 0, "PowerComponentController");
    qmlRegisterType<RadioComponentController>("QGroundControl.Controllers", 1, 0, "RadioComponentController");
    qmlRegisterType<ScreenToolsController>("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    
#ifndef __mobile__
    qmlRegisterType<FirmwareUpgradeController>("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
#endif
    
    //-- Create QML Singleton Interfaces
    qmlRegisterSingletonType<ScreenToolsController>("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavManager>("QGroundControl.MavManager", 1, 0, "MavManager", mavManagerSingletonFactory);
    
    //-- Register Waypoint Interface
    qmlRegisterInterface<Waypoint>("Waypoint");
360 361 362 363 364
    // 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;
365
        }
366 367 368 369
    } else if (settings.allKeys().count()) {
        // Settings version key is missing and there are settings. This is an upgrade scenario.
        settingsUpgraded = true;
    }
370

371
    if (settingsUpgraded) {
372
        settings.clear();
373
        settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
374 375 376
        QGCMessageBox::information(tr("Settings Cleared"),
                                   tr("The format for QGroundControl saved settings has been modified. "
                                      "Your saved settings have been reset to defaults."));
377
    }
378

379 380
    _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool();
    _loadCurrentStyle();
381

Don Gagne's avatar
Don Gagne committed
382
    // Load saved files location and validate
383

Don Gagne's avatar
Don Gagne committed
384 385 386
    QString savedFilesLocation;
    if (settings.contains(_savedFilesLocationKey)) {
        savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
387 388 389 390 391 392 393
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
    
    if (savedFilesLocation.isEmpty()) {
        // No location set (or invalid). Create a default one in Documents standard location.
394

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

Don Gagne's avatar
Don Gagne committed
397 398
        QDir documentsDir(documentsLocation);
        Q_ASSERT(documentsDir.exists());
399

400
        bool pathCreated = documentsDir.mkpath(_defaultSavedFileDirectoryName);
401
        Q_UNUSED(pathCreated);
402 403
        Q_ASSERT(pathCreated);
        savedFilesLocation = documentsDir.filePath(_defaultSavedFileDirectoryName);
Don Gagne's avatar
Don Gagne committed
404
    }
405

Don Gagne's avatar
Don Gagne committed
406 407 408 409 410
    if (!savedFilesLocation.isEmpty()) {
        if (!validatePossibleSavedFilesLocation(savedFilesLocation)) {
            savedFilesLocation.clear();
        }
    }
411
    qDebug() << "Saved files location" << savedFilesLocation;
412 413 414 415 416 417
    settings.setValue(_savedFilesLocationKey, savedFilesLocation);
}

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

419
    _createSingletons();
420

421
    // Show splash screen
Don Gagne's avatar
Don Gagne committed
422
    QPixmap splashImage(":/res/SplashScreen");
423
    QSplashScreen* splashScreen = new QSplashScreen(splashImage);
424 425
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
426
    splashScreen->show();
lm's avatar
lm committed
427
    processEvents();
428
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
429 430 431
    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

pixhawk's avatar
pixhawk committed
432
    // Start the user interface
433
    splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
434
    MainWindow* mainWindow = MainWindow::_create(splashScreen);
Don Gagne's avatar
Don Gagne committed
435
    Q_CHECK_PTR(mainWindow);
436

437 438 439 440 441
    // 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()) {
442 443 444
        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."));
445 446
        mainWindow->showSettings();
    }
447

lm's avatar
lm committed
448
    // Remove splash screen
Don Gagne's avatar
Don Gagne committed
449 450
    splashScreen->finish(mainWindow);
    mainWindow->splashScreenFinished();
451

452
    // Now that main window is up check for lost log files
453 454
    connect(this, &QGCApplication::checkForLostLogFiles, MAVLinkProtocol::instance(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();
455 456 457 458

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

459
    return true;
pixhawk's avatar
pixhawk committed
460 461
}

462
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
463
{
464
    return true;
pixhawk's avatar
pixhawk committed
465 466
}

Don Gagne's avatar
Don Gagne committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
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
488

Don Gagne's avatar
Don Gagne committed
489
    QString filename = QDir(location).filePath("QGCTempXXXXXXXX.tmp");
490
    QGCTemporaryFile tempFile(filename);
Don Gagne's avatar
Don Gagne committed
491 492 493
    if (!tempFile.open()) {
        return false;
    }
494

495
    tempFile.remove();
496

Don Gagne's avatar
Don Gagne committed
497 498 499 500 501 502
    return true;
}

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

Don Gagne's avatar
Don Gagne committed
504 505 506 507 508 509 510
    return settings.value(_savedFilesLocationKey).toString();
}

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

Don Gagne's avatar
Don Gagne committed
512
    location = parentDir.filePath(_savedFileParameterDirectoryName);
513

Don Gagne's avatar
Don Gagne committed
514 515 516 517 518 519 520
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileParameterDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
521

Don Gagne's avatar
Don Gagne committed
522 523 524 525 526 527 528
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
530
    location = parentDir.filePath(_savedFileMavlinkLogDirectoryName);
531

Don Gagne's avatar
Don Gagne committed
532 533 534 535 536 537 538
    if (!QDir(location).exists()) {
        // If directory doesn't exist, try to create it
        if (!parentDir.mkpath(_savedFileMavlinkLogDirectoryName)) {
            // Return an error
            location.clear();
        }
    }
539

Don Gagne's avatar
Don Gagne committed
540 541 542 543 544 545
    return location;
}

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

Don Gagne's avatar
Don Gagne committed
547 548 549 550 551 552 553 554 555 556 557 558
    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)
{
559 560 561 562 563 564 565 566 567
    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
568
    // The order here is important since the singletons reference each other
569

Don Gagne's avatar
Don Gagne committed
570 571 572 573 574 575 576 577 578 579 580 581 582 583
    // No dependencies
    FirmwarePlugin* firmwarePlugin = GenericFirmwarePlugin::_createSingleton();
    Q_UNUSED(firmwarePlugin);
    Q_ASSERT(firmwarePlugin);
    
    // No dependencies
    firmwarePlugin = PX4FirmwarePlugin::_createSingleton();
    
    // No dependencies
    FirmwarePluginManager* firmwarePluginManager = FirmwarePluginManager::_createSingleton();
    Q_UNUSED(firmwarePluginManager);
    Q_ASSERT(firmwarePluginManager);
    
    // No dependencies
584 585 586
    GAudioOutput* audio = GAudioOutput::_createSingleton();
    Q_UNUSED(audio);
    Q_ASSERT(audio);
587

Don Gagne's avatar
Don Gagne committed
588
    // No dependencies
Don Gagne's avatar
Don Gagne committed
589
    LinkManager* linkManager = LinkManager::_createSingleton();
590 591 592
    Q_UNUSED(linkManager);
    Q_ASSERT(linkManager);

Don Gagne's avatar
Don Gagne committed
593 594
    // Needs LinkManager
    UASManagerInterface* uasManager = UASManager::_createSingleton();
595 596 597
    Q_UNUSED(uasManager);
    Q_ASSERT(uasManager);

Don Gagne's avatar
Don Gagne committed
598 599
    // Need UASManager
    AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::_createSingleton();
600 601
    Q_UNUSED(pluginManager);
    Q_ASSERT(pluginManager);
Don Gagne's avatar
Don Gagne committed
602

603
    // Need UASManager
604
    UASMessageHandler* messageHandler = UASMessageHandler::_createSingleton();
605 606 607
    Q_UNUSED(messageHandler);
    Q_ASSERT(messageHandler);

Don Gagne's avatar
Don Gagne committed
608 609
    // Needs UASManager
    FactSystem* factSystem = FactSystem::_createSingleton();
Don Gagne's avatar
Don Gagne committed
610 611
    Q_UNUSED(factSystem);
    Q_ASSERT(factSystem);
612

613 614 615 616
    // Needs everything!
    MAVLinkProtocol* mavlink = MAVLinkProtocol::_createSingleton();
    Q_UNUSED(mavlink);
    Q_ASSERT(mavlink);
617

618 619
}

Don Gagne's avatar
Don Gagne committed
620
void QGCApplication::_destroySingletons(void)
621
{
622 623 624
    if (MainWindow::instance()) {
        delete MainWindow::instance();
    }
625

626 627 628 629 630 631 632 633 634
    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();
    }
635

636 637
    // Let the signals flow through the main thread
    processEvents(QEventLoop::ExcludeUserInputEvents);
638

Don Gagne's avatar
Don Gagne committed
639
    // Take down singletons in reverse order of creation
640

641
    MAVLinkProtocol::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
642
    FactSystem::_deleteSingleton();
643
    UASMessageHandler::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
644 645 646
    AutoPilotPluginManager::_deleteSingleton();
    UASManager::_deleteSingleton();
    LinkManager::_deleteSingleton();
647
    GAudioOutput::_deleteSingleton();
Don Gagne's avatar
Don Gagne committed
648
}
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666

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)
{
667 668 669 670 671 672 673 674 675 676 677 678 679
    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)){
680
                // if the file cannot be removed, prompt user and ask new path
681
                saveError = true;
682
                QGCMessageBox::warning("File Error","Could not overwrite existing file.\nPlease provide a different file name to save to.");
683 684 685
            } else if(!QFile::copy(tempLogfile, saveFilename)) {
                // if file could not be copied, prompt user and ask new path
                saveError = true;
686
                QGCMessageBox::warning("File Error","Could not create file.\nPlease provide a different file name to save to.");
687 688 689
            }
        }
    } while(saveError); // if the file could not be overwritten, ask for new file
690 691
    QFile::remove(tempLogfile);
}
692 693 694 695

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

697 698 699 700 701 702 703 704 705 706
    settings.setValue(_styleKey, styleIsDark);
    _styleIsDark = styleIsDark;
    _loadCurrentStyle();
    emit styleChanged(_styleIsDark);
}

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

708 709
    // Signal to the user that the app will pause to apply a new stylesheet
    setOverrideCursor(Qt::WaitCursor);
710

711 712 713 714 715 716 717 718 719
    // 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;
    }
720

721 722 723 724 725 726 727 728 729 730 731
    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
732
    
733
    // Now that we have the styles loaded we need to adjust the font sizes.
Don Gagne's avatar
Don Gagne committed
734 735 736 737 738 739
    
    QString fSmall  = QString("%1px;").arg(ScreenToolsController::smallFontPixelSize_s());
    QString fNormal = QString("%1px;").arg(ScreenToolsController::defaultFontPixelSize_s());
    QString fLarge  = QString("%1px;").arg(ScreenToolsController::largeFontPixelSize_s());
    
    qDebug() << fSmall << fNormal << fLarge;
740 741 742 743 744

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

746 747 748 749
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
750

751
    QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
752

753 754 755
    // Finally restore the cursor before returning.
    restoreOverrideCursor();
}
Don Gagne's avatar
Don Gagne committed
756

Don Gagne's avatar
Don Gagne committed
757
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
758
{
Don Gagne's avatar
Don Gagne committed
759 760
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
761
}
762

Don Gagne's avatar
Don Gagne committed
763 764
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
765
{
Don Gagne's avatar
Don Gagne committed
766
    Q_ASSERT(_missingParams.count());
767
    
Don Gagne's avatar
Don Gagne committed
768 769 770 771
    QString params;
    foreach (QString name, _missingParams) {
        if (params.isEmpty()) {
            params += name;
772
        } else {
Don Gagne's avatar
Don Gagne committed
773
            params += QString(", %1").arg(name);
774 775
        }
    }
Don Gagne's avatar
Don Gagne committed
776
    _missingParams.clear();
777
    
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
    QGCMessageBox::critical(
        "Missing Parameters",
        QString("Parameters missing from firmware: %1.\n\n"
                "You should quit QGroundControl immediately and update your firmware.").arg(params));
}

void QGCApplication::setMavManager(MavManager* pMgr)
{
    if(!_pMavManager)
        _pMavManager = pMgr;
}

MavManager* QGCApplication::getMavManager()
{
    return _pMavManager;
dogmaphobic's avatar
dogmaphobic committed
793
}
Don Gagne's avatar
Don Gagne committed
794 795 796 797 798 799 800 801 802 803

void QGCApplication::showToolBarMessage(const QString& message)
{
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        mainWindow->getMainToolBar()->showToolBarMessage(message);
    } else {
        QGCMessageBox::information("", message);
    }
}