Skip to content
QGCApplication.cc 32.5 KiB
Newer Older
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
pixhawk's avatar
pixhawk committed
/**
 * @file
Don Gagne's avatar
Don Gagne committed
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QAction>
#include <QDesktopWidget>
pixhawk's avatar
pixhawk committed
#include <QFile>
#include <QFlags>
#include <QPainter>
#include <QPixmap>
Don Gagne's avatar
 
Don Gagne committed
#include <QRegularExpression>
#include <QStringListModel>
#include <QStyleFactory>
pixhawk's avatar
pixhawk committed

dogmaphobic's avatar
dogmaphobic committed
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

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

#include "AppMessages.h"
#include "AudioOutput.h"
#include "AutoPilotPlugin.h"
#include "CameraCalc.h"
#include "CmdLineOptParser.h"
#include "CoordinateVector.h"
Don Gagne's avatar
Don Gagne committed
#include "CustomCommandWidgetController.h"
dogmaphobic's avatar
dogmaphobic committed
#include "ESP8266ComponentController.h"
#include "EditPositionDialogController.h"
#include "FactValueSliderListModel.h"
#include "FirmwareImage.h"
Don Gagne's avatar
Don Gagne committed
#include "FirmwarePluginManager.h"
#include "FlightMapSettings.h"
#include "FollowMe.h"
#include "JoystickConfigController.h"
#include "JoystickManager.h"
#include "LinkManager.h"
#include "LogDownloadController.h"
#include "MissionCommandTree.h"
Don Gagne's avatar
Don Gagne committed
#include "MissionManager.h"
#include "MultiVehicleManager.h"
#include "ParameterEditorController.h"
#include "ParameterManager.h"
#include "PlanMasterController.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
#include "PositionManager.h"
#include "QGC.h"
#include "QGCApplication.h"
#include "QGCCameraManager.h"
#include "QGCCorePlugin.h"
#include "QGCFileDialogController.h"
#include "QGCFileDownload.h"
#include "QGCGeoBoundingCube.h"
#include "QGCLoggingCategory.h"
#include "QGCMapCircle.h"
#include "QGCMapPalette.h"
#include "QGCMapPolygon.h"
#include "QGCPalette.h"
#include "QGCTemporaryFile.h"
#include "QGroundControlQmlGlobal.h"
#include "QmlObjectListModel.h"
#include "RCChannelMonitorController.h"
#include "ScreenToolsController.h"
#include "SettingsManager.h"
#include "ShapeFileHelper.h"
#include "SimulatedPosition.h"
#include "SyslinkComponentController.h"
#include "UASMessageHandler.h"
#include "UDPLink.h"
#include "ValuesWidgetController.h"
#include "Vehicle.h"
#include "VehicleComponent.h"
#include "VideoManager.h"
#include "VideoReceiver.h"
#include "VideoSurface.h"
#include "ViewWidgetController.h"
#include "VisualMissionItem.h"
#include "Wima/Snake/NemoInterface.h"
#include "Wima/WimaController.h"
#include "Wima/WimaPlaner.h"
Gus Grubba's avatar
Gus Grubba committed
#ifndef NO_SERIAL_LINK
#include "SerialLink.h"
#endif

#ifndef __mobile__
#include "FirmwareUpgradeController.h"
#include "GPS/GPSManager.h"
#include "GeoTagController.h"
#include "MainWindow.h"
#include "MavlinkConsoleController.h"
#include "QGCMessageBox.h"
#include "QGCQFileDialog.h"
#ifdef QGC_RTLAB_ENABLED
#include "OpalLink.h"
Don Gagne's avatar
Don Gagne committed
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <sys/types.h>
#include <unistd.h>
Don Gagne's avatar
Don Gagne committed
#endif
#endif
dogmaphobic's avatar
dogmaphobic committed
#include "QGCMapEngine.h"

QGCApplication *QGCApplication::_app = nullptr;
pixhawk's avatar
pixhawk committed

const char *QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char *QGCApplication::_settingsVersionKey = "SettingsVersion";
const char *QGCApplication::_darkStyleFile = ":/res/styles/style-dark.css";
const char *QGCApplication::_lightStyleFile = ":/res/styles/style-light.css";
// Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];

// Qml Singleton factories
static QObject *screenToolsControllerSingletonFactory(QQmlEngine *,
                                                      QJSEngine *) {
  ScreenToolsController *screenToolsController = new ScreenToolsController;
  return screenToolsController;
static QObject *qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine *,
                                                        QJSEngine *) {
  // We create this object as a QGCTool even though it isn't in the toolbox
  QGroundControlQmlGlobal *qmlGlobal =
      new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox());
  qmlGlobal->setToolbox(qgcApp()->toolbox());
  return qmlGlobal;
static QObject *shapeFileHelperSingletonFactory(QQmlEngine *, QJSEngine *) {
  return new ShapeFileHelper;
pixhawk's avatar
pixhawk committed

QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
#ifdef __mobile__
    : QGuiApplication(argc, argv), _qmlAppEngine(nullptr)
    : QApplication(argc, argv)
      ,
      _runningUnitTests(unitTesting), _logOutput(false), _fakeMobile(false),
      _settingsUpgraded(false), _majorVersion(0), _minorVersion(0),
      _buildVersion(0), _currentVersionDownload(nullptr),
      _gpsRtkFactGroup(nullptr), _toolbox(nullptr), _bluetoothAvailable(false) {
  _app = this;

  QLocale locale = QLocale::system();
  //-- Some forced locales for testing
  // QLocale locale = QLocale(QLocale::German);
  // QLocale locale = QLocale(QLocale::French);
  // QLocale locale = QLocale(QLocale::Chinese);
#if defined(__macos__)
  locale = QLocale(locale.name());
  qDebug() << "System reported locale:" << locale << locale.name();
  //-- Our localization
  if (_QGCTranslator.load(locale, "qgc_", "", ":/localization"))
    _app->installTranslator(&_QGCTranslator);
    // This prevents usage of QQuickWidget to fail since it doesn't support
    // native widget siblings
dogmaphobic's avatar
dogmaphobic committed
#ifndef __android__
  setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
#endif
dogmaphobic's avatar
dogmaphobic committed

  // Setup for network proxy support
  QNetworkProxyFactory::setUseSystemConfiguration(true);
#ifdef Q_OS_LINUX
#ifndef __mobile__
  if (!_runningUnitTests) {
    if (getuid() == 0) {
      QMessageBox msgBox;
      msgBox.setInformativeText(
          tr("You are running %1 as root. "
             "You should not do this since it will cause other issues with %1. "
             "%1 will now exit. "
             "If you are having serial port issues on Ubuntu, execute the "
             "following commands to fix most issues:\n"
             "sudo usermod -a -G dialout $USER\n"
             "sudo apt-get remove modemmanager")
              .arg(qgcApp()->applicationName()));
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.setDefaultButton(QMessageBox::Ok);
      msgBox.exec();
      _exit(0);
    }
    // Determine if we have the correct permissions to access USB serial devices
    QFile permFile("/etc/group");
    if (permFile.open(QIODevice::ReadOnly)) {
      while (!permFile.atEnd()) {
        QString line = permFile.readLine();
        if (line.contains("dialout") && !line.contains(getenv("USER"))) {
          QMessageBox msgBox;
          msgBox.setInformativeText(
              "The current user does not have the correct permissions to "
              "access serial devices. "
              "You should also remove modemmanager since it also interferes. "
              "If you are using Ubuntu, execute the following commands to fix "
              "these issues:\n"
              "sudo usermod -a -G dialout $USER\n"
              "sudo apt-get remove modemmanager");
          msgBox.setStandardButtons(QMessageBox::Ok);
          msgBox.setDefaultButton(QMessageBox::Ok);
          msgBox.exec();
          break;
      }
      permFile.close();
  // Parse command line options

  bool fClearSettingsOptions = false; // Clear stored settings
  bool fClearCache = false;           // Clear parameter/airframe caches
  bool logging = false;               // Turn on logging
  QString loggingOptions;

  CmdLineOpt_t rgCmdLineOptions[] = {
      {"--clear-settings", &fClearSettingsOptions, nullptr},
      {"--clear-cache", &fClearCache, nullptr},
      {"--logging", &logging, &loggingOptions},
      {"--fake-mobile", &_fakeMobile, nullptr},
      {"--log-output", &_logOutput, nullptr},
      // Add additional command line option flags here
  };

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

  // Set up timer for delayed missing fact display
  _missingParamsDelayedDisplayTimer.setSingleShot(true);
  _missingParamsDelayedDisplayTimer.setInterval(
      _missingParamsDelayedDisplayTimerTimeout);
  connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this,
          &QGCApplication::_missingParamsDisplay);

  // Set application information
  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);

  this->setApplicationVersion(QString(GIT_VERSION));

  // Set settings format
  QSettings::setDefaultFormat(QSettings::IniFormat);
  QSettings settings;
  qDebug() << "Settings location" << settings.fileName()
           << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
#ifdef UNITTEST_BUILD
  if (!settings.isWritable()) {
    qWarning() << "Setings location is not writable";
  }
Don Gagne's avatar
Don Gagne committed
#endif
  // The setting will delete all settings on this boot
  fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);

  if (_runningUnitTests) {
    // Unit tests run with clean settings
    fClearSettingsOptions = true;
  }

  if (fClearSettingsOptions) {
    // User requested settings to be cleared on command line
    settings.clear();

    // Clear parameter cache
    QDir paramDir(ParameterManager::parameterCacheDir());
    paramDir.removeRecursively();
    paramDir.mkpath(paramDir.absolutePath());
  } else {
    // Determine if upgrade message for settings version bump is required. Check
    // and clear must happen before toolbox is started since that will write
    // some settings.
    if (settings.contains(_settingsVersionKey)) {
      if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
        settings.clear();
        _settingsUpgraded = true;
      }
    } else if (settings.allKeys().count()) {
      // Settings version key is missing and there are settings. This is an
      // upgrade scenario.
      settings.clear();
      _settingsUpgraded = true;
Don Gagne's avatar
 
Don Gagne committed
    }
  }
  settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);

  if (fClearCache) {
    QDir dir(ParameterManager::parameterCacheDir());
    dir.removeRecursively();
    QFile airframe(cachedAirframeMetaDataFile());
    airframe.remove();
    QFile parameter(cachedParameterMetaDataFile());
    parameter.remove();
  }

  // Set up our logging filters
  QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(
      loggingOptions);

  // Initialize Bluetooth
Valentin Platzgummer's avatar
Valentin Platzgummer committed
#ifdef QGC_ENABLE_BLUETOOTHsubPolylines
  QBluetoothLocalDevice localDevice;
  if (localDevice.isValid()) {
    _bluetoothAvailable = true;
  }
dogmaphobic's avatar
dogmaphobic committed
#endif

  // Gstreamer debug settings
#if defined(__ios__) || defined(__android__)
  // Initialize Video Streaming
  initializeVideoStreaming(argc, argv, nullptr, nullptr);
#else
  QString savePath, gstDebugLevel;
  if (settings.contains(AppSettings::savePathName)) {
    savePath = settings.value(AppSettings::savePathName).toString();
  }
  if (savePath.isEmpty()) {
    savePath = "/tmp";
  }
  savePath = savePath + "/Logs/gst";
  if (!QDir(savePath).exists()) {
    QDir().mkpath(savePath);
  }
  if (settings.contains(AppSettings::gstDebugLevelName)) {
    gstDebugLevel =
        "*:" + settings.value(AppSettings::gstDebugLevelName).toString();
  }
  // Initialize Video Streaming
  initializeVideoStreaming(argc, argv, savePath.toUtf8().data(),
                           gstDebugLevel.toUtf8().data());
#endif
  _toolbox = new QGCToolbox(this);
  _toolbox->setChildToolboxes();
Don Gagne's avatar
 
Don Gagne committed

Don Gagne's avatar
 
Don Gagne committed
#ifndef __mobile__
  _gpsRtkFactGroup = new GPSRTKFactGroup(this);
  GPSManager *gpsManager = _toolbox->gpsManager();
  if (gpsManager) {
    connect(gpsManager, &GPSManager::onConnect, this,
            &QGCApplication::_onGPSConnect);
    connect(gpsManager, &GPSManager::onDisconnect, this,
            &QGCApplication::_onGPSDisconnect);
    connect(gpsManager, &GPSManager::surveyInStatus, this,
            &QGCApplication::_gpsSurveyInStatus);
    connect(gpsManager, &GPSManager::satelliteUpdate, this,
            &QGCApplication::_gpsNumSatellites);
  }
Don Gagne's avatar
 
Don Gagne committed
#endif /* __mobile__ */

  _checkForNewVersion();
