1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

Allow sampling devices to be refreshed while SDRangel is running.

(Possibly need to add a mutex for MainCore::getPluginManager, so only
can be used by one thread)
Add option to automatically update My Position based on GPS. This is
started in MainCore, so we get position as soon as possible.
Don't set QFileDialog::DontUseNativeDialog on Android, as Qt's file
dialog can't access user storage. Set globally for Linux/Windows, rather
than for each dialog.

 MainWindow:
 - Add welcome dialog for Android.
 - Don't show menu bar or status bar on Android to save screen space.
 - On Android, change tab position when screen orientation changes.
 - Load default configurations and presets first time SDRangel is run.
 - Change loadConfiguration to use a QProgressDialog rather than
   QMessageBox, as the latter was crashing on Android.
   - Use DialogPositioner to ensure dialogs are on screen.
This commit is contained in:
Jon Beniston 2022-12-20 21:06:39 +00:00
parent cedd7c20d1
commit 68603fec43
34 changed files with 1125 additions and 416 deletions

View File

@ -26,6 +26,9 @@
#include <QGLFormat> #include <QGLFormat>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#endif #endif
#ifdef ANDROID
#include "util/android.h"
#endif
#include "loggerwithfile.h" #include "loggerwithfile.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -49,6 +52,9 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif #endif
#ifndef ANDROID
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs); // Don't use on Android, otherwise we can't access files on internal storage
#endif
QApplication a(argc, argv); QApplication a(argc, argv);
@ -111,6 +117,20 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#endif #endif
#endif #endif
#ifdef ANDROID
// Default sized sliders can be hard to move using touch GUIs, so increase szie
// FIXME: How can we do a double border around the handle, as Fusion style seems to use?
// Dialog borders are hard to see as is (perhaps as Android doesn't have a title bar), so use same color as for MDI
qApp->setStyleSheet("QSlider {min-height: 20px; } "
"QSlider::groove:horizontal { border: 1px solid #2e2e2e; height: 1px; background: #444444; margin: 1px 0;}"
"QSlider::handle:horizontal { background: #585858; border: 1px double #676767; width: 16px; margin: -8px 0px; border-radius: 3px;}"
"QSlider::sub-page {background: #ff8c00; border: 1px solid #2e2e2e;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-top-left-radius: 5px;border-bottom-left-radius: 5px;}"
"QSlider::add-page {background: #444444; border: 1px solid #2e2e2e;border-top-right-radius: 5px;border-bottom-right-radius: 5px;border-top-left-radius: 0px;border-bottom-left-radius: 0px;}"
"QDialog { border: 1px solid #ff8c00;}"
);
#endif
MainParser parser; MainParser parser;
parser.parse(*qApp); parser.parse(*qApp);
@ -160,10 +180,18 @@ int main(int argc, char* argv[])
QSurfaceFormat::setDefaultFormat(sfc); QSurfaceFormat::setDefaultFormat(sfc);
#endif #endif
#ifdef ANDROID
qtwebapp::LoggerWithFile *logger = nullptr;
qInstallMessageHandler(Android::messageHandler);
#else
qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp); qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp);
logger->installMsgHandler(); logger->installMsgHandler();
#endif
int res = runQtApplication(argc, argv, logger); int res = runQtApplication(argc, argv, logger);
delete logger; delete logger;
qWarning("SDRangel quit."); qWarning("SDRangel quit.");
return res; return res;
} }

View File

