Companion 2.5 (#555)
* Introduce yaml in Companion * EdgeTX Companion Replace some OpenTX refs Data conversion to v220 Update Timers ui to cater for mode field split * Fix some simulators crashing when started from Companion * Disable firmware and app update checking and downloads from companion * Some more rebranding * Disable manual download actions and another rebrand * Fix cmake warning * Fix mode ON conversion gtests and enable switch in model edit * Stop update message when updates turned off * OpenTX #8580 Fixes missing negative sign for telemetry sources * OpenTX #8604 fix curves tooltip colors * OpenTX #8616 Fix release notes url references * OpenTX #8548 Align file sync folder references * OpenTX #8548 Link field once * OpenTX #8550 Confirmation prompt before write models and settings to radio * OpenTX #8550 Translate strings * Title and splash images * Change DEB package maintainer * Start NV14 * NV14 continued * NV14 sim * Timer countdownStart data conversion and ui fixes * Fix screen width * Merge OpenTX PR 8675 * NV14 sim add computer keys and mouse to activate some functions * Only allow supported protocols for each module on conversion * Clear mismatched modules on conversion * Replace colorlcd theme and layout blobs with detailed data structs in companion * NV14 has 2 topbar zones * Fix layout init * Fix layout init again * Reset to eepromimportexport.h to main branch * Import/export does not like enum fields * Model edit add custom screens tab for color and bw telemetry * Free memory
@@ -19,7 +19,7 @@ endif()
|
||||
# This the name that the user will see in the generated DMG and what the application
|
||||
# will be called under /Applications. We include the version string to make installing
|
||||
# different versions for different eeproms easier, i.e. without manually renaming
|
||||
set(COMPANION_OSX_APP_BUNDLE_NAME "OpenTX Companion ${VERSION_MAJOR}.${VERSION_MINOR}")
|
||||
set(COMPANION_OSX_APP_BUNDLE_NAME "EdgeTX Companion ${VERSION_MAJOR}.${VERSION_MINOR}")
|
||||
|
||||
# On OS X we copy dfu-util to the application bundle. This the path from which we copy
|
||||
# the binary (default homebrew path)
|
||||
@@ -494,7 +494,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_NAME "companion${C9X_NAME_SUFFIX}")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Models and settings editor for the OpenTX open source firmware")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Models and settings editor for the EdgeTX open source firmware")
|
||||
string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
|
||||
|
||||
# The file stripping is deliberately disabled, with the stripped file we get
|
||||
@@ -515,8 +515,8 @@ IF(APPLE)
|
||||
# Use a non standard Info.plist that adds Retina support flags
|
||||
set_target_properties(${COMPANION_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/companion/targets/mac/MacOSXBundleInfo.plist.in)
|
||||
set_target_properties(${SIMULATOR_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/companion/targets/mac/MacOSXBundleInfo.plist.in)
|
||||
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.opentx.companion")
|
||||
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.edgetx.companion")
|
||||
|
||||
INSTALL(TARGETS ${COMPANION_NAME}
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
@@ -584,7 +584,7 @@ if(DPKG_PROGRAM)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Bertrand Songis <bsongis-nosp@m-gmail.com>")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Raphael Coeffic")
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION ${VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
|
||||
@@ -596,7 +596,7 @@ elseif(APPLE)
|
||||
# set(CPACK_GENERATOR "TGZ") # for quick testing
|
||||
set(CPACK_BINARY_DRAGNDROP ON)
|
||||
set(CPACK_DMG_BACKGROUND_IMAGE ${COMPANION_SRC_DIRECTORY}/images/splash_dmg.png)
|
||||
set(CPACK_DMG_VOLUME_NAME "OpenTX Companion")
|
||||
set(CPACK_DMG_VOLUME_NAME "EdgeTX Companion")
|
||||
set(CPACK_DMG_DS_STORE ${PROJECT_SOURCE_DIR}/companion/targets/mac/DS_Store)
|
||||
set(CPACK_PACKAGE_FILE_NAME "opentx-${CPACK_PACKAGE_NAME_LOWERCASE}-${VERSION}")
|
||||
else(DPKG_PROGRAM)
|
||||
|
||||
@@ -243,6 +243,10 @@
|
||||
<file>images/simulator/TX16S/left_tele.png</file>
|
||||
<file>images/simulator/TX16S/right_ent.png</file>
|
||||
<file>images/simulator/TX16S/right_mdl.png</file>
|
||||
<file>images/simulator/NV14/left.png</file>
|
||||
<file>images/simulator/NV14/right.png</file>
|
||||
<file>images/simulator/NV14/top.png</file>
|
||||
<file>images/simulator/NV14/bottom.png</file>
|
||||
<file>images/wizard/ailerons.png</file>
|
||||
<file>images/wizard/airbrakes.png</file>
|
||||
<file>images/wizard/elevons.png</file>
|
||||
@@ -1224,4 +1228,19 @@
|
||||
<file>themes/yerico/48/write_eeprom.png</file>
|
||||
<file>themes/yerico/48/sdsync.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/layouts/">
|
||||
<file alias="mask_layout1p2.png">../../radio/src/gui/colorlcd/layouts/mask_layout1+2.png</file>
|
||||
<file alias="mask_layout1p3.png">../../radio/src/gui/colorlcd/layouts/mask_layout1+3.png</file>
|
||||
<file alias="mask_layout1x1.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x1.png</file>
|
||||
<file alias="mask_layout1x2.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x2.png</file>
|
||||
<file alias="mask_layout1x3.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x3.png</file>
|
||||
<file alias="mask_layout1x4.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x4.png</file>
|
||||
<file alias="mask_layout2p1.png">../../radio/src/gui/colorlcd/layouts/mask_layout2+1.png</file>
|
||||
<file alias="mask_layout2p3.png">../../radio/src/gui/colorlcd/layouts/mask_layout2+3.png</file>
|
||||
<file alias="mask_layout2x1.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x1.png</file>
|
||||
<file alias="mask_layout2x2.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x2.png</file>
|
||||
<file alias="mask_layout2x3.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x3.png</file>
|
||||
<file alias="mask_layout2x4.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x4.png</file>
|
||||
<file alias="mask_layout4p2.png">../../radio/src/gui/colorlcd/layouts/mask_layout4+2.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#define CPN_MAX_GYRO_ANALOGS 2
|
||||
#define CPN_MAX_ANALOGS (CPN_MAX_STICKS + CPN_MAX_POTS + CPN_MAX_MOUSE_ANALOGS + CPN_MAX_GYRO_ANALOGS)
|
||||
|
||||
#define CPN_STR_APP_NAME QCoreApplication::translate("Companion", "OpenTX Companion")
|
||||
#define CPN_STR_APP_NAME QCoreApplication::translate("Companion", "EdgeTX Companion")
|
||||
#define CPN_STR_TTL_INFO QCoreApplication::translate("Companion", "Information") // shared Title Case words, eg. for a window title or section heading
|
||||
#define CPN_STR_TTL_WARNING QCoreApplication::translate("Companion", "Warning")
|
||||
#define CPN_STR_TTL_ERROR QCoreApplication::translate("Companion", "Error")
|
||||
@@ -59,7 +59,8 @@
|
||||
#define DFU_FILES_FILTER "DFU " % CPN_STR_FILES % " (*.dfu);;"
|
||||
#define EEPE_FILES_FILTER "EEPE " % CPN_STR_FILES % " (*.eepe);;"
|
||||
#define OTX_FILES_FILTER "OpenTX " % CPN_STR_FILES % " (*.otx);;"
|
||||
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.otx *.eepe *.bin *.hex);;" % OTX_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER
|
||||
#define YML_FILES_FILTER "YAML " % CPN_STR_FILES % " (*.yml);;"
|
||||
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.otx *.yml *.eepe *.bin *.hex);;" % OTX_FILES_FILTER % YML_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER
|
||||
#define FLASH_FILES_FILTER "FLASH " % CPN_STR_FILES % " (*.bin *.hex *.dfu);;" % BIN_FILES_FILTER % HEX_FILES_FILTER % DFU_FILES_FILTER
|
||||
#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin *.hex);;" % BIN_FILES_FILTER % HEX_FILES_FILTER
|
||||
#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE"
|
||||
@@ -78,4 +79,6 @@
|
||||
#define CPN_STR_SW_INDICATOR_NEUT QCoreApplication::translate("RawSwitch", "-") // Switch neutral (middle) position indicator.
|
||||
#define CPN_STR_SW_INDICATOR_REV QCoreApplication::translate("RawSwitch", "!") // Switch reversed logic (NOT) indicator.
|
||||
|
||||
#define OPENTX_HOME_PAGE_URL "http://www.edge-tx.org"
|
||||
|
||||
#endif // _CONSTANTS_H_
|
||||
|
||||
@@ -176,7 +176,6 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett
|
||||
addItems(SWITCH_TYPE_SWITCH, -board->getCapability(Board::SwitchPositions));
|
||||
|
||||
// Ascending switch direction (including zero)
|
||||
addItems(SWITCH_TYPE_TIMER_MODE, 5);
|
||||
addItems(SWITCH_TYPE_NONE, 1);
|
||||
addItems(SWITCH_TYPE_SWITCH, board->getCapability(Board::SwitchPositions));
|
||||
addItems(SWITCH_TYPE_MULTIPOS_POT, board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions));
|
||||
@@ -214,14 +213,6 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count)
|
||||
context &= ~RawSwitch::GlobalFunctionsContext;
|
||||
break;
|
||||
|
||||
case SWITCH_TYPE_TIMER_MODE:
|
||||
context = RawSwitch::TimersContext;
|
||||
break;
|
||||
|
||||
case SWITCH_TYPE_NONE:
|
||||
context &= ~RawSwitch::TimersContext;
|
||||
break;
|
||||
|
||||
case SWITCH_TYPE_ON:
|
||||
case SWITCH_TYPE_ONE:
|
||||
context = RawSwitch::SpecialFunctionsContext | RawSwitch::GlobalFunctionsContext;
|
||||
|
||||
@@ -123,31 +123,28 @@ static QWidget * folderSelectorWidget(QLineEdit * le, QWidget * parent)
|
||||
|
||||
void FileSyncDialog::setupUi()
|
||||
{
|
||||
const QString srcArw = CPN_STR_SW_INDICATOR_UP % " ";
|
||||
const QString dstArw = CPN_STR_SW_INDICATOR_DN % " ";
|
||||
m_folderNameA = tr("Folder A");
|
||||
m_folderNameB = tr("Folder B");
|
||||
int row = 0;
|
||||
|
||||
CompanionIcon playIcon("play.png");
|
||||
playIcon.addImage("stop.png", QIcon::Normal, QIcon::On);
|
||||
|
||||
QLabel * lblSrc = new QLabel(tr("Source Folder:"), this);
|
||||
AutoLineEdit * leSrc = new AutoLineEdit(this, true);
|
||||
leSrc->setField(m_syncOptions.folderA);
|
||||
leSrc->setText(QDir::toNativeSeparators(m_syncOptions.folderA));
|
||||
QWidget * wdgSrc = folderSelectorWidget(leSrc, this);
|
||||
QLabel * lblFolderA = new QLabel(m_folderNameA % ":", this);
|
||||
AutoLineEdit * leFolderA = new AutoLineEdit(this, true);
|
||||
leFolderA->setField(m_syncOptions.folderA);
|
||||
leFolderA->setText(QDir::toNativeSeparators(m_syncOptions.folderA));
|
||||
QWidget * wdgFolderA = folderSelectorWidget(leFolderA, this);
|
||||
|
||||
QLabel * lblDst = new QLabel(tr("Destination Folder:"), this);
|
||||
AutoLineEdit * leDst = new AutoLineEdit(this, true);
|
||||
leDst->setField(m_syncOptions.folderB);
|
||||
leDst->setText(QDir::toNativeSeparators(m_syncOptions.folderB));
|
||||
QWidget * wdgDst = folderSelectorWidget(leDst, this);
|
||||
QLabel * lblFolderB = new QLabel(m_folderNameB % ":", this);
|
||||
AutoLineEdit * leFolderB = new AutoLineEdit(this, true);
|
||||
leFolderB->setField(m_syncOptions.folderB);
|
||||
leFolderB->setText(QDir::toNativeSeparators(m_syncOptions.folderB));
|
||||
QWidget * wdgFolderB = folderSelectorWidget(leFolderB, this);
|
||||
|
||||
AutoComboBox * syncDir = new AutoComboBox(this);
|
||||
syncDir->addItem(tr("%1%2 Both directions, to destination folder first").arg(dstArw, srcArw), SyncProcess::SYNC_A2B_B2A);
|
||||
syncDir->addItem(tr("%1%2 Both directions, to source folder first").arg(srcArw, dstArw), SyncProcess::SYNC_B2A_A2B);
|
||||
syncDir->addItem(tr(" %1 Only from source folder to destination folder").arg(dstArw), SyncProcess::SYNC_A2B);
|
||||
syncDir->addItem(tr(" %1 Only from destination folder to source folder").arg(srcArw), SyncProcess::SYNC_B2A);
|
||||
syncDir->setCurrentIndex(-1); // we set the default option later
|
||||
ui_syncDir = new AutoComboBox(this);
|
||||
setSyncDirList();
|
||||
ui_syncDir->setField(m_syncOptions.direction);
|
||||
|
||||
AutoComboBox * copyMode = new AutoComboBox(this);
|
||||
copyMode->setToolTip(tr("How to handle overwriting files which already exist in the destination folder."));
|
||||
@@ -274,12 +271,12 @@ void FileSyncDialog::setupUi()
|
||||
ui_optionsPanel = new QGroupBox(tr("Options"), this);
|
||||
QGridLayout * optsLay = new QGridLayout(ui_optionsPanel);
|
||||
row = 0;
|
||||
optsLay->addWidget(lblSrc, row, 0);
|
||||
optsLay->addWidget(wdgSrc, row++, 1);
|
||||
optsLay->addWidget(lblDst, row, 0);
|
||||
optsLay->addWidget(wdgDst, row++, 1);
|
||||
optsLay->addWidget(lblFolderA, row, 0);
|
||||
optsLay->addWidget(wdgFolderA, row++, 1);
|
||||
optsLay->addWidget(lblFolderB, row, 0);
|
||||
optsLay->addWidget(wdgFolderB, row++, 1);
|
||||
optsLay->addWidget(new QLabel(tr("Sync. Direction:"), this), row, 0);
|
||||
optsLay->addWidget(syncDir, row++, 1);
|
||||
optsLay->addWidget(ui_syncDir, row++, 1);
|
||||
optsLay->addWidget(new QLabel(tr("Existing Files:"), this), row, 0);
|
||||
optsLay->addWidget(copyMode, row++, 1);
|
||||
optsLay->addWidget(new QLabel(tr("Max. File Size:"), this), row, 0);
|
||||
@@ -299,13 +296,13 @@ void FileSyncDialog::setupUi()
|
||||
|
||||
// Connect all signal handlers
|
||||
|
||||
// source path
|
||||
connect(leSrc, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
|
||||
// destination path
|
||||
connect(leDst, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
|
||||
// folder A path
|
||||
connect(leFolderA, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
|
||||
// folder B path
|
||||
connect(leFolderB, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
|
||||
|
||||
// function to dis/enable the OVERWR_ALWAYS option depending on sync direction
|
||||
connect(syncDir, &AutoComboBox::currentDataChanged, [=](int dir) {
|
||||
connect(ui_syncDir, &AutoComboBox::currentDataChanged, [=](int dir) {
|
||||
int idx = copyMode->findData(SyncProcess::OVERWR_ALWAYS);
|
||||
int flg = (dir == SyncProcess::SYNC_A2B || dir == SyncProcess::SYNC_B2A) ? 33 : 0;
|
||||
if (!flg && idx == copyMode->currentIndex())
|
||||
@@ -342,7 +339,7 @@ void FileSyncDialog::setupUi()
|
||||
});
|
||||
|
||||
// we set these after connections are made because we want the signal processors to run
|
||||
syncDir->setField(m_syncOptions.direction);
|
||||
ui_syncDir->setField(m_syncOptions.direction);
|
||||
maxSize->setValue(m_syncOptions.maxFileSize / 1024);
|
||||
|
||||
// connect Reset action to all fields
|
||||
@@ -354,14 +351,16 @@ void FileSyncDialog::setupUi()
|
||||
connect(this, &FileSyncDialog::optionsWereReset, a, &AutoBitsetCheckBox::updateValue);
|
||||
connect(this, &FileSyncDialog::optionsWereReset, [=]() { maxSize->setValue(m_syncOptions.maxFileSize / 1024); });
|
||||
|
||||
// React to name changes for source and destination path labels
|
||||
// React to name changes for folder path labels
|
||||
connect(this, &FileSyncDialog::folderNameAChanged, [=](const QString &text) {
|
||||
lblSrc->setText(text % ":");
|
||||
wdgSrc->setProperty("fileDialogTitle", text);
|
||||
lblFolderA->setText(text % ":");
|
||||
wdgFolderA->setProperty("fileDialogTitle", text);
|
||||
setSyncDirList();
|
||||
});
|
||||
connect(this, &FileSyncDialog::folderNameBChanged, [=](const QString &text) {
|
||||
lblDst->setText(text % ":");
|
||||
wdgDst->setProperty("fileDialogTitle", text);
|
||||
lblFolderB->setText(text % ":");
|
||||
wdgFolderB->setProperty("fileDialogTitle", text);
|
||||
setSyncDirList();
|
||||
});
|
||||
|
||||
// button actions
|
||||
@@ -487,11 +486,11 @@ bool FileSyncDialog::validate()
|
||||
{
|
||||
QString msg;
|
||||
if (m_syncOptions.folderA.isEmpty() || !QFileInfo(m_syncOptions.folderA).exists())
|
||||
msg = tr("Source folder not found.");
|
||||
msg = tr("%1 not found.").arg(m_folderNameA);
|
||||
else if (m_syncOptions.folderB.isEmpty() || !QFileInfo(m_syncOptions.folderB).exists())
|
||||
msg = tr("Destination folder not found.");
|
||||
msg = tr("%1 not found.").arg(m_folderNameB);
|
||||
else if (QFileInfo(m_syncOptions.folderA).canonicalFilePath() == QFileInfo(m_syncOptions.folderB).canonicalFilePath())
|
||||
msg = tr("Source and destination folders are the same.");
|
||||
msg = tr("Folders are the same.");
|
||||
|
||||
setStatusText(msg, QtCriticalMsg);
|
||||
ui_btnStartStop->setEnabled(msg.isEmpty());
|
||||
@@ -502,3 +501,16 @@ void FileSyncDialog::adjustSizeDelayed()
|
||||
{
|
||||
QTimer::singleShot(0, [=]() { adjustSize(); });
|
||||
}
|
||||
|
||||
void FileSyncDialog::setSyncDirList()
|
||||
{
|
||||
const QString upArw = CPN_STR_SW_INDICATOR_UP % " ";
|
||||
const QString downArw = CPN_STR_SW_INDICATOR_DN % " ";
|
||||
|
||||
ui_syncDir->clear();
|
||||
ui_syncDir->addItem(tr("%1%2 Both directions, to %3 first").arg(downArw, upArw, m_folderNameB), SyncProcess::SYNC_A2B_B2A);
|
||||
ui_syncDir->addItem(tr("%1%2 Both directions, to %3 first").arg(upArw, downArw, m_folderNameA), SyncProcess::SYNC_B2A_A2B);
|
||||
ui_syncDir->addItem(tr(" %1 Only from %2 to %3").arg(downArw, m_folderNameA, m_folderNameB), SyncProcess::SYNC_A2B);
|
||||
ui_syncDir->addItem(tr(" %1 Only from %2 to %3").arg(upArw, m_folderNameB, m_folderNameA), SyncProcess::SYNC_B2A);
|
||||
ui_syncDir->updateValue();
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef FILESYNCDIALOG_H
|
||||
#define FILESYNCDIALOG_H
|
||||
#pragma once
|
||||
|
||||
#include "process_sync.h"
|
||||
|
||||
@@ -30,6 +29,7 @@
|
||||
#include <QToolButton>
|
||||
|
||||
class ProgressWidget;
|
||||
class AutoComboBox;
|
||||
|
||||
class FileSyncDialog : public QDialog
|
||||
{
|
||||
@@ -76,6 +76,7 @@ class FileSyncDialog : public QDialog
|
||||
void updateRunningState();
|
||||
void onStatusUpdate(const SyncProcess::SyncStatus & stat);
|
||||
void adjustSizeDelayed();
|
||||
void setSyncDirList();
|
||||
|
||||
private:
|
||||
SyncProcess::SyncOptions m_syncOptions;
|
||||
@@ -91,6 +92,5 @@ class FileSyncDialog : public QDialog
|
||||
QToolButton * ui_btnStartStop;
|
||||
QToolButton * ui_btnClose;
|
||||
QLabel * ui_statusLabel;
|
||||
AutoComboBox * ui_syncDir;
|
||||
};
|
||||
|
||||
#endif // FILESYNCDIALOG_H
|
||||
|
||||
@@ -37,6 +37,7 @@ string(REPLACE ".cpp" ".h" firmwares_HDRS "${firmwares_SRCS}")
|
||||
list(APPEND firmwares_HDRS
|
||||
eepromimportexport.h
|
||||
moduledata.h
|
||||
customisation_data.h
|
||||
)
|
||||
|
||||
set(firmwares_QT
|
||||
|
||||
@@ -92,6 +92,8 @@ uint32_t Boards::getFourCC(Type board)
|
||||
return 0x4178746F;
|
||||
case BOARD_RADIOMASTER_T8:
|
||||
return 0x4378746F;
|
||||
case BOARD_FLYSKY_NV14:
|
||||
return 0x3A78746F;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -128,6 +130,7 @@ int Boards::getEEpromSize(Board::Type board)
|
||||
case BOARD_JUMPER_T16:
|
||||
case BOARD_JUMPER_T18:
|
||||
case BOARD_RADIOMASTER_TX16S:
|
||||
case BOARD_FLYSKY_NV14:
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
@@ -163,6 +166,7 @@ int Boards::getFlashSize(Type board)
|
||||
case BOARD_JUMPER_T16:
|
||||
case BOARD_JUMPER_T18:
|
||||
case BOARD_RADIOMASTER_TX16S:
|
||||
case BOARD_FLYSKY_NV14:
|
||||
return FSIZE_HORUS;
|
||||
case BOARD_UNKNOWN:
|
||||
return FSIZE_MAX;
|
||||
@@ -261,6 +265,20 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index)
|
||||
if (index < DIM(switches))
|
||||
return switches[index];
|
||||
}
|
||||
else if (IS_FLYSKY_NV14(board)) {
|
||||
const Board::SwitchInfo switches[] = {
|
||||
{SWITCH_2POS, "SA"},
|
||||
{SWITCH_3POS, "SB"},
|
||||
{SWITCH_TOGGLE, "SC"},
|
||||
{SWITCH_2POS, "SD"},
|
||||
{SWITCH_TOGGLE, "SE"},
|
||||
{SWITCH_3POS, "SF"},
|
||||
{SWITCH_3POS, "SG"},
|
||||
{SWITCH_TOGGLE, "SH"}
|
||||
};
|
||||
if (index < DIM(switches))
|
||||
return switches[index];
|
||||
}
|
||||
else if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
const Board::SwitchInfo switches[] = {
|
||||
{SWITCH_3POS, "SA"},
|
||||
@@ -337,6 +355,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
return 5;
|
||||
else if (IS_HORUS_X12S(board))
|
||||
return 3;
|
||||
else if (IS_FLYSKY_NV14(board))
|
||||
return 2;
|
||||
else
|
||||
return 3;
|
||||
|
||||
@@ -388,6 +408,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
return 8;
|
||||
else if (board == BOARD_JUMPER_TLITE)
|
||||
return 4;
|
||||
else if (board == BOARD_FLYSKY_NV14)
|
||||
return 8;
|
||||
else if (IS_FAMILY_T12(board))
|
||||
return 8;
|
||||
else if (IS_TARANIS_XLITE(board))
|
||||
@@ -414,13 +436,13 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
return getCapability(board, Board::Switches);
|
||||
|
||||
case SwitchPositions:
|
||||
if (IS_HORUS_OR_TARANIS(board))
|
||||
if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board))
|
||||
return getCapability(board, Board::Switches) * 3;
|
||||
else
|
||||
return 9;
|
||||
|
||||
case NumTrims:
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))
|
||||
return 6;
|
||||
else
|
||||
return 4;
|
||||
@@ -431,6 +453,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
case HasRTC:
|
||||
return IS_STM32(board) ? true : false;
|
||||
|
||||
case HasColorLcd:
|
||||
return IS_FAMILY_HORUS_OR_T16(board);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -528,6 +553,14 @@ QString Boards::getAnalogInputName(Board::Type board, int index)
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_FLYSKY_NV14(board)) {
|
||||
const QString pots[] = {
|
||||
"VRA",
|
||||
"VRB",
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) {
|
||||
const QString pots[] = {
|
||||
"S1",
|
||||
@@ -599,6 +632,8 @@ QString Boards::getBoardName(Board::Type board)
|
||||
return "Radiomaster TX12";
|
||||
case BOARD_RADIOMASTER_T8:
|
||||
return "Radiomaster T8";
|
||||
case BOARD_FLYSKY_NV14:
|
||||
return "FlySky NV14";
|
||||
default:
|
||||
return CPN_STR_UNKNOWN_ITEM;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace Board {
|
||||
BOARD_RADIOMASTER_TX12,
|
||||
BOARD_RADIOMASTER_T8,
|
||||
BOARD_JUMPER_TLITE,
|
||||
BOARD_FLYSKY_NV14,
|
||||
BOARD_TYPE_COUNT,
|
||||
BOARD_TYPE_MAX = BOARD_TYPE_COUNT - 1
|
||||
};
|
||||
@@ -140,7 +141,8 @@ namespace Board {
|
||||
FactoryInstalledSwitches,
|
||||
NumTrims,
|
||||
NumTrimSwitches,
|
||||
HasRTC
|
||||
HasRTC,
|
||||
HasColorLcd
|
||||
};
|
||||
|
||||
struct SwitchInfo
|
||||
@@ -269,6 +271,11 @@ inline bool IS_FAMILY_T12(Board::Type board)
|
||||
return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_RADIOMASTER_T8 || board == Board::BOARD_JUMPER_TLITE;
|
||||
}
|
||||
|
||||
inline bool IS_FLYSKY_NV14(Board::Type board)
|
||||
{
|
||||
return (board == Board::BOARD_FLYSKY_NV14);
|
||||
}
|
||||
|
||||
inline bool IS_TARANIS_XLITE(Board::Type board)
|
||||
{
|
||||
return board == Board::BOARD_TARANIS_XLITE || board == Board::BOARD_TARANIS_XLITES;
|
||||
@@ -346,7 +353,7 @@ inline bool IS_FAMILY_HORUS(Board::Type board)
|
||||
|
||||
inline bool IS_FAMILY_HORUS_OR_T16(Board::Type board)
|
||||
{
|
||||
return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board);
|
||||
return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board)/*generally*/;
|
||||
}
|
||||
|
||||
inline bool IS_HORUS_OR_TARANIS(Board::Type board)
|
||||
@@ -356,7 +363,7 @@ inline bool IS_HORUS_OR_TARANIS(Board::Type board)
|
||||
|
||||
inline bool IS_STM32(Board::Type board)
|
||||
{
|
||||
return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board);
|
||||
return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(board);
|
||||
}
|
||||
|
||||
inline bool IS_ARM(Board::Type board)
|
||||
@@ -384,3 +391,8 @@ inline bool IS_ACCESS_RADIO(Board::Type board, const QString & id)
|
||||
return (IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || board == Board::BOARD_TARANIS_X9DP_2019 || board == Board::BOARD_X10_EXPRESS || IS_TARANIS_X7_ACCESS(board) ||
|
||||
(IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess")));
|
||||
}
|
||||
|
||||
inline bool HAS_EEPROM_YAML(Board::Type board)
|
||||
{
|
||||
return IS_FAMILY_HORUS_OR_T16(board);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "constants.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
/*
|
||||
|
||||
Note: this code is consolidated from various radio/src files and modified if necessary to run on desktop
|
||||
|
||||
*/
|
||||
|
||||
constexpr int MAX_CUSTOM_SCREENS {5};
|
||||
constexpr int THEME_NAME_LEN {8};
|
||||
constexpr int MAX_THEME_OPTIONS {5};
|
||||
constexpr int LEN_ZONE_OPTION_STRING {8};
|
||||
constexpr int MAX_LAYOUT_ZONES {10};
|
||||
constexpr int MAX_LAYOUT_OPTIONS {10};
|
||||
constexpr int WIDGET_NAME_LEN {10};
|
||||
constexpr int MAX_WIDGET_OPTIONS {5};
|
||||
constexpr int MAX_TOPBAR_ZONES {4};
|
||||
constexpr int MAX_TOPBAR_OPTIONS {1};
|
||||
constexpr int LAYOUT_ID_LEN {10};
|
||||
|
||||
// Common 'ZoneOptionValue's among all layouts
|
||||
enum {
|
||||
LAYOUT_OPTION_TOPBAR = 0,
|
||||
LAYOUT_OPTION_FM,
|
||||
LAYOUT_OPTION_SLIDERS,
|
||||
LAYOUT_OPTION_TRIMS,
|
||||
LAYOUT_OPTION_MIRRORED,
|
||||
|
||||
LAYOUT_OPTION_LAST_DEFAULT=LAYOUT_OPTION_MIRRORED
|
||||
};
|
||||
|
||||
struct ZoneOptionValue // union in radio/src/datastructs.h
|
||||
{
|
||||
unsigned int unsignedValue;
|
||||
int signedValue;
|
||||
unsigned int boolValue;
|
||||
char stringValue[LEN_ZONE_OPTION_STRING + 1];
|
||||
};
|
||||
|
||||
enum ZoneOptionValueEnum {
|
||||
ZOV_Unsigned,
|
||||
ZOV_Signed,
|
||||
ZOV_Bool,
|
||||
ZOV_String,
|
||||
ZOV_LAST = ZOV_String
|
||||
};
|
||||
|
||||
inline const char * zoneOptionValueEnumToString(ZoneOptionValueEnum zovenum) {
|
||||
switch (zovenum) {
|
||||
case ZOV_Unsigned:
|
||||
return "unsigned";
|
||||
case ZOV_Signed:
|
||||
return "signed";
|
||||
case ZOV_Bool:
|
||||
return "bool";
|
||||
case ZOV_String:
|
||||
return "string";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct ZoneOption
|
||||
{
|
||||
enum Type {
|
||||
Integer,
|
||||
Source,
|
||||
Bool,
|
||||
String,
|
||||
File,
|
||||
TextSize,
|
||||
Timer,
|
||||
Switch,
|
||||
Color
|
||||
};
|
||||
|
||||
const char * name;
|
||||
Type type;
|
||||
ZoneOptionValue deflt;
|
||||
ZoneOptionValue min;
|
||||
ZoneOptionValue max;
|
||||
};
|
||||
|
||||
struct ZoneOptionValueTyped
|
||||
{
|
||||
ZoneOptionValueEnum type;
|
||||
ZoneOptionValue value;
|
||||
};
|
||||
|
||||
inline ZoneOptionValueEnum zoneValueEnumFromType(ZoneOption::Type type)
|
||||
{
|
||||
switch(type) {
|
||||
case ZoneOption::File:
|
||||
case ZoneOption::String:
|
||||
return ZOV_String;
|
||||
|
||||
case ZoneOption::Integer:
|
||||
return ZOV_Signed;
|
||||
|
||||
case ZoneOption::Bool:
|
||||
return ZOV_Bool;
|
||||
|
||||
case ZoneOption::Color:
|
||||
case ZoneOption::Timer:
|
||||
case ZoneOption::Switch:
|
||||
case ZoneOption::Source:
|
||||
case ZoneOption::TextSize:
|
||||
default:
|
||||
return ZOV_Unsigned;
|
||||
}
|
||||
}
|
||||
|
||||
struct WidgetPersistentData {
|
||||
ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS];
|
||||
};
|
||||
|
||||
struct ZonePersistentData {
|
||||
char widgetName[WIDGET_NAME_LEN + 1];
|
||||
WidgetPersistentData widgetData;
|
||||
};
|
||||
|
||||
template<int N, int O>
|
||||
struct WidgetsContainerPersistentData {
|
||||
ZonePersistentData zones[N];
|
||||
ZoneOptionValueTyped options[O];
|
||||
};
|
||||
|
||||
typedef WidgetsContainerPersistentData<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS> LayoutPersistentData;
|
||||
|
||||
typedef WidgetsContainerPersistentData<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS> TopBarPersistentData;
|
||||
|
||||
inline void setZoneOptionValue(ZoneOptionValue & zov, bool value) { zov.boolValue = value; }
|
||||
inline void setZoneOptionValue(ZoneOptionValue & zov, int value) { zov.signedValue = value; }
|
||||
inline void setZoneOptionValue(ZoneOptionValue & zov, char value) { memset(&zov.stringValue, value, LEN_ZONE_OPTION_STRING); }
|
||||
inline void setZoneOptionValue(ZoneOptionValue & zov, unsigned int value) { zov.unsignedValue = value; }
|
||||
|
||||
// cannot use QColor so use formula from libopenui_defines.h
|
||||
#define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3))
|
||||
#define WHITE RGB(0xFF, 0xFF, 0xFF)
|
||||
#define RED RGB(229, 32, 30)
|
||||
|
||||
class RadioTheme
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(RadioTheme)
|
||||
|
||||
public:
|
||||
struct PersistentData {
|
||||
ZoneOptionValueTyped options[MAX_THEME_OPTIONS];
|
||||
};
|
||||
|
||||
struct ThemeData {
|
||||
char themeName[THEME_NAME_LEN + 1];
|
||||
PersistentData themePersistentData;
|
||||
};
|
||||
|
||||
static void init(const char * themeName, ThemeData & themeData)
|
||||
{
|
||||
memset(&themeData, 0, sizeof(ThemeData));
|
||||
|
||||
memcpy(&themeData.themeName, themeName, THEME_NAME_LEN);
|
||||
|
||||
PersistentData & persistentData = themeData.themePersistentData;
|
||||
|
||||
persistentData.options[0].type = zoneValueEnumFromType(ZoneOption::Type::Color);
|
||||
setZoneOptionValue(persistentData.options[0].value, (unsigned int)WHITE);
|
||||
|
||||
persistentData.options[1].type = zoneValueEnumFromType(ZoneOption::Type::Color);
|
||||
setZoneOptionValue(persistentData.options[1].value, (unsigned int)RED);
|
||||
}
|
||||
};
|
||||
|
||||
class RadioLayout
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(RadioLayout)
|
||||
|
||||
public:
|
||||
struct CustomScreenData {
|
||||
char layoutId[LAYOUT_ID_LEN + 1];
|
||||
LayoutPersistentData layoutPersistentData;
|
||||
};
|
||||
|
||||
struct CustomScreens {
|
||||
CustomScreenData customScreenData[MAX_CUSTOM_SCREENS];
|
||||
};
|
||||
|
||||
static void init(const char * layoutId, CustomScreens & customScreens)
|
||||
{
|
||||
memset(&customScreens, 0, sizeof(CustomScreens));
|
||||
|
||||
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
|
||||
if (i == 0)
|
||||
memcpy(&customScreens.customScreenData[i].layoutId, layoutId, LAYOUT_ID_LEN);
|
||||
|
||||
LayoutPersistentData & persistentData = customScreens.customScreenData[i].layoutPersistentData;
|
||||
|
||||
int j = 0;
|
||||
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
|
||||
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
|
||||
|
||||
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
|
||||
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
|
||||
|
||||
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
|
||||
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
|
||||
|
||||
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
|
||||
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
|
||||
|
||||
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
|
||||
setZoneOptionValue(persistentData.options[j++].value, (bool)false);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -588,7 +588,7 @@ class UnionField: public DataField {
|
||||
virtual bool select(const selectorT& attr) const = 0;
|
||||
virtual DataField* getField() = 0;
|
||||
};
|
||||
|
||||
|
||||
class TransformedMember: public UnionMember, public TransformedField {
|
||||
public:
|
||||
TransformedMember(DataField* parent, DataField& field):
|
||||
|
||||
@@ -158,7 +158,8 @@ enum Capability {
|
||||
HasBluetooth,
|
||||
HasAntennaChoice,
|
||||
HasADCJitterFilter,
|
||||
HasTelemetryBaudrate
|
||||
HasTelemetryBaudrate,
|
||||
TopBarZones
|
||||
};
|
||||
|
||||
class EEPROMInterface
|
||||
|
||||
@@ -90,9 +90,9 @@ t_Ersky9xGeneral::t_Ersky9xGeneral()
|
||||
Ersky9xGeneral::operator GeneralSettings ()
|
||||
{
|
||||
GeneralSettings result;
|
||||
|
||||
|
||||
result.version = myVers;
|
||||
|
||||
|
||||
for (int i=0; i<CPN_MAX_STICKSnPOTS; i++) {
|
||||
result.calibMid[i] = calibMid[i];
|
||||
result.calibSpanNeg[i] = calibSpanNeg[i];
|
||||
@@ -280,7 +280,7 @@ t_Ersky9xMixData_v11::operator MixData ()
|
||||
}
|
||||
else if (srcRaw > 45) {
|
||||
c9x.srcRaw = RawSource(SOURCE_TYPE_GVAR, srcRaw-46);
|
||||
}
|
||||
}
|
||||
else if (srcRaw <= 12) {
|
||||
c9x.srcRaw = RawSource(SOURCE_TYPE_CYC, srcRaw-10);
|
||||
}
|
||||
@@ -504,7 +504,7 @@ t_Ersky9xModelData_v10::operator ModelData ()
|
||||
c9x.used = true;
|
||||
getEEPROMString(c9x.name, name, sizeof(name));
|
||||
for (int i=0; i<2; i++) {
|
||||
c9x.timers[i].mode = getErSky9xTimerMode(timer[i].tmrModeA);
|
||||
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
|
||||
c9x.timers[i].val = timer[i].tmrVal;
|
||||
}
|
||||
switch(protocol) {
|
||||
@@ -619,7 +619,7 @@ t_Ersky9xModelData_v11::operator ModelData ()
|
||||
c9x.used = true;
|
||||
getEEPROMString(c9x.name, name, sizeof(name));
|
||||
for (int i=0; i<2; i++) {
|
||||
c9x.timers[i].mode = getErSky9xTimerMode(timer[i].tmrModeA);
|
||||
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
|
||||
c9x.timers[i].val = timer[i].tmrVal;
|
||||
}
|
||||
switch(protocol) {
|
||||
|
||||
@@ -121,12 +121,12 @@ GeneralSettings::GeneralSettings()
|
||||
|
||||
if (IS_JUMPER_T16(board))
|
||||
strcpy(bluetoothName, "t16");
|
||||
else if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
else if (IS_FLYSKY_NV14(board))
|
||||
strcpy(bluetoothName, "nv14");
|
||||
else if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
strcpy(bluetoothName, "horus");
|
||||
}
|
||||
else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board)) {
|
||||
else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board))
|
||||
strcpy(bluetoothName, "taranis");
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
trainer.mix[i].mode = TrainerMix::TRN_MIX_MODE_SUBST;
|
||||
@@ -233,11 +233,8 @@ GeneralSettings::GeneralSettings()
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(themeName, "default");
|
||||
ThemeOptionData option1 = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
|
||||
memcpy(&themeOptionValue[0], option1, sizeof(ThemeOptionData));
|
||||
ThemeOptionData option2 = { 0x03, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
|
||||
memcpy(&themeOptionValue[1], option2, sizeof(ThemeOptionData));
|
||||
const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX";
|
||||
RadioTheme::init(themeName, themeData);
|
||||
}
|
||||
|
||||
void GeneralSettings::setDefaultControlTypes(Board::Type board)
|
||||
@@ -251,11 +248,15 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board)
|
||||
return;
|
||||
|
||||
// TODO: move to Boards, like with switches
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) {
|
||||
potConfig[0] = Board::POT_WITH_DETENT;
|
||||
potConfig[1] = Board::POT_MULTIPOS_SWITCH;
|
||||
potConfig[2] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else if (IS_FLYSKY_NV14(board)) {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITHOUT_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS_XLITE(board)) {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITHOUT_DETENT;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "customfunctiondata.h"
|
||||
#include "rawsource.h"
|
||||
#include "datahelpers.h"
|
||||
#include "customisation_data.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
@@ -94,7 +95,6 @@ constexpr int OWNER_NAME_LEN {10};
|
||||
constexpr int BLUETOOTH_NAME_LEN {10};
|
||||
constexpr int TTS_LANGUAGE_LEN {2};
|
||||
constexpr int HARDWARE_NAME_LEN {3};
|
||||
constexpr int THEME_NAME_LEN {8};
|
||||
constexpr int REGISTRATION_ID_LEN {8};
|
||||
|
||||
class GeneralSettings {
|
||||
@@ -236,13 +236,12 @@ class GeneralSettings {
|
||||
char sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1];
|
||||
unsigned int sliderConfig[CPN_MAX_SLIDERS];
|
||||
|
||||
char themeName[THEME_NAME_LEN + 1];
|
||||
typedef uint8_t ThemeOptionData[8 + 1];
|
||||
ThemeOptionData themeOptionValue[5];
|
||||
RadioTheme::ThemeData themeData;
|
||||
|
||||
char registrationId[REGISTRATION_ID_LEN + 1];
|
||||
int gyroMax;
|
||||
int gyroOffset;
|
||||
int uartSampleMode;
|
||||
|
||||
int pwrOnSpeed;
|
||||
int pwrOffSpeed;
|
||||
|
||||
@@ -176,8 +176,8 @@ void ModelData::clear()
|
||||
for (unsigned i = 0; i < CPN_MAX_SENSORS; i++)
|
||||
sensorData[i].clear();
|
||||
|
||||
static const uint8_t blob[] = { 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x32, 0x50, 0x31, 0x00, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
memcpy(customScreenData[0], blob, sizeof(blob));
|
||||
const char * layoutId = "Layout2P1"; // currently all using same default though might change for NV14
|
||||
RadioLayout::init(layoutId, customScreens);
|
||||
}
|
||||
|
||||
bool ModelData::isEmpty() const
|
||||
@@ -407,6 +407,10 @@ void ModelData::convert(RadioDataConversionState & cstate)
|
||||
thrTraceSrc = RawSource(SOURCE_TYPE_STICK, (int)thrTraceSrc + 3).convert(cstate).index - 3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CPN_MAX_MODULES; i++) {
|
||||
moduleData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
|
||||
timers[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
@@ -430,10 +434,6 @@ void ModelData::convert(RadioDataConversionState & cstate)
|
||||
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
|
||||
flightModeData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < CPN_MAX_MODULES; i++) {
|
||||
moduleData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_REF_UPDATES 100
|
||||
@@ -575,7 +575,7 @@ int ModelData::updateReference()
|
||||
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
|
||||
TimerData *td = &timers[i];
|
||||
if (!td->isModeOff()) {
|
||||
updateTimerMode(td->mode);
|
||||
updateTimerMode(td->swtch);
|
||||
if (td->isModeOff())
|
||||
appendUpdateReferenceParams(REF_UPD_TYPE_TIMER, REF_UPD_ACT_CLEAR, i);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "sensordata.h"
|
||||
#include "telem_data.h"
|
||||
#include "timerdata.h"
|
||||
#include "customisation_data.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
@@ -67,34 +68,6 @@ class ScriptData {
|
||||
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(ScriptData)); }
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO ...
|
||||
*/
|
||||
#if 0
|
||||
class CustomScreenOptionData {
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
class CustomScreenZoneData {
|
||||
public:
|
||||
char widgetName[10+1];
|
||||
WidgetOptionData widgetOptions[5];
|
||||
};
|
||||
|
||||
class CustomScreenData {
|
||||
public:
|
||||
CustomScreenData();
|
||||
|
||||
char layoutName[10+1];
|
||||
CustomScreenZoneData zones[];
|
||||
CustomScreenOptionData options[];
|
||||
};
|
||||
#else
|
||||
typedef char CustomScreenData[610+1];
|
||||
typedef char TopbarData[216+1];
|
||||
#endif
|
||||
|
||||
enum TrainerMode {
|
||||
TRAINER_MODE_MASTER_TRAINER_JACK,
|
||||
TRAINER_MODE_SLAVE,
|
||||
@@ -176,7 +149,7 @@ class ModelData {
|
||||
|
||||
unsigned int trainerMode; // TrainerMode
|
||||
|
||||
ModuleData moduleData[CPN_MAX_MODULES+1/*trainer*/];
|
||||
ModuleData moduleData[CPN_MAX_MODULES + 1/*trainer*/];
|
||||
|
||||
ScriptData scriptData[CPN_MAX_SCRIPTS];
|
||||
|
||||
@@ -184,9 +157,9 @@ class ModelData {
|
||||
|
||||
unsigned int toplcdTimer;
|
||||
|
||||
CustomScreenData customScreenData[5];
|
||||
|
||||
TopbarData topbarData;
|
||||
RadioLayout::CustomScreens customScreens;
|
||||
TopBarPersistentData topBarData;
|
||||
unsigned int view;
|
||||
|
||||
char registrationId[8+1];
|
||||
|
||||
|
||||
@@ -26,7 +26,29 @@
|
||||
|
||||
void ModuleData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
if (protocol == PULSES_PXX_R9M && (IS_TARANIS_XLITE(cstate.fromType) || IS_TARANIS_XLITE(cstate.toType))) {
|
||||
Firmware *fw = getCurrentFirmware();
|
||||
|
||||
cstate.setSubComp(indexToString(cstate.subCompIdx, fw));
|
||||
cstate.setItemType("Protocol", 1);
|
||||
RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE;
|
||||
RadioDataConversionState::LogField oldData(protocol, protocolToString(protocol));
|
||||
|
||||
if (fw->getBoard() == cstate.toType) {
|
||||
if (cstate.subCompIdx < fw->getCapability(NumFirstUsableModule) || cstate.subCompIdx > fw->getCapability(NumModules) - 1) {
|
||||
if ((PulsesProtocol) protocol != PULSES_OFF)
|
||||
evt = RadioDataConversionState::EVT_INV;
|
||||
}
|
||||
else if (!fw->isAvailable((PulsesProtocol) protocol, cstate.subCompIdx)) {
|
||||
evt = RadioDataConversionState::EVT_INV;
|
||||
}
|
||||
}
|
||||
else {
|
||||
evt = RadioDataConversionState::EVT_INV;
|
||||
qDebug() << "Error - current firmware board does not match conversion to board!";
|
||||
}
|
||||
|
||||
if (evt == RadioDataConversionState::EVT_INV) {
|
||||
cstate.setInvalid(oldData);
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ inline int MAX_POTS_SOURCES(Board::Type board, int version)
|
||||
|
||||
inline int MAX_SLIDERS_STORAGE(Board::Type board, int version)
|
||||
{
|
||||
if (version >= 219 && IS_FAMILY_HORUS_OR_T16(board))
|
||||
if (version >= 219 && (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)))
|
||||
return 4;
|
||||
return Boards::getCapability(board, Board::Sliders);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ inline int SWITCHES_CONFIG_SIZE(Board::Type board, int version)
|
||||
|
||||
inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version)
|
||||
{
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
@@ -160,7 +160,7 @@ inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version)
|
||||
#define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board)) ? 60 : 40))
|
||||
#define NUM_PPM_INPUTS(board, version) 16
|
||||
#define ROTENC_COUNT(board, version) ((IS_STM32(board) && version >= 218) ? 0 : 1)
|
||||
#define MAX_AUX_TRIMS(board) (IS_FAMILY_HORUS_OR_T16(board) ? 2 : 0)
|
||||
#define MAX_AUX_TRIMS(board) ((IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) ? 2 : 0)
|
||||
|
||||
inline int switchIndex(int i, Board::Type board, unsigned int version)
|
||||
{
|
||||
@@ -693,10 +693,18 @@ class FlightModeField: public TransformedField {
|
||||
}
|
||||
}
|
||||
if (version >= 218) {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<10>(this, phase.name, "Flight mode name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, phase.name, "Flight mode name"));
|
||||
if (version >= 220) {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new CharField<10>(this, phase.name, "Flight mode name"));
|
||||
else
|
||||
internalField.Append(new CharField<6>(this, phase.name, "Flight mode name"));
|
||||
}
|
||||
else {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<10>(this, phase.name, "Flight mode name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, phase.name, "Flight mode name"));
|
||||
}
|
||||
internalField.Append(new SwitchField<9>(this, phase.swtch, board, version));
|
||||
internalField.Append(new SpareBitsField<7>(this));
|
||||
}
|
||||
@@ -819,12 +827,16 @@ class MixField: public TransformedField {
|
||||
internalField.Append(new UnsignedField<8>(this, mix.delayDown));
|
||||
internalField.Append(new UnsignedField<8>(this, mix.speedUp));
|
||||
internalField.Append(new UnsignedField<8>(this, mix.speedDown));
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<6>(this, mix.name));
|
||||
else if (HAS_LARGE_LCD(board) && version <= 218)
|
||||
internalField.Append(new ZCharField<8>(this, mix.name));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, mix.name));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<6>(this, mix.name));
|
||||
else {
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<6>(this, mix.name));
|
||||
else if (HAS_LARGE_LCD(board) && version <= 218)
|
||||
internalField.Append(new ZCharField<8>(this, mix.name));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, mix.name));
|
||||
}
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
internalField.Append(new UnsignedField<8>(this, _destCh));
|
||||
@@ -955,12 +967,16 @@ class InputField: public TransformedField {
|
||||
internalField.Append(new UnsignedField<9>(this, expo.flightModes));
|
||||
internalField.Append(new SignedField<8>(this, _weight, "Weight"));
|
||||
internalField.Append(new SpareBitsField<1>(this));
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<6>(this, expo.name));
|
||||
else if (HAS_LARGE_LCD(board) && version <= 218)
|
||||
internalField.Append(new ZCharField<8>(this, expo.name));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, expo.name));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<6>(this, expo.name));
|
||||
else {
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<6>(this, expo.name));
|
||||
else if (HAS_LARGE_LCD(board) && version <= 218)
|
||||
internalField.Append(new ZCharField<8>(this, expo.name));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, expo.name));
|
||||
}
|
||||
internalField.Append(new SignedField<8>(this, _offset, "Offset"));
|
||||
internalField.Append(new CurveReferenceField(this, expo.curve, board, version));
|
||||
}
|
||||
@@ -1091,10 +1107,18 @@ class LimitField: public StructField {
|
||||
Append(new BoolField<1>(this, limit.revert));
|
||||
Append(new SpareBitsField<3>(this));
|
||||
Append(new SignedField<8>(this, limit.curve.value));
|
||||
if (HAS_LARGE_LCD(board))
|
||||
Append(new ZCharField<6>(this, limit.name));
|
||||
else
|
||||
Append(new ZCharField<4>(this, limit.name));
|
||||
if (version >= 220) {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
Append(new CharField<6>(this, limit.name));
|
||||
else
|
||||
Append(new CharField<4>(this, limit.name));
|
||||
}
|
||||
else {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
Append(new ZCharField<6>(this, limit.name));
|
||||
else
|
||||
Append(new ZCharField<4>(this, limit.name));
|
||||
}
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
Append(new ConversionField< SignedField<11> >(this, limit.min, exportLimitValue<1000, 1024>, importLimitValue<1000, 1024>));
|
||||
@@ -1137,7 +1161,10 @@ class CurvesField: public TransformedField {
|
||||
internalField.Append(new UnsignedField<1>(this, (unsigned int &)curves[i].type));
|
||||
internalField.Append(new BoolField<1>(this, curves[i].smooth));
|
||||
internalField.Append(new ConversionField< SignedField<6> >(this, curves[i].count, -5));
|
||||
internalField.Append(new ZCharField<3>(this, curves[i].name));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, curves[i].name));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, curves[i].name));
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
internalField.Append(new UnsignedField<3>(this, (unsigned int &)curves[i].type));
|
||||
@@ -1892,7 +1919,10 @@ class SensorField: public TransformedField {
|
||||
{
|
||||
internalField.Append(new UnsignedField<16>(this, _id, "id/persistentValue"));
|
||||
internalField.Append(new UnsignedField<8>(this, _instance, "instance/formula"));
|
||||
internalField.Append(new ZCharField<4>(this, sensor.label));
|
||||
if (version>= 220)
|
||||
internalField.Append(new CharField<4>(this, sensor.label));
|
||||
else
|
||||
internalField.Append(new ZCharField<4>(this, sensor.label));
|
||||
if (version >= 219) {
|
||||
internalField.Append(new UnsignedField<8>(this, _subid, "subid"));
|
||||
internalField.Append(new UnsignedField<2>(this, sensor.type, "type"));
|
||||
@@ -2275,6 +2305,257 @@ class ModuleField: public TransformedField {
|
||||
ProtocolsConversionTable protocolsConversionTable;
|
||||
};
|
||||
|
||||
class TimerField: public TransformedField {
|
||||
public:
|
||||
TimerField(DataField * parent, TimerData & timer, Board::Type board, unsigned int version):
|
||||
TransformedField(parent, internalField),
|
||||
internalField(this, "Timer"),
|
||||
timer(timer),
|
||||
board(board),
|
||||
version(version),
|
||||
switchesConversionTable(SwitchesConversionTable::getInstance(board, version))
|
||||
{
|
||||
if (version >= 220) {
|
||||
internalField.Append(new UnsignedField<22>(this, timer.val));
|
||||
internalField.Append(new SwitchField<10>(this, _swtch, board, version));
|
||||
internalField.Append(new SignedField<22>(this, timer.pvalue));
|
||||
internalField.Append(new UnsignedField<3>(this, _mode));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
|
||||
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.persistent));
|
||||
internalField.Append(new SignedField<2>(this, timer.countdownStart));
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new CharField<8>(this, timer.name, "Timer name"));
|
||||
else
|
||||
internalField.Append(new CharField<3>(this, timer.name, "Timer name"));
|
||||
}
|
||||
else if (version >= 218) {
|
||||
internalField.Append(new SignedField<9>(this, _mode_pre220));
|
||||
internalField.Append(new UnsignedField<23>(this, timer.val));
|
||||
internalField.Append(new SignedField<24>(this, timer.pvalue));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
|
||||
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.persistent));
|
||||
internalField.Append(new SignedField<2>(this, timer.countdownStart));
|
||||
internalField.Append(new SpareBitsField<1>(this)); // direction
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<8>(this, timer.name, "Timer name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, timer.name, "Timer name"));
|
||||
}
|
||||
else {
|
||||
internalField.Append(new SignedField<8>(this, _mode_pre220));
|
||||
internalField.Append(new UnsignedField<24>(this, timer.val));
|
||||
internalField.Append(new SignedField<24>(this, timer.pvalue));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
|
||||
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
|
||||
internalField.Append(new UnsignedField<2>(this, timer.persistent));
|
||||
internalField.Append(new SpareBitsField<3>(this));
|
||||
if (IS_TARANIS(board))
|
||||
internalField.Append(new ZCharField<8>(this, timer.name, "Timer name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, timer.name, "Timer name"));
|
||||
}
|
||||
}
|
||||
|
||||
void beforeExport() override
|
||||
{
|
||||
_swtch = timer.swtch;
|
||||
_mode = timer.mode;
|
||||
}
|
||||
|
||||
void afterImport() override
|
||||
{
|
||||
if (version >= 220) {
|
||||
timer.swtch = _swtch;
|
||||
timer.mode = _mode;
|
||||
}
|
||||
else {
|
||||
if (_mode_pre220 >= 0 and _mode_pre220 <= 4) {
|
||||
timer.swtch = _swtch;
|
||||
timer.mode = _mode_pre220;
|
||||
if (timer.mode >= TimerData::TIMERMODE_START)
|
||||
timer.mode += 1;
|
||||
}
|
||||
else {
|
||||
timer.mode = TimerData::TIMERMODE_ON;
|
||||
if (_mode_pre220 > 0)
|
||||
_mode_pre220 -= 4;
|
||||
switchesConversionTable->importValue(_mode_pre220, _sw1);
|
||||
timer.swtch = RawSwitch(_sw1);
|
||||
}
|
||||
|
||||
timer.countdownStart = (timer.countdownStart * -1) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
StructField internalField;
|
||||
TimerData & timer;
|
||||
Board::Type board;
|
||||
unsigned int version;
|
||||
SwitchesConversionTable * switchesConversionTable;
|
||||
int _mode_pre220 = 0;
|
||||
unsigned int _mode = 0;
|
||||
RawSwitch _swtch = RawSwitch(SWITCH_TYPE_NONE, 0);
|
||||
int _sw1;
|
||||
};
|
||||
|
||||
class ZoneOptionValueUnionField: public UnionField<ZoneOptionValueEnum> {
|
||||
|
||||
class ZOVUnsignedField: public UnionField::UnionMember, public StructField {
|
||||
public:
|
||||
ZOVUnsignedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
StructField(this, "ZOV unsigned")
|
||||
{
|
||||
Append(new UnsignedField<32>(this, option.value.unsignedValue));
|
||||
}
|
||||
|
||||
bool select(const ZoneOptionValueEnum & attr) const override {
|
||||
return attr == ZOV_Unsigned;
|
||||
}
|
||||
|
||||
DataField * getField() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
class ZOVSignedField: public UnionField::UnionMember, public StructField {
|
||||
public:
|
||||
ZOVSignedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
StructField(this, "ZOV signed")
|
||||
{
|
||||
Append(new SignedField<32>(this, option.value.signedValue));
|
||||
}
|
||||
|
||||
bool select(const ZoneOptionValueEnum & attr) const override {
|
||||
return attr == ZOV_Signed;
|
||||
}
|
||||
|
||||
DataField * getField() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
class ZOVBoolField: public UnionField::UnionMember, public StructField {
|
||||
public:
|
||||
ZOVBoolField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
StructField(this, "ZOV bool")
|
||||
{
|
||||
Append(new UnsignedField<32>(this, option.value.boolValue));
|
||||
}
|
||||
|
||||
bool select(const ZoneOptionValueEnum & attr) const override {
|
||||
return attr == ZOV_Bool;
|
||||
}
|
||||
|
||||
DataField * getField() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
class ZOVStringField: public UnionField::UnionMember, public StructField {
|
||||
public:
|
||||
ZOVStringField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
StructField(this, "ZOV string")
|
||||
{
|
||||
Append(new CharField<LEN_ZONE_OPTION_STRING>(this, option.value.stringValue));
|
||||
}
|
||||
|
||||
bool select(const ZoneOptionValueEnum & attr) const override {
|
||||
return attr == ZOV_String;
|
||||
}
|
||||
|
||||
DataField * getField() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
ZoneOptionValueUnionField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
UnionField<ZoneOptionValueEnum>(parent, option.type, "Zone Option Value Union")
|
||||
{
|
||||
Append(new ZOVUnsignedField(parent, option, board, version));
|
||||
Append(new ZOVSignedField(parent, option, board, version));
|
||||
Append(new ZOVBoolField(parent, option, board, version));
|
||||
Append(new ZOVStringField(parent, option, board, version));
|
||||
}
|
||||
};
|
||||
|
||||
class ZoneOptionValueTypedField: public StructField {
|
||||
public:
|
||||
ZoneOptionValueTypedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
|
||||
StructField(this, "Zone Option Value Typed")
|
||||
{
|
||||
Append(new SignedField<32>(this, (int &)option.type, "Zone Option Value Enum"));
|
||||
Append(new ZoneOptionValueUnionField(this, option, board, version));
|
||||
}
|
||||
};
|
||||
|
||||
class WidgetPersistentDataField: public StructField {
|
||||
public:
|
||||
WidgetPersistentDataField(DataField * parent, WidgetPersistentData & persistentData, Board::Type board, unsigned int version):
|
||||
StructField(this, "Widget Persistent")
|
||||
{
|
||||
for (int i = 0; i < MAX_WIDGET_OPTIONS; i++) {
|
||||
Append(new ZoneOptionValueTypedField(this, persistentData.options[i], board, version));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ZonePersistentDataField: public StructField {
|
||||
public:
|
||||
ZonePersistentDataField(DataField * parent, ZonePersistentData & persistentData, Board::Type board, unsigned int version):
|
||||
StructField(this, "Zone Persistent")
|
||||
{
|
||||
Append(new CharField<WIDGET_NAME_LEN>(this, persistentData.widgetName, "Widget name"));
|
||||
Append(new SpareBitsField<16>(this)); // pad to word boundary
|
||||
Append(new WidgetPersistentDataField(this, persistentData.widgetData, board, version));
|
||||
}
|
||||
};
|
||||
|
||||
template <class container>
|
||||
class WidgetsContainerPersistentField: public StructField {
|
||||
public:
|
||||
WidgetsContainerPersistentField(DataField * parent, container & persistentData, int zonecnt, int optioncnt, Board::Type board, unsigned int version):
|
||||
StructField(this, "Widgets Container Persistent")
|
||||
{
|
||||
for (int i = 0; i < zonecnt; i++) {
|
||||
Append(new ZonePersistentDataField(this, persistentData.zones[i], board, version));
|
||||
}
|
||||
|
||||
for (int i = 0; i < optioncnt; i++) {
|
||||
Append(new ZoneOptionValueTypedField(this, persistentData.options[i], board, version));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CustomScreenField: public StructField {
|
||||
public:
|
||||
CustomScreenField(DataField * parent, RadioLayout::CustomScreenData & customScreen, Board::Type board, unsigned int version):
|
||||
StructField(this, "Custom Screen")
|
||||
{
|
||||
Append(new CharField<10>(this, customScreen.layoutId, "Layout id"));
|
||||
//Append(new SpareBitsField<16>(this)); // pad to word boundary not required in this case
|
||||
Append(new WidgetsContainerPersistentField<LayoutPersistentData>(this, customScreen.layoutPersistentData, MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS, board, version));
|
||||
//dump();
|
||||
}
|
||||
};
|
||||
|
||||
class TopBarField: public StructField {
|
||||
public:
|
||||
TopBarField(DataField * parent, TopBarPersistentData & topBar, Board::Type board, unsigned int version):
|
||||
StructField(parent, "Top Bar")
|
||||
{
|
||||
Append(new WidgetsContainerPersistentField<TopBarPersistentData>(this, topBar, IS_FLYSKY_NV14(board) ? 2 : 4, MAX_TOPBAR_OPTIONS, board, version));
|
||||
//dump();
|
||||
}
|
||||
};
|
||||
|
||||
OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsigned int version, unsigned int variant):
|
||||
TransformedField(nullptr, internalField),
|
||||
internalField(this, "ModelData"),
|
||||
@@ -2287,12 +2568,22 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
|
||||
qDebug() << QString("OpenTxModelData::OpenTxModelData(name: %1, board: %2, ver: %3, var: %4)").arg(name).arg(board).arg(version).arg(variant);
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<15>(this, modelData.name, "Model name"));
|
||||
else if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<12>(this, modelData.name, "Model name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<10>(this, modelData.name, "Model name"));
|
||||
if (version >= 220) {
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new CharField<15>(this, modelData.name, "Model name"));
|
||||
else if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new CharField<12>(this, modelData.name, "Model name"));
|
||||
else
|
||||
internalField.Append(new CharField<10>(this, modelData.name, "Model name"));
|
||||
}
|
||||
else {
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new ZCharField<15>(this, modelData.name, "Model name"));
|
||||
else if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<12>(this, modelData.name, "Model name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<10>(this, modelData.name, "Model name"));
|
||||
}
|
||||
|
||||
internalField.Append(new UnsignedField<8>(this, modelData.moduleData[0].modelId));
|
||||
internalField.Append(new UnsignedField<8>(this, modelData.moduleData[1].modelId));
|
||||
@@ -2304,34 +2595,8 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
internalField.Append(new CharField<10>(this, modelData.bitmap, true, "Model bitmap"));
|
||||
}
|
||||
|
||||
for (int i=0; i<MAX_TIMERS(board, version); i++) {
|
||||
if (version >= 218) {
|
||||
internalField.Append(new SwitchField<9>(this, modelData.timers[i].mode, board, version, true));
|
||||
internalField.Append(new UnsignedField<23>(this, modelData.timers[i].val));
|
||||
internalField.Append(new SignedField<24>(this, modelData.timers[i].pvalue));
|
||||
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].countdownBeep));
|
||||
internalField.Append(new BoolField<1>(this, modelData.timers[i].minuteBeep));
|
||||
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].persistent));
|
||||
internalField.Append(new SignedField<2>(this, modelData.timers[i].countdownStart));
|
||||
internalField.Append(new UnsignedField<1>(this, modelData.timers[i].direction));
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<8>(this, modelData.timers[i].name, "Timer name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, modelData.timers[i].name, "Timer name"));
|
||||
}
|
||||
else {
|
||||
internalField.Append(new SwitchField<8>(this, modelData.timers[i].mode, board, version, true));
|
||||
internalField.Append(new UnsignedField<24>(this, modelData.timers[i].val));
|
||||
internalField.Append(new SignedField<24>(this, modelData.timers[i].pvalue));
|
||||
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].countdownBeep));
|
||||
internalField.Append(new BoolField<1>(this, modelData.timers[i].minuteBeep));
|
||||
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].persistent));
|
||||
internalField.Append(new SpareBitsField<3>(this));
|
||||
if (IS_TARANIS(board))
|
||||
internalField.Append(new ZCharField<8>(this, modelData.timers[i].name, "Timer name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, modelData.timers[i].name, "Timer name"));
|
||||
}
|
||||
for (int i = 0; i < MAX_TIMERS(board, version); i++) {
|
||||
internalField.Append(new TimerField(this, modelData.timers[i], board, version));
|
||||
}
|
||||
|
||||
internalField.Append(new UnsignedField<3>(this, modelData.telemetryProtocol));
|
||||
@@ -2385,9 +2650,12 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
else if (!IS_FAMILY_HORUS_OR_T16(board))
|
||||
internalField.Append(new UnsignedField<8>(this, modelData.switchWarningEnable));
|
||||
|
||||
for (int i=0; i<MAX_GVARS(board, version); i++) {
|
||||
for (int i = 0; i < MAX_GVARS(board, version); i++) {
|
||||
if (version >= 218) {
|
||||
internalField.Append(new ZCharField<3>(this, modelData.gvarData[i].name, "GVar name"));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, modelData.gvarData[i].name, "GVar name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, modelData.gvarData[i].name, "GVar name"));
|
||||
internalField.Append(new UnsignedField<12>(this, (unsigned &)modelData.gvarData[i].min));
|
||||
internalField.Append(new UnsignedField<12>(this, (unsigned &)modelData.gvarData[i].max));
|
||||
internalField.Append(new BoolField<1>(this, modelData.gvarData[i].popup));
|
||||
@@ -2457,24 +2725,27 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
}
|
||||
|
||||
if (IS_TARANIS(board) && version < 218) {
|
||||
for (int i=0; i<MAX_CURVES(board, version); i++) {
|
||||
for (int i = 0; i < MAX_CURVES(board, version); i++) {
|
||||
internalField.Append(new ZCharField<6>(this, modelData.curves[i].name, "Curve name"));
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_STM32(board)) {
|
||||
if (version >= 218) {
|
||||
for (int i=0; i<MAX_SCRIPTS(board); i++) {
|
||||
for (int i = 0; i < MAX_SCRIPTS(board); i++) {
|
||||
ScriptData & script = modelData.scriptData[i];
|
||||
internalField.Append(new CharField<6>(this, script.filename, true, "Script filename"));
|
||||
internalField.Append(new ZCharField<6>(this, script.name, "Script name"));
|
||||
for (int j=0; j<6; j++) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<6>(this, script.name, "Script name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<6>(this, script.name, "Script name"));
|
||||
for (int j = 0; j < 6; j++) {
|
||||
internalField.Append(new SignedField<16>(this, script.inputs[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<7; i++) {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
ScriptData & script = modelData.scriptData[i];
|
||||
internalField.Append(new CharField<8>(this, script.filename, true, "Script filename"));
|
||||
internalField.Append(new ZCharField<8>(this, script.name, "Script name"));
|
||||
@@ -2485,21 +2756,29 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<4>(this, modelData.inputNames[i], "Input name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, modelData.inputNames[i], "Input name"));
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (version >= 220) {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new CharField<4>(this, modelData.inputNames[i], "Input name"));
|
||||
else
|
||||
internalField.Append(new CharField<3>(this, modelData.inputNames[i], "Input name"));
|
||||
}
|
||||
else {
|
||||
if (HAS_LARGE_LCD(board))
|
||||
internalField.Append(new ZCharField<4>(this, modelData.inputNames[i], "Input name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, modelData.inputNames[i], "Input name"));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (i < Boards::getCapability(board, Board::Pots) + Boards::getCapability(board, Board::Sliders))
|
||||
internalField.Append(new BoolField<1>(this, modelData.potsWarnEnabled[i]));
|
||||
else
|
||||
internalField.Append(new SpareBitsField<1>(this));
|
||||
}
|
||||
|
||||
for (int i=0; i < MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version); i++) {
|
||||
for (int i = 0; i < MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version); i++) {
|
||||
internalField.Append(new SignedField<8>(this, modelData.potsWarnPosition[i]));
|
||||
}
|
||||
|
||||
@@ -2512,7 +2791,7 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
internalField.Append(new SpareBitsField<8>(this));
|
||||
}
|
||||
|
||||
for (int i=0; i<MAX_TELEMETRY_SENSORS(board, version); ++i) {
|
||||
for (int i = 0; i < MAX_TELEMETRY_SENSORS(board, version); ++i) {
|
||||
internalField.Append(new SensorField(this, modelData, modelData.sensorData[i], board, version));
|
||||
}
|
||||
|
||||
@@ -2521,11 +2800,13 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
}
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
internalField.Append(new CharField<610>(this, modelData.customScreenData[i], false, "Custom screen blob"));
|
||||
if (version >= 220) { // data from earlier versions cannot be converted so fields initialised in afterImport
|
||||
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
|
||||
internalField.Append(new CustomScreenField(this, modelData.customScreens.customScreenData[i], board, version));
|
||||
}
|
||||
internalField.Append(new TopBarField(this, modelData.topBarData, board, version));
|
||||
internalField.Append(new UnsignedField<8>(this, modelData.view));
|
||||
}
|
||||
internalField.Append(new CharField<216>(this, modelData.topbarData, false, "Top bar blob"));
|
||||
internalField.Append(new SpareBitsField<8>(this)); // current view
|
||||
}
|
||||
else if (version >= 219) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -2537,7 +2818,10 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
|
||||
internalField.Append(new SpareBitsField<8>(this)); // current view
|
||||
}
|
||||
|
||||
if (version >= 219) {
|
||||
if (version >= 220) {
|
||||
internalField.Append(new CharField<8>(this, modelData.registrationId, "ACCESS Registration ID"));
|
||||
}
|
||||
else if (version >= 219) {
|
||||
internalField.Append(new ZCharField<8>(this, modelData.registrationId, "ACCESS Registration ID"));
|
||||
}
|
||||
}
|
||||
@@ -2572,6 +2856,12 @@ void OpenTxModelData::afterImport()
|
||||
newSwitchWarningStates |= ((value & 0x03) - 1) << (2*i);
|
||||
}
|
||||
modelData.switchWarningStates = newSwitchWarningStates;
|
||||
|
||||
if (version < 220) { // re-initialise as no conversion possible
|
||||
const char * layoutId = "Layout2P1"; // currently all using same default though might change for NV14
|
||||
RadioLayout::init(layoutId, modelData.customScreens);
|
||||
memset(&modelData.topBarData, 0, sizeof(TopBarPersistentData));
|
||||
}
|
||||
}
|
||||
|
||||
if (version <= 218 && IS_HORUS_X10(board) && modelData.thrTraceSrc > 3) {
|
||||
@@ -2610,7 +2900,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
|
||||
|
||||
internalField.Append(new UnsignedField<16>(this, chkSum));
|
||||
|
||||
if (!IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
if (!IS_FAMILY_HORUS_OR_T16(board) || (IS_FLYSKY_NV14(board))) {
|
||||
internalField.Append(new UnsignedField<8>(this, generalData.currModelIndex));
|
||||
internalField.Append(new UnsignedField<8>(this, generalData.contrast));
|
||||
}
|
||||
@@ -2676,11 +2966,11 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
|
||||
internalField.Append(new SignedField<8>(this, generalData.PPM_Multiplier));
|
||||
internalField.Append(new SignedField<8>(this, generalData.hapticLength));
|
||||
|
||||
if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board))) {
|
||||
if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) {
|
||||
internalField.Append(new UnsignedField<8>(this, generalData.reNavigation));
|
||||
}
|
||||
|
||||
if (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
if ((!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) {
|
||||
internalField.Append(new UnsignedField<8>(this, generalData.stickReverse));
|
||||
}
|
||||
|
||||
@@ -2832,23 +3122,35 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
|
||||
internalField.Append(new SpareBitsField<16>(this)); // switchUnlockStates
|
||||
|
||||
if (version == 217) {
|
||||
for (int i=0; i<MAX_CUSTOM_FUNCTIONS(board, version); i++) {
|
||||
for (int i = 0; i < MAX_CUSTOM_FUNCTIONS(board, version); i++) {
|
||||
internalField.Append(new ArmCustomFunctionField(this, generalData.customFn[i], board, version, variant));
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
for (int i=0; i<MAX_SWITCHES(board, version); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
for (int i = 0; i < MAX_SWITCHES(board, version); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
}
|
||||
for (int i=0; i<CPN_MAX_STICKS; ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
for (int i = 0; i < CPN_MAX_STICKS; ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
}
|
||||
for (int i=0; i<MAX_POTS_STORAGE(board, version); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
for (int i = 0; i < MAX_POTS_STORAGE(board, version); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
}
|
||||
for (int i=0; i<MAX_SLIDERS_STORAGE(board, version); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
for (int i = 0; i < MAX_SLIDERS_STORAGE(board, version); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
}
|
||||
if (version <= 218 && IS_HORUS_X10(board)) {
|
||||
internalField.Append(new SpareBitsField<48>(this)); // DUMMY_ANAS
|
||||
@@ -2856,44 +3158,67 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
|
||||
internalField.Append(new CharField<17>(this, generalData.currModelFilename, true, "Current model filename"));
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
for (int i=0; i<SWITCHES_CONFIG_SIZE(board, version) / 2; i++) {
|
||||
for (int i = 0; i < SWITCHES_CONFIG_SIZE(board, version) / 2; i++) {
|
||||
if (i < MAX_SWITCHES(board, version))
|
||||
internalField.Append(new UnsignedField<2>(this, generalData.switchConfig[i]));
|
||||
else
|
||||
internalField.Append(new SpareBitsField<2>(this));
|
||||
}
|
||||
for (int i=0; i<MAX_SWITCHES(board, version); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
for (int i = 0; i < MAX_SWITCHES(board, version); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
|
||||
}
|
||||
for (int i=0; i<CPN_MAX_STICKS; ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
for (int i = 0; i < CPN_MAX_STICKS; ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
|
||||
}
|
||||
for (int i=0; i<Boards::getCapability(board, Board::Pots); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
for (int i = 0; i < Boards::getCapability(board, Board::Pots); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
|
||||
}
|
||||
for (int i=0; i<Boards::getCapability(board, Board::Sliders); ++i) {
|
||||
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
for (int i = 0; i < Boards::getCapability(board, Board::Sliders); ++i) {
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
internalField.Append(new SpareBitsField<1>(this));
|
||||
internalField.Append(new UnsignedField<7>(this, generalData.backlightOffBright));
|
||||
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
}
|
||||
else if (IS_TARANIS_X9E(board) || (version >= 219 && (IS_TARANIS_X7(board) || IS_TARANIS_X9D(board) || IS_TARANIS_XLITE(board)))) {
|
||||
internalField.Append(new SpareBitsField<8>(this));
|
||||
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
if (version >= 220)
|
||||
internalField.Append(new CharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
else
|
||||
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
|
||||
}
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
internalField.Append(new CharField<8>(this, generalData.themeName, true, "Theme name"));
|
||||
for (int i=0; i<5; i++) {
|
||||
internalField.Append(new CharField<8>(this, (char *)generalData.themeOptionValue[i], true, "Theme blob"));
|
||||
if (version >= 220) { // data from earlier versions cannot be converted so fields initialised in afterImport
|
||||
internalField.Append(new CharField<8>(this, generalData.themeData.themeName, true, "Theme name"));
|
||||
for (int i = 0; i < MAX_THEME_OPTIONS; i++) {
|
||||
internalField.Append(new ZoneOptionValueTypedField(this, generalData.themeData.themePersistentData.options[i], board, version));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 219) {
|
||||
if (version >= 220) {
|
||||
internalField.Append(new CharField<8>(this, generalData.registrationId, "ACCESS Registration ID"));
|
||||
}
|
||||
else if (version >= 219) {
|
||||
internalField.Append(new ZCharField<8>(this, generalData.registrationId, "ACCESS Registration ID"));
|
||||
}
|
||||
|
||||
@@ -2901,6 +3226,11 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
|
||||
internalField.Append(new SignedField<8>(this, generalData.gyroMax, "Gyro full scale"));
|
||||
internalField.Append(new SignedField<8>(this, generalData.gyroOffset, "Gyro Offset"));
|
||||
}
|
||||
|
||||
if (version >= 220) {
|
||||
internalField.Append(new SignedField<2>(this, generalData.uartSampleMode, "Uart Sample Mode"));
|
||||
//internalField.Append(new SpareBitsField<6>(this)); // may need padding to end of 8 byte boundary
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTxGeneralData::beforeExport()
|
||||
@@ -2908,7 +3238,7 @@ void OpenTxGeneralData::beforeExport()
|
||||
uint16_t sum = 0;
|
||||
|
||||
int count = 0;
|
||||
for (int i=0; i<inputsCount; i++) {
|
||||
for (int i = 0; i < inputsCount; i++) {
|
||||
sum += generalData.calibMid[i];
|
||||
if (++count == 12) break;
|
||||
sum += generalData.calibSpanNeg[i];
|
||||
@@ -2922,4 +3252,11 @@ void OpenTxGeneralData::beforeExport()
|
||||
|
||||
void OpenTxGeneralData::afterImport()
|
||||
{
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
if (version < 220) { // re-initialise as no conversion possible
|
||||
const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX";
|
||||
RadioTheme::init(themeName, generalData.themeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ const char * OpenTxEepromInterface::getName()
|
||||
return "OpenTX for FrSky X10";
|
||||
case BOARD_X10_EXPRESS:
|
||||
return "OpenTX for FrSky X10 Express";
|
||||
case BOARD_FLYSKY_NV14:
|
||||
return "OpenTX for FlySky NV14";
|
||||
default:
|
||||
return "OpenTX for an unknown board";
|
||||
}
|
||||
@@ -280,7 +282,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
|
||||
|
||||
uint8_t OpenTxEepromInterface::getLastDataVersion(Board::Type board)
|
||||
{
|
||||
return 219;
|
||||
return 220;
|
||||
}
|
||||
|
||||
void OpenTxEepromInterface::showErrors(const QString & title, const QStringList & errors)
|
||||
@@ -627,7 +629,9 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
case HasSDLogs:
|
||||
return true;
|
||||
case LcdWidth:
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
if (IS_FLYSKY_NV14(board))
|
||||
return 320;
|
||||
else if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
return 480;
|
||||
else if (IS_TARANIS_SMALL(board))
|
||||
return 128;
|
||||
@@ -636,7 +640,9 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
else
|
||||
return 128;
|
||||
case LcdHeight:
|
||||
if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
if (IS_FLYSKY_NV14(board))
|
||||
return 480;
|
||||
else if (IS_FAMILY_HORUS_OR_T16(board))
|
||||
return 272;
|
||||
else
|
||||
return 64;
|
||||
@@ -726,16 +732,17 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
case HasAuxSerialMode:
|
||||
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board)) ? true : false;
|
||||
case HasAux2SerialMode:
|
||||
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board)) ? true : false;
|
||||
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board) && !IS_FLYSKY_NV14(board)) ? true : false;
|
||||
case HasBluetooth:
|
||||
return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board)) ? true : false;
|
||||
return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board) || IS_FLYSKY_NV14(board)) ? true : false;
|
||||
case HasAntennaChoice:
|
||||
return ((IS_FAMILY_HORUS_OR_T16(board) && board != Board::BOARD_X10_EXPRESS) || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))) ? true : false;
|
||||
case HasADCJitterFilter:
|
||||
return IS_HORUS_OR_TARANIS(board) ? true : false;
|
||||
case HasTelemetryBaudrate:
|
||||
return IS_HORUS_OR_TARANIS(board) ? true : false;
|
||||
|
||||
case TopBarZones:
|
||||
return getCapability(LcdWidth) > getCapability(LcdHeight) ? 4 : 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -764,14 +771,16 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
|
||||
return true;
|
||||
case PULSES_PXX_XJT_X16:
|
||||
case PULSES_PXX_XJT_LR12:
|
||||
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board);
|
||||
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
|
||||
case PULSES_PXX_XJT_D8:
|
||||
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board);
|
||||
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
|
||||
case PULSES_ACCESS_ISRM:
|
||||
case PULSES_ACCST_ISRM_D16:
|
||||
return IS_ACCESS_RADIO(board, id);
|
||||
case PULSES_MULTIMODULE:
|
||||
return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board) || IS_JUMPER_T18(board) || IS_RADIOMASTER_TX12(board) || IS_JUMPER_TLITE(board);
|
||||
case PULSES_AFHDS3:
|
||||
return IS_FLYSKY_NV14(board);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -927,6 +936,24 @@ EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version)
|
||||
return OLD_VERSION;
|
||||
|
||||
case 219:
|
||||
// 60 (Horus / X9) / 40 (others) telemetry sensors instead of 32
|
||||
// ALL: ReceiverData array added
|
||||
// ALL: registrationId added
|
||||
// ALL: failsafeChannels moved from ModuleData to ModelData
|
||||
// ALL: ModuleData / TrainerModuleData modified
|
||||
// PCBX9 : 6 chars for expos / mixes names instead of 8
|
||||
// on X7: 2 additional switches
|
||||
// on X9D / X9D+: 1 additional switch
|
||||
// on xlite : 2 more storage switches
|
||||
// on X10: 2 additional pots => 12 multipos switches
|
||||
return OLD_VERSION;
|
||||
|
||||
case 220:
|
||||
// Refer radio/src/storage/conversions/conversions_219_220.cpp
|
||||
// ALL: TimerData split mode into separate mode and switch and change modes
|
||||
// ALL: TimerData countdownStart invert value
|
||||
// ALL: convert more fields from zchar to normal string
|
||||
// COLORLCD: change CustomScreenData and TopBarPersistentData
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1112,7 +1139,7 @@ QString OpenTxFirmware::getFirmwareUrl()
|
||||
|
||||
QString OpenTxFirmware::getReleaseNotesUrl()
|
||||
{
|
||||
return getFirmwareBaseUrl() % QStringLiteral("releasenotes.txt");
|
||||
return QString("%1/downloads").arg(OPENTX_HOME_PAGE_URL);
|
||||
}
|
||||
|
||||
QString OpenTxFirmware::getStampUrl()
|
||||
@@ -1368,24 +1395,31 @@ void registerOpenTxFirmwares()
|
||||
registerOpenTxFirmware(firmware);
|
||||
addOpenTxRfOptions(firmware, FLEX);
|
||||
|
||||
/* FlySky NV14 board */
|
||||
firmware = new OpenTxFirmware("opentx-nv14", QCoreApplication::translate("Firmware", "FlySky NV14"), BOARD_FLYSKY_NV14);
|
||||
addOpenTxFrskyOptions(firmware);
|
||||
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
|
||||
addOpenTxRfOptions(firmware, FLEX + AFHDS3);
|
||||
registerOpenTxFirmware(firmware);
|
||||
|
||||
/* 9XR-Pro */
|
||||
firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO);
|
||||
addOpenTxArm9xOptions(firmware, false);
|
||||
registerOpenTxFirmware(firmware);
|
||||
registerOpenTxFirmware(firmware, true);
|
||||
|
||||
/* ar9x board */
|
||||
firmware = new OpenTxFirmware("opentx-ar9x", Firmware::tr("9X with AR9X board"), BOARD_AR9X);
|
||||
addOpenTxArm9xOptions(firmware);
|
||||
addOpenTxArm9xOptions(firmware, true);
|
||||
//firmware->addOption("rtc", Firmware::tr("Optional RTC added"));
|
||||
//firmware->addOption("volume", Firmware::tr("i2c volume control added"));
|
||||
registerOpenTxFirmware(firmware);
|
||||
registerOpenTxFirmware(firmware, true);
|
||||
|
||||
/* Sky9x board */
|
||||
firmware = new OpenTxFirmware("opentx-sky9x", Firmware::tr("9X with Sky9x board"), BOARD_SKY9X);
|
||||
addOpenTxArm9xOptions(firmware);
|
||||
registerOpenTxFirmware(firmware);
|
||||
registerOpenTxFirmware(firmware, true);
|
||||
|
||||
Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-x9d+"));
|
||||
Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-tx16s"));
|
||||
Firmware::setCurrentVariant(Firmware::getDefaultVariant());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,15 +50,15 @@ QString RawSwitch::toString(Board::Type board, const GeneralSettings * const gen
|
||||
tr("TrmV Down"), tr("TrmV Up")
|
||||
};
|
||||
|
||||
static const QString rotaryEncoders[] = {
|
||||
tr("REa"), tr("REb")
|
||||
};
|
||||
|
||||
static const QString timerModes[] = {
|
||||
tr("OFF"), tr("ON"),
|
||||
tr("THs"), tr("TH%"), tr("THt")
|
||||
};
|
||||
|
||||
static const QString rotaryEncoders[] = {
|
||||
tr("REa"), tr("REb")
|
||||
};
|
||||
|
||||
static const QStringList directionIndicators = QStringList()
|
||||
<< CPN_STR_SW_INDICATOR_UP
|
||||
<< CPN_STR_SW_INDICATOR_NEUT
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef RAWSWITCH_H
|
||||
#define RAWSWITCH_H
|
||||
#pragma once
|
||||
|
||||
#include "boards.h"
|
||||
#include "constants.h"
|
||||
@@ -102,5 +101,3 @@ class RawSwitch {
|
||||
RawSwitchType type;
|
||||
int index;
|
||||
};
|
||||
|
||||
#endif // RAWSWITCH_H
|
||||
|
||||
@@ -322,10 +322,10 @@ QString SensorData::precToString(const int value)
|
||||
}
|
||||
|
||||
// static
|
||||
QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign)
|
||||
QString SensorData::sourceToString(const ModelData * model, const int index, const bool positivesign)
|
||||
{
|
||||
if (model) {
|
||||
const QString prfx = sign ? index < 0 ? "-" : "+" : "";
|
||||
const QString prfx = index < 0 ? "-" : positivesign ? "+" : "";
|
||||
|
||||
if (abs(index) > 0) {
|
||||
const SensorData &sd = model->sensorData[abs(index) - 1];
|
||||
|
||||
@@ -197,7 +197,7 @@ class SensorData {
|
||||
void formulaChanged();
|
||||
void unitChanged();
|
||||
|
||||
static QString sourceToString(const ModelData * model, const int index, const bool sign = false);
|
||||
static QString sourceToString(const ModelData * model, const int index, const bool positivesign = false);
|
||||
static bool isSourceAvailable(const ModelData * model, const int index);
|
||||
static QString idToString(const int value);
|
||||
static QString typeToString(const int value);
|
||||
|
||||
@@ -26,23 +26,23 @@ void TimerData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
cstate.setComponent(tr("TMR"), 1);
|
||||
cstate.setSubComp(tr("Timer %1").arg(cstate.subCompIdx + 1));
|
||||
mode.convert(cstate);
|
||||
swtch.convert(cstate);
|
||||
}
|
||||
|
||||
void TimerData::clear()
|
||||
{
|
||||
memset(reinterpret_cast<void *>(this), 0, sizeof(TimerData));
|
||||
mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
|
||||
swtch = RawSwitch(SWITCH_TYPE_NONE);
|
||||
}
|
||||
|
||||
bool TimerData::isEmpty()
|
||||
{
|
||||
return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWNBEEP_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/);
|
||||
return (swtch == RawSwitch(SWITCH_TYPE_NONE, 0) && mode == TIMERMODE_OFF && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWNBEEP_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/);
|
||||
}
|
||||
|
||||
bool TimerData::isModeOff()
|
||||
{
|
||||
return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
|
||||
return swtch == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
|
||||
}
|
||||
|
||||
QString TimerData::nameToString(int index) const
|
||||
@@ -78,12 +78,23 @@ QString TimerData::valToString() const
|
||||
return valToString(val);
|
||||
}
|
||||
|
||||
QString TimerData::modeToString() const
|
||||
{
|
||||
return modeToString(mode);
|
||||
}
|
||||
|
||||
void TimerData::countdownBeepChanged()
|
||||
{
|
||||
if (countdownBeep == COUNTDOWNBEEP_SILENT)
|
||||
countdownStart = 0;
|
||||
}
|
||||
|
||||
void TimerData::modeChanged()
|
||||
{
|
||||
if (mode != TIMERMODE_START)
|
||||
swtch = RawSwitch(SWITCH_TYPE_NONE);
|
||||
}
|
||||
|
||||
// static
|
||||
QString TimerData::countdownBeepToString(const int value)
|
||||
{
|
||||
@@ -145,6 +156,27 @@ QString TimerData::valToString(const int value)
|
||||
return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_ZEROHRS);
|
||||
}
|
||||
|
||||
// static
|
||||
QString TimerData::modeToString(const int value)
|
||||
{
|
||||
switch(value) {
|
||||
case TIMERMODE_OFF:
|
||||
return tr("OFF");
|
||||
case TIMERMODE_ON:
|
||||
return tr("ON");
|
||||
case TIMERMODE_START:
|
||||
return tr("Start");
|
||||
case TIMERMODE_THR:
|
||||
return tr("THs");
|
||||
case TIMERMODE_THR_REL:
|
||||
return tr("TH%");
|
||||
case TIMERMODE_THR_START:
|
||||
return tr("THt");
|
||||
default:
|
||||
return CPN_STR_UNKNOWN_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
AbstractStaticItemModel * TimerData::countdownBeepItemModel()
|
||||
{
|
||||
@@ -165,7 +197,7 @@ AbstractStaticItemModel * TimerData::countdownStartItemModel()
|
||||
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
|
||||
mdl->setName(AIM_TIMER_COUNTDOWNSTART);
|
||||
|
||||
for (int i = COUNTDOWNSTART_LAST; i >= COUNTDOWNSTART_FIRST; i--) {
|
||||
for (int i = COUNTDOWNSTART_FIRST; i <= COUNTDOWNSTART_LAST; i++) {
|
||||
mdl->appendToItemList(countdownStartToString(i), i);
|
||||
}
|
||||
|
||||
@@ -186,3 +218,17 @@ AbstractStaticItemModel * TimerData::persistentItemModel()
|
||||
mdl->loadItemList();
|
||||
return mdl;
|
||||
}
|
||||
|
||||
// static
|
||||
AbstractStaticItemModel * TimerData::modeItemModel()
|
||||
{
|
||||
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
|
||||
mdl->setName(AIM_TIMER_MODE);
|
||||
|
||||
for (int i = 0; i < TIMERMODE_COUNT; i++) {
|
||||
mdl->appendToItemList(modeToString(i), i);
|
||||
}
|
||||
|
||||
mdl->loadItemList();
|
||||
return mdl;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class AbstractStaticItemModel;
|
||||
constexpr char AIM_TIMER_COUNTDOWNBEEP[] {"timerdata.countdownBeep"};
|
||||
constexpr char AIM_TIMER_COUNTDOWNSTART[] {"timerdata.countdownStart"};
|
||||
constexpr char AIM_TIMER_PERSISTENT[] {"timerdata.persistent"};
|
||||
constexpr char AIM_TIMER_MODE[] {"timerdata.mode"};
|
||||
|
||||
constexpr int TIMER_NAME_LEN {8};
|
||||
|
||||
@@ -47,12 +48,12 @@ class TimerData {
|
||||
};
|
||||
|
||||
enum CountDownStart {
|
||||
COUNTDOWNSTART_30 = -2,
|
||||
COUNTDOWNSTART_FIRST = COUNTDOWNSTART_30,
|
||||
COUNTDOWNSTART_20,
|
||||
COUNTDOWNSTART_5 = -2,
|
||||
COUNTDOWNSTART_FIRST = COUNTDOWNSTART_5,
|
||||
COUNTDOWNSTART_10,
|
||||
COUNTDOWNSTART_5,
|
||||
COUNTDOWNSTART_LAST = COUNTDOWNSTART_5
|
||||
COUNTDOWNSTART_20,
|
||||
COUNTDOWNSTART_30,
|
||||
COUNTDOWNSTART_LAST = COUNTDOWNSTART_30
|
||||
};
|
||||
|
||||
enum PersistentType {
|
||||
@@ -62,16 +63,27 @@ class TimerData {
|
||||
PERSISTENT_COUNT
|
||||
};
|
||||
|
||||
enum TimerMode {
|
||||
TIMERMODE_OFF,
|
||||
TIMERMODE_ON,
|
||||
TIMERMODE_START,
|
||||
TIMERMODE_THR,
|
||||
TIMERMODE_THR_REL,
|
||||
TIMERMODE_THR_START,
|
||||
TIMERMODE_COUNT,
|
||||
TIMERMODE_MAX = TIMERMODE_COUNT - 1
|
||||
};
|
||||
|
||||
TimerData() { clear(); }
|
||||
|
||||
RawSwitch mode;
|
||||
RawSwitch swtch;
|
||||
unsigned int mode;
|
||||
char name[TIMER_NAME_LEN + 1];
|
||||
bool minuteBeep;
|
||||
unsigned int countdownBeep;
|
||||
unsigned int val;
|
||||
unsigned int persistent;
|
||||
int countdownStart;
|
||||
unsigned int direction;
|
||||
int pvalue;
|
||||
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
@@ -84,15 +96,19 @@ class TimerData {
|
||||
QString persistentToString(const bool verbose = true) const;
|
||||
QString pvalueToString() const;
|
||||
QString valToString() const;
|
||||
QString modeToString() const;
|
||||
void countdownBeepChanged();
|
||||
void modeChanged();
|
||||
|
||||
static QString countdownBeepToString(const int value);
|
||||
static QString countdownStartToString(const int value);
|
||||
static QString persistentToString(const int value, const bool verbose = true);
|
||||
static QString pvalueToString(const int value);
|
||||
static QString valToString(const int value);
|
||||
static QString modeToString(const int value);
|
||||
static AbstractStaticItemModel * countdownBeepItemModel();
|
||||
static AbstractStaticItemModel * countdownStartItemModel();
|
||||
static AbstractStaticItemModel * persistentItemModel();
|
||||
static AbstractStaticItemModel * modeItemModel();
|
||||
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 48 KiB |
@@ -48,6 +48,7 @@
|
||||
|
||||
#include "dialogs/filesyncdialog.h"
|
||||
#include "profilechooser.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include <QtGui>
|
||||
#include <QFileInfo>
|
||||
@@ -64,8 +65,8 @@
|
||||
#define INTERACTIVE_DOWNLOAD 4
|
||||
#define AUTOMATIC_DOWNLOAD 8
|
||||
|
||||
#define OPENTX_DOWNLOADS_PAGE_URL QStringLiteral("http://www.open-tx.org/downloads")
|
||||
#define DONATE_STR QStringLiteral("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QUZ48K4SEXDP2")
|
||||
#define OPENTX_DOWNLOADS_PAGE_URL QStringLiteral("http://www.edge-tx.org/downloads")
|
||||
#define DONATE_STR QStringLiteral("https://www.edge-tx.org")
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#define COMPANION_STAMP QStringLiteral("companion-macosx.stamp")
|
||||
@@ -129,7 +130,7 @@ MainWindow::MainWindow():
|
||||
else {
|
||||
if (!g.previousVersion().isEmpty())
|
||||
g.warningId(g.warningId() | AppMessages::MSG_UPGRADED);
|
||||
|
||||
|
||||
if (g.promptProfile()) {
|
||||
chooseProfile();
|
||||
}
|
||||
@@ -263,6 +264,9 @@ void MainWindow::checkForUpdates()
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Updates via Companion currently unavailable. Please go to the EdgeTX <a href='%1'>website</a> for installation instructions. Update the application settings to disable this message.").arg("https://github.com/EdgeTX/edgetx.github.io/wiki/EdgeTX-Installation-Guide"));
|
||||
return;
|
||||
|
||||
if (networkManager)
|
||||
disconnect(networkManager, 0, this, 0);
|
||||
else
|
||||
@@ -378,7 +382,7 @@ void MainWindow::checkForCompanionUpdateFinished(QNetworkReply * reply)
|
||||
}
|
||||
}
|
||||
#else
|
||||
QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the <a href='%1'>OpenTX website!</a>").arg(OPENTX_DOWNLOADS_PAGE_URL));
|
||||
QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the <a href='%1'>EdgeTX website!</a>").arg(OPENTX_DOWNLOADS_PAGE_URL));
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
@@ -521,17 +525,26 @@ void MainWindow::checkForFirmwareUpdateFinished(QNetworkReply * reply)
|
||||
int currentVersion = g.fwRev.get(Firmware::getCurrentVariant()->getId());
|
||||
QString currentVersionString = index2version(currentVersion);
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(CPN_STR_APP_NAME);
|
||||
QSpacerItem * horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
QGridLayout * layout = (QGridLayout*)msgBox.layout();
|
||||
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
|
||||
QString msgText;
|
||||
if (currentVersion == 0)
|
||||
msgText = tr("Firmware %1 does not seem to have ever been downloaded.\nVersion %2 is available.\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
|
||||
.arg(Firmware::getCurrentVariant()->getId()).arg(fullVersionString);
|
||||
else if (version > currentVersion)
|
||||
msgText = tr("A new version of %1 firmware is available:\n - current is %2\n - newer is %3\n\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
|
||||
.arg(Firmware::getCurrentVariant()->getId()).arg(currentVersionString).arg(fullVersionString);
|
||||
else if (checkForUpdatesState == INTERACTIVE_DOWNLOAD)
|
||||
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
|
||||
|
||||
if (currentVersion == 0 || version > currentVersion) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(CPN_STR_APP_NAME);
|
||||
QSpacerItem * horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
QGridLayout * layout = (QGridLayout*)msgBox.layout();
|
||||
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
|
||||
|
||||
if (currentVersion == 0) {
|
||||
QString rn = getCurrentFirmware()->getReleaseNotesUrl();
|
||||
QAbstractButton *rnButton = nullptr;
|
||||
msgBox.setText(tr("Firmware %1 does not seem to have ever been downloaded.\nVersion %2 is available.\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
|
||||
.arg(Firmware::getCurrentVariant()->getId()).arg(fullVersionString));
|
||||
msgBox.setText(msgText);
|
||||
QAbstractButton *YesButton = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
|
||||
msgBox.addButton(tr("No"), QMessageBox::NoRole);
|
||||
if (!rn.isEmpty()) {
|
||||
@@ -541,57 +554,17 @@ void MainWindow::checkForFirmwareUpdateFinished(QNetworkReply * reply)
|
||||
msgBox.resize(0, 0);
|
||||
msgBox.exec();
|
||||
if (msgBox.clickedButton() == rnButton) {
|
||||
ReleaseNotesFirmwareDialog * dialog = new ReleaseNotesFirmwareDialog(this, rn);
|
||||
dialog->exec();
|
||||
QDesktopServices::openUrl(QUrl(rn));
|
||||
int ret2 = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to download version %1 now ?").arg(fullVersionString), QMessageBox::Yes | QMessageBox::No);
|
||||
if (ret2 == QMessageBox::Yes)
|
||||
download = true;
|
||||
else
|
||||
ignore = true;
|
||||
}
|
||||
else if (msgBox.clickedButton() == YesButton ) {
|
||||
else if (msgBox.clickedButton() == YesButton )
|
||||
download = true;
|
||||
}
|
||||
else {
|
||||
else
|
||||
ignore = true;
|
||||
}
|
||||
}
|
||||
else if (version > currentVersion) {
|
||||
QString rn = getCurrentFirmware()->getReleaseNotesUrl();
|
||||
QAbstractButton *rnButton = nullptr;
|
||||
msgBox.setText(tr("A new version of %1 firmware is available:\n - current is %2\n - newer is %3\n\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
|
||||
.arg(Firmware::getCurrentVariant()->getId()).arg(currentVersionString).arg(fullVersionString));
|
||||
QAbstractButton *YesButton = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
|
||||
msgBox.addButton(tr("No"), QMessageBox::NoRole);
|
||||
if (!rn.isEmpty()) {
|
||||
rnButton = msgBox.addButton(tr("Release Notes"), QMessageBox::ActionRole);
|
||||
}
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.resize(0,0);
|
||||
msgBox.exec();
|
||||
if( msgBox.clickedButton() == rnButton ) {
|
||||
ReleaseNotesFirmwareDialog * dialog = new ReleaseNotesFirmwareDialog(this, rn);
|
||||
dialog->exec();
|
||||
int ret2 = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to download version %1 now ?").arg(fullVersionString),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (ret2 == QMessageBox::Yes) {
|
||||
download = true;
|
||||
}
|
||||
else {
|
||||
ignore = true;
|
||||
}
|
||||
}
|
||||
else if (msgBox.clickedButton() == YesButton ) {
|
||||
download = true;
|
||||
}
|
||||
else {
|
||||
ignore = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (checkForUpdatesState == INTERACTIVE_DOWNLOAD) {
|
||||
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +710,7 @@ void MainWindow::newFile()
|
||||
|
||||
void MainWindow::openDocURL()
|
||||
{
|
||||
QString link = "http://www.open-tx.org/documents.html";
|
||||
QString link = "http://www.edge-tx.org/";
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
|
||||
@@ -906,7 +879,7 @@ void MainWindow::sdsync()
|
||||
|
||||
void MainWindow::changelog()
|
||||
{
|
||||
QString link = "http://www.open-tx.org";
|
||||
QString link = "http://www.edge-tx.org";
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
|
||||
@@ -1037,15 +1010,15 @@ void MainWindow::logFile()
|
||||
void MainWindow::about()
|
||||
{
|
||||
QString aboutStr = "<center><img src=\":/images/companion-title.png\"></center><br/>";
|
||||
aboutStr.append(tr("OpenTX Home Page: <a href='%1'>%1</a>").arg("http://www.open-tx.org"));
|
||||
aboutStr.append(tr("EdgeTX Home Page: <a href='%1'>%1</a>").arg("http://www.edge-tx.org"));
|
||||
aboutStr.append("<br/><br/>");
|
||||
aboutStr.append(tr("The OpenTX Companion project was originally forked from <a href='%1'>eePe</a>").arg("http://code.google.com/p/eepe"));
|
||||
aboutStr.append(tr("The EdgeTX Companion project was originally forked from <a href='%1'>OpenTX</a>").arg("https://github.com/opentx/opentx"));
|
||||
aboutStr.append("<br/><br/>");
|
||||
aboutStr.append(tr("If you've found this program useful, please support by <a href='%1'>donating</a>").arg(DONATE_STR));
|
||||
aboutStr.append("<br/><br/>");
|
||||
aboutStr.append(QString("Version %1, %2").arg(VERSION).arg(__DATE__));
|
||||
aboutStr.append("<br/><br/>");
|
||||
aboutStr.append(tr("Copyright OpenTX Team") + QString("<br/>© 2011-%1<br/>").arg(QString(__DATE__).right(4)));
|
||||
aboutStr.append(tr("Copyright EdgeTX Team") + QString("<br/>© 2011-%1<br/>").arg(QString(__DATE__).right(4)));
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowIcon(CompanionIcon("information.png"));
|
||||
msgBox.setWindowTitle(tr("About Companion"));
|
||||
@@ -1126,7 +1099,7 @@ void MainWindow::updateMenus()
|
||||
|
||||
updateRecentFileActions();
|
||||
updateProfilesActions();
|
||||
setWindowTitle(tr("OpenTX Companion %1 - Radio: %2 - Profile: %3").arg(VERSION).arg(getCurrentFirmware()->getName()).arg(g.profile[g.id()].name()));
|
||||
setWindowTitle(tr("EdgeTX Companion %1 - Radio: %2 - Profile: %3").arg(VERSION).arg(getCurrentFirmware()->getName()).arg(g.profile[g.id()].name()));
|
||||
}
|
||||
|
||||
MdiChild * MainWindow::createMdiChild()
|
||||
@@ -1208,7 +1181,7 @@ void MainWindow::retranslateUi(bool showMsg)
|
||||
trAct(logsAct, tr("View Log File..."), tr("Open and view log file"));
|
||||
trAct(appPrefsAct, tr("Settings..."), tr("Edit Settings"));
|
||||
trAct(fwPrefsAct, tr("Download..."), tr("Download firmware and voice files"));
|
||||
trAct(checkForUpdatesAct, tr("Check for Updates..."), tr("Check OpenTX and Companion updates"));
|
||||
trAct(checkForUpdatesAct, tr("Check for Updates..."), tr("Check EdgeTX and Companion updates"));
|
||||
trAct(changelogAct, tr("Release notes..."), tr("Show release notes"));
|
||||
trAct(compareAct, tr("Compare Models..."), tr("Compare models"));
|
||||
trAct(editSplashAct, tr("Edit Radio Splash Image..."), tr("Edit the splash image of your Radio"));
|
||||
@@ -1216,13 +1189,13 @@ void MainWindow::retranslateUi(bool showMsg)
|
||||
trAct(writeFlashAct, tr("Write Firmware to Radio"), tr("Write firmware to Radio"));
|
||||
trAct(sdsyncAct, tr("Synchronize SD"), tr("SD card synchronization"));
|
||||
|
||||
trAct(openDocURLAct, tr("Manuals and other Documents"), tr("Open the OpenTX document page in a web browser"));
|
||||
trAct(openDocURLAct, tr("Manuals and other Documents"), tr("Open the EdgeTX document page in a web browser"));
|
||||
trAct(writeEepromAct, tr("Write Models and Settings To Radio"), tr("Write Models and Settings to Radio"));
|
||||
trAct(readEepromAct, tr("Read Models and Settings From Radio"), tr("Read Models and Settings from Radio"));
|
||||
trAct(burnConfigAct, tr("Configure Communications..."), tr("Configure software for communicating with the Radio"));
|
||||
trAct(writeBUToRadioAct, tr("Write Backup to Radio"), tr("Write Backup from file to Radio"));
|
||||
trAct(readBUToFileAct, tr("Backup Radio to File"), tr("Save a complete backup file of all settings and model data in the Radio"));
|
||||
|
||||
|
||||
trAct(createProfileAct, tr("Add Radio Profile"), tr("Create a new Radio Settings Profile"));
|
||||
trAct(copyProfileAct, tr("Copy Current Radio Profile"), tr("Duplicate current Radio Settings Profile"));
|
||||
trAct(deleteProfileAct, tr("Delete Current Radio Profile..."), tr("Delete the current Radio Settings Profile"));
|
||||
@@ -1297,7 +1270,7 @@ void MainWindow::createActions()
|
||||
aboutAct = addAct("information.png", SLOT(about()));
|
||||
openDocURLAct = addAct("changelog.png", SLOT(openDocURL()));
|
||||
changelogAct = addAct("changelog.png", SLOT(changelog()));
|
||||
|
||||
|
||||
// these two get assigned menus in createMenus()
|
||||
recentFilesAct = addAct("recentdocument.png");
|
||||
profilesMenuAct = addAct("profiles.png");
|
||||
@@ -1311,6 +1284,7 @@ void MainWindow::createActions()
|
||||
|
||||
actTabbedWindows->setCheckable(true);
|
||||
compareAct->setEnabled(false);
|
||||
fwPrefsAct->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::createMenus()
|
||||
@@ -1391,7 +1365,7 @@ void MainWindow::createMenus()
|
||||
helpMenu->addSeparator();
|
||||
helpMenu->addAction(changelogAct);
|
||||
helpMenu->addSeparator();
|
||||
|
||||
|
||||
recentFilesMenu = new QMenu(this);
|
||||
recentFilesMenu->setToolTipsVisible(true);
|
||||
for ( int i = 0; i < g.historySize(); ++i) {
|
||||
|
||||
@@ -1375,10 +1375,10 @@ bool MdiChild::saveAs(bool isNew)
|
||||
#ifdef __APPLE__
|
||||
QString filter;
|
||||
#else
|
||||
QString filter(OTX_FILES_FILTER);
|
||||
QString filter(OTX_FILES_FILTER % YML_FILES_FILTER);
|
||||
#endif
|
||||
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName(), filter);
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" + fi.fileName(), filter);
|
||||
if (fileName.isEmpty())
|
||||
return false;
|
||||
g.eepromDir( QFileInfo(fileName).dir().absolutePath() );
|
||||
@@ -1530,6 +1530,22 @@ int MdiChild::askQuestion(const QString & msg, QMessageBox::StandardButtons butt
|
||||
|
||||
void MdiChild::writeEeprom() // write to Tx
|
||||
{
|
||||
if (g.confirmWriteModelsAndSettings()) {
|
||||
QMessageBox msgbox;
|
||||
msgbox.setText(tr("You are about to overwrite ALL models on the Radio."));
|
||||
msgbox.setInformativeText(tr("Do you want to continue?"));
|
||||
msgbox.setIcon(QMessageBox::Icon::Question);
|
||||
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::Abort);
|
||||
msgbox.setDefaultButton(QMessageBox::Abort);
|
||||
|
||||
QCheckBox *cb = new QCheckBox(tr("Don't show this message again"));
|
||||
msgbox.setCheckBox(cb);
|
||||
connect(cb, &QCheckBox::stateChanged, [=](const int &state){ g.confirmWriteModelsAndSettings(!state); });
|
||||
|
||||
if (msgbox.exec() == QMessageBox::Abort)
|
||||
return;
|
||||
}
|
||||
|
||||
Board::Type board = getCurrentBoard();
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
QString radioPath = findMassstoragePath("RADIO", true);
|
||||
|
||||
@@ -25,6 +25,8 @@ set(modeledit_SRCS
|
||||
mixerslistwidget.cpp
|
||||
# node.cpp ## node and edge are built in common lib because also used by simulator
|
||||
# edge.cpp ## commenting them here avoids a "duplicate target" warning eg. from ninja
|
||||
colorcustomscreens.cpp
|
||||
telemetry_customscreens.cpp
|
||||
)
|
||||
|
||||
set(modeledit_HDRS
|
||||
@@ -36,6 +38,8 @@ set(modeledit_HDRS
|
||||
customfunctions.h
|
||||
mixerslistwidget.h
|
||||
# node.h
|
||||
colorcustomscreens.h
|
||||
telemetry_customscreens.h
|
||||
)
|
||||
|
||||
set(modeledit_UIS
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#include "colorcustomscreens.h"
|
||||
#include "customisation_data.h"
|
||||
#include "appdata.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QFile>
|
||||
|
||||
// used to screen out raw data that cannot be displayed in a user meaningful format at this time TODO remove
|
||||
#define SHOW_RAW_INFO (1==0)
|
||||
|
||||
UserInterfacePanel::UserInterfacePanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
|
||||
ModelPanel(parent, model, generalSettings, firmware)
|
||||
{
|
||||
RadioTheme::ThemeData & td = generalSettings.themeData;
|
||||
|
||||
grid = new QGridLayout(this);
|
||||
|
||||
int col = 0;
|
||||
int row = 0;
|
||||
|
||||
addGridBlankRow(grid, row);
|
||||
addGridLabel(grid, tr("Theme"), row, col++);
|
||||
addGridLabel(grid, td.themeName, row++, col++);
|
||||
|
||||
col = 0;
|
||||
addGridBlankRow(grid, row);
|
||||
addGridLabel(grid, tr("Background"), row, col++);
|
||||
|
||||
QLabel * img = new QLabel(this);
|
||||
img->setText(tr("background image"));
|
||||
QString path = QString(g.profile[g.id()].sdPath()).trimmed();
|
||||
if (!path.isEmpty()) {
|
||||
path.append(QString("/THEMES/%1/background.png").arg(td.themeName));
|
||||
QFile f(path);
|
||||
if (f.exists()) {
|
||||
QImage image(path);
|
||||
if (!image.isNull()) {
|
||||
img->setText("");
|
||||
img->setFixedSize(QSize(firmware->getCapability(LcdWidth) / 2, firmware->getCapability(LcdHeight) / 2));
|
||||
img->setPixmap(QPixmap::fromImage(image.scaled(firmware->getCapability(LcdWidth) / 2, firmware->getCapability(LcdHeight) / 2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grid->addWidget(img, row++, col++);
|
||||
|
||||
col = 0;
|
||||
addGridLabel(grid, tr("Top Bar"), row, col++);
|
||||
|
||||
int widgetdetailsrow = row;
|
||||
int widgetdetailscol = col + 1;
|
||||
int firstenabledbtn = -1;
|
||||
|
||||
QGridLayout * tbgrid = new QGridLayout();
|
||||
|
||||
for (int i = 0; i < firmware->getCapability(TopBarZones); i++) {
|
||||
ZonePersistentData & zpd = model.topBarData.zones[i];
|
||||
QPushButton * btn = new QPushButton(zpd.widgetName, this);
|
||||
btn->setProperty("index", i);
|
||||
btn->setFixedSize(QSize(90, 30));
|
||||
widgetbtns.append(btn);
|
||||
|
||||
if (zpd.widgetName[0] != '\0' && SHOW_RAW_INFO) {
|
||||
if (firstenabledbtn < 0)
|
||||
firstenabledbtn = i;
|
||||
|
||||
QFrame * wgt = new QFrame();
|
||||
wgt->setFrameStyle(QFrame::StyledPanel);
|
||||
wgt->setFrameShadow(QFrame::Plain);
|
||||
wgt->setVisible(false);
|
||||
optswidgets.append(wgt);
|
||||
|
||||
QGridLayout * layout = new QGridLayout(wgt); // must be owned by QWidget so visibility can be set
|
||||
layout->addLayout(addOptionsLayout<WidgetPersistentData>(zpd.widgetData, MAX_WIDGET_OPTIONS, zpd.widgetName), 0, 0);
|
||||
optsgrids.append(layout);
|
||||
|
||||
connect(btn, &QPushButton::clicked, this, [&]() {
|
||||
bool ok;
|
||||
int idx = sender()->property("index").toInt(&ok);
|
||||
if (ok) {
|
||||
if (currindex >= 0) {
|
||||
optswidgets.at(currindex)->setVisible(false);
|
||||
}
|
||||
optswidgets.at(idx)->setVisible(true);
|
||||
currindex = idx;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (SHOW_RAW_INFO) {
|
||||
btn->setEnabled(false);
|
||||
}
|
||||
|
||||
div_t posn = div(i, MAX_TOPBAR_ZONES);
|
||||
tbgrid->addWidget(btn, posn.quot, posn.rem);
|
||||
}
|
||||
|
||||
grid->addLayout(tbgrid, row++, col++, Qt::AlignTop);
|
||||
|
||||
addGridBlankRow(grid, row);
|
||||
|
||||
col = 0;
|
||||
|
||||
if (SHOW_RAW_INFO) {
|
||||
addGridLabel(grid, tr("Theme"), row, col++);
|
||||
grid->addLayout(addOptionsLayout<RadioTheme::PersistentData>(td.themePersistentData, MAX_THEME_OPTIONS), row, col);
|
||||
}
|
||||
|
||||
// the grid must be fully built for the rowspan to work as required
|
||||
foreach (QWidget * wgt, optswidgets) {
|
||||
grid->addWidget(wgt, widgetdetailsrow, widgetdetailscol, 3, Qt::AlignTop);
|
||||
}
|
||||
|
||||
addHSpring(grid, grid->columnCount(), 0);
|
||||
addVSpring(grid, 0, grid->rowCount());
|
||||
disableMouseScrolling();
|
||||
|
||||
if (firstenabledbtn >= 0)
|
||||
widgetbtns.at(firstenabledbtn)->click();
|
||||
}
|
||||
|
||||
UserInterfacePanel::~UserInterfacePanel()
|
||||
{
|
||||
foreach (QGridLayout * grid, optsgrids) {
|
||||
delete grid;
|
||||
}
|
||||
foreach (QFrame * wgt, optswidgets) {
|
||||
delete wgt;
|
||||
}
|
||||
foreach (QPushButton * btn, widgetbtns) {
|
||||
delete btn;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CustomScreenPanel
|
||||
*/
|
||||
|
||||
CustomScreenPanel::CustomScreenPanel(QWidget * parent, ModelData & model, int index, GeneralSettings & generalSettings, Firmware * firmware):
|
||||
ModelPanel(parent, model, generalSettings, firmware)
|
||||
{
|
||||
RadioLayout::CustomScreens & scrns = model.customScreens;
|
||||
|
||||
grid = new QGridLayout(this);
|
||||
|
||||
int col = 0;
|
||||
int row = 0;
|
||||
|
||||
addGridBlankRow(grid, row);
|
||||
addGridLabel(grid, tr("Layout:"), row, col++);
|
||||
|
||||
// currently no point continuing if no layout but will change if editing becomes possible
|
||||
if (model.customScreens.customScreenData[index].layoutId[0] == '\0') {
|
||||
addGridLabel(grid, tr("None"), row, col);
|
||||
addHSpring(grid, grid->columnCount(), 0);
|
||||
addVSpring(grid, 0, grid->rowCount());
|
||||
return;
|
||||
}
|
||||
|
||||
QLabel * img = new QLabel(this);
|
||||
|
||||
QString path(QString(":/layouts/mask_%1.png").arg(QString(scrns.customScreenData[index].layoutId).toLower()));
|
||||
QFile f(path);
|
||||
if (f.exists()) {
|
||||
QImage image(path);
|
||||
if (!image.isNull()) {
|
||||
img->setFixedSize(QSize(firmware->getCapability(LcdWidth) / 5, firmware->getCapability(LcdHeight) / 5));
|
||||
img->setPixmap(QPixmap::fromImage(image.scaled(firmware->getCapability(LcdWidth) / 5, firmware->getCapability(LcdHeight) / 5)));
|
||||
}
|
||||
}
|
||||
|
||||
grid->addWidget(img, row++, col++);
|
||||
|
||||
LayoutPersistentData & lpd = scrns.customScreenData[index].layoutPersistentData;
|
||||
|
||||
addGridBlankRow(grid, row);
|
||||
|
||||
col = 0;
|
||||
addGridLabel(grid, tr("Widgets:"), row, col++);
|
||||
|
||||
int widgetdetailsrow = row;
|
||||
int widgetdetailscol = col + 1;
|
||||
int firstenabledbtn = -1;
|
||||
|
||||
QGridLayout * tbgrid = new QGridLayout();
|
||||
|
||||
for (int i = 0; i < MAX_LAYOUT_ZONES; i++) {
|
||||
ZonePersistentData & zpd = lpd.zones[i];
|
||||
QPushButton * btn = new QPushButton(zpd.widgetName, this);
|
||||
btn->setProperty("index", i);
|
||||
btn->setFixedSize(QSize(90, 30));
|
||||
widgetbtns.append(btn);
|
||||
|
||||
if (zpd.widgetName[0] != '\0' && SHOW_RAW_INFO) {
|
||||
if (firstenabledbtn < 0)
|
||||
firstenabledbtn = i;
|
||||
|
||||
QFrame * wgt = new QFrame();
|
||||
wgt->setFrameStyle(QFrame::StyledPanel);
|
||||
wgt->setFrameShadow(QFrame::Plain);
|
||||
wgt->setVisible(false);
|
||||
optswidgets.append(wgt);
|
||||
|
||||
QGridLayout * layout = new QGridLayout(wgt); // must be owned by QWidget so isibility can be set
|
||||
layout->addLayout(addOptionsLayout<WidgetPersistentData>(zpd.widgetData, MAX_WIDGET_OPTIONS, zpd.widgetName), 0, 0, Qt::AlignTop);
|
||||
optsgrids.append(layout);
|
||||
|
||||
connect(btn, &QPushButton::clicked, this, [&]() {
|
||||
bool ok;
|
||||
int idx = sender()->property("index").toInt(&ok);
|
||||
if (ok) {
|
||||
if (currindex >= 0) {
|
||||
optswidgets.at(currindex)->setVisible(false);
|
||||
}
|
||||
optswidgets.at(idx)->setVisible(true);
|
||||
currindex = idx;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (SHOW_RAW_INFO) {
|
||||
btn->setEnabled(false);
|
||||
}
|
||||
|
||||
div_t posn = div(i, MAX_LAYOUT_ZONES / 2);
|
||||
tbgrid->addWidget(btn, posn.quot, posn.rem);
|
||||
}
|
||||
|
||||
grid->addLayout(tbgrid, row++, col++, Qt::AlignTop);
|
||||
|
||||
addGridBlankRow(grid, row);
|
||||
|
||||
// Note: options are defined by each layout but we do not have access to the definitions yet!
|
||||
// We assumed the first five to be the common options and the description of any extras is unknown
|
||||
|
||||
int cnt = SHOW_RAW_INFO ? MAX_LAYOUT_OPTIONS : LAYOUT_OPTION_LAST_DEFAULT + 1;
|
||||
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
QString str;
|
||||
switch(i) {
|
||||
case LAYOUT_OPTION_TOPBAR:
|
||||
str = tr("Top bar");
|
||||
break;
|
||||
case LAYOUT_OPTION_FM:
|
||||
str = tr("Flight mode");
|
||||
break;
|
||||
case LAYOUT_OPTION_SLIDERS:
|
||||
str = tr("Sliders");
|
||||
break;
|
||||
case LAYOUT_OPTION_TRIMS:
|
||||
str = tr("Trims");
|
||||
break;
|
||||
case LAYOUT_OPTION_MIRRORED:
|
||||
str = tr("Mirror");
|
||||
break;
|
||||
default:
|
||||
str = tr("Option #%1").arg(i + 1);
|
||||
}
|
||||
|
||||
col = 0;
|
||||
addGridLabel(grid, str, row, col++);
|
||||
|
||||
QCheckBox *chk = nullptr;
|
||||
QLabel *lbl = nullptr;
|
||||
|
||||
switch (lpd.options[i].type) {
|
||||
case ZOV_Bool:
|
||||
chk = new QCheckBox(this);
|
||||
chk->setChecked(lpd.options[i].value.boolValue);
|
||||
break;
|
||||
case ZOV_Unsigned:
|
||||
lbl = new QLabel(this);
|
||||
lbl->setText(QString("%1").arg(lpd.options[i].value.unsignedValue));
|
||||
break;
|
||||
case ZOV_Signed:
|
||||
lbl = new QLabel(this);
|
||||
lbl->setText(QString("%1").arg(lpd.options[i].value.signedValue));
|
||||
break;
|
||||
case ZOV_String:
|
||||
lbl = new QLabel(this);
|
||||
lbl->setText(lpd.options[i].value.stringValue);
|
||||
break;
|
||||
default:
|
||||
lbl = new QLabel(this);
|
||||
lbl->setText(QString("%1").arg(lpd.options[i].value.unsignedValue));
|
||||
}
|
||||
|
||||
if (chk) {
|
||||
chk->setEnabled(false);
|
||||
grid->addWidget(chk, row++, col++);
|
||||
}
|
||||
else {
|
||||
grid->addWidget(lbl, row++, col++);
|
||||
}
|
||||
}
|
||||
|
||||
// the grid must be fully built so the rowspan works
|
||||
foreach (QWidget * wgt, optswidgets) {
|
||||
grid->addWidget(wgt, widgetdetailsrow, widgetdetailscol, MAX_WIDGET_OPTIONS + 2/*title and column headings*/, Qt::AlignTop);
|
||||
}
|
||||
|
||||
addHSpring(grid, grid->columnCount(), 0);
|
||||
addVSpring(grid, 0, grid->rowCount());
|
||||
disableMouseScrolling();
|
||||
|
||||
if (firstenabledbtn >= 0)
|
||||
widgetbtns.at(firstenabledbtn)->click();
|
||||
}
|
||||
|
||||
CustomScreenPanel::~CustomScreenPanel()
|
||||
{
|
||||
foreach (QGridLayout * grid, optsgrids) {
|
||||
delete grid;
|
||||
}
|
||||
foreach (QFrame * wgt, optswidgets) {
|
||||
delete wgt;
|
||||
}
|
||||
foreach (QPushButton * btn, widgetbtns) {
|
||||
delete btn;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ColorCustomScreensPanel
|
||||
*/
|
||||
|
||||
ColorCustomScreensPanel::ColorCustomScreensPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
CompoundItemModelFactory * sharedItemModels):
|
||||
ModelPanel(parent, model, generalSettings, firmware)
|
||||
{
|
||||
grid = new QGridLayout(this);
|
||||
tabWidget = new QTabWidget(this);
|
||||
|
||||
UserInterfacePanel * tab = new UserInterfacePanel(tabWidget, model, generalSettings, firmware);
|
||||
tab->setProperty("index", 0);
|
||||
tabWidget->addTab(tab, getTabName(0));
|
||||
panels << tab;
|
||||
|
||||
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
|
||||
CustomScreenPanel * tab = new CustomScreenPanel(tabWidget, model, i, generalSettings, firmware);
|
||||
tab->setProperty("index", i + 1);
|
||||
tabWidget->addTab(tab, getTabName(i + 1));
|
||||
panels << tab;
|
||||
}
|
||||
|
||||
grid->addWidget(tabWidget, 0, 0, 1, 1);
|
||||
}
|
||||
|
||||
ColorCustomScreensPanel::~ColorCustomScreensPanel()
|
||||
{
|
||||
foreach (GenericPanel * pnl, panels) {
|
||||
delete pnl;
|
||||
}
|
||||
}
|
||||
|
||||
QString ColorCustomScreensPanel::getTabName(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return tr("User Interface");
|
||||
else
|
||||
return tr("Main View %1").arg(index);
|
||||
}
|
||||
|
||||
void addGridLine(QGridLayout * grid, int row, int col, int colspan)
|
||||
{
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
line->setLineWidth(1);
|
||||
line->setMidLineWidth(0);
|
||||
grid->addWidget(line, row, col, 1, colspan);
|
||||
}
|
||||
|
||||
void addGridLabel(QGridLayout * grid, QString text, int row, int col, int colspan)
|
||||
{
|
||||
QLabel * label = new QLabel();
|
||||
label->setText(text);
|
||||
grid->addWidget(label, row, col, 1, colspan, Qt::AlignTop);
|
||||
}
|
||||
|
||||
void addGridBlankRow(QGridLayout * grid, int & row)
|
||||
{
|
||||
addGridLabel(grid, QString(), row++, 0);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QGridLayout * addOptionsLayout(T & persistentData, int optioncnt, QString title)
|
||||
{
|
||||
QGridLayout * grid = new QGridLayout();
|
||||
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
if (!title.isEmpty()) {
|
||||
QLabel * label = new QLabel();
|
||||
label->setFrameShape(QFrame::Panel);
|
||||
label->setFrameShadow(QFrame::Plain);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setMinimumHeight(30);
|
||||
label->setMaximumHeight(30);
|
||||
label->setText(title);
|
||||
grid->addWidget(label, row++, col, 1, 2, Qt::AlignTop);
|
||||
}
|
||||
|
||||
addGridLabel(grid, "Option", row, col++);
|
||||
//addGridLabel(grid, "Type", row, col++);
|
||||
addGridLabel(grid, "Value", row++, col++);
|
||||
|
||||
for (int i = 0; i < optioncnt; i++) {
|
||||
ZoneOptionValueTyped & zovt = persistentData.options[i];
|
||||
|
||||
int col = 0;
|
||||
addGridLabel(grid, QString("#%1").arg(i + 1), row, col++);
|
||||
//addGridLabel(grid, QString("%1:%2").arg(zovt.type).arg(zoneOptionValueEnumToString(zovt.type)), row, col++);
|
||||
QString val;
|
||||
|
||||
switch (zovt.type) {
|
||||
case ZOV_Bool:
|
||||
val = QString("%1").arg(zovt.value.boolValue);
|
||||
break;
|
||||
case ZOV_Unsigned:
|
||||
val = QString("%1").arg(zovt.value.unsignedValue);
|
||||
break;
|
||||
case ZOV_Signed:
|
||||
val = QString("%1").arg(zovt.value.signedValue);
|
||||
break;
|
||||
case ZOV_String:
|
||||
val = QString("%1").arg(zovt.value.stringValue);
|
||||
break;
|
||||
default:
|
||||
val = QString("%1").arg(zovt.value.unsignedValue);
|
||||
}
|
||||
|
||||
addGridLabel(grid, QString("%1").arg(val), row++, col++);
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modeledit.h"
|
||||
#include "eeprominterface.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class UserInterfacePanel : public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UserInterfacePanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware);
|
||||
virtual ~UserInterfacePanel();
|
||||
|
||||
private:
|
||||
QGridLayout * grid;
|
||||
int currindex = -1;
|
||||
QVector<QPushButton *> widgetbtns;
|
||||
QVector<QFrame *> optswidgets;
|
||||
QVector<QGridLayout *> optsgrids;
|
||||
};
|
||||
|
||||
class CustomScreenPanel : public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CustomScreenPanel(QWidget *parent, ModelData & model, int index, GeneralSettings & generalSettings, Firmware * firmware);
|
||||
virtual ~CustomScreenPanel();
|
||||
|
||||
private:
|
||||
QGridLayout * grid;
|
||||
int currindex = -1;
|
||||
QVector<QPushButton *> widgetbtns;
|
||||
QVector<QFrame *> optswidgets;
|
||||
QVector<QGridLayout *> optsgrids;
|
||||
};
|
||||
|
||||
class ColorCustomScreensPanel : public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
CompoundItemModelFactory * sharedItemModels);
|
||||
virtual ~ColorCustomScreensPanel();
|
||||
|
||||
private:
|
||||
QTabWidget *tabWidget;
|
||||
QGridLayout * grid;
|
||||
QVector<GenericPanel *> panels;
|
||||
|
||||
QString getTabName(int index);
|
||||
};
|
||||
|
||||
void addGridLabel(QGridLayout * grid, QString text, int row, int col, int colspan = 1);
|
||||
void addGridBlankRow(QGridLayout * grid, int & row);
|
||||
void addGridLine(QGridLayout * grid, int row, int col, int colspan);
|
||||
|
||||
template <class T>
|
||||
QGridLayout * addOptionsLayout(T & persistentData, int optioncnt, QString title = "");
|
||||
@@ -149,13 +149,18 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings &
|
||||
QPushButton * edit = new QPushButton(this);
|
||||
edit->setProperty("index", i);
|
||||
|
||||
QString style = QString(
|
||||
"QPushButton {"
|
||||
#ifdef __GNUC__
|
||||
// creates more compact and likely consistent buttons across OS flavors
|
||||
edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name()));
|
||||
"background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;"
|
||||
#else
|
||||
edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name()));
|
||||
"background-color: %1; color: white;"
|
||||
#endif
|
||||
"}"
|
||||
"QToolTip { background: white; color: black; }");
|
||||
|
||||
edit->setStyleSheet(style.arg(colors[i].name()));
|
||||
edit->setText(tr("Curve %1").arg(i + 1));
|
||||
edit->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
edit->setToolTip(tr("Popup menu available"));
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "logicalswitches.h"
|
||||
#include "customfunctions.h"
|
||||
#include "telemetry.h"
|
||||
#include "colorcustomscreens.h"
|
||||
#include "telemetry_customscreens.h"
|
||||
#include "appdata.h"
|
||||
#include "compounditemmodels.h"
|
||||
|
||||
@@ -104,6 +106,15 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw
|
||||
s1.report("Telemetry");
|
||||
}
|
||||
|
||||
if (Boards::getCapability(firmware->getBoard(), Board::HasColorLcd)) {
|
||||
addTab(new ColorCustomScreensPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Custom Screens"));
|
||||
s1.report("ColorLcd Custom Screens");
|
||||
}
|
||||
else if (firmware->getCapability(TelemetryCustomScreens)) {
|
||||
addTab(new TelemetryCustomScreensPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Custom Screens"));
|
||||
s1.report("Telemetry Custom Screens");
|
||||
}
|
||||
|
||||
connect(setupPanel, &SetupPanel::extendedLimitsToggled, channelsPanel, &ChannelsPanel::refreshExtendedLimits);
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ModelEdit::onTabIndexChanged);
|
||||
connect(ui->pushButton, &QPushButton::clicked, this, &ModelEdit::launchSimulation);
|
||||
|
||||
@@ -57,8 +57,12 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G
|
||||
ui->value->setField(timer.val, this);
|
||||
ui->value->setMaximumTime(firmware->getMaxTimerStart());
|
||||
|
||||
ui->mode->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
|
||||
ui->mode->setModel(panelItemModels->getItemModel(AIM_TIMER_MODE));
|
||||
ui->mode->setField(timer.mode, this);
|
||||
connect(ui->mode, SIGNAL(currentDataChanged(int)), this, SLOT(onModeChanged(int)));
|
||||
|
||||
ui->swtch->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
|
||||
ui->swtch->setField(timer.swtch, this);
|
||||
|
||||
ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP));
|
||||
ui->countdownBeep->setField(timer.countdownBeep, this);
|
||||
@@ -102,11 +106,17 @@ void TimerPanel::update()
|
||||
|
||||
ui->name->updateValue();
|
||||
ui->mode->updateValue();
|
||||
ui->swtch->updateValue();
|
||||
ui->value->updateValue();
|
||||
ui->countdownBeep->updateValue();
|
||||
ui->minuteBeep->updateValue();
|
||||
ui->countdownStart->updateValue();
|
||||
|
||||
if (timer.mode == TimerData::TIMERMODE_ON || timer.mode == TimerData::TIMERMODE_START)
|
||||
ui->swtch->setEnabled(true);
|
||||
else
|
||||
ui->swtch->setEnabled(false);
|
||||
|
||||
if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) {
|
||||
ui->countdownStartLabel->setEnabled(false);
|
||||
ui->countdownStart->setEnabled(false);
|
||||
@@ -161,6 +171,12 @@ void TimerPanel::onCountdownBeepChanged(int index)
|
||||
update();
|
||||
}
|
||||
|
||||
void TimerPanel::onModeChanged(int index)
|
||||
{
|
||||
timer.modeChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define FAILSAFE_CHANNEL_HOLD 2000
|
||||
@@ -998,7 +1014,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
|
||||
panelFilteredModels = new FilteredItemModelFactory();
|
||||
|
||||
panelFilteredModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch),
|
||||
RawSwitch::TimersContext),
|
||||
RawSwitch::AllSwitchContexts),
|
||||
FIM_TIMERSWITCH);
|
||||
connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
|
||||
|
||||
@@ -1010,6 +1026,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
|
||||
panelItemModels->registerItemModel(TimerData::countdownBeepItemModel());
|
||||
panelItemModels->registerItemModel(TimerData::countdownStartItemModel());
|
||||
panelItemModels->registerItemModel(TimerData::persistentItemModel());
|
||||
panelItemModels->registerItemModel(TimerData::modeItemModel());
|
||||
|
||||
Board::Type board = firmware->getBoard();
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class TimerPanel : public ModelPanel
|
||||
void onItemModelAboutToBeUpdated();
|
||||
void onItemModelUpdateComplete();
|
||||
void onCountdownBeepChanged(int index);
|
||||
void onModeChanged(int index);
|
||||
|
||||
signals:
|
||||
void nameChanged();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>874</width>
|
||||
<width>1047</width>
|
||||
<height>33</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -58,6 +58,9 @@
|
||||
<item>
|
||||
<widget class="AutoComboBox" name="mode"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AutoComboBox" name="swtch"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="countdownBeepLabel">
|
||||
<property name="text">
|
||||
|
||||
@@ -20,13 +20,10 @@
|
||||
|
||||
#include "telemetry.h"
|
||||
#include "ui_telemetry.h"
|
||||
#include "ui_telemetry_customscreen.h"
|
||||
#include "ui_telemetry_sensor.h"
|
||||
#include "helpers.h"
|
||||
#include "appdata.h"
|
||||
|
||||
#include <TimerEdit>
|
||||
|
||||
constexpr char FIM_RAWSOURCE[] {"Raw Source"};
|
||||
constexpr char FIM_TELEALLSRC[] {"Tele All Source"};
|
||||
constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"};
|
||||
@@ -37,294 +34,6 @@ constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"};
|
||||
constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"};
|
||||
constexpr char FIM_RSSISOURCE[] {"Rssi Source"};
|
||||
|
||||
TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings,
|
||||
Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels):
|
||||
ModelPanel(parent, model, generalSettings, firmware),
|
||||
ui(new Ui::TelemetryCustomScreen),
|
||||
screen(screen),
|
||||
modelsUpdateCnt(0),
|
||||
parentLock(parentLock)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE);
|
||||
connectItemModelEvents(rawSourceFilteredModel);
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
|
||||
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
|
||||
fieldsCB[l][c] = new QComboBox(this);
|
||||
fieldsCB[l][c]->setProperty("index", c + (l << 8));
|
||||
fieldsCB[l][c]->setModel(rawSourceFilteredModel);
|
||||
ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1);
|
||||
connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
|
||||
barsCB[l] = new QComboBox(this);
|
||||
barsCB[l]->setProperty("index", l);
|
||||
barsCB[l]->setModel(rawSourceFilteredModel);
|
||||
connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int)));
|
||||
ui->screenBarsLayout->addWidget(barsCB[l], l, 0, 1, 1);
|
||||
|
||||
minSB[l] = new QDoubleSpinBox(this);
|
||||
minSB[l]->setProperty("index", l);
|
||||
connect(minSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMinChanged(double)));
|
||||
ui->screenBarsLayout->addWidget(minSB[l], l, 1, 1, 1);
|
||||
|
||||
minTime[l] = new TimerEdit(this);
|
||||
minTime[l]->setProperty("index", l);
|
||||
minTime[l]->setProperty("type", "min");
|
||||
connect(minTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
|
||||
ui->screenBarsLayout->addWidget(minTime[l], l, 1, 1, 1);
|
||||
minTime[l]->hide();
|
||||
|
||||
QLabel * label = new QLabel(this);
|
||||
label->setAutoFillBackground(false);
|
||||
label->setStyleSheet(QString::fromUtf8("Background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 128, 255), stop:0.339795 rgba(0, 0, 128, 255), stop:0.339799 rgba(255, 255, 255, 255), stop:0.662444 rgba(255, 255, 255, 255),)\n"""));
|
||||
label->setFrameShape(QFrame::Panel);
|
||||
label->setFrameShadow(QFrame::Raised);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
ui->screenBarsLayout->addWidget(label, l, 2, 1, 1);
|
||||
|
||||
maxSB[l] = new QDoubleSpinBox(this);
|
||||
maxSB[l]->setProperty("index", l);
|
||||
connect(maxSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMaxChanged(double)));
|
||||
ui->screenBarsLayout->addWidget(maxSB[l], l, 3, 1, 1);
|
||||
|
||||
maxTime[l] = new TimerEdit(this);
|
||||
maxTime[l]->setProperty("index", l);
|
||||
maxTime[l]->setProperty("type", "max");
|
||||
connect(maxTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
|
||||
ui->screenBarsLayout->addWidget(maxTime[l], l, 3, 1, 1);
|
||||
maxTime[l]->hide();
|
||||
}
|
||||
|
||||
disableMouseScrolling();
|
||||
|
||||
lock = true;
|
||||
ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE);
|
||||
ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS);
|
||||
ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS);
|
||||
if (IS_TARANIS(firmware->getBoard()))
|
||||
ui->screenType->addItem(tr("Script"), TELEMETRY_SCREEN_SCRIPT);
|
||||
ui->screenType->setField(screen.type, this);
|
||||
lock = false;
|
||||
|
||||
if (IS_TARANIS(firmware->getBoard())) {
|
||||
QSet<QString> scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 6);
|
||||
Helpers::populateFileComboBox(ui->scriptName, scriptsSet, screen.body.script.filename);
|
||||
connect(ui->scriptName, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptNameEdited()));
|
||||
connect(ui->scriptName, SIGNAL(editTextChanged ( const QString)), this, SLOT(scriptNameEdited()));
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
TelemetryCustomScreen::~TelemetryCustomScreen()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::update()
|
||||
{
|
||||
lock = true;
|
||||
|
||||
ui->scriptName->setVisible(screen.type == TELEMETRY_SCREEN_SCRIPT);
|
||||
ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS);
|
||||
ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS);
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
|
||||
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
|
||||
fieldsCB[l][c]->setCurrentIndex(fieldsCB[l][c]->findData(screen.body.lines[l].source[c].toValue()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
|
||||
barsCB[l]->setCurrentIndex(barsCB[l]->findData(screen.body.bars[l].source.toValue()));
|
||||
}
|
||||
|
||||
if (screen.type == TELEMETRY_SCREEN_BARS) {
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreensBars); i++) {
|
||||
updateBar(i);
|
||||
}
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::updateBar(int line)
|
||||
{
|
||||
lock = true;
|
||||
|
||||
RawSource source = screen.body.bars[line].source;
|
||||
|
||||
minTime[line]->setVisible(false);
|
||||
maxTime[line]->setVisible(false);
|
||||
|
||||
if (source.type != SOURCE_TYPE_NONE) {
|
||||
RawSourceRange range = source.getRange(model, generalSettings);
|
||||
float minVal = range.getValue(screen.body.bars[line].barMin);
|
||||
float maxVal = screen.body.bars[line].barMax;
|
||||
maxVal = range.getValue(maxVal);
|
||||
|
||||
if (source.isTimeBased()) {
|
||||
minTime[line]->setVisible(true);
|
||||
minTime[line]->setTimeRange(range.min, range.max);
|
||||
minTime[line]->setSingleStep(range.step);
|
||||
minTime[line]->setPageStep(range.step * 60);
|
||||
minTime[line]->setShowSeconds(range.step != 60);
|
||||
minTime[line]->setTime((int)minVal);
|
||||
|
||||
maxTime[line]->setVisible(true);
|
||||
maxTime[line]->setTimeRange(range.min, range.max);
|
||||
maxTime[line]->setSingleStep(range.step);
|
||||
maxTime[line]->setPageStep(range.step * 60);
|
||||
maxTime[line]->setShowSeconds(range.step != 60);
|
||||
maxTime[line]->setTime((int)maxVal);
|
||||
|
||||
minSB[line]->setVisible(false);
|
||||
maxSB[line]->setVisible(false);
|
||||
}
|
||||
else {
|
||||
minSB[line]->setVisible(true);
|
||||
minSB[line]->setEnabled(true);
|
||||
minSB[line]->setDecimals(range.decimals);
|
||||
minSB[line]->setMinimum(range.min);
|
||||
minSB[line]->setMaximum(range.max);
|
||||
minSB[line]->setSingleStep(range.step);
|
||||
minSB[line]->setSuffix(" " + range.unit);
|
||||
minSB[line]->setValue(minVal);
|
||||
|
||||
maxSB[line]->setVisible(true);
|
||||
maxSB[line]->setEnabled(true);
|
||||
maxSB[line]->setDecimals(range.decimals);
|
||||
maxSB[line]->setMinimum(range.min);
|
||||
maxSB[line]->setMaximum(range.max);
|
||||
maxSB[line]->setSingleStep(range.step);
|
||||
maxSB[line]->setSuffix(" " + range.unit);
|
||||
maxSB[line]->setValue(maxVal);
|
||||
}
|
||||
}
|
||||
else {
|
||||
minSB[line]->setDisabled(true);
|
||||
maxSB[line]->setDisabled(true);
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
memset(reinterpret_cast<void *>(&screen.body), 0, sizeof(screen.body));
|
||||
update();
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::scriptNameEdited()
|
||||
{
|
||||
if (!isLocked()) {
|
||||
lock = true;
|
||||
Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8);
|
||||
emit modified();
|
||||
lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::customFieldChanged(int value)
|
||||
{
|
||||
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
const int index = sender()->property("index").toInt(&ok),
|
||||
i = index / 256,
|
||||
m = index % 256;
|
||||
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
|
||||
if (ok && screen.body.lines[i].source[m].toValue() != src.toValue()) {
|
||||
screen.body.lines[i].source[m] = src;
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::barSourceChanged(int value)
|
||||
{
|
||||
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
const int index = sender()->property("index").toInt(&ok);
|
||||
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
|
||||
if (!ok || screen.body.bars[index].source.toValue() == src.toValue())
|
||||
return;
|
||||
|
||||
screen.body.bars[index].source = src;
|
||||
screen.body.bars[index].barMin = 0;
|
||||
screen.body.bars[index].barMax = 0;
|
||||
updateBar(index);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::barMinChanged(double value)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
screen.body.bars[line].barMin = round(value / minSB[line]->singleStep());
|
||||
// TODO set min (maxSB)
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::barMaxChanged(double value)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep());
|
||||
// TODO set max (minSB)
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::barTimeChanged()
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax);
|
||||
TimerEdit * te = qobject_cast<TimerEdit *>(sender());
|
||||
if (!te)
|
||||
return;
|
||||
|
||||
valRef = round(te->timeInSeconds() / te->singleStep());
|
||||
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel)
|
||||
{
|
||||
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreen::onItemModelAboutToBeUpdated);
|
||||
connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreen::onItemModelUpdateComplete);
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::onItemModelAboutToBeUpdated()
|
||||
{
|
||||
lock = true;
|
||||
modelsUpdateCnt++;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreen::onItemModelUpdateComplete()
|
||||
{
|
||||
modelsUpdateCnt--;
|
||||
if (modelsUpdateCnt < 1) {
|
||||
// leave updating to parent
|
||||
lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
|
||||
TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model,
|
||||
GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock,
|
||||
FilteredItemModelFactory * panelFilteredItemModels) :
|
||||
@@ -755,14 +464,6 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin
|
||||
ui->topbarGB->hide();
|
||||
}
|
||||
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
|
||||
TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock,
|
||||
panelFilteredItemModels);
|
||||
ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1));
|
||||
telemetryCustomScreens[i] = tab;
|
||||
connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified);
|
||||
}
|
||||
|
||||
disableMouseScrolling();
|
||||
|
||||
setup();
|
||||
@@ -798,10 +499,6 @@ void TelemetryPanel::update()
|
||||
sensorPanels[i]->update();
|
||||
}
|
||||
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
|
||||
telemetryCustomScreens[i]->update();
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
@@ -901,8 +598,6 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
model->frsky.usrProto = index;
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++)
|
||||
telemetryCustomScreens[i]->update();
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,49 +30,10 @@ class AutoComboBox;
|
||||
class TimerEdit;
|
||||
|
||||
namespace Ui {
|
||||
class TelemetryCustomScreen;
|
||||
class TelemetrySensor;
|
||||
class Telemetry;
|
||||
}
|
||||
|
||||
class TelemetryCustomScreen: public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels);
|
||||
~TelemetryCustomScreen();
|
||||
void update();
|
||||
|
||||
private slots:
|
||||
void on_screenType_currentIndexChanged(int index);
|
||||
void scriptNameEdited();
|
||||
void customFieldChanged(int index);
|
||||
void barSourceChanged(int index);
|
||||
void barMinChanged(double value);
|
||||
void barMaxChanged(double value);
|
||||
void barTimeChanged();
|
||||
void onItemModelAboutToBeUpdated();
|
||||
void onItemModelUpdateComplete();
|
||||
|
||||
private:
|
||||
void updateBar(int line);
|
||||
Ui::TelemetryCustomScreen * ui;
|
||||
FrSkyScreenData & screen;
|
||||
QComboBox * fieldsCB[4][3];
|
||||
QComboBox * barsCB[4];
|
||||
QDoubleSpinBox * minSB[4];
|
||||
QDoubleSpinBox * maxSB[4];
|
||||
TimerEdit * minTime[4];
|
||||
TimerEdit * maxTime[4];
|
||||
int modelsUpdateCnt;
|
||||
const bool &parentLock;
|
||||
|
||||
inline bool isLocked() { return lock | parentLock; }
|
||||
void connectItemModelEvents(const FilteredItemModel * itemModel);
|
||||
};
|
||||
|
||||
class TelemetrySensorPanel: public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -165,7 +126,6 @@ class TelemetryPanel : public ModelPanel
|
||||
|
||||
private:
|
||||
Ui::Telemetry *ui;
|
||||
TelemetryCustomScreen *telemetryCustomScreens[4];
|
||||
TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS];
|
||||
int sensorCapability;
|
||||
CompoundItemModelFactory *sharedItemModels;
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>861</width>
|
||||
<height>737</height>
|
||||
<height>742</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0,0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
@@ -999,19 +999,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="customScreens">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -1059,7 +1046,6 @@
|
||||
<tabstop>frskyVoltCB</tabstop>
|
||||
<tabstop>frskyCurrentCB</tabstop>
|
||||
<tabstop>bladesCount</tabstop>
|
||||
<tabstop>customScreens</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom Screen Type</string>
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -124,6 +124,19 @@
|
||||
<layout class="QGridLayout" name="screenNumsLayout"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#include "telemetry_customscreens.h"
|
||||
#include "ui_telemetry_customscreen.h"
|
||||
#include "helpers.h"
|
||||
#include "appdata.h"
|
||||
|
||||
#include <TimerEdit>
|
||||
|
||||
constexpr char FIM_RAWSOURCE[] {"Raw Source"};
|
||||
|
||||
CustomScreen::CustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings,
|
||||
Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels):
|
||||
ModelPanel(parent, model, generalSettings, firmware),
|
||||
ui(new Ui::TelemetryCustomScreen),
|
||||
screen(screen),
|
||||
modelsUpdateCnt(0),
|
||||
parentLock(parentLock)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE);
|
||||
connectItemModelEvents(rawSourceFilteredModel);
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
|
||||
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
|
||||
fieldsCB[l][c] = new QComboBox(this);
|
||||
fieldsCB[l][c]->setProperty("index", c + (l << 8));
|
||||
fieldsCB[l][c]->setModel(rawSourceFilteredModel);
|
||||
ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1);
|
||||
connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
|
||||
barsCB[l] = new QComboBox(this);
|
||||
barsCB[l]->setProperty("index", l);
|
||||
barsCB[l]->setModel(rawSourceFilteredModel);
|
||||
connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int)));
|
||||
ui->screenBarsLayout->addWidget(barsCB[l], l+1, 0, 1, 1);
|
||||
|
||||
minSB[l] = new QDoubleSpinBox(this);
|
||||
minSB[l]->setProperty("index", l);
|
||||
connect(minSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMinChanged(double)));
|
||||
ui->screenBarsLayout->addWidget(minSB[l], l+1, 1, 1, 1);
|
||||
|
||||
minTime[l] = new TimerEdit(this);
|
||||
minTime[l]->setProperty("index", l);
|
||||
minTime[l]->setProperty("type", "min");
|
||||
connect(minTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
|
||||
ui->screenBarsLayout->addWidget(minTime[l], l+1, 1, 1, 1);
|
||||
minTime[l]->hide();
|
||||
|
||||
QLabel * label = new QLabel(this);
|
||||
label->setAutoFillBackground(false);
|
||||
label->setStyleSheet(QString::fromUtf8("Background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 128, 255), stop:0.339795 rgba(0, 0, 128, 255), stop:0.339799 rgba(255, 255, 255, 255), stop:0.662444 rgba(255, 255, 255, 255),)\n"""));
|
||||
label->setFrameShape(QFrame::Panel);
|
||||
label->setFrameShadow(QFrame::Raised);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
ui->screenBarsLayout->addWidget(label, l+1, 2, 1, 1);
|
||||
|
||||
maxSB[l] = new QDoubleSpinBox(this);
|
||||
maxSB[l]->setProperty("index", l);
|
||||
connect(maxSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMaxChanged(double)));
|
||||
ui->screenBarsLayout->addWidget(maxSB[l], l+1, 3, 1, 1);
|
||||
|
||||
maxTime[l] = new TimerEdit(this);
|
||||
maxTime[l]->setProperty("index", l);
|
||||
maxTime[l]->setProperty("type", "max");
|
||||
connect(maxTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
|
||||
ui->screenBarsLayout->addWidget(maxTime[l], l+1, 3, 1, 1);
|
||||
maxTime[l]->hide();
|
||||
}
|
||||
|
||||
disableMouseScrolling();
|
||||
|
||||
lock = true;
|
||||
ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE);
|
||||
ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS);
|
||||
ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS);
|
||||
if (IS_TARANIS(firmware->getBoard()))
|
||||
ui->screenType->addItem(tr("Script"), TELEMETRY_SCREEN_SCRIPT);
|
||||
ui->screenType->setField(screen.type, this);
|
||||
lock = false;
|
||||
|
||||
if (IS_TARANIS(firmware->getBoard())) {
|
||||
QSet<QString> scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 6);
|
||||
Helpers::populateFileComboBox(ui->scriptName, scriptsSet, screen.body.script.filename);
|
||||
connect(ui->scriptName, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptNameEdited()));
|
||||
connect(ui->scriptName, SIGNAL(editTextChanged ( const QString)), this, SLOT(scriptNameEdited()));
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
CustomScreen::~CustomScreen()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void CustomScreen::update()
|
||||
{
|
||||
lock = true;
|
||||
|
||||
ui->scriptName->setVisible(screen.type == TELEMETRY_SCREEN_SCRIPT);
|
||||
ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS);
|
||||
ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS);
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
|
||||
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
|
||||
fieldsCB[l][c]->setCurrentIndex(fieldsCB[l][c]->findData(screen.body.lines[l].source[c].toValue()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
|
||||
barsCB[l]->setCurrentIndex(barsCB[l]->findData(screen.body.bars[l].source.toValue()));
|
||||
}
|
||||
|
||||
if (screen.type == TELEMETRY_SCREEN_BARS) {
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreensBars); i++) {
|
||||
updateBar(i);
|
||||
}
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
void CustomScreen::updateBar(int line)
|
||||
{
|
||||
lock = true;
|
||||
|
||||
RawSource source = screen.body.bars[line].source;
|
||||
|
||||
minTime[line]->setVisible(false);
|
||||
maxTime[line]->setVisible(false);
|
||||
|
||||
if (source.type != SOURCE_TYPE_NONE) {
|
||||
RawSourceRange range = source.getRange(model, generalSettings);
|
||||
float minVal = range.getValue(screen.body.bars[line].barMin);
|
||||
float maxVal = screen.body.bars[line].barMax;
|
||||
maxVal = range.getValue(maxVal);
|
||||
|
||||
if (source.isTimeBased()) {
|
||||
minTime[line]->setVisible(true);
|
||||
minTime[line]->setTimeRange(range.min, range.max);
|
||||
minTime[line]->setSingleStep(range.step);
|
||||
minTime[line]->setPageStep(range.step * 60);
|
||||
minTime[line]->setShowSeconds(range.step != 60);
|
||||
minTime[line]->setTime((int)minVal);
|
||||
|
||||
maxTime[line]->setVisible(true);
|
||||
maxTime[line]->setTimeRange(range.min, range.max);
|
||||
maxTime[line]->setSingleStep(range.step);
|
||||
maxTime[line]->setPageStep(range.step * 60);
|
||||
maxTime[line]->setShowSeconds(range.step != 60);
|
||||
maxTime[line]->setTime((int)maxVal);
|
||||
|
||||
minSB[line]->setVisible(false);
|
||||
maxSB[line]->setVisible(false);
|
||||
}
|
||||
else {
|
||||
minSB[line]->setVisible(true);
|
||||
minSB[line]->setEnabled(true);
|
||||
minSB[line]->setDecimals(range.decimals);
|
||||
minSB[line]->setMinimum(range.min);
|
||||
minSB[line]->setMaximum(range.max);
|
||||
minSB[line]->setSingleStep(range.step);
|
||||
minSB[line]->setSuffix(" " + range.unit);
|
||||
minSB[line]->setValue(minVal);
|
||||
|
||||
maxSB[line]->setVisible(true);
|
||||
maxSB[line]->setEnabled(true);
|
||||
maxSB[line]->setDecimals(range.decimals);
|
||||
maxSB[line]->setMinimum(range.min);
|
||||
maxSB[line]->setMaximum(range.max);
|
||||
maxSB[line]->setSingleStep(range.step);
|
||||
maxSB[line]->setSuffix(" " + range.unit);
|
||||
maxSB[line]->setValue(maxVal);
|
||||
}
|
||||
}
|
||||
else {
|
||||
minSB[line]->setDisabled(true);
|
||||
maxSB[line]->setDisabled(true);
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
void CustomScreen::on_screenType_currentIndexChanged(int index)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
memset(reinterpret_cast<void *>(&screen.body), 0, sizeof(screen.body));
|
||||
update();
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::scriptNameEdited()
|
||||
{
|
||||
if (!isLocked()) {
|
||||
lock = true;
|
||||
Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8);
|
||||
emit modified();
|
||||
lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::customFieldChanged(int value)
|
||||
{
|
||||
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
const int index = sender()->property("index").toInt(&ok),
|
||||
i = index / 256,
|
||||
m = index % 256;
|
||||
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
|
||||
if (ok && screen.body.lines[i].source[m].toValue() != src.toValue()) {
|
||||
screen.body.lines[i].source[m] = src;
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::barSourceChanged(int value)
|
||||
{
|
||||
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
const int index = sender()->property("index").toInt(&ok);
|
||||
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
|
||||
if (!ok || screen.body.bars[index].source.toValue() == src.toValue())
|
||||
return;
|
||||
|
||||
screen.body.bars[index].source = src;
|
||||
screen.body.bars[index].barMin = 0;
|
||||
screen.body.bars[index].barMax = 0;
|
||||
updateBar(index);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void CustomScreen::barMinChanged(double value)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
screen.body.bars[line].barMin = round(value / minSB[line]->singleStep());
|
||||
// TODO set min (maxSB)
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::barMaxChanged(double value)
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep());
|
||||
// TODO set max (minSB)
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::barTimeChanged()
|
||||
{
|
||||
if (!isLocked()) {
|
||||
int line = sender()->property("index").toInt();
|
||||
int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax);
|
||||
TimerEdit * te = qobject_cast<TimerEdit *>(sender());
|
||||
if (!te)
|
||||
return;
|
||||
|
||||
valRef = round(te->timeInSeconds() / te->singleStep());
|
||||
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel)
|
||||
{
|
||||
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CustomScreen::onItemModelAboutToBeUpdated);
|
||||
connect(itemModel, &FilteredItemModel::updateComplete, this, &CustomScreen::onItemModelUpdateComplete);
|
||||
}
|
||||
|
||||
void CustomScreen::onItemModelAboutToBeUpdated()
|
||||
{
|
||||
lock = true;
|
||||
modelsUpdateCnt++;
|
||||
}
|
||||
|
||||
void CustomScreen::onItemModelUpdateComplete()
|
||||
{
|
||||
modelsUpdateCnt--;
|
||||
if (modelsUpdateCnt < 1) {
|
||||
// leave updating to parent
|
||||
lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
|
||||
TelemetryCustomScreensPanel::TelemetryCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
CompoundItemModelFactory * sharedItemModels):
|
||||
ModelPanel(parent, model, generalSettings, firmware),
|
||||
sharedItemModels(sharedItemModels),
|
||||
modelsUpdateCnt(0)
|
||||
{
|
||||
panelFilteredItemModels = new FilteredItemModelFactory();
|
||||
int id;
|
||||
|
||||
id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)),
|
||||
FIM_RAWSOURCE);
|
||||
connectItemModelEvents(id);
|
||||
|
||||
grid = new QGridLayout(this);
|
||||
tabWidget = new QTabWidget(this);
|
||||
|
||||
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
|
||||
CustomScreen * tab = new CustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock,
|
||||
panelFilteredItemModels);
|
||||
tab->setProperty("index", i + 1);
|
||||
tabWidget->addTab(tab, getTabName(i + 1));
|
||||
panels << tab;
|
||||
connect(tab, &CustomScreen::modified, this, &TelemetryCustomScreensPanel::onModified);
|
||||
}
|
||||
|
||||
grid->addWidget(tabWidget, 0, 0, 1, 1);
|
||||
|
||||
addHSpring(grid, grid->columnCount(), 0);
|
||||
addVSpring(grid, 0, grid->rowCount());
|
||||
disableMouseScrolling();
|
||||
}
|
||||
|
||||
TelemetryCustomScreensPanel::~TelemetryCustomScreensPanel()
|
||||
{
|
||||
delete panelFilteredItemModels;
|
||||
foreach(GenericPanel * gp, panels) {
|
||||
delete gp;
|
||||
}
|
||||
}
|
||||
|
||||
QString TelemetryCustomScreensPanel::getTabName(int index)
|
||||
{
|
||||
return tr("Telemetry screen %1").arg(index);
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::update()
|
||||
{
|
||||
lock = true;
|
||||
|
||||
foreach(GenericPanel * pnl, panels) {
|
||||
pnl->update();
|
||||
}
|
||||
|
||||
lock = false;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::onModified()
|
||||
{
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::on_dataModifiedSensor()
|
||||
{
|
||||
sharedItemModels->update(AbstractItemModel::IMUE_TeleSensors);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::connectItemModelEvents(const int id)
|
||||
{
|
||||
FilteredItemModel * itemModel = panelFilteredItemModels->getItemModel(id);
|
||||
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreensPanel::onItemModelAboutToBeUpdated);
|
||||
connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreensPanel::onItemModelUpdateComplete);
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::onItemModelAboutToBeUpdated()
|
||||
{
|
||||
lock = true;
|
||||
modelsUpdateCnt++;
|
||||
}
|
||||
|
||||
void TelemetryCustomScreensPanel::onItemModelUpdateComplete()
|
||||
{
|
||||
modelsUpdateCnt--;
|
||||
if (modelsUpdateCnt < 1) {
|
||||
update();
|
||||
lock = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modeledit.h"
|
||||
#include "eeprominterface.h"
|
||||
#include "filtereditemmodels.h"
|
||||
|
||||
class AutoComboBox;
|
||||
class TimerEdit;
|
||||
|
||||
namespace Ui {
|
||||
class TelemetryCustomScreen;
|
||||
}
|
||||
|
||||
class CustomScreen: public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels);
|
||||
~CustomScreen();
|
||||
void update();
|
||||
|
||||
private slots:
|
||||
void on_screenType_currentIndexChanged(int index);
|
||||
void scriptNameEdited();
|
||||
void customFieldChanged(int index);
|
||||
void barSourceChanged(int index);
|
||||
void barMinChanged(double value);
|
||||
void barMaxChanged(double value);
|
||||
void barTimeChanged();
|
||||
void onItemModelAboutToBeUpdated();
|
||||
void onItemModelUpdateComplete();
|
||||
|
||||
private:
|
||||
void updateBar(int line);
|
||||
Ui::TelemetryCustomScreen * ui;
|
||||
FrSkyScreenData & screen;
|
||||
QComboBox * fieldsCB[4][3];
|
||||
QComboBox * barsCB[4];
|
||||
QDoubleSpinBox * minSB[4];
|
||||
QDoubleSpinBox * maxSB[4];
|
||||
TimerEdit * minTime[4];
|
||||
TimerEdit * maxTime[4];
|
||||
int modelsUpdateCnt;
|
||||
const bool &parentLock;
|
||||
|
||||
inline bool isLocked() { return lock | parentLock; }
|
||||
void connectItemModelEvents(const FilteredItemModel * itemModel);
|
||||
};
|
||||
|
||||
class TelemetryCustomScreensPanel : public ModelPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TelemetryCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
|
||||
CompoundItemModelFactory * sharedItemModels);
|
||||
virtual ~TelemetryCustomScreensPanel();
|
||||
virtual void update();
|
||||
|
||||
signals:
|
||||
void updated();
|
||||
|
||||
private slots:
|
||||
void onModified();
|
||||
void on_dataModifiedSensor();
|
||||
void onItemModelAboutToBeUpdated();
|
||||
void onItemModelUpdateComplete();
|
||||
|
||||
private:
|
||||
CompoundItemModelFactory *sharedItemModels;
|
||||
FilteredItemModelFactory *panelFilteredItemModels;
|
||||
int modelsUpdateCnt;
|
||||
|
||||
QTabWidget *tabWidget;
|
||||
QGridLayout *grid;
|
||||
QVector<GenericPanel *> panels;
|
||||
|
||||
QString getTabName(int index);
|
||||
|
||||
void setup();
|
||||
void telBarUpdate();
|
||||
void connectItemModelEvents(const int id);
|
||||
inline bool isLocked() { return lock; }
|
||||
};
|
||||
@@ -334,7 +334,7 @@ QString MultiModelPrinter::printTimers()
|
||||
QString str;
|
||||
MultiColumns columns(modelPrinterMap.size());
|
||||
columns.appendSectionTableStart();
|
||||
columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Switch") << tr("Countdown") << tr("Start") << tr("Min.call") << tr("Persist"));
|
||||
columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Mode") << tr("Switch") << tr("Countdown") << tr("Start") << tr("Min.call") << tr("Persist"));
|
||||
|
||||
for (int i=0; i<firmware->getCapability(Timers); i++) {
|
||||
columns.appendRowStart();
|
||||
@@ -342,11 +342,12 @@ QString MultiModelPrinter::printTimers()
|
||||
COMPARE(model->timers[i].nameToString(i));
|
||||
columns.appendCellEnd(true);
|
||||
COMPARECELLWIDTH(model->timers[i].valToString(), 10);
|
||||
COMPARECELLWIDTH(model->timers[i].mode.toString(), 10);
|
||||
COMPARECELLWIDTH(model->timers[i].modeToString(), 10);
|
||||
COMPARECELLWIDTH(modelPrinter->printFlightModeSwitch(model->timers[i].swtch), 5);
|
||||
COMPARECELLWIDTH(model->timers[i].countdownBeepToString(), 10);
|
||||
COMPARECELLWIDTH(model->timers[i].countdownStartToString(), 10);
|
||||
COMPARECELLWIDTH(DataHelpers::boolToString(model->timers[i].minuteBeep, DataHelpers::BOOL_FMT_YESNO), 10);
|
||||
COMPARECELLWIDTH(model->timers[i].persistentToString(false), 15);
|
||||
COMPARECELLWIDTH(model->timers[i].persistentToString(false), 10);
|
||||
columns.appendRowEnd();
|
||||
}
|
||||
columns.appendTableEnd();
|
||||
|
||||
@@ -112,10 +112,11 @@ void AutoBitsetCheckBox::setInverted(bool invert)
|
||||
|
||||
void AutoBitsetCheckBox::updateValue()
|
||||
{
|
||||
if (m_field)
|
||||
if (m_field) {
|
||||
setLock(true);
|
||||
setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask));
|
||||
setLock(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AutoBitsetCheckBox::onToggled(bool checked)
|
||||
|
||||
@@ -29,7 +29,7 @@ AutoComboBox::AutoComboBox(QWidget * parent):
|
||||
m_rawSource(nullptr),
|
||||
m_rawSwitch(nullptr)
|
||||
{
|
||||
connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
|
||||
connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AutoComboBox::onCurrentIndexChanged);
|
||||
}
|
||||
|
||||
AutoComboBox::~AutoComboBox()
|
||||
|
||||
@@ -26,7 +26,7 @@ AutoDoubleSpinBox::AutoDoubleSpinBox(QWidget * parent):
|
||||
m_field(nullptr),
|
||||
m_offset(0)
|
||||
{
|
||||
connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double)));
|
||||
connect(this, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AutoDoubleSpinBox::onValueChanged);
|
||||
}
|
||||
|
||||
AutoDoubleSpinBox::~AutoDoubleSpinBox()
|
||||
|
||||
@@ -19,6 +19,7 @@ set(simulation_SRCS
|
||||
simulateduiwidgetTX12.cpp
|
||||
simulateduiwidgetT8.cpp
|
||||
simulateduiwidgetTX16S.cpp
|
||||
simulateduiwidgetNV14.cpp
|
||||
simulatorinterface.cpp
|
||||
simulatormainwindow.cpp
|
||||
simulatorstartupdialog.cpp
|
||||
@@ -48,6 +49,7 @@ set(simulation_UIS
|
||||
simulateduiwidgetTX12.ui
|
||||
simulateduiwidgetT8.ui
|
||||
simulateduiwidgetTX16S.ui
|
||||
simulateduiwidgetNV14.ui
|
||||
simulatormainwindow.ui
|
||||
simulatorstartupdialog.ui
|
||||
simulatorwidget.ui
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace Ui {
|
||||
class SimulatedUIWidgetTX16S;
|
||||
class SimulatedUIWidgetTX12;
|
||||
class SimulatedUIWidgetT8;
|
||||
class SimulatedUIWidgetNV14;
|
||||
}
|
||||
|
||||
class SimulatedUIWidget9X: public SimulatedUIWidget
|
||||
@@ -316,5 +317,16 @@ class SimulatedUIWidgetTX16S: public SimulatedUIWidget
|
||||
Ui::SimulatedUIWidgetTX16S * ui;
|
||||
};
|
||||
|
||||
class SimulatedUIWidgetNV14: public SimulatedUIWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SimulatedUIWidgetNV14(SimulatorInterface * simulator, QWidget * parent = nullptr);
|
||||
virtual ~SimulatedUIWidgetNV14();
|
||||
|
||||
private:
|
||||
Ui::SimulatedUIWidgetNV14 * ui;
|
||||
};
|
||||
|
||||
#endif // SIMULATEDUIWIDGET_H
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h
|
||||
|
||||
#include "simulateduiwidget.h"
|
||||
#include "ui_simulateduiwidgetNV14.h"
|
||||
|
||||
SimulatedUIWidgetNV14::SimulatedUIWidgetNV14(SimulatorInterface *simulator, QWidget * parent):
|
||||
SimulatedUIWidget(simulator, parent),
|
||||
ui(new Ui::SimulatedUIWidgetNV14)
|
||||
{
|
||||
RadioUiAction * act;
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
// add actions in order of appearance on the help menu
|
||||
|
||||
// Note: the NV14 has no physical buttons though at some point the trim joystick is repurposed
|
||||
// allow for colorlcd key events and see what works
|
||||
// the mouse click areas do not map to visual buttons on the background images
|
||||
|
||||
act = new RadioUiAction(3, QList<int>() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL);
|
||||
addRadioWidget(ui->rightbuttons->addArea(QRect(10, 1, 80, 35), "NV14/left.png", act));
|
||||
|
||||
m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ROT_DN);
|
||||
addRadioWidget(ui->rightbuttons->addArea(QRect(10, 40, 80, 35), "NV14/left.png", m_mouseMidClickAction));
|
||||
|
||||
act = new RadioUiAction(6, QList<int>() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS);
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 80, 80, 35), "NV14/left.png", act));
|
||||
|
||||
act = new RadioUiAction(5, QList<int>() << Qt::Key_Right, SIMU_STR_HLP_KEY_RGT, SIMU_STR_HLP_ACT_TELE);
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 120, 80, 35), "NV14/left.png", act));
|
||||
|
||||
act = new RadioUiAction(1, QList<int>() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN);
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 160, 80, 35), "NV14/left.png", act));
|
||||
|
||||
act = new RadioUiAction(0, QList<int>() << Qt::Key_PageUp, SIMU_STR_HLP_KEY_PGUP, SIMU_STR_HLP_ACT_PGUP);
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 200, 80, 35), "NV14/left.png", act));
|
||||
|
||||
act = new RadioUiAction(4, QList<int>() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace,
|
||||
SIMU_STR_HLP_KEY_DN % "<br>" % SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_RTN);
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 240, 80, 35), "NV14/left.png", act));
|
||||
|
||||
m_scrollUpAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Minus, SIMU_STR_HLP_KEY_MIN % "|" % SIMU_STR_HLP_MOUSE_UP, SIMU_STR_HLP_ACT_ROT_LFT);
|
||||
m_scrollDnAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal, SIMU_STR_HLP_KEY_PLS % "|" % SIMU_STR_HLP_MOUSE_DN, SIMU_STR_HLP_ACT_ROT_RGT);
|
||||
connectScrollActions();
|
||||
|
||||
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 280, 30, 30), "NV14/left.png", m_screenshotAction));
|
||||
|
||||
m_backlightColors << QColor(47, 123, 227);
|
||||
|
||||
setLcd(ui->lcd);
|
||||
}
|
||||
|
||||
SimulatedUIWidgetNV14::~SimulatedUIWidgetNV14()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SimulatedUIWidgetNV14</class>
|
||||
<widget class="QWidget" name="SimulatedUIWidgetNV14">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>520</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>520</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="2" rowspan="3">
|
||||
<widget class="ButtonsWidget" name="rightbuttons" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:url(:/images/simulator/NV14/right.png)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="LcdWidget" name="lcd" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>480</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>480</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>5</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="3">
|
||||
<widget class="ButtonsWidget" name="leftbuttons" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="mouseTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:url(:/images/simulator/NV14/left.png);</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QWidget" name="top" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>5</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:url(:/images/simulator/NV14/top.png)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QWidget" name="bottom" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>5</pointsize>
|
||||
<kerning>false</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:url(:/images/simulator/NV14/bottom.png)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LcdWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>lcdwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ButtonsWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>buttonswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -86,7 +86,7 @@ void SimulatorStartupDialog::changeEvent(QEvent *e)
|
||||
// FIXME : need a better way to check for this
|
||||
bool SimulatorStartupDialog::usesCategorizedStorage(const QString & name)
|
||||
{
|
||||
return name.contains(QRegExp("(x12|x10|horus|16|18)", Qt::CaseInsensitive));
|
||||
return name.contains(QRegExp("(x12|x10|horus|16|18|nv14)", Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
bool SimulatorStartupDialog::usesCategorizedStorage()
|
||||
|
||||
@@ -109,6 +109,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato
|
||||
case Board::BOARD_RADIOMASTER_TX16S:
|
||||
radioUiWidget = new SimulatedUIWidgetTX16S(simulator, this);
|
||||
break;
|
||||
case Board::BOARD_FLYSKY_NV14:
|
||||
radioUiWidget = new SimulatedUIWidgetNV14(simulator, this);
|
||||
break;
|
||||
default:
|
||||
radioUiWidget = new SimulatedUIWidget9X(simulator, this);
|
||||
break;
|
||||
|
||||
@@ -13,6 +13,7 @@ set(storage_NAMES
|
||||
categorized
|
||||
sdcard
|
||||
otx
|
||||
yaml
|
||||
)
|
||||
|
||||
set(storage_SRCS
|
||||
|
||||
@@ -45,11 +45,11 @@
|
||||
//! CPN_SETTINGS_VERSION is used for settings data version tracking.
|
||||
#define CPN_SETTINGS_VERSION ((VERSION_NUMBER << 8) | CPN_SETTINGS_REVISION)
|
||||
|
||||
#define COMPANY QStringLiteral("OpenTX")
|
||||
#define COMPANY_DOMAIN QStringLiteral("open-tx.org")
|
||||
#define COMPANY QStringLiteral("EdgeTX")
|
||||
#define COMPANY_DOMAIN QStringLiteral("edge-tx.org")
|
||||
#define PRODUCT QStringLiteral("Companion " QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR))
|
||||
#define APP_COMPANION QStringLiteral("OpenTX Companion")
|
||||
#define APP_SIMULATOR QStringLiteral("OpenTX Simulator")
|
||||
#define APP_COMPANION QStringLiteral("EdgeTX Companion")
|
||||
#define APP_SIMULATOR QStringLiteral("EdgeTX Simulator")
|
||||
|
||||
//! Default location for OpenTX-related user documents (settigns, logs, etc)
|
||||
#define CPN_DOCUMENTS_LOCATION QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" % COMPANY
|
||||
@@ -58,7 +58,7 @@
|
||||
#define CPN_SETTINGS_INI_FILE QString(PRODUCT % " " % QCoreApplication::translate("Companion", "settings") % " %1.ini")
|
||||
#define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE)
|
||||
|
||||
#define CPN_URL_DOWNLOAD "https://downloads.open-tx.org/"
|
||||
#define CPN_URL_DOWNLOAD "https://downloads.edge-tx.org/"
|
||||
#define CPN_URL_DOWNLOAD_CUR_VERS CPN_URL_DOWNLOAD QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR) "/"
|
||||
#define CPN_URL_DOWNLOAD_CUR_REL CPN_URL_DOWNLOAD_CUR_VERS "release/"
|
||||
#define CPN_URL_DOWNLOAD_CUR_RC CPN_URL_DOWNLOAD_CUR_VERS "rc/"
|
||||
@@ -573,8 +573,8 @@ class AppData: public CompStoreObj
|
||||
PROPERTY4(bool, jsSupport, "js_support", false)
|
||||
PROPERTY4(bool, showSplash, "show_splash", true)
|
||||
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
|
||||
PROPERTY4(bool, autoCheckApp, "startup_check_companion", true)
|
||||
PROPERTY4(bool, autoCheckFw, "startup_check_fw", true)
|
||||
PROPERTY4(bool, autoCheckApp, "startup_check_companion", false)
|
||||
PROPERTY4(bool, autoCheckFw, "startup_check_fw", false)
|
||||
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
|
||||
|
||||
PROPERTY(bool, enableBackup, false)
|
||||
@@ -593,6 +593,9 @@ class AppData: public CompStoreObj
|
||||
PROPERTY(int, simuLastProfId, -1)
|
||||
PROPERTY(bool, simuSW, true)
|
||||
|
||||
// Message box confirmations
|
||||
PROPERTY(bool, confirmWriteModelsAndSettings, true)
|
||||
|
||||
bool firstUse;
|
||||
QString upgradeFromVersion;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "eepe.h"
|
||||
#include "otx.h"
|
||||
#include "sdcard.h"
|
||||
#include "yaml.h"
|
||||
#include "firmwareinterface.h"
|
||||
#include "eeprominterface.h"
|
||||
#include <QFileInfo>
|
||||
@@ -41,6 +42,8 @@ StorageType getStorageType(const QString & filename)
|
||||
return STORAGE_TYPE_XML;
|
||||
else if (suffix == "OTX")
|
||||
return STORAGE_TYPE_OTX;
|
||||
else if (suffix == "YML")
|
||||
return STORAGE_TYPE_YML;
|
||||
else
|
||||
return STORAGE_TYPE_UNKNOWN;
|
||||
}
|
||||
@@ -61,6 +64,7 @@ void registerStorageFactories()
|
||||
registerStorageFactory(new DefaultStorageFactory<EepeFormat>("eepe"));
|
||||
registerStorageFactory(new DefaultStorageFactory<HexEepromFormat>("hex"));
|
||||
registerStorageFactory(new DefaultStorageFactory<OtxFormat>("otx"));
|
||||
registerStorageFactory(new DefaultStorageFactory<YamlFormat>("yml"));
|
||||
registerStorageFactory(new SdcardStorageFactory());
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ enum StorageType
|
||||
STORAGE_TYPE_EEPM,
|
||||
STORAGE_TYPE_XML,
|
||||
STORAGE_TYPE_SDCARD,
|
||||
STORAGE_TYPE_OTX
|
||||
STORAGE_TYPE_OTX,
|
||||
STORAGE_TYPE_YML
|
||||
};
|
||||
|
||||
StorageType getStorageType(const QString & filename);
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#include "yaml.h"
|
||||
#include "../../../radio/src/storage/yaml/yaml_tree_walker.h" // just to confirm valid compiler path
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
bool YamlFormat::loadFile(QByteArray & filedata, const QString & filename)
|
||||
{
|
||||
QString path = this->filename + "/" + filename;
|
||||
QFile file(path);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
setError(tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
filedata = file.readAll();
|
||||
qDebug() << "File" << path << "read, size:" << filedata.size();
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool YamlFormat::writeFile(const QByteArray & filedata, const QString & filename)
|
||||
{
|
||||
QString path = this->filename + "/" + filename;
|
||||
QFile file(path);
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
setError(tr("Error opening file %1 in write mode:\n%2.").arg(path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
file.write(filedata.data(), filedata.size());
|
||||
file.close();
|
||||
qDebug() << "File" << path << "written, size:" << filedata.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool YamlFormat::load(RadioData & radioData)
|
||||
{
|
||||
QByteArray radioSettingsBuffer;
|
||||
if (!loadFile(radioSettingsBuffer, "RADIO/radio.yml")) {
|
||||
setError(tr("Can't extract RADIO/radio.yml"));
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Warning: format ignored - under development";
|
||||
return false; // force failure until fully developed
|
||||
}
|
||||
|
||||
bool YamlFormat::write(const RadioData & radioData)
|
||||
{
|
||||
/* Need this test in case of a new sdcard
|
||||
Board::Type board = getCurrentBoard();
|
||||
if (!HAS_EEPROM_YAML(board)) {
|
||||
qDebug() << "Board does not support YAML format";
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure directories exist on sd card
|
||||
QDir dir(filename);
|
||||
dir.mkdir("RADIO");
|
||||
dir.mkdir("MODELS");
|
||||
*/
|
||||
|
||||
qDebug() << "Warning: format ignored - under development";
|
||||
return false; // force failure until fully developed
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class YamlFormat : public StorageFormat
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(YamlFormat)
|
||||
|
||||
public:
|
||||
YamlFormat(const QString & filename):
|
||||
StorageFormat(filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual QString name() { return "yml"; }
|
||||
virtual bool load(RadioData & radioData);
|
||||
virtual bool write(const RadioData & radioData);
|
||||
|
||||
protected:
|
||||
bool loadFile(QByteArray & fileData, const QString & fileName);
|
||||
bool writeFile(const QByteArray & fileData, const QString & fileName);
|
||||
};
|
||||
@@ -22,7 +22,7 @@ if(GTEST_INCDIR AND GTEST_SRCDIR AND Qt5Widgets_FOUND)
|
||||
|
||||
use_cxx11() # ensure gnu++11 in CXX_FLAGS with CMake < 3.1
|
||||
|
||||
add_executable(gtests-companion EXCLUDE_FROM_ALL ${TEST_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/location.h)
|
||||
add_executable(gtests-companion EXCLUDE_FROM_ALL ${TEST_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/location.h.in)
|
||||
add_dependencies(gtests-companion gtests-companion-lib)
|
||||
target_link_libraries(gtests-companion gtests-companion-lib simulation firmwares storage common)
|
||||
message(STATUS "Added optional gtests-companion target")
|
||||
|
||||
@@ -19,10 +19,11 @@ TEST(Conversions, ConversionX9DPFrom22)
|
||||
|
||||
EXPECT_STREQ("Tes", settings.switchName[0]);
|
||||
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
|
||||
|
||||
|
||||
const ModelData& model = radioData.models[0];
|
||||
EXPECT_STREQ("Test", model.name);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model.timers[0].mode);
|
||||
EXPECT_EQ(TimerData::TIMERMODE_ON, model.timers[0].mode);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model.timers[0].swtch);
|
||||
EXPECT_EQ(80, model.mixData[0].weight);
|
||||
EXPECT_EQ(900, model.limitData[0].max); // -100
|
||||
EXPECT_EQ(80, model.expoData[0].weight);
|
||||
@@ -64,7 +65,8 @@ TEST(Conversions, ConversionX9DPFrom23)
|
||||
|
||||
const ModelData & model1 = radioData.models[0];
|
||||
EXPECT_STREQ("Test", model1.name);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model1.timers[0].mode);
|
||||
EXPECT_EQ(TimerData::TIMERMODE_ON, model1.timers[0].mode);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model1.timers[0].swtch);
|
||||
EXPECT_EQ(80, model1.mixData[0].weight);
|
||||
EXPECT_EQ(900, model1.limitData[0].max); // -100
|
||||
EXPECT_EQ(80, model1.expoData[0].weight);
|
||||
@@ -82,7 +84,8 @@ TEST(Conversions, ConversionX9DPFrom23)
|
||||
|
||||
const ModelData & model2 = radioData.models[1];
|
||||
EXPECT_STREQ("Test", model2.name);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model2.timers[0].mode);
|
||||
EXPECT_EQ(TimerData::TIMERMODE_ON, model2.timers[0].mode);
|
||||
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model2.timers[0].swtch);
|
||||
EXPECT_EQ(80, model2.mixData[0].weight);
|
||||
EXPECT_EQ(900, model2.limitData[0].max); // -100
|
||||
EXPECT_EQ(80, model2.expoData[0].weight);
|
||||
@@ -114,7 +117,7 @@ TEST(Conversions, ConversionX7From22)
|
||||
|
||||
EXPECT_STREQ("Tes", settings.switchName[0]);
|
||||
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
|
||||
|
||||
|
||||
const ModelData& model = radioData.models[0];
|
||||
EXPECT_STREQ("Test", model.name);
|
||||
EXPECT_EQ(PULSES_PXX_R9M, model.moduleData[1].protocol);
|
||||
@@ -216,7 +219,7 @@ TEST(Conversions, ConversionX10From22)
|
||||
|
||||
const GeneralSettings& settings = radio.generalSettings;
|
||||
const ModelData& model = radio.models[0];
|
||||
#else
|
||||
#else
|
||||
ASSERT_EQ(true, loadFile(byteBuffer, RADIO_TESTS_PATH "/model_22_x10/RADIO/radio.bin"));
|
||||
|
||||
GeneralSettings settings;
|
||||
@@ -248,7 +251,7 @@ TEST(Conversions, ConversionX10From22)
|
||||
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
|
||||
|
||||
EXPECT_STREQ("BT_X10", settings.bluetoothName);
|
||||
EXPECT_STREQ("Default", settings.themeName);
|
||||
EXPECT_STREQ("EdgeTX", settings.themeData.themeName);
|
||||
|
||||
#if !defined(USE_OTX)
|
||||
byteBuffer.clear();
|
||||
@@ -257,7 +260,7 @@ TEST(Conversions, ConversionX10From22)
|
||||
ModelData model;
|
||||
ASSERT_NE(nullptr, loadModelFromByteArray(model, byteBuffer));
|
||||
#endif
|
||||
|
||||
|
||||
EXPECT_STREQ("Test", model.name);
|
||||
EXPECT_EQ(0, model.noGlobalFunctions);
|
||||
EXPECT_EQ(0, model.beepANACenter);
|
||||
@@ -325,7 +328,7 @@ TEST(Conversions, ConversionX12SFrom22)
|
||||
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
|
||||
|
||||
EXPECT_STREQ("BT", settings.bluetoothName);
|
||||
EXPECT_STREQ("Default", settings.themeName);
|
||||
EXPECT_STREQ("EdgeTX", settings.themeData.themeName);
|
||||
|
||||
#if !defined(USE_OTX)
|
||||
byteBuffer.clear();
|
||||
|
||||
@@ -28,19 +28,19 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#define CPN_STR_MSG_WELCOME QCoreApplication::translate("Companion", \
|
||||
"<p><b>Welcome to OpenTX %1.</b></p>" \
|
||||
"<p><b>Welcome to EdgeTX %1.</b></p>" \
|
||||
"<p>As the first step, please configure the initial Radio Profile by selecting your Radio Type, Menu Language, and Build Options.</p>" \
|
||||
"<p>You may also want to take this time to review the other available options in the displayed Settings dialog.</p>" \
|
||||
"<p>After saving your settings, we recommend you download the latest firmware for your radio by using the <i>File -> Download</i> menu option.</p>" \
|
||||
"<p>Please visit <a href='http://www.open-tx.org'>open-tx.org</a> for latest news, updates and documentation. Thank you for choosing OpenTX!</p>" \
|
||||
"- The OpenTX Team.")
|
||||
"<p>Please visit <a href='http://www.edge-tx.org'>edge-tx.org</a> for latest news, updates and documentation. Thank you for choosing EdgeTX!</p>" \
|
||||
"- The EdgeTX Team.")
|
||||
|
||||
#define CPN_STR_MSG_UPGRADED QCoreApplication::translate("Companion", \
|
||||
"<p><b>Thank you for upgrading to OpenTX %1.</b></p>" \
|
||||
"<p><b>Thank you for upgrading to EdgeTX %1.</b></p>" \
|
||||
"<p>This is a major upgrade that adds and modifies a lot of things, so please make sure that you read release notes carefully" \
|
||||
" to learn about the changes, and thoroughly check each of your models for proper function.</p>" \
|
||||
"<p>Please visit <a href='http://www.open-tx.org'>open-tx.org</a> for release notes and other documentation.</p>" \
|
||||
"- The OpenTX Team.")
|
||||
"<p>Please visit <a href='http://www.edge-tx.org'>edge-tx.org</a> for release notes and other documentation.</p>" \
|
||||
"- The EdgeTX Team.")
|
||||
|
||||
#define CPN_STR_MSG_NO_RADIO_TYPE QCoreApplication::translate("Companion", \
|
||||
"<p>The radio type in the selected profile does not exist. Using the default type instead.</p> <p><b>Please update your profile settings!</b></p>")
|
||||
|
||||
@@ -133,8 +133,7 @@ WizMix::operator ModelData()
|
||||
if (options[FLIGHT_TIMER_OPTION] && throttleChannel >= 0){
|
||||
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
|
||||
strncpy(model.timers[timerIndex].name, "Flt", sizeof(model.timers[timerIndex].name)-1);
|
||||
model.timers[timerIndex].mode.type = SWITCH_TYPE_TIMER_MODE;
|
||||
model.timers[timerIndex].mode.index = TMRMODE_THR_TRG;
|
||||
model.timers[timerIndex].mode = TimerData::TIMERMODE_START;
|
||||
timerIndex++;
|
||||
}
|
||||
|
||||
@@ -142,8 +141,7 @@ WizMix::operator ModelData()
|
||||
if (options[THROTTLE_TIMER_OPTION] && throttleChannel >= 0){
|
||||
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
|
||||
strncpy(model.timers[timerIndex].name, "Thr", sizeof(model.timers[timerIndex].name)-1);
|
||||
model.timers[timerIndex].mode.type = SWITCH_TYPE_TIMER_MODE;
|
||||
model.timers[timerIndex].mode.index = TMRMODE_THR;
|
||||
model.timers[timerIndex].mode = TimerData::TIMERMODE_THR;
|
||||
timerIndex++;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,13 @@ if(Qt5Widgets_FOUND)
|
||||
add_dependencies(${SIMULATOR_TARGET} ${RADIO_DEPENDENCIES})
|
||||
target_compile_definitions(${SIMULATOR_TARGET} PUBLIC ${APP_COMMON_DEFINES}) # set in top-level CMakeLists
|
||||
target_link_libraries(${SIMULATOR_TARGET} ${SDL_LIBRARY} Qt5::Core)
|
||||
# Introduced at 2.5 - TODO replace with a code change
|
||||
# When compiled shared simulators using libopenui and Companion have classes named MainWindow
|
||||
# When the simulator is launched from Companion the runtime dynamic linking can result in Companion overiding the simulator
|
||||
# and crashing the simulator under some OSes
|
||||
# The flag forces the runtime linker to always look for references in the simulator first
|
||||
target_link_options(${SIMULATOR_TARGET} PRIVATE "LINKER:-Bsymbolic")
|
||||
#
|
||||
add_custom_target(libsimulator DEPENDS ${SIMULATOR_TARGET})
|
||||
|
||||
# Prepare the "all-simu-libs" target to build simulator libraries for *every* supported PCB type (PCB_TYPES list)
|
||||
@@ -101,7 +108,7 @@ if(FOX_FOUND)
|
||||
endif()
|
||||
add_executable(simu WIN32 ${SIMU_SRC} ${RADIO_SRC_DIR}/simu.cpp)
|
||||
add_dependencies(simu ${RADIO_DEPENDENCIES})
|
||||
|
||||
|
||||
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} )
|
||||
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL_LIBRARY})
|
||||
target_compile_definitions(simu PUBLIC -DSIMU)
|
||||
|
||||
@@ -603,6 +603,8 @@ class OpenTxSimulatorFactory: public SimulatorFactory
|
||||
return Board::BOARD_TARANIS_X9LITES;
|
||||
#elif defined(PCBX9LITE)
|
||||
return Board::BOARD_TARANIS_X9LITE;
|
||||
#elif defined(PCBNV14)
|
||||
return Board::BOARD_FLYSKY_NV14;
|
||||
#else
|
||||
return Board::BOARD_TARANIS_X9D;
|
||||
#endif
|
||||
|
||||