void QGCApplication::_shutdown(void) {
  // This code is specifically not in the destructor since the application
  // object may not be available in the destructor. This cause problems for
  // deleting object like settings which are in the toolbox which may have qml
  // references. By moving them here and having main.cc call this prior to
  // deleting the app object we make sure app object is still around while these
  // things are shutting down.
Don Gagne's avatar
Don Gagne committed
#ifndef __mobile__
  MainWindow *mainWindow = MainWindow::instance();
  if (mainWindow) {
    delete mainWindow;
  }
Don Gagne's avatar
Don Gagne committed
#endif
  shutdownVideoStreaming();
  delete _toolbox;
}

QGCApplication::~QGCApplication() {
  // Place shutdown code in _shutdown
  _app = nullptr;
}

void QGCApplication::_initCommon(void) {
  static const char *kRefOnly = "Reference only";
  static const char *kQGCControllers = "QGroundControl.Controllers";
  static const char *kQGCVehicle = "QGroundControl.Vehicle";

  QSettings settings;

  // Register our Qml objects

  qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
  qmlRegisterType<QGCMapPalette>("QGroundControl.Palette", 1, 0,
                                 "QGCMapPalette");

  qmlRegisterUncreatableType<Vehicle>(kQGCVehicle, 1, 0, "Vehicle", kRefOnly);
  qmlRegisterUncreatableType<MissionItem>(kQGCVehicle, 1, 0, "MissionItem",
                                          kRefOnly);
  qmlRegisterUncreatableType<MissionManager>(kQGCVehicle, 1, 0,
                                             "MissionManager", kRefOnly);
  qmlRegisterUncreatableType<ParameterManager>(kQGCVehicle, 1, 0,
                                               "ParameterManager", kRefOnly);
  qmlRegisterUncreatableType<QGCCameraManager>(kQGCVehicle, 1, 0,
                                               "QGCCameraManager", kRefOnly);
  qmlRegisterUncreatableType<QGCCameraControl>(kQGCVehicle, 1, 0,
                                               "QGCCameraControl", kRefOnly);
  qmlRegisterUncreatableType<QGCVideoStreamInfo>(
      kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);
  qmlRegisterUncreatableType<LinkInterface>(kQGCVehicle, 1, 0, "LinkInterface",
                                            kRefOnly);
  qmlRegisterUncreatableType<MissionController>(kQGCControllers, 1, 0,
                                                "MissionController", kRefOnly);
  qmlRegisterUncreatableType<GeoFenceController>(
      kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);
  qmlRegisterUncreatableType<RallyPointController>(
      kQGCControllers, 1, 0, "RallyPointController", kRefOnly);
  qmlRegisterUncreatableType<VisualMissionItem>(kQGCControllers, 1, 0,
                                                "VisualMissionItem", kRefOnly);

  qmlRegisterUncreatableType<CoordinateVector>("QGroundControl", 1, 0,
                                               "CoordinateVector", kRefOnly);
  qmlRegisterUncreatableType<QmlObjectListModel>(
      "QGroundControl", 1, 0, "QmlObjectListModel", kRefOnly);
  qmlRegisterUncreatableType<MissionCommandTree>(
      "QGroundControl", 1, 0, "MissionCommandTree", kRefOnly);
  qmlRegisterUncreatableType<CameraCalc>("QGroundControl", 1, 0, "CameraCalc",
                                         kRefOnly);

  qmlRegisterUncreatableType<AutoPilotPlugin>(
      "QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly);
  qmlRegisterUncreatableType<VehicleComponent>(
      "QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly);
  qmlRegisterUncreatableType<JoystickManager>(
      "QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly);
  qmlRegisterUncreatableType<Joystick>("QGroundControl.JoystickManager", 1, 0,
                                       "Joystick", kRefOnly);
  qmlRegisterUncreatableType<QGCPositionManager>(
      "QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager",
      kRefOnly);
  qmlRegisterUncreatableType<FactValueSliderListModel>(
      "QGroundControl.FactControls", 1, 0, "FactValueSliderListModel",
      kRefOnly);

  qmlRegisterUncreatableType<QGCMapPolygon>("QGroundControl.FlightMap", 1, 0,
                                            "QGCMapPolygon", kRefOnly);

  qmlRegisterUncreatableType<QGCGeoBoundingCube>(
      "QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly);

  qmlRegisterType<QGCMapCircle>("QGroundControl.FlightMap", 1, 0,
                                "QGCMapCircle");

  qmlRegisterType<ParameterEditorController>(kQGCControllers, 1, 0,
                                             "ParameterEditorController");
  qmlRegisterType<ESP8266ComponentController>(kQGCControllers, 1, 0,
                                              "ESP8266ComponentController");
  qmlRegisterType<ScreenToolsController>(kQGCControllers, 1, 0,
                                         "ScreenToolsController");
  qmlRegisterType<PlanMasterController>(kQGCControllers, 1, 0,
                                        "PlanMasterController");
  qmlRegisterType<ValuesWidgetController>(kQGCControllers, 1, 0,
                                          "ValuesWidgetController");
  qmlRegisterType<QGCFileDialogController>(kQGCControllers, 1, 0,
                                           "QGCFileDialogController");
  qmlRegisterType<RCChannelMonitorController>(kQGCControllers, 1, 0,
                                              "RCChannelMonitorController");
  qmlRegisterType<JoystickConfigController>(kQGCControllers, 1, 0,
                                            "JoystickConfigController");
  qmlRegisterType<LogDownloadController>(kQGCControllers, 1, 0,
                                         "LogDownloadController");
  qmlRegisterType<SyslinkComponentController>(kQGCControllers, 1, 0,
                                              "SyslinkComponentController");
  qmlRegisterType<EditPositionDialogController>(kQGCControllers, 1, 0,
                                                "EditPositionDialogController");
Don Gagne's avatar
Don Gagne committed
#ifndef __mobile__
  qmlRegisterType<ViewWidgetController>(kQGCControllers, 1, 0,
                                        "ViewWidgetController");
  qmlRegisterType<CustomCommandWidgetController>(
      kQGCControllers, 1, 0, "CustomCommandWidgetController");
  qmlRegisterType<FirmwareUpgradeController>(kQGCControllers, 1, 0,
                                             "FirmwareUpgradeController");
  qmlRegisterType<GeoTagController>(kQGCControllers, 1, 0, "GeoTagController");
  qmlRegisterType<MavlinkConsoleController>(kQGCControllers, 1, 0,
                                            "MavlinkConsoleController");
Don Gagne's avatar
Don Gagne committed
#endif
  // Wima
  qmlRegisterType<WimaController>("Wima", 1, 0, "WimaController");
  qmlRegisterType<WimaPlaner>("Wima", 1, 0, "WimaPlaner");
  qmlRegisterType<NemoInterface>("Wima", 1, 0, "NemoInterface");
  // Register Qml Singletons
  qmlRegisterSingletonType<QGroundControlQmlGlobal>(
      "QGroundControl", 1, 0, "QGroundControl",
      qgroundcontrolQmlGlobalSingletonFactory);
  qmlRegisterSingletonType<ScreenToolsController>(
      "QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController",
      screenToolsControllerSingletonFactory);
  qmlRegisterSingletonType<ShapeFileHelper>("QGroundControl.ShapeFileHelper", 1,
                                            0, "ShapeFileHelper",
                                            shapeFileHelperSingletonFactory);
bool QGCApplication::_initForNormalAppBoot(void) {
  QSettings settings;
  _loadCurrentStyleSheet();
Lorenz Meier's avatar
Lorenz Meier committed

  // Exit main application when last window is closed
  connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
Don Gagne's avatar
Don Gagne committed
#ifdef __mobile__
  _qmlAppEngine = toolbox()->corePlugin()->createRootWindow(this);
Don Gagne's avatar
Don Gagne committed
#else
  // Start the user interface
  MainWindow *mainWindow = MainWindow::_create();
  Q_CHECK_PTR(mainWindow);
  // Now that main window is up check for lost log files
  connect(this, &QGCApplication::checkForLostLogFiles,
          toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
  emit checkForLostLogFiles();
  // Load known link configurations
  toolbox()->linkManager()->loadLinkConfigurationList();
  // Probe for joysticks
  toolbox()->joystickManager()->init();
  if (_settingsUpgraded) {
    showMessage(
        QString(tr("The format for %1 saved settings has been modified. "
                   "Your saved settings have been reset to defaults."))
            .arg(applicationName()));
  }
Don Gagne's avatar
Don Gagne committed

  // Connect links with flag AutoconnectLink
  toolbox()->linkManager()->startAutoConnectedLinks();
  if (getQGCMapEngine()->wasCacheReset()) {
    showMessage(tr("The Offline Map Cache database has been upgraded. "
                   "Your old map cache sets have been reset."));
  }
dogmaphobic's avatar
dogmaphobic committed

  settings.sync();
  return true;
pixhawk's avatar
pixhawk committed
}

bool QGCApplication::_initForUnitTests(void) { return true; }
pixhawk's avatar
pixhawk committed

void QGCApplication::deleteAllSettingsNextBoot(void) {
  QSettings settings;
  settings.setValue(_deleteAllSettingsKey, true);
void QGCApplication::clearDeleteAllSettingsNextBoot(void) {
  QSettings settings;
  settings.remove(_deleteAllSettingsKey);
Don Gagne's avatar
Don Gagne committed
}

/// @brief Returns the QGCApplication object singleton.
QGCApplication *qgcApp(void) { return QGCApplication::_app; }
void QGCApplication::informationMessageBoxOnMainThread(const QString &title,
                                                       const QString &msg) {
  Q_UNUSED(title);
  showMessage(msg);
void QGCApplication::warningMessageBoxOnMainThread(const QString &title,
                                                   const QString &msg) {
  Q_UNUSED(title)
  showMessage(msg);
  QGCMessageBox::warning(title, msg);
void QGCApplication::criticalMessageBoxOnMainThread(const QString &title,
                                                    const QString &msg) {
  Q_UNUSED(title)
  showMessage(msg);
  QGCMessageBox::critical(title, msg);
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile) {
  // The vehicle is gone now and we are shutting down so we need to use a
  // message box for errors to hold shutdown and show the error
  if (_checkTelemetrySavePath(true /* useMessageBox */)) {
    QString saveDirPath =
        _toolbox->settingsManager()->appSettings()->telemetrySavePath();
    QDir saveDir(saveDirPath);
    QString nameFormat("%1%2.%3");
    QString dtFormat("yyyy-MM-dd hh-mm-ss");

    int tryIndex = 1;
    QString saveFileName =
        nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat))
            .arg("")
            .arg(toolbox()
                     ->settingsManager()
                     ->appSettings()
                     ->telemetryFileExtension);
    while (saveDir.exists(saveFileName)) {
      saveFileName =
          nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat))
              .arg(QStringLiteral(".%1").arg(tryIndex++))
              .arg(toolbox()
                       ->settingsManager()
                       ->appSettings()
                       ->telemetryFileExtension);
    }
    QString saveFilePath = saveDir.absoluteFilePath(saveFileName);

    QFile tempFile(tempLogfile);
    if (!tempFile.copy(saveFilePath)) {
      QString error = tr("Unable to save telemetry log. Error copying "
                         "telemetry to '%1': '%2'.")
                          .arg(saveFilePath)
                          .arg(tempFile.errorString());
#ifndef __mobile__
      QGCMessageBox::warning(tr("Telemetry Save Error"), error);
      showMessage(error);
  }
  QFile::remove(tempLogfile);
void QGCApplication::checkTelemetrySavePathOnMainThread(void) {
  // This is called with an active vehicle so don't pop message boxes which
  // holds ui thread
  _checkTelemetrySavePath(false /* useMessageBox */);
bool QGCApplication::_checkTelemetrySavePath(bool useMessageBox) {
  QString errorTitle = tr("Telemetry save error");
  QString saveDirPath =
      _toolbox->settingsManager()->appSettings()->telemetrySavePath();
  if (saveDirPath.isEmpty()) {
    QString error = tr(
        "Unable to save telemetry log. Application save directory is not set.");
#ifndef __mobile__
    if (useMessageBox) {
      QGCMessageBox::warning(errorTitle, error);
    } else {
      Q_UNUSED(useMessageBox);
      showMessage(error);
#ifndef __mobile__
  QDir saveDir(saveDirPath);
  if (!saveDir.exists()) {
    QString error = tr("Unable to save telemetry log. Telemetry save directory "
                       "\"%1\" does not exist.")
                        .arg(saveDirPath);
#ifndef __mobile__
    if (useMessageBox) {
      QGCMessageBox::warning(errorTitle, error);
    } else {
      showMessage(error);
#ifndef __mobile__
void QGCApplication::_loadCurrentStyleSheet(void) {
Don Gagne's avatar
Don Gagne committed
#ifndef __mobile__
  bool success = true;
  QString styles;

  // 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;
  }

  if (success && !_toolbox->settingsManager()
                      ->appSettings()
                      ->indoorPalette()
                      ->rawValue()
                      .toBool()) {
    // Load the slave light stylesheet.
    QFile styleSheet(_lightStyleFile);
    if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
      styles += styleSheet.readAll();
      qWarning() << "Unable to load slave light sheet:";
      success = false;
  setStyleSheet(styles);
  if (!success) {
    // Fall back to plastique if we can't load our own
    setStyle("plastique");
  }
Don Gagne's avatar
Don Gagne committed
#endif
void QGCApplication::reportMissingParameter(int componentId,
                                            const QString &name) {
  _missingParams += QString("%1:%2").arg(componentId).arg(name);
  _missingParamsDelayedDisplayTimer.start();
Don Gagne's avatar
Don Gagne committed
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void) {
  if (_missingParams.count()) {
    QString params;
    foreach (const QString &name, _missingParams) {
      if (params.isEmpty()) {
        params += name;
      } else {
        params += QString(", %1").arg(name);
      }
DonLakeFlyer's avatar
DonLakeFlyer committed
    }
    _missingParams.clear();

    showMessage(tr("Parameters are missing from firmware. You may be running a "
                   "version of firmware QGC does not work correctly with or "
                   "your firmware has a bug in it. Missing params: %1")
                    .arg(params));
  }
QObject *QGCApplication::_rootQmlObject() {
Don Gagne's avatar
Don Gagne committed
#ifdef __mobile__
  if (_qmlAppEngine && _qmlAppEngine->rootObjects().size())
    return _qmlAppEngine->rootObjects()[0];
  return nullptr;
Don Gagne's avatar
Don Gagne committed
#else
  MainWindow *mainWindow = MainWindow::instance();
  if (mainWindow) {
    return mainWindow->rootQmlObject();
  } else if (runningUnitTests()) {
    // Unit test can run without a main window
    return nullptr;
  } else {
    qWarning() << "Why is MainWindow missing?";
    return nullptr;
  }
Don Gagne's avatar
Don Gagne committed
#endif
}

void QGCApplication::showMessage(const QString &message) {
  // PreArm messages are handled by Vehicle and shown in Map
  if (message.startsWith(QStringLiteral("PreArm")) ||
      message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
    return;
  }
Don Gagne's avatar
Don Gagne committed

  QObject *rootQmlObject = _rootQmlObject();
  if (rootQmlObject) {
    QVariant varReturn;
    QVariant varMessage = QVariant::fromValue(message);
Don Gagne's avatar
Don Gagne committed

    QMetaObject::invokeMethod(_rootQmlObject(), "showMessage",
                              Q_RETURN_ARG(QVariant, varReturn),
                              Q_ARG(QVariant, varMessage));
#ifndef __mobile__
  } else if (runningUnitTests()) {
    // Unit test can run without a main window which will lead to no root qml
    // object. Use QGCMessageBox instead
    QGCMessageBox::information("Unit Test", message);
  } else {
    qWarning() << "Internal error";
  }
void QGCApplication::showSetupView(void) {
  if (_rootQmlObject()) {
    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
  }
void QGCApplication::qmlAttemptWindowClose(void) {
  if (_rootQmlObject()) {
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
  }
Don Gagne's avatar
Don Gagne committed
}
bool QGCApplication::isInternetAvailable() {
  return getQGCMapEngine()->isInternetActive();
Don Gagne's avatar
 
Don Gagne committed

void QGCApplication::_checkForNewVersion(void) {
Don Gagne's avatar
 
Don Gagne committed
#ifndef __mobile__
  if (!_runningUnitTests) {
    if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion,
                          _buildVersion)) {
      QString versionCheckFile =
          toolbox()->corePlugin()->stableVersionCheckFileUrl();
      if (!versionCheckFile.isEmpty()) {
        _currentVersionDownload = new QGCFileDownload(this);
        connect(_currentVersionDownload, &QGCFileDownload::downloadFinished,
                this, &QGCApplication::_currentVersionDownloadFinished);
        connect(_currentVersionDownload, &QGCFileDownload::error, this,
                &QGCApplication::_currentVersionDownloadError);
        _currentVersionDownload->download(versionCheckFile);
      }
Don Gagne's avatar
 
Don Gagne committed
    }
Don Gagne's avatar
 
Don Gagne committed
#endif
}

void QGCApplication::_currentVersionDownloadFinished(QString remoteFile,
                                                     QString localFile) {
  Q_UNUSED(remoteFile);
Don Gagne's avatar
 
Don Gagne committed

Don Gagne's avatar
 
Don Gagne committed
#ifdef __mobile__
  Q_UNUSED(localFile);
Don Gagne's avatar
 
Don Gagne committed
#else
  QFile versionFile(localFile);
  if (versionFile.open(QIODevice::ReadOnly)) {
    QTextStream textStream(&versionFile);
    QString version = textStream.readLine();

    qDebug() << version;

    int majorVersion, minorVersion, buildVersion;
    if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
      if (_majorVersion < majorVersion ||
          (_majorVersion == majorVersion && _minorVersion < minorVersion) ||
          (_majorVersion == majorVersion && _minorVersion == minorVersion &&
           _buildVersion < buildVersion)) {
        QGCMessageBox::information(
            tr("New Version Available"),
            tr("There is a newer version of %1 available. You can download it "
               "from %2.")
                .arg(applicationName())
                .arg(toolbox()->corePlugin()->stableDownloadLocation()));
      }
Don Gagne's avatar
 
Don Gagne committed
    }
Don Gagne's avatar
 
Don Gagne committed

  _currentVersionDownload->deleteLater();
Don Gagne's avatar
 
Don Gagne committed
#endif
Don Gagne's avatar
 
Don Gagne committed
}

void QGCApplication::_currentVersionDownloadError(QString errorMsg) {
  Q_UNUSED(errorMsg);
  _currentVersionDownload->deleteLater();
Don Gagne's avatar
 
Don Gagne committed
}

bool QGCApplication::_parseVersionText(const QString &versionString,
                                       int &majorVersion, int &minorVersion,
                                       int &buildVersion) {
  QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
  QRegularExpressionMatch match = regExp.match(versionString);
  if (match.hasMatch() && match.lastCapturedIndex() == 3) {
    majorVersion = match.captured(1).toInt();
    minorVersion = match.captured(2).toInt();
    buildVersion = match.captured(3).toInt();
    return true;
  }
Don Gagne's avatar
 
Don Gagne committed

Don Gagne's avatar
 
Don Gagne committed
}
Don Gagne's avatar
 
Don Gagne committed

void QGCApplication::_onGPSConnect() {
  _gpsRtkFactGroup->connected()->setRawValue(true);
Don Gagne's avatar
 
Don Gagne committed
}

void QGCApplication::_onGPSDisconnect() {
  _gpsRtkFactGroup->connected()->setRawValue(false);
Don Gagne's avatar
 
Don Gagne committed
}

void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM,
                                        double latitude, double longitude,
                                        float altitude, bool valid,
                                        bool active) {
  _gpsRtkFactGroup->currentDuration()->setRawValue(duration);
  _gpsRtkFactGroup->currentAccuracy()->setRawValue(accuracyMM / 1000.0);
  _gpsRtkFactGroup->currentLatitude()->setRawValue(latitude);
  _gpsRtkFactGroup->currentLongitude()->setRawValue(longitude);
  _gpsRtkFactGroup->currentAltitude()->setRawValue(altitude);
  _gpsRtkFactGroup->valid()->setRawValue(valid);
  _gpsRtkFactGroup->active()->setRawValue(active);
Don Gagne's avatar
 
Don Gagne committed
}

void QGCApplication::_gpsNumSatellites(int numSatellites) {
  _gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
Don Gagne's avatar
 
Don Gagne committed
}

QString QGCApplication::cachedParameterMetaDataFile(void) {
  QSettings settings;
  QDir parameterDir = QFileInfo(settings.fileName()).dir();
  return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
Don Gagne's avatar
 
Don Gagne committed
}

QString QGCApplication::cachedAirframeMetaDataFile(void) {
  QSettings settings;
  QDir airframeDir = QFileInfo(settings.fileName()).dir();
  return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
Don Gagne's avatar
 
Don Gagne committed
}