@ -1,6 +1,7 @@
project (sdrbase) project (sdrbase)
if(WIN32) if(WIN32)
# This should probably be in ../CMakeLists.txt with the other variables like it
set(OPUS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/external/windows/libopus/include") set(OPUS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/external/windows/libopus/include")
set(OPUS_LIBRARIES "${CMAKE_SOURCE_DIR}/external/windows/libopus/lib/x64/libopus.lib") set(OPUS_LIBRARIES "${CMAKE_SOURCE_DIR}/external/windows/libopus/lib/x64/libopus.lib")
endif() endif()
@ -45,10 +46,11 @@ if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(sdrbase_LIBSIGMF_LIB ${LIBSIGMF_LIBRARIES}) set(sdrbase_LIBSIGMF_LIB ${LIBSIGMF_LIBRARIES})
endif() endif()
# serialdv now required if (LIBSERIALDV_FOUND)
add_definitions(-DDSD_USE_SERIALDV) add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR}) include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY}) set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
endif()
set(sdrbase_SOURCES set(sdrbase_SOURCES
${sdrbase_SOURCES} ${sdrbase_SOURCES}
@ -168,6 +170,7 @@ set(sdrbase_SOURCES
settings/rollupstate.cpp settings/rollupstate.cpp
util/ais.cpp util/ais.cpp
util/android.cpp
util/aviationweather.cpp util/aviationweather.cpp
util/ax25.cpp util/ax25.cpp
util/aprs.cpp util/aprs.cpp
@ -388,6 +391,7 @@ set(sdrbase_HEADERS
settings/rollupstate.h settings/rollupstate.h
util/ais.h util/ais.h
util/android.h
util/aviationweather.h util/aviationweather.h
util/ax25.h util/ax25.h
util/aprs.h util/aprs.h
@ -491,6 +495,7 @@ target_link_libraries(sdrbase
Qt::Core Qt::Core
Qt::Multimedia Qt::Multimedia
Qt::WebSockets Qt::WebSockets
Qt::Positioning
httpserver httpserver
logging logging
qrtplib qrtplib
@ -499,8 +504,15 @@ target_link_libraries(sdrbase
if (Qt6_FOUND) if (Qt6_FOUND)
target_link_libraries(sdrbase target_link_libraries(sdrbase
Qt::Core5Compat Qt::Core5Compat
Qt::CorePrivate
) )
endif() endif()
if(ANDROID AND NOT ENABLE_QT6)
target_link_libraries(sdrbase Qt::AndroidExtras)
endif()
if(ANDROID)
target_link_libraries(sdrbase log)
endif()
install(TARGETS sdrbase DESTINATION ${INSTALL_LIB_DIR}) install(TARGETS sdrbase DESTINATION ${INSTALL_LIB_DIR})

View File

@ -17,8 +17,6 @@
#include <QGlobalStatic> #include <QGlobalStatic>
#include "plugin/pluginmanager.h"
#include "deviceenumerator.h" #include "deviceenumerator.h"
Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator) Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator)
@ -246,89 +244,101 @@ bool DeviceEnumerator::isMIMOEnumerated(const QString& deviceHwId, int deviceSeq
return false; return false;
} }
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type)
{ {
m_rxEnumeration.clear(); DevicesEnumeration tempEnumeration;
PluginAPI::SamplingDeviceRegistrations& rxDeviceRegistrations = pluginManager->getSourceDeviceRegistrations(); PluginInterface::OriginDevices originDevices;
int index = 0; QStringList originDevicesHwIds;
for (int i = 0; i < rxDeviceRegistrations.count(); i++) for (int i = 0; i < deviceRegistrations.count(); i++)
{ {
qDebug("DeviceEnumerator::enumerateRxDevices: %s", qPrintable(rxDeviceRegistrations[i].m_deviceId)); qDebug("DeviceEnumerator::enumerateDevices: %s", qPrintable(deviceRegistrations[i].m_deviceId));
rxDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices); deviceRegistrations[i].m_plugin->enumOriginDevices(originDevicesHwIds, originDevices);
PluginInterface::SamplingDevices samplingDevices = rxDeviceRegistrations[i].m_plugin->enumSampleSources(m_originDevices); PluginInterface::SamplingDevices samplingDevices;
if (type == PluginInterface::SamplingDevice::StreamSingleRx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSources(originDevices);
} else if (type == PluginInterface::SamplingDevice::StreamSingleTx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSinks(originDevices);
} else {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleMIMO(originDevices);
}
for (int j = 0; j < samplingDevices.count(); j++) for (int j = 0; j < samplingDevices.count(); j++)
{ {
m_rxEnumeration.push_back( tempEnumeration.push_back(
DeviceEnumeration( DeviceEnumeration(
samplingDevices[j], samplingDevices[j],
rxDeviceRegistrations[i].m_plugin, deviceRegistrations[i].m_plugin,
index 0 // Index will be set when adding to enumeration below
) )
); );
index++;
} }
} }
// We don't remove devices that are no-longer found from the enumeration list, in case their
// index is referenced in some other object (E.g. DeviceGUI)
// Instead we mark as removed. If later re-added, then we re-use the same index
for (DevicesEnumeration::iterator it = enumeration.begin(); it != enumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = tempEnumeration.begin(); it2 != tempEnumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
found = true;
break;
}
}
if (!found) {
it->m_samplingDevice.removed = true;
}
}
// Add new entries and update existing (in case re-added or sequence number has changed)
int index = enumeration.size();
for (DevicesEnumeration::iterator it = tempEnumeration.begin(); it != tempEnumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = enumeration.begin(); it2 != enumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
it2->m_samplingDevice.removed = false;
it2->m_samplingDevice.displayedName = it->m_samplingDevice.displayedName;
found = true;
break;
}
}
if (!found)
{
it->m_index = index++;
enumeration.push_back(*it);
}
}
}
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager)
{
enumerateDevices(pluginManager->getSourceDeviceRegistrations(), m_rxEnumeration, PluginInterface::SamplingDevice::StreamSingleRx);
} }
void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager)
{ {
m_txEnumeration.clear(); enumerateDevices(pluginManager->getSinkDeviceRegistrations(), m_txEnumeration, PluginInterface::SamplingDevice::StreamSingleTx);
PluginAPI::SamplingDeviceRegistrations& txDeviceRegistrations = pluginManager->getSinkDeviceRegistrations();
int index = 0;
for (int i = 0; i < txDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateTxDevices: %s", qPrintable(txDeviceRegistrations[i].m_deviceId));
txDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = txDeviceRegistrations[i].m_plugin->enumSampleSinks(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_txEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
txDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
} }
void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager)
{ {
m_mimoEnumeration.clear(); enumerateDevices(pluginManager->getMIMODeviceRegistrations(), m_mimoEnumeration, PluginInterface::SamplingDevice::StreamMIMO);
PluginAPI::SamplingDeviceRegistrations& mimoDeviceRegistrations = pluginManager->getMIMODeviceRegistrations();
int index = 0;
for (int i = 0; i < mimoDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateMIMODevices: %s", qPrintable(mimoDeviceRegistrations[i].m_deviceId));
mimoDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = mimoDeviceRegistrations[i].m_plugin->enumSampleMIMO(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_mimoEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
mimoDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
} }
void DeviceEnumerator::listRxDeviceNames(QList<QString>& list, std::vector<int>& indexes) const void DeviceEnumerator::listRxDeviceNames(QList<QString>& list, std::vector<int>& indexes) const
{ {
for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);
@ -340,7 +350,7 @@ void DeviceEnumerator::listTxDeviceNames(QList<QString>& list, std::vector<int>&
{ {
for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);
@ -352,7 +362,7 @@ void DeviceEnumerator::listMIMODeviceNames(QList<QString>& list, std::vector<int
{ {
for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);

View File

@ -21,6 +21,7 @@
#include <vector> #include <vector>
#include "plugin/plugininterface.h" #include "plugin/plugininterface.h"
#include "plugin/pluginmanager.h"
#include "device/deviceuserargs.h" #include "device/deviceuserargs.h"
#include "export.h" #include "export.h"
@ -85,9 +86,8 @@ private:
DevicesEnumeration m_rxEnumeration; DevicesEnumeration m_rxEnumeration;
DevicesEnumeration m_txEnumeration; DevicesEnumeration m_txEnumeration;
DevicesEnumeration m_mimoEnumeration; DevicesEnumeration m_mimoEnumeration;
PluginInterface::OriginDevices m_originDevices;
QStringList m_originDevicesHwIds;
void enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type);
PluginInterface *getRxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getRxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getTxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getTxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getMIMORegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getMIMORegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);

View File

@ -18,6 +18,8 @@
#include <QGlobalStatic> #include <QGlobalStatic>
#include <QCoreApplication> #include <QCoreApplication>
#include <QString> #include <QString>
#include <QDebug>
#include <QGeoPositionInfoSource>
#include "loggerwithfile.h" #include "loggerwithfile.h"
#include "dsp/dsptypes.h" #include "dsp/dsptypes.h"
@ -71,6 +73,8 @@ MainCore::MainCore()
m_masterTimer.start(50); m_masterTimer.start(50);
m_startMsecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); m_startMsecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
m_masterElapsedTimer.start(); m_masterElapsedTimer.start();
// Position can take a while to determine, so we start updates at program startup
initPosition();
} }
MainCore::~MainCore() MainCore::~MainCore()
@ -335,3 +339,36 @@ void MainCore::debugMaps()
feIt.value()->getIndex(), feIt.key()->getIndexInFeatureSet(), qPrintable(feIt.key()->getURI()), qPrintable(feIt.key()->getName())); feIt.value()->getIndex(), feIt.key()->getIndexInFeatureSet(), qPrintable(feIt.key()->getURI()), qPrintable(feIt.key()->getName()));
} }
} }
void MainCore::initPosition()
{
m_positionSource = QGeoPositionInfoSource::createDefaultSource(this);
if (m_positionSource)
{
connect(m_positionSource, &QGeoPositionInfoSource::positionUpdated, this, &MainCore::positionUpdated);
m_positionSource->startUpdates();
}
else
{
qDebug() << "MainCore::initPosition: No position source.";
}
}
const QGeoPositionInfo& MainCore::getPosition() const
{
return m_position;
}
void MainCore::positionUpdated(const QGeoPositionInfo &info)
{
if (info.isValid())
{
m_position = info;
if (m_settings.getAutoUpdatePosition())
{
m_settings.setLatitude(m_position.coordinate().latitude());
m_settings.setLongitude(m_position.coordinate().longitude());
m_settings.setAltitude(m_position.coordinate().altitude());
}
}
}

View File

@ -25,6 +25,7 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QDateTime> #include <QDateTime>
#include <QObject> #include <QObject>
#include <QGeoPositionInfo>
#include "export.h" #include "export.h"
#include "settings/mainsettings.h" #include "settings/mainsettings.h"
@ -38,6 +39,7 @@ class FeatureSet;
class Feature; class Feature;
class PluginManager; class PluginManager;
class MessageQueue; class MessageQueue;
class QGeoPositionInfoSource;
namespace qtwebapp { namespace qtwebapp {
class LoggerWithFile; class LoggerWithFile;
@ -846,7 +848,7 @@ public:
qint64 getStartMsecsSinceEpoch() const { return m_startMsecsSinceEpoch; } //!< Epoch timestamp in millisecodns close to elapsed timer start qint64 getStartMsecsSinceEpoch() const { return m_startMsecsSinceEpoch; } //!< Epoch timestamp in millisecodns close to elapsed timer start
const MainSettings& getSettings() const { return m_settings; } const MainSettings& getSettings() const { return m_settings; }
MessageQueue *getMainMessageQueue() { return m_mainMessageQueue; } MessageQueue *getMainMessageQueue() { return m_mainMessageQueue; }
const PluginManager *getPluginManager() const { return m_pluginManager; } PluginManager *getPluginManager() const { return m_pluginManager; }
std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; } std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; }
std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; } std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; }
void setLoggingOptions(); void setLoggingOptions();
@ -875,6 +877,8 @@ public:
// pipes // pipes
MessagePipes& getMessagePipes() { return m_messagePipes; } MessagePipes& getMessagePipes() { return m_messagePipes; }
DataPipes& getDataPipes() { return m_dataPipes; } DataPipes& getDataPipes() { return m_dataPipes; }
// Position
const QGeoPositionInfo& getPosition() const;
friend class MainServer; friend class MainServer;
friend class MainWindow; friend class MainWindow;
@ -883,6 +887,9 @@ public:
friend class DeviceSetPresetsDialog; friend class DeviceSetPresetsDialog;
friend class ConfigurationsDialog; friend class ConfigurationsDialog;
public slots:
void positionUpdated(const QGeoPositionInfo &info);
signals: signals:
void deviceSetAdded(int index, DeviceAPI *device); void deviceSetAdded(int index, DeviceAPI *device);
void deviceChanged(int index); void deviceChanged(int index);
@ -912,7 +919,10 @@ private:
PluginManager* m_pluginManager; PluginManager* m_pluginManager;
MessagePipes m_messagePipes; MessagePipes m_messagePipes;
DataPipes m_dataPipes; DataPipes m_dataPipes;
QGeoPositionInfoSource *m_positionSource;
QGeoPositionInfo m_position;
void initPosition();
void debugMaps(); void debugMaps();
}; };

View File

@ -66,6 +66,7 @@ public:
int deviceNbItems; //!< Number of items (or streams) in the device. >1 for composite devices. int deviceNbItems; //!< Number of items (or streams) in the device. >1 for composite devices.
int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized
int claimed; //!< This is the device set index if claimed else -1 int claimed; //!< This is the device set index if claimed else -1
bool removed; //!< Set if device has been removed
SamplingDevice(const QString& _displayedName, SamplingDevice(const QString& _displayedName,
const QString& _hardwareId, const QString& _hardwareId,
@ -85,8 +86,17 @@ public:
streamType(_streamType), streamType(_streamType),
deviceNbItems(_deviceNbItems), deviceNbItems(_deviceNbItems),
deviceItemIndex(_deviceItemIndex), deviceItemIndex(_deviceItemIndex),
claimed(-1) claimed(-1),
removed(false)
{ } { }
bool operator==(const SamplingDevice& rhs) const
{
return displayedName == rhs.displayedName
&& hardwareId == rhs.hardwareId
&& id == rhs.id
&& serial == rhs.serial;
}
}; };
typedef QList<SamplingDevice> SamplingDevices; typedef QList<SamplingDevice> SamplingDevices;

View File

@ -151,6 +151,12 @@ public:
emit preferenceChanged(Preferences::Altitude); emit preferenceChanged(Preferences::Altitude);
} }
bool getAutoUpdatePosition() const { return m_preferences.getAutoUpdatePosition(); }
void setAutoUpdatePosition(bool autoUpdatePosition)
{
m_preferences.setAutoUpdatePosition(autoUpdatePosition);
}
QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); } QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel) void setConsoleMinLogLevel(const QtMsgType& minLogLevel)
{ {

View File

@ -17,6 +17,7 @@ void Preferences::resetToDefaults()
m_latitude = 49.012423; // Set an interesting location so map doesn't open up in the middle of the ocean m_latitude = 49.012423; // Set an interesting location so map doesn't open up in the middle of the ocean
m_longitude = 8.418125; m_longitude = 8.418125;
m_altitude = 0.0f; m_altitude = 0.0f;
m_autoUpdatePosition = true;
m_useLogFile = false; m_useLogFile = false;
m_logFileName = "sdrangel.log"; m_logFileName = "sdrangel.log";
m_consoleMinLogLevel = QtDebugMsg; m_consoleMinLogLevel = QtDebugMsg;
@ -41,6 +42,7 @@ QByteArray Preferences::serialize() const
s.writeFloat((int) Altitude, m_altitude); s.writeFloat((int) Altitude, m_altitude);
s.writeS32((int) SourceItemIndex, m_sourceItemIndex); s.writeS32((int) SourceItemIndex, m_sourceItemIndex);
s.writeS32((int) Multisampling, m_multisampling); s.writeS32((int) Multisampling, m_multisampling);
s.writeBool((int) AutoUpdatePosition, m_autoUpdatePosition);
return s.final(); return s.final();
} }
@ -95,9 +97,11 @@ bool Preferences::deserialize(const QByteArray& data)
} }
d.readS32((int) Multisampling, &m_multisampling, 0); d.readS32((int) Multisampling, &m_multisampling, 0);
d.readBool((int) AutoUpdatePosition, &m_autoUpdatePosition, true);
return true; return true;
} else }
else
{ {
resetToDefaults(); resetToDefaults();
return false; return false;

View File

@ -22,7 +22,8 @@ public:
StationName, StationName,
Altitude, Altitude,
SourceItemIndex, SourceItemIndex,
Multisampling Multisampling,
AutoUpdatePosition
}; };
Preferences(); Preferences();
@ -60,6 +61,9 @@ public:
float getAltitude() const { return m_altitude; } float getAltitude() const { return m_altitude; }
void setAltitude(float altitude) { m_altitude = altitude; } void setAltitude(float altitude) { m_altitude = altitude; }
bool getAutoUpdatePosition() const { return m_autoUpdatePosition; }
void setAutoUpdatePosition(bool autoUpdatePosition) { m_autoUpdatePosition = autoUpdatePosition; }
QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; } QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; } void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; }
@ -87,6 +91,7 @@ protected:
float m_latitude; //!< Position of the station float m_latitude; //!< Position of the station
float m_longitude; float m_longitude;
float m_altitude; //!< Altitude in metres float m_altitude; //!< Altitude in metres
bool m_autoUpdatePosition; //!< Automatically update position from GPS
QtMsgType m_consoleMinLogLevel; QtMsgType m_consoleMinLogLevel;
QtMsgType m_fileMinLogLevel; QtMsgType m_fileMinLogLevel;

213
sdrbase/util/android.cpp Normal file
View File

@ -0,0 +1,213 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program 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 as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifdef ANDROID
#include <QDebug>
#include <android/log.h>
#include "android.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtCore/private/qandroidextras_p.h>
#include <QJniObject>
#include <QJniEnvironment>
void Android::sendIntent()
{
QJniObject url = QJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QJniObject intent = QJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroidPrivate::startActivity(intent, 0, [](int requestCode, int resultCode, const QJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QJniEnvironment jniEnv;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = QJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#else // QT_VERSION
#include <QtAndroid>
#include <QAndroidIntent>
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include <QAndroidActivityResultReceiver>
void Android::sendIntent()
{
QAndroidJniObject url = QAndroidJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QAndroidJniObject intent = QAndroidJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroid::startActivity(intent, 0, [](int requestCode, int resultCode, const QAndroidJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QAndroidJniEnvironment jniEnv;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QAndroidJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = QAndroidJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#endif // QT6
// Redirect qDebug/qWarning to Android log, so we can view remotely with adb
void Android::messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
QString report = msg;
if (context.file && !QString(context.file).isEmpty())
{
report += " in file ";
report += QString(context.file);
report += " line ";
report += QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty())
{
report += +" function ";
report += QString(context.function);
}
const char * const local = report.toLocal8Bit().constData();
const char * const applicationName = "sdrangel";
switch (type)
{
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG, applicationName, local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO, applicationName, local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN, applicationName, local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR, applicationName, local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL, applicationName, local);
abort();
}
}
#endif // ANDROID

44
sdrbase/util/android.h Normal file
View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program 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 as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_ANDROID_H_
#define SDRBASE_ANDROID_H_
#ifdef ANDROID
#include <QtGlobal>
#include <QString>
#include "export.h"
// Android specific functions
class SDRBASE_API Android
{
public:
static void sendIntent();
static QStringList listUSBDeviceSerials(int vid, int pid);
static int openUSBDevice(const QString &serial);
static void closeUSBDevice(int fd);
static void moveTaskToBack();
static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
};
#endif // ANDROID
#endif // SDRBASE_ANDROID_H_

View File

@ -27,11 +27,26 @@
#include "settings/configuration.h" #include "settings/configuration.h"
ConfigurationsDialog::ConfigurationsDialog(QWidget* parent) : ConfigurationsDialog::ConfigurationsDialog(bool openOnly, QWidget* parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::ConfigurationsDialog) ui(new Ui::ConfigurationsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
if (openOnly)
{
ui->buttonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Open);
ui->configurationDelete->setVisible(false);
ui->configurationEdit->setVisible(false);
ui->configurationExport->setVisible(false);
ui->configurationImport->setVisible(false);
ui->configurationLoad->setVisible(false);
ui->configurationSave->setVisible(false);
ui->configurationUpdate->setVisible(false);
}
else
{
ui->description->setVisible(false);
}
} }
ConfigurationsDialog::~ConfigurationsDialog() ConfigurationsDialog::~ConfigurationsDialog()
@ -354,9 +369,7 @@ void ConfigurationsDialog::on_configurationExport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Configuration export files (*.cfgx)"), tr("Configuration export files (*.cfgx)"),
0, 0);
QFileDialog::DontUseNativeDialog
);
if (fileName != "") if (fileName != "")
{ {
@ -404,8 +417,7 @@ void ConfigurationsDialog::on_configurationImport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.cfgx)"), tr("Preset export files (*.cfgx)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")
@ -497,3 +509,9 @@ void ConfigurationsDialog::deleteConfigurationGroup(const QString& groupName)
} }
} }
} }
void ConfigurationsDialog::accept()
{
on_configurationLoad_clicked();
QDialog::accept();
}

View File

@ -38,7 +38,7 @@ namespace Ui {
class SDRGUI_API ConfigurationsDialog : public QDialog { class SDRGUI_API ConfigurationsDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigurationsDialog(QWidget* parent = nullptr); explicit ConfigurationsDialog(bool openOnly, QWidget* parent = nullptr);
~ConfigurationsDialog(); ~ConfigurationsDialog();
void setConfigurations(QList<Configuration*>* configurations) { m_configurations = configurations; } void setConfigurations(QList<Configuration*>* configurations) { m_configurations = configurations; }
void populateTree(); void populateTree();
@ -71,6 +71,7 @@ private slots:
void on_configurationLoad_clicked(); void on_configurationLoad_clicked();
void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column); void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column);
void accept() override;
signals: signals:
void saveConfiguration(Configuration*); void saveConfiguration(Configuration*);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>392</width> <width>410</width>
<height>414</height> <height>424</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -19,32 +19,17 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Configurations</string> <string>Configurations</string>
</property> </property>
<widget class="QDialogButtonBox" name="buttonBox"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="geometry"> <item>
<rect> <widget class="QWidget" name="widget" native="true">
<x>40</x> <layout class="QVBoxLayout" name="verticalLayout">
<y>380</y> <item>
<width>341</width> <widget class="QLabel" name="description">
<height>32</height> <property name="text">
</rect> <string>Select configuration to load:</string>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="widget" native="true"> </item>
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>392</width>
<height>310</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QTreeWidget" name="configurationsTree"> <widget class="QTreeWidget" name="configurationsTree">
<property name="toolTip"> <property name="toolTip">
@ -71,15 +56,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="layoutWidget"> </item>
<property name="geometry"> <item>
<rect>
<x>0</x>
<y>330</y>
<width>392</width>
<height>34</height>
</rect>
</property>
<layout class="QHBoxLayout" name="configurationsControl"> <layout class="QHBoxLayout" name="configurationsControl">
<item> <item>
<widget class="QToolButton" name="configurationSave"> <widget class="QToolButton" name="configurationSave">
@ -230,7 +208,18 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget> </widget>
</item>
</layout>
</widget> </widget>
<resources> <resources>
<include location="../resources/res.qrc"/> <include location="../resources/res.qrc"/>

View File

@ -292,8 +292,7 @@ void DeviceSetPresetsDialog::on_presetExport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.prex)"), tr("Preset export files (*.prex)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")
@ -342,8 +341,7 @@ void DeviceSetPresetsDialog::on_presetImport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.prex)"), tr("Preset export files (*.prex)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>324</width> <width>324</width>
<height>229</height> <height>201</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -19,44 +19,24 @@
<property name="windowTitle"> <property name="windowTitle">
<string>My Position</string> <string>My Position</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>My Station Position</string> <string>My Station Position</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0"> <item row="1" column="1">
<widget class="QLabel" name="latitudeLabel">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="toolTip">
<string>Longitude in decimal degrees</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="latitudeSpinBox"> <widget class="QDoubleSpinBox" name="latitudeSpinBox">
<property name="toolTip"> <property name="toolTip">
<string>Latitude in decimal degrees</string> <string>Latitude in decimal degrees</string>
@ -72,24 +52,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="1" column="0">
<widget class="QSpinBox" name="altitudeSpinBox"> <widget class="QLabel" name="latitudeLabel">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text"> <property name="text">
<string>Altitude (m)</string> <string>Latitude</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="name"> <widget class="QLineEdit" name="name">
<property name="toolTip"> <property name="toolTip">
<string>Enter the name of your station</string> <string>Enter the name of your station</string>
@ -99,6 +69,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text">
<string>Altitude (m)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="nameLabel"> <widget class="QLabel" name="nameLabel">
<property name="text"> <property name="text">
@ -106,17 +90,64 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="4" column="1">
<widget class="QCheckBox" name="autoUpdatePosition">
<property name="toolTip">
<string>Automatically update position using GPS (when available)</string>
</property>
<property name="text">
<string/>
</property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="1">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="orientation"> <property name="toolTip">
<enum>Qt::Horizontal</enum> <string>Longitude in decimal degrees</string>
</property> </property>
<property name="standardButtons"> <property name="decimals">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <number>6</number>
</property> </property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="autoUpdatePositionLabel">
<property name="text">
<string>Auto-update from GPS</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="gps">
<property name="toolTip">
<string>Set position using GPS (if available)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/gps.png</normaloff>:/gps.png</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="altitudeSpinBox">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -127,7 +158,9 @@
<tabstop>longitudeSpinBox</tabstop> <tabstop>longitudeSpinBox</tabstop>
<tabstop>altitudeSpinBox</tabstop> <tabstop>altitudeSpinBox</tabstop>
</tabstops> </tabstops>
<resources/> <resources>
<include location="../resources/res.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB // // Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths // // written by Edouard Griffiths //
// // // //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify // // This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by // // it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or // // the Free Software Foundation as version 3 of the License, or //
@ -21,7 +18,9 @@
#include "gui/mypositiondialog.h" #include "gui/mypositiondialog.h"
#include "ui_myposdialog.h" #include "ui_myposdialog.h"
#include "maincore.h"
#include <QGeoCoordinate>
MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent) : MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent) :
QDialog(parent), QDialog(parent),
@ -33,6 +32,7 @@ MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent)
ui->latitudeSpinBox->setValue(m_mainSettings.getLatitude()); ui->latitudeSpinBox->setValue(m_mainSettings.getLatitude());
ui->longitudeSpinBox->setValue(m_mainSettings.getLongitude()); ui->longitudeSpinBox->setValue(m_mainSettings.getLongitude());
ui->altitudeSpinBox->setValue(m_mainSettings.getAltitude()); ui->altitudeSpinBox->setValue(m_mainSettings.getAltitude());
ui->autoUpdatePosition->setChecked(m_mainSettings.getAutoUpdatePosition());
} }
MyPositionDialog::~MyPositionDialog() MyPositionDialog::~MyPositionDialog()
@ -46,5 +46,22 @@ void MyPositionDialog::accept()
m_mainSettings.setLatitude(ui->latitudeSpinBox->value()); m_mainSettings.setLatitude(ui->latitudeSpinBox->value());
m_mainSettings.setLongitude(ui->longitudeSpinBox->value()); m_mainSettings.setLongitude(ui->longitudeSpinBox->value());
m_mainSettings.setAltitude(ui->altitudeSpinBox->value()); m_mainSettings.setAltitude(ui->altitudeSpinBox->value());
m_mainSettings.setAutoUpdatePosition(ui->autoUpdatePosition->isChecked());
QDialog::accept(); QDialog::accept();
} }
void MyPositionDialog::on_gps_clicked()
{
const QGeoPositionInfo& position = MainCore::instance()->getPosition();
if (position.isValid())
{
QGeoCoordinate coord = position.coordinate();
ui->latitudeSpinBox->setValue(coord.latitude());
ui->longitudeSpinBox->setValue(coord.longitude());
ui->altitudeSpinBox->setValue(coord.altitude());
}
else
{
qDebug() << "MyPositionDialog::on_gps_clicked: Position is not valid.";
}
}

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB // // Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths // // written by Edouard Griffiths //
// // // //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify // // This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by // // it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or // // the Free Software Foundation as version 3 of the License, or //
@ -43,6 +40,7 @@ private:
private slots: private slots:
void accept(); void accept();
void on_gps_clicked();
}; };
#endif /* SDRBASE_GUI_MYPOSITIONDIALOG_H_ */ #endif /* SDRBASE_GUI_MYPOSITIONDIALOG_H_ */

View File

@ -22,7 +22,7 @@
#include "samplingdevicedialog.h" #include "samplingdevicedialog.h"
#include "ui_samplingdevicedialog.h" #include "ui_samplingdevicedialog.h"
#include "device/deviceenumerator.h" #include "device/deviceenumerator.h"
#include "maincore.h"
SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) : SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
QDialog(parent), QDialog(parent),
@ -32,19 +32,7 @@ SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
m_hasChanged(false) m_hasChanged(false)
{ {
ui->setupUi(this); ui->setupUi(this);
on_refreshDevices_clicked();
QList<QString> deviceDisplayNames;
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
QStringList devicesNamesList(deviceDisplayNames);
ui->deviceSelect->addItems(devicesNamesList);
} }
SamplingDeviceDialog::~SamplingDeviceDialog() SamplingDeviceDialog::~SamplingDeviceDialog()
@ -58,6 +46,23 @@ int SamplingDeviceDialog::exec()
return QDialog::exec(); return QDialog::exec();
} }
void SamplingDeviceDialog::displayDevices()
{
QList<QString> deviceDisplayNames;
m_deviceIndexes.clear();
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
ui->deviceSelect->clear();
ui->deviceSelect->addItems(deviceDisplayNames);
}
void SamplingDeviceDialog::setSelectedDeviceIndex(int deviceIndex) void SamplingDeviceDialog::setSelectedDeviceIndex(int deviceIndex)
{ {
ui->deviceSelect->blockSignals(true); ui->deviceSelect->blockSignals(true);
@ -77,6 +82,21 @@ void SamplingDeviceDialog::on_deviceSelect_currentIndexChanged(int index)
m_hasChanged = true; m_hasChanged = true;
} }
void SamplingDeviceDialog::on_refreshDevices_clicked()
{
PluginManager *pluginManager = MainCore::instance()->getPluginManager();
if (m_deviceType == 0) {
DeviceEnumerator::instance()->enumerateRxDevices(pluginManager);
} else if (m_deviceType == 1) {
DeviceEnumerator::instance()->enumerateTxDevices(pluginManager);
} else if (m_deviceType == 2) {
DeviceEnumerator::instance()->enumerateMIMODevices(pluginManager);
}
displayDevices();
}
void SamplingDeviceDialog::accept() void SamplingDeviceDialog::accept()
{ {
m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()]; m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()];

View File

@ -50,8 +50,11 @@ private:
std::vector<int> m_deviceIndexes; std::vector<int> m_deviceIndexes;
bool m_hasChanged; bool m_hasChanged;
void displayDevices();
private slots: private slots:
void on_deviceSelect_currentIndexChanged(int index); void on_deviceSelect_currentIndexChanged(int index);
void on_refreshDevices_clicked();
void accept(); void accept();
void reject(); void reject();
}; };

View File

@ -31,8 +31,8 @@
<property name="title"> <property name="title">
<string>Select from list</string> <string>Select from list</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item row="0" column="1"> <item>
<widget class="QComboBox" name="deviceSelect"> <widget class="QComboBox" name="deviceSelect">
<property name="font"> <property name="font">
<font> <font>
@ -42,6 +42,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="refreshDevices">
<property name="toolTip">
<string>Refresh list of devices</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -66,7 +80,9 @@
<tabstops> <tabstops>
<tabstop>buttonBox</tabstop> <tabstop>buttonBox</tabstop>
</tabstops> </tabstops>
<resources/> <resources>
<include location="../resources/res.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -18,7 +18,9 @@
#include <QInputDialog> #include <QInputDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QProgressDialog>
#include <QLabel> #include <QLabel>
#include <QToolButton>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QFileDialog> #include <QFileDialog>
@ -31,10 +33,11 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QDesktopServices> #include <QDesktopServices>
#include <QProcess> #include <QProcess>
#include <QDirIterator>
#include <QAction> #include <QAction>
#include <QMenuBar> #include <QMenuBar>
#include <QStatusBar> #include <QStatusBar>
#include <QScreen>
#include "device/devicegui.h" #include "device/devicegui.h"
#include "device/deviceapi.h" #include "device/deviceapi.h"
@ -68,6 +71,8 @@
#include "gui/devicesetpresetsdialog.h" #include "gui/devicesetpresetsdialog.h"
#include "gui/commandsdialog.h" #include "gui/commandsdialog.h"
#include "gui/configurationsdialog.h" #include "gui/configurationsdialog.h"
#include "gui/dialogpositioner.h"
#include "gui/welcomedialog.h"
#include "dsp/dspengine.h" #include "dsp/dspengine.h"
#include "dsp/spectrumvis.h" #include "dsp/spectrumvis.h"
#include "dsp/dspcommands.h" #include "dsp/dspcommands.h"
@ -85,6 +90,9 @@
#include "webapi/webapiserver.h" #include "webapi/webapiserver.h"
#include "webapi/webapiadapter.h" #include "webapi/webapiadapter.h"
#include "commands/command.h" #include "commands/command.h"
#ifdef ANDROID
#include "util/android.h"
#endif
#include "mainwindow.h" #include "mainwindow.h"
@ -107,9 +115,13 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
m_mainCore(MainCore::instance()), m_mainCore(MainCore::instance()),
m_dspEngine(DSPEngine::instance()), m_dspEngine(DSPEngine::instance()),
m_lastEngineState(DeviceAPI::StNotStarted), m_lastEngineState(DeviceAPI::StNotStarted),
m_dateTimeWidget(nullptr),
m_showSystemWidget(nullptr),
m_commandKeyReceiver(nullptr), m_commandKeyReceiver(nullptr),
m_fftWisdomProcess(nullptr) m_fftWisdomProcess(nullptr)
{ {
bool showWelcome = ANDROID;
qDebug() << "MainWindow::MainWindow: start"; qDebug() << "MainWindow::MainWindow: start";
setWindowTitle("SDRangel"); setWindowTitle("SDRangel");
@ -134,10 +146,21 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
splash->showStatusMessage("starting...", Qt::white); splash->showStatusMessage("starting...", Qt::white);
setWindowIcon(QIcon(":/sdrangel_icon.png")); setWindowIcon(QIcon(":/sdrangel_icon.png"));
createMenuBar(); #ifndef ANDROID
// To save screen space on Android, don't have menu bar. Instead menus are accessed via toolbar button
createMenuBar(nullptr);
createStatusBar(); createStatusBar();
#endif
#ifdef ANDROID
if (screen()->isLandscape(screen()->primaryOrientation())) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West); setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#else
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
#endif
setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
@ -146,6 +169,12 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection);
connect(screen(), &QScreen::orientationChanged, this, &MainWindow::orientationChanged);
screen()->setOrientationUpdateMask(Qt::PortraitOrientation
| Qt::LandscapeOrientation
| Qt::InvertedPortraitOrientation
| Qt::InvertedLandscapeOrientation);
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000); m_statusTimer.start(1000);
@ -209,6 +238,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
{ {
qDebug() << "MainWindow::MainWindow: no or empty current configuration, creating empty workspace..."; qDebug() << "MainWindow::MainWindow: no or empty current configuration, creating empty workspace...";
addWorkspace(); addWorkspace();
// If no configurations, load some basic examples
if (m_mainCore->m_settings.getConfigurations().size() == 0) {
loadDefaultConfigurations();
}
}
else
{
// Only show welcome dialog first time program is run
showWelcome = false;
} }
} }
@ -240,6 +279,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
restoreGeometry(qUncompress(QByteArray::fromBase64(s.value("mainWindowGeometry").toByteArray()))); restoreGeometry(qUncompress(QByteArray::fromBase64(s.value("mainWindowGeometry").toByteArray())));
restoreState(qUncompress(QByteArray::fromBase64(s.value("mainWindowState").toByteArray()))); restoreState(qUncompress(QByteArray::fromBase64(s.value("mainWindowState").toByteArray())));
if (showWelcome)
{
// Show welcome dialog
WelcomeDialog welcomeDialog(this);
new DialogPositioner(&welcomeDialog, true);
welcomeDialog.exec();
// Show configurations
openConfigurationDialog(true);
}
qDebug() << "MainWindow::MainWindow: end"; qDebug() << "MainWindow::MainWindow: end";
} }
@ -340,6 +389,11 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
#ifdef ANDROID
// Seemingly needed on some versions of Android, otherwise the new windows aren't always displayed??
deviceWorkspace->repaint();
#endif
} }
void MainWindow::sampleSourceCreate( void MainWindow::sampleSourceCreate(
@ -1165,8 +1219,10 @@ void MainWindow::loadSettings()
m_mainCore->m_settings.load(); m_mainCore->m_settings.load();
m_mainCore->m_settings.sortPresets(); m_mainCore->m_settings.sortPresets();
m_mainCore->m_settings.sortCommands(); m_mainCore->m_settings.sortCommands();
if (m_mainCore->m_logger) {
m_mainCore->setLoggingOptions(); m_mainCore->setLoggingOptions();
} }
}
void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex) void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex)
{ {
@ -1230,6 +1286,59 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat
featureUI->saveFeatureSetSettings(preset); featureUI->saveFeatureSetSettings(preset);
} }
void MainWindow::loadDefaultConfigurations()
{
QDirIterator configurationsIt(":configurations", QDirIterator::Subdirectories);
while (configurationsIt.hasNext())
{
QString group = configurationsIt.next();
QDirIterator groupIt(group, {"*.cfgx"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Configuration* configuration = MainCore::instance()->m_settings.newConfiguration("", "");
configuration->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open configuration " << file.fileName();
}
}
}
QDirIterator presetIt(":presets", QDirIterator::Subdirectories);
while (presetIt.hasNext())
{
QString group = presetIt.next();
QDirIterator groupIt(group, {"*.prex"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Preset* preset = MainCore::instance()->m_settings.newPreset("", "");
preset->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open preset " << file.fileName();
}
}
}
}
void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog) void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog)
{ {
qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)", qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)",
@ -1240,23 +1349,24 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
configuration->getFeatureSetPreset().getFeatureCount() configuration->getFeatureSetPreset().getFeatureCount()
); );
QMessageBox *waitBox = nullptr; QProgressDialog *waitBox = nullptr;
if (fromDialog) if (fromDialog)
{ {
waitBox = new QMessageBox(this); waitBox = new QProgressDialog("Loading configuration...", "", 0, 100, this);
waitBox->setStandardButtons(QMessageBox::NoButton); waitBox->setWindowModality(Qt::WindowModal);
waitBox->setWindowModality(Qt::NonModal); waitBox->setAttribute(Qt::WA_DeleteOnClose, true);
waitBox->setIcon(QMessageBox::Information); waitBox->setMinimumDuration(0);
waitBox->setText("Loading configuration "); waitBox->setCancelButton(nullptr);
waitBox->setInformativeText("Deleting existing..."); waitBox->setValue(1);
waitBox->setWindowTitle("Please wait");
waitBox->show();
waitBox->raise();
} }
// Wipe out everything first // Wipe out everything first
if (waitBox)
{
waitBox->setLabelText("Deleting existing...");
waitBox->setValue(5);
}
// Device sets // Device sets
while (m_deviceUIs.size() > 0) { while (m_deviceUIs.size() > 0) {
removeLastDeviceSet(); removeLastDeviceSet();
@ -1276,6 +1386,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
{ {
addWorkspace(); addWorkspace();
m_workspaces[i]->setAutoStackOption(configuration->getWorkspaceAutoStackOptions()[i]); m_workspaces[i]->setAutoStackOption(configuration->getWorkspaceAutoStackOptions()[i]);
m_workspaces[i]->setTabSubWindowsOption(configuration->getWorkspaceTabSubWindowsOptions()[i]);
} }
if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces
@ -1283,8 +1394,10 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
} }
// Device sets // Device sets
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Loading device sets..."); {
waitBox->setLabelText("Loading device sets...");
waitBox->setValue(25);
} }
const QList<Preset>& deviceSetPresets = configuration->getDeviceSetPresets(); const QList<Preset>& deviceSetPresets = configuration->getDeviceSetPresets();
@ -1354,11 +1467,17 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry()); m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry());
m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry()); m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry());
m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr);
if (waitBox) {
waitBox->setValue(waitBox->value() + 50/deviceSetPresets.size());
}
} }
// Features // Features
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Loading device sets..."); {
waitBox->setLabelText("Loading feature sets...");
waitBox->setValue(75);
} }
m_featureUIs[0]->loadFeatureSetSettings( m_featureUIs[0]->loadFeatureSetSettings(
@ -1381,8 +1500,10 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
} }
// Lastly restore workspaces geometry // Lastly restore workspaces geometry
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Finalizing..."); {
waitBox->setValue(90);
waitBox->setLabelText("Finalizing...");
} }
for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++) for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++)
@ -1390,12 +1511,16 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->adjustSubWindowsAfterRestore(); m_workspaces[i]->adjustSubWindowsAfterRestore();
#ifdef ANDROID
// On Android, workspaces seem to be restored to 0,20, rather than 0,0
m_workspaces[i]->move(m_workspaces[i]->pos().x(), 0);
// Need to call updateGeometry, otherwise sometimes the layout is corrupted
m_workspaces[i]->updateGeometry();
#endif
} }
if (waitBox) if (waitBox) {
{ waitBox->setValue(100);
waitBox->close();
delete waitBox;
} }
} }
@ -1430,6 +1555,7 @@ void MainWindow::saveConfiguration(Configuration *configuration)
{ {
configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry()); configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry());
configuration->getWorkspaceAutoStackOptions().push_back(workspace->getAutoStackOption()); configuration->getWorkspaceAutoStackOptions().push_back(workspace->getAutoStackOption());
configuration->getWorkspaceTabSubWindowsOptions().push_back(workspace->getTabSubWindowsOption());
} }
} }
@ -1461,24 +1587,46 @@ QString MainWindow::openGLVersion()
} }
} }
void MainWindow::createMenuBar() void MainWindow::createMenuBar(QToolButton *button)
{
QMenu *fileMenu, *viewMenu, *workspacesMenu, *preferencesMenu, *helpMenu;
if (button == nullptr)
{ {
QMenuBar *menuBar = this->menuBar(); QMenuBar *menuBar = this->menuBar();
fileMenu = menuBar->addMenu("&File");
viewMenu = menuBar->addMenu("&View");
workspacesMenu = menuBar->addMenu("&Workspaces");
preferencesMenu = menuBar->addMenu("&Preferences");
helpMenu = menuBar->addMenu("&Help");
}
else
{
QMenu *menu = new QMenu();
fileMenu = new QMenu("&File");
menu->addMenu(fileMenu);
viewMenu = new QMenu("&View");
menu->addMenu(viewMenu);
workspacesMenu = new QMenu("&Workspaces");
menu->addMenu(workspacesMenu);
preferencesMenu = new QMenu("&Preferences");
menu->addMenu(preferencesMenu);
helpMenu = new QMenu("&Help");
menu->addMenu(helpMenu);
button->setMenu(menu);
}
QMenu *fileMenu = menuBar->addMenu("&File");
QAction *exitAction = fileMenu->addAction("E&xit"); QAction *exitAction = fileMenu->addAction("E&xit");
exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
exitAction->setToolTip("Exit"); exitAction->setToolTip("Exit");
QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close); QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close);
QMenu *viewMenu = menuBar->addMenu("&View");
QAction *fullscreenAction = viewMenu->addAction("&Fullscreen"); QAction *fullscreenAction = viewMenu->addAction("&Fullscreen");
fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11)); fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11));
fullscreenAction->setToolTip("Toggle fullscreen view"); fullscreenAction->setToolTip("Toggle fullscreen view");
fullscreenAction->setCheckable(true); fullscreenAction->setCheckable(true);
QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled); QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled);
QMenu *workspacesMenu = menuBar->addMenu("&Workspaces");
QAction *newWorkspaceAction = workspacesMenu->addAction("&New"); QAction *newWorkspaceAction = workspacesMenu->addAction("&New");
newWorkspaceAction->setToolTip("Add a new workspace"); newWorkspaceAction->setToolTip("Add a new workspace");
QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace); QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace);
@ -1489,7 +1637,6 @@ void MainWindow::createMenuBar()
removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces"); removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces");
QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces); QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces);
QMenu *preferencesMenu = menuBar->addMenu("&Preferences");
QAction *configurationsAction = preferencesMenu->addAction("&Configurations..."); QAction *configurationsAction = preferencesMenu->addAction("&Configurations...");
configurationsAction->setToolTip("Manage configurations"); configurationsAction->setToolTip("Manage configurations");
QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered); QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered);
@ -1519,7 +1666,6 @@ void MainWindow::createMenuBar()
saveAllAction->setToolTip("Save all current settings"); saveAllAction->setToolTip("Save all current settings");
QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered); QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered);
QMenu *helpMenu = menuBar->addMenu("&Help");
QAction *quickStartAction = helpMenu->addAction("&Quick start..."); QAction *quickStartAction = helpMenu->addAction("&Quick start...");
quickStartAction->setToolTip("Instructions for quick start"); quickStartAction->setToolTip("Instructions for quick start");
QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered); QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered);
@ -1882,7 +2028,11 @@ void MainWindow::handleWorkspaceVisibility(Workspace *workspace, bool visibility
void MainWindow::addWorkspace() void MainWindow::addWorkspace()
{ {
int workspaceIndex = m_workspaces.size(); int workspaceIndex = m_workspaces.size();
m_workspaces.push_back(new Workspace(workspaceIndex)); Workspace *workspace = new Workspace(workspaceIndex);
m_workspaces.push_back(workspace);
if (workspace->getMenuButton()) {
createMenuBar(workspace->getMenuButton());
}
QStringList featureNames; QStringList featureNames;
m_pluginManager->listFeatures(featureNames); m_pluginManager->listFeatures(featureNames);
m_workspaces.back()->addAvailableFeatures(featureNames); m_workspaces.back()->addAvailableFeatures(featureNames);
@ -1923,6 +2073,13 @@ void MainWindow::addWorkspace()
&MainWindow::openFeaturePresetsDialog &MainWindow::openFeaturePresetsDialog
); );
QObject::connect(
m_workspaces.back(),
&Workspace::configurationPresetsDialogRequested,
this,
&MainWindow::on_action_Configurations_triggered
);
QObject::connect( QObject::connect(
m_workspaces.back(), m_workspaces.back(),
&Workspace::startAllDevices, &Workspace::startAllDevices,
@ -2006,6 +2163,13 @@ void MainWindow::removeEmptyWorkspaces()
} }
} }
} }
#ifdef ANDROID
// Need at least one workspace on Android, as no menus without
if (m_workspaces.size() == 0) {
addWorkspace();
}
#endif
} }
void MainWindow::on_action_View_Fullscreen_toggled(bool checked) void MainWindow::on_action_View_Fullscreen_toggled(bool checked)
@ -2063,7 +2227,12 @@ void MainWindow::on_action_Loaded_Plugins_triggered()
void MainWindow::on_action_Configurations_triggered() void MainWindow::on_action_Configurations_triggered()
{ {
ConfigurationsDialog dialog(this); openConfigurationDialog(false);
}
void MainWindow::openConfigurationDialog(bool openOnly)
{
ConfigurationsDialog dialog(openOnly, this);
dialog.setConfigurations(m_mainCore->m_settings.getConfigurations()); dialog.setConfigurations(m_mainCore->m_settings.getConfigurations());
dialog.populateTree(); dialog.populateTree();
QObject::connect( QObject::connect(
@ -2078,24 +2247,28 @@ void MainWindow::on_action_Configurations_triggered()
this, this,
[=](const Configuration* configuration) { this->loadConfiguration(configuration, true); } [=](const Configuration* configuration) { this->loadConfiguration(configuration, true); }
); );
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
} }
void MainWindow::on_action_Audio_triggered() void MainWindow::on_action_Audio_triggered()
{ {
AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this); AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this);
new DialogPositioner(&audioDialog, true);
audioDialog.exec(); audioDialog.exec();
} }
void MainWindow::on_action_Graphics_triggered() void MainWindow::on_action_Graphics_triggered()
{ {
GraphicsDialog graphicsDialog(m_mainCore->m_settings, this); GraphicsDialog graphicsDialog(m_mainCore->m_settings, this);
new DialogPositioner(&graphicsDialog, true);
graphicsDialog.exec(); graphicsDialog.exec();
} }
void MainWindow::on_action_Logging_triggered() void MainWindow::on_action_Logging_triggered()
{ {
LoggingDialog loggingDialog(m_mainCore->m_settings, this); LoggingDialog loggingDialog(m_mainCore->m_settings, this);
new DialogPositioner(&loggingDialog, true);
loggingDialog.exec(); loggingDialog.exec();
m_mainCore->setLoggingOptions(); m_mainCore->setLoggingOptions();
} }
@ -2103,6 +2276,7 @@ void MainWindow::on_action_Logging_triggered()
void MainWindow::on_action_My_Position_triggered() void MainWindow::on_action_My_Position_triggered()
{ {
MyPositionDialog myPositionDialog(m_mainCore->m_settings, this); MyPositionDialog myPositionDialog(m_mainCore->m_settings, this);
new DialogPositioner(&myPositionDialog, true);
myPositionDialog.exec(); myPositionDialog.exec();
} }
@ -2110,6 +2284,7 @@ void MainWindow::on_action_DeviceUserArguments_triggered()
{ {
qDebug("MainWindow::on_action_DeviceUserArguments_triggered"); qDebug("MainWindow::on_action_DeviceUserArguments_triggered");
DeviceUserArgsDialog deviceUserArgsDialog(DeviceEnumerator::instance(), m_mainCore->m_settings.getDeviceUserArgs(), this); DeviceUserArgsDialog deviceUserArgsDialog(DeviceEnumerator::instance(), m_mainCore->m_settings.getDeviceUserArgs(), this);
new DialogPositioner(&deviceUserArgsDialog, true);
deviceUserArgsDialog.exec(); deviceUserArgsDialog.exec();
} }
@ -2121,6 +2296,7 @@ void MainWindow::on_action_commands_triggered()
commandsDialog.setApiPort(m_apiServer->getPort()); commandsDialog.setApiPort(m_apiServer->getPort());
commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver); commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver);
commandsDialog.populateTree(); commandsDialog.populateTree();
new DialogPositioner(&commandsDialog, true);
commandsDialog.exec(); commandsDialog.exec();
} }
@ -2140,6 +2316,7 @@ void MainWindow::on_action_FFT_triggered()
this, this,
SLOT(fftWisdomProcessFinished(int, QProcess::ExitStatus))); SLOT(fftWisdomProcessFinished(int, QProcess::ExitStatus)));
FFTWisdomDialog fftWisdomDialog(m_fftWisdomProcess, this); FFTWisdomDialog fftWisdomDialog(m_fftWisdomProcess, this);
new DialogPositioner(&fftWisdomDialog, true);
if (fftWisdomDialog.exec() == QDialog::Rejected) if (fftWisdomDialog.exec() == QDialog::Rejected)
{ {
@ -2790,6 +2967,7 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace)
dialog.setWorkspaces(&m_workspaces); dialog.setWorkspaces(&m_workspaces);
dialog.populateTree(); dialog.populateTree();
dialog.move(p); dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
if (dialog.wasPresetLoaded()) if (dialog.wasPresetLoaded())
@ -2820,6 +2998,7 @@ void MainWindow::openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI)
dialog.setWorkspaces(&m_workspaces); dialog.setWorkspaces(&m_workspaces);
dialog.populateTree((int) deviceGUI->getDeviceType()); dialog.populateTree((int) deviceGUI->getDeviceType());
dialog.move(p); dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
} }
@ -2840,8 +3019,10 @@ void MainWindow::on_action_About_triggered()
void MainWindow::updateStatus() void MainWindow::updateStatus()
{ {
if (m_dateTimeWidget) {
m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t")); m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t"));
} }
}
void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release) void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release)
{ {
@ -2862,3 +3043,30 @@ void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifie
} }
} }
} }
void MainWindow::keyPressEvent(QKeyEvent* event)
{
#ifdef ANDROID
if (event->key() == Qt::Key_Back)
{
// On Android, we don't want to exit when back key is pressed, just run in the background
Android::moveTaskToBack();
}
else
#endif
{
QMainWindow::keyPressEvent(event);
}
}
void MainWindow::orientationChanged(Qt::ScreenOrientation orientation)
{
#ifdef ANDROID
// Adjust workspace tab position, to leave max space for MDI windows
if ((orientation == Qt::LandscapeOrientation) || (orientation == Qt::InvertedLandscapeOrientation)) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#endif
}

View File

@ -34,6 +34,7 @@
class QLabel; class QLabel;
class QTreeWidgetItem; class QTreeWidgetItem;
class QDir; class QDir;
class QToolButton;
class DSPEngine; class DSPEngine;
class DSPDeviceSourceEngine; class DSPDeviceSourceEngine;
@ -136,7 +137,7 @@ private:
void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex);
QString openGLVersion(); QString openGLVersion();
void createMenuBar(); void createMenuBar(QToolButton *button);
void createStatusBar(); void createStatusBar();
void closeEvent(QCloseEvent*); void closeEvent(QCloseEvent*);
void applySettings(); void applySettings();
@ -171,6 +172,9 @@ private:
bool handleMessage(const Message& cmd); bool handleMessage(const Message& cmd);
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
private slots: private slots:
void handleMessages(); void handleMessages();
void handleWorkspaceVisibility(Workspace *workspace, bool visibility); void handleWorkspaceVisibility(Workspace *workspace, bool visibility);
@ -194,6 +198,8 @@ private slots:
void addWorkspace(); void addWorkspace();
void viewAllWorkspaces(); void viewAllWorkspaces();
void removeEmptyWorkspaces(); void removeEmptyWorkspaces();
void openConfigurationDialog(bool openOnly);
void loadDefaultConfigurations();
void loadConfiguration(const Configuration *configuration, bool fromDialog = false); void loadConfiguration(const Configuration *configuration, bool fromDialog = false);
void saveConfiguration(Configuration *configuration); void saveConfiguration(Configuration *configuration);
void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex); void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex);
@ -215,6 +221,7 @@ private slots:
void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI); void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI);
void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release); void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release);
void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void orientationChanged(Qt::ScreenOrientation orientation);
}; };
#endif // INCLUDE_MAINWINDOW_H #endif // INCLUDE_MAINWINDOW_H

View File

@ -0,0 +1 @@
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

View File

@ -0,0 +1 @@
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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgNEQUKAAxuQAAEBcAEHZGVmYXVsdHACB25vIG5hbWUAZAAAZAEBgGVCAdnQywADAAAAAAAAAAAAAAAAAZoAAAMlAAAAAAAAAAAAAAGaAAADJQAAAAAAAAAAAZsAAAAAAAAAAAAAAZoAAAMlAMgBAYHJBB2QAAEBcAEHZGVmYXVsdHACB25vIG5hbWUwAwQNcx2AgAQAgAXikAABAQABAgQAAAIAAAMBBEAEBAAAAABABQRCjAAAYAYBAWAHAQFgCAEAYAkBAAAKAQFgCwEBAA0BEgAOAQEADwEeYBABAQARATJAEgQ/KPXDABMBAQAUAQpgFQEAcBYJMTI3LjAuMC4xEBcCIrdgGAEAYBkBAQAaATJgGwEAABwBAmAdAQAAHgBgHwEAACABA3AhBUFuZ2VsACIBAgAjAAAkAicQACUCJxAAJgInEAAnAQVgKgEBACsBBQAsAQEALQEBAC4AYC8BAGAwAQAAZAAAbgCAKAQAAAAAgCkEAAAAAGAGAQEABwBgCAEBgAlCAdnQywADAAAAABOIAAAAAAAAFQMAAAFuAAATiAAAAAAAABUDAAABbv////8AAAAAAZsAABOIAAAAAAAAFQMAAAFuAAoAgAtCAdnQywADAAAAABOIAAAAAAAAFPMAAAEEAAATiAAAAAAAABTzAAABBP////8AAAAAAZsAABOIAAAAAAAAFPMAAAEEAAwAcA0cc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAOCDAwMDAwMDAxAA8AABAAABQBAXAYHHNkcmFuZ2VsLnNhbXBsZXNvdXJjZS5ydGxzZHJwGQgwMDAwMDAwMQAaAIAbXpAAAQEAAgIBpAADABAEAGAFAQBgBgEAAAcBAgAIAx9AAGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5kYWJkZW1vZIHKAZWQAAEBAAEAAAIAcAMAQAQESbufQEAFBECgAABgBgEAcAcVU3lzdGVtIGRlZmF1bHQgZGV2aWNlgAgjkAABAQABABACBP9NaRlwAw9EQUIgRGVtb2R1bGF0b3IABwAQCQT/TWkZcAoPREFCIERlbW9kdWxhdG9yYAsBAHAMCTEyNy4wLjAuMRANAiK4EA4AEA8AgBCeAAAA/wAAAAAAAAAEAAAAIgBzAGUAdAB0AGkAbgBnAHMAQwBvAG4AdABhAGkAbgBlAHIAAAABAAAAIABwAHIAbwBnAHIAYQBtAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAYwB1AHIAcgBlAG4AdABQAHIAbwBnAHIAYQBtAAAAAQAAABQAcwB0AGEAdABpAHMAdABpAGMAcwAAAAEAEQCAEkIB2dDLAAMAAAAAAAAAAAAAAAABmgAAAvwAAAAAAAAAAP////7////+AAAAAAIAAAABmwAAAAAAAAAAAAABmgAAAvxgEwEAAGQAAGUBAQBmAQIAyAH/AMkB/wDKAf8EASwBAWQBLQEABAGQAQFkAZEBAQ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE4MAMECDgI0IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE5MAMECCuk8IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

13
settings/settings.qrc Normal file
View File

@ -0,0 +1,13 @@
<RCC>
<qresource prefix="/">
<file>configurations/rtlsdr/adsb.cfgx</file>
<file>configurations/rtlsdr/ais.cfgx</file>
<file>configurations/rtlsdr/apt.cfgx</file>
<file>configurations/rtlsdr/dab.cfgx</file>
<file>configurations/rtlsdr/isspacket.cfgx</file>
<file>configurations/rtlsdr/radiosonde.cfgx</file>
<file>configurations/airspyhf/radioclockeu.cfgx</file>
<file>presets/rtlsdr/noaa18.prex</file>
<file>presets/rtlsdr/noaa19.prex</file>
</qresource>
</RCC>