From a9812688b0701b83307b7a8f354c4ae41114b5c5 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Wed, 8 Sep 2021 10:56:39 +1000 Subject: [PATCH] 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 --- companion/src/CMakeLists.txt | 12 +- companion/src/companion.qrc | 19 + companion/src/constants.h | 7 +- .../src/datamodels/compounditemmodels.cpp | 9 - companion/src/dialogs/filesyncdialog.cpp | 86 +-- companion/src/dialogs/filesyncdialog.h | 8 +- companion/src/firmwares/CMakeLists.txt | 1 + companion/src/firmwares/boards.cpp | 39 +- companion/src/firmwares/boards.h | 18 +- companion/src/firmwares/customisation_data.h | 236 ++++++++ companion/src/firmwares/eepromimportexport.h | 2 +- companion/src/firmwares/eeprominterface.h | 3 +- .../src/firmwares/ersky9x/ersky9xeeprom.cpp | 10 +- companion/src/firmwares/generalsettings.cpp | 21 +- companion/src/firmwares/generalsettings.h | 7 +- companion/src/firmwares/modeldata.cpp | 14 +- companion/src/firmwares/modeldata.h | 37 +- companion/src/firmwares/moduledata.cpp | 24 +- .../src/firmwares/opentx/opentxeeprom.cpp | 551 ++++++++++++++---- .../src/firmwares/opentx/opentxinterface.cpp | 62 +- companion/src/firmwares/rawswitch.cpp | 8 +- companion/src/firmwares/rawswitch.h | 5 +- companion/src/firmwares/sensordata.cpp | 4 +- companion/src/firmwares/sensordata.h | 2 +- companion/src/firmwares/timerdata.cpp | 56 +- companion/src/firmwares/timerdata.h | 30 +- companion/src/images/companion-title.png | Bin 8856 -> 14657 bytes .../src/images/simulator/NV14/bottom.png | Bin 0 -> 178 bytes companion/src/images/simulator/NV14/left.png | Bin 0 -> 1273 bytes companion/src/images/simulator/NV14/right.png | Bin 0 -> 1273 bytes companion/src/images/simulator/NV14/top.png | Bin 0 -> 178 bytes companion/src/images/splash.png | Bin 131019 -> 49035 bytes companion/src/mainwindow.cpp | 106 ++-- companion/src/mdichild.cpp | 20 +- companion/src/modeledit/CMakeLists.txt | 4 + .../src/modeledit/colorcustomscreens.cpp | 456 +++++++++++++++ companion/src/modeledit/colorcustomscreens.h | 82 +++ companion/src/modeledit/curves.cpp | 9 +- companion/src/modeledit/modeledit.cpp | 11 + companion/src/modeledit/setup.cpp | 21 +- companion/src/modeledit/setup.h | 1 + companion/src/modeledit/setup_timer.ui | 5 +- companion/src/modeledit/telemetry.cpp | 305 ---------- companion/src/modeledit/telemetry.h | 40 -- companion/src/modeledit/telemetry.ui | 18 +- .../src/modeledit/telemetry_customscreen.ui | 15 +- .../src/modeledit/telemetry_customscreens.cpp | 405 +++++++++++++ .../src/modeledit/telemetry_customscreens.h | 106 ++++ companion/src/multimodelprinter.cpp | 7 +- companion/src/shared/autobitsetcheckbox.cpp | 3 +- companion/src/shared/autocombobox.cpp | 2 +- companion/src/shared/autodoublespinbox.cpp | 2 +- companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 12 + .../src/simulation/simulateduiwidgetNV14.cpp | 76 +++ .../src/simulation/simulateduiwidgetNV14.ui | 206 +++++++ .../src/simulation/simulatorstartupdialog.cpp | 2 +- companion/src/simulation/simulatorwidget.cpp | 3 + companion/src/storage/CMakeLists.txt | 1 + companion/src/storage/appdata.h | 17 +- companion/src/storage/storage.cpp | 4 + companion/src/storage/storage.h | 3 +- companion/src/storage/yaml.cpp | 84 +++ companion/src/storage/yaml.h | 44 ++ companion/src/tests/CMakeLists.txt | 2 +- companion/src/tests/conversions.cpp | 21 +- companion/src/warnings.h | 12 +- companion/src/wizarddata.cpp | 6 +- radio/src/targets/simu/CMakeLists.txt | 9 +- radio/src/targets/simu/opentxsimulator.cpp | 2 + 70 files changed, 2656 insertions(+), 739 deletions(-) create mode 100644 companion/src/firmwares/customisation_data.h create mode 100644 companion/src/images/simulator/NV14/bottom.png create mode 100644 companion/src/images/simulator/NV14/left.png create mode 100644 companion/src/images/simulator/NV14/right.png create mode 100644 companion/src/images/simulator/NV14/top.png create mode 100644 companion/src/modeledit/colorcustomscreens.cpp create mode 100644 companion/src/modeledit/colorcustomscreens.h create mode 100644 companion/src/modeledit/telemetry_customscreens.cpp create mode 100644 companion/src/modeledit/telemetry_customscreens.h create mode 100644 companion/src/simulation/simulateduiwidgetNV14.cpp create mode 100644 companion/src/simulation/simulateduiwidgetNV14.ui create mode 100644 companion/src/storage/yaml.cpp create mode 100644 companion/src/storage/yaml.h diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index b022c3bcf..cdd2941ee 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -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 ") + 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) diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index 5c231c5a2..4c39ec17d 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -243,6 +243,10 @@ images/simulator/TX16S/left_tele.png images/simulator/TX16S/right_ent.png images/simulator/TX16S/right_mdl.png + images/simulator/NV14/left.png + images/simulator/NV14/right.png + images/simulator/NV14/top.png + images/simulator/NV14/bottom.png images/wizard/ailerons.png images/wizard/airbrakes.png images/wizard/elevons.png @@ -1224,4 +1228,19 @@ themes/yerico/48/write_eeprom.png themes/yerico/48/sdsync.png + + ../../radio/src/gui/colorlcd/layouts/mask_layout1+2.png + ../../radio/src/gui/colorlcd/layouts/mask_layout1+3.png + ../../radio/src/gui/colorlcd/layouts/mask_layout1x1.png + ../../radio/src/gui/colorlcd/layouts/mask_layout1x2.png + ../../radio/src/gui/colorlcd/layouts/mask_layout1x3.png + ../../radio/src/gui/colorlcd/layouts/mask_layout1x4.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2+1.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2+3.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2x1.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2x2.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2x3.png + ../../radio/src/gui/colorlcd/layouts/mask_layout2x4.png + ../../radio/src/gui/colorlcd/layouts/mask_layout4+2.png + diff --git a/companion/src/constants.h b/companion/src/constants.h index b635f2d23..81ca8df23 100644 --- a/companion/src/constants.h +++ b/companion/src/constants.h @@ -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_ diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 2f04a34fb..f4a135ee7 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -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; diff --git a/companion/src/dialogs/filesyncdialog.cpp b/companion/src/dialogs/filesyncdialog.cpp index 30026c931..6a9077946 100644 --- a/companion/src/dialogs/filesyncdialog.cpp +++ b/companion/src/dialogs/filesyncdialog.cpp @@ -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(); +} diff --git a/companion/src/dialogs/filesyncdialog.h b/companion/src/dialogs/filesyncdialog.h index 6375baa5b..486c38b66 100644 --- a/companion/src/dialogs/filesyncdialog.h +++ b/companion/src/dialogs/filesyncdialog.h @@ -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 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 diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index b79d8fd42..e3e075c2e 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -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 diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 5a44d246e..b22519097 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -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; } diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 33ac887d9..ef9cd0de8 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -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); +} diff --git a/companion/src/firmwares/customisation_data.h b/companion/src/firmwares/customisation_data.h new file mode 100644 index 000000000..c21649b26 --- /dev/null +++ b/companion/src/firmwares/customisation_data.h @@ -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 + +/* + + 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 +struct WidgetsContainerPersistentData { + ZonePersistentData zones[N]; + ZoneOptionValueTyped options[O]; +}; + +typedef WidgetsContainerPersistentData LayoutPersistentData; + +typedef WidgetsContainerPersistentData 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); + } + } +}; diff --git a/companion/src/firmwares/eepromimportexport.h b/companion/src/firmwares/eepromimportexport.h index d325f00b8..96eb35fbf 100644 --- a/companion/src/firmwares/eepromimportexport.h +++ b/companion/src/firmwares/eepromimportexport.h @@ -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): diff --git a/companion/src/firmwares/eeprominterface.h b/companion/src/firmwares/eeprominterface.h index 00980339c..05d436f2e 100644 --- a/companion/src/firmwares/eeprominterface.h +++ b/companion/src/firmwares/eeprominterface.h @@ -158,7 +158,8 @@ enum Capability { HasBluetooth, HasAntennaChoice, HasADCJitterFilter, - HasTelemetryBaudrate + HasTelemetryBaudrate, + TopBarZones }; class EEPROMInterface diff --git a/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp b/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp index f6e6752ff..263ca1577 100644 --- a/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp +++ b/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp @@ -90,9 +90,9 @@ t_Ersky9xGeneral::t_Ersky9xGeneral() Ersky9xGeneral::operator GeneralSettings () { GeneralSettings result; - + result.version = myVers; - + for (int i=0; i 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) { diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index d1575d3e2..80f68dedf 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -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; diff --git a/companion/src/firmwares/generalsettings.h b/companion/src/firmwares/generalsettings.h index 466d630af..f039c0f36 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -25,6 +25,7 @@ #include "customfunctiondata.h" #include "rawsource.h" #include "datahelpers.h" +#include "customisation_data.h" #include @@ -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; diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 679e90f5f..bad3e96d3 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -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); } diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index f834a0779..fcc529a4f 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -34,6 +34,7 @@ #include "sensordata.h" #include "telem_data.h" #include "timerdata.h" +#include "customisation_data.h" #include @@ -67,34 +68,6 @@ class ScriptData { void clear() { memset(reinterpret_cast(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]; diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index 49a079d79..37bf5be4c 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -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(); } } diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index b82e76795..92c835073 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -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 { + + 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(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(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(this, persistentData.widgetName, "Widget name")); + Append(new SpareBitsField<16>(this)); // pad to word boundary + Append(new WidgetPersistentDataField(this, persistentData.widgetData, board, version)); + } +}; + +template +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(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(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= 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= 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(this, modelData.curves[i].name, "Curve name")); } } if (IS_STM32(board)) { if (version >= 218) { - for (int i=0; i(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(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(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(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(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(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(this, generalData.switchConfig[i])); else internalField.Append(new SpareBitsField<2>(this)); } - for (int i=0; i(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(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(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(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 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()); } diff --git a/companion/src/firmwares/rawswitch.cpp b/companion/src/firmwares/rawswitch.cpp index f609e7942..45d13b1fc 100644 --- a/companion/src/firmwares/rawswitch.cpp +++ b/companion/src/firmwares/rawswitch.cpp @@ -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 diff --git a/companion/src/firmwares/rawswitch.h b/companion/src/firmwares/rawswitch.h index 7c0707a6a..762ff83ff 100644 --- a/companion/src/firmwares/rawswitch.h +++ b/companion/src/firmwares/rawswitch.h @@ -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 diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 9a26a6d82..2e0f96dc3 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -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]; diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 1581b444b..cec9a6a63 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -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); diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index 9034ed7e0..676b3b3c4 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -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(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; +} diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h index 07cc1eedd..42d008664 100644 --- a/companion/src/firmwares/timerdata.h +++ b/companion/src/firmwares/timerdata.h @@ -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(); }; diff --git a/companion/src/images/companion-title.png b/companion/src/images/companion-title.png index 05f3b8709f66036f4c5334cf528f825ea5e6a487..1f62cbf12d996bce181986e995b033621c88164a 100644 GIT binary patch literal 14657 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 literal 8856 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 literal 131019 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≫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

+WefK9lu-&_Q}SsHz)L4lA@C)hj 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 #include @@ -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 website 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 OpenTX website!").arg(OPENTX_DOWNLOADS_PAGE_URL)); + QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the EdgeTX website!").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 = "


"; - aboutStr.append(tr("OpenTX Home Page: %1").arg("http://www.open-tx.org")); + aboutStr.append(tr("EdgeTX Home Page: %1").arg("http://www.edge-tx.org")); aboutStr.append("

"); - aboutStr.append(tr("The OpenTX Companion project was originally forked from eePe").arg("http://code.google.com/p/eepe")); + aboutStr.append(tr("The EdgeTX Companion project was originally forked from OpenTX").arg("https://github.com/opentx/opentx")); aboutStr.append("

"); aboutStr.append(tr("If you've found this program useful, please support by donating").arg(DONATE_STR)); aboutStr.append("

"); aboutStr.append(QString("Version %1, %2").arg(VERSION).arg(__DATE__)); aboutStr.append("

"); - aboutStr.append(tr("Copyright OpenTX Team") + QString("
© 2011-%1
").arg(QString(__DATE__).right(4))); + aboutStr.append(tr("Copyright EdgeTX Team") + QString("
© 2011-%1
").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) { diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index 1a85aafb7..600f44851 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -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); diff --git a/companion/src/modeledit/CMakeLists.txt b/companion/src/modeledit/CMakeLists.txt index 31e727081..73f0acf92 100644 --- a/companion/src/modeledit/CMakeLists.txt +++ b/companion/src/modeledit/CMakeLists.txt @@ -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 diff --git a/companion/src/modeledit/colorcustomscreens.cpp b/companion/src/modeledit/colorcustomscreens.cpp new file mode 100644 index 000000000..e47153085 --- /dev/null +++ b/companion/src/modeledit/colorcustomscreens.cpp @@ -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 +#include + +// 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(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(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(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 +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; +} diff --git a/companion/src/modeledit/colorcustomscreens.h b/companion/src/modeledit/colorcustomscreens.h new file mode 100644 index 000000000..c287d84d0 --- /dev/null +++ b/companion/src/modeledit/colorcustomscreens.h @@ -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 + +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 widgetbtns; + QVector optswidgets; + QVector 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 widgetbtns; + QVector optswidgets; + QVector 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 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 +QGridLayout * addOptionsLayout(T & persistentData, int optioncnt, QString title = ""); diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 600f58e97..210130efa 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -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")); diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 146bc01f0..60ad7781f 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -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); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index de6609591..38ea12fef 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -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(); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 955f2189b..aaf13ddc4 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -50,6 +50,7 @@ class TimerPanel : public ModelPanel void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); void onCountdownBeepChanged(int index); + void onModeChanged(int index); signals: void nameChanged(); diff --git a/companion/src/modeledit/setup_timer.ui b/companion/src/modeledit/setup_timer.ui index 67b929bdb..63ddfa9eb 100644 --- a/companion/src/modeledit/setup_timer.ui +++ b/companion/src/modeledit/setup_timer.ui @@ -9,7 +9,7 @@ 0 0 - 874 + 1047 33 @@ -58,6 +58,9 @@ + + + diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index bb32790b8..4aeff932f 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -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 - 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 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(&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(sender())) - return; - - bool ok; - const int index = sender()->property("index").toInt(&ok), - i = index / 256, - m = index % 256; - const RawSource src(qobject_cast(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(sender())) - return; - - bool ok; - const int index = sender()->property("index").toInt(&ok); - const RawSource src(qobject_cast(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(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(); } } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 3f04bebf6..d8400e794 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -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; diff --git a/companion/src/modeledit/telemetry.ui b/companion/src/modeledit/telemetry.ui index b064167ba..c55054af9 100644 --- a/companion/src/modeledit/telemetry.ui +++ b/companion/src/modeledit/telemetry.ui @@ -7,13 +7,13 @@ 0 0 861 - 737 + 742 - + 4 @@ -999,19 +999,6 @@ - - - - - 0 - 0 - - - - -1 - - - @@ -1059,7 +1046,6 @@ frskyVoltCB frskyCurrentCB bladesCount - customScreens diff --git a/companion/src/modeledit/telemetry_customscreen.ui b/companion/src/modeledit/telemetry_customscreen.ui index f20accba5..17dce2f68 100644 --- a/companion/src/modeledit/telemetry_customscreen.ui +++ b/companion/src/modeledit/telemetry_customscreen.ui @@ -31,7 +31,7 @@ - Custom Screen Type + Type @@ -124,6 +124,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/companion/src/modeledit/telemetry_customscreens.cpp b/companion/src/modeledit/telemetry_customscreens.cpp new file mode 100644 index 000000000..e9cb7c3f9 --- /dev/null +++ b/companion/src/modeledit/telemetry_customscreens.cpp @@ -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 + +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 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(&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(sender())) + return; + + bool ok; + const int index = sender()->property("index").toInt(&ok), + i = index / 256, + m = index % 256; + const RawSource src(qobject_cast(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(sender())) + return; + + bool ok; + const int index = sender()->property("index").toInt(&ok); + const RawSource src(qobject_cast(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(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; + } +} diff --git a/companion/src/modeledit/telemetry_customscreens.h b/companion/src/modeledit/telemetry_customscreens.h new file mode 100644 index 000000000..9c377761f --- /dev/null +++ b/companion/src/modeledit/telemetry_customscreens.h @@ -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 panels; + + QString getTabName(int index); + + void setup(); + void telBarUpdate(); + void connectItemModelEvents(const int id); + inline bool isLocked() { return lock; } +}; diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 0ebef1884..6dfb42c65 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -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; igetCapability(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(); diff --git a/companion/src/shared/autobitsetcheckbox.cpp b/companion/src/shared/autobitsetcheckbox.cpp index 93faf35e5..55c0495f1 100644 --- a/companion/src/shared/autobitsetcheckbox.cpp +++ b/companion/src/shared/autobitsetcheckbox.cpp @@ -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) diff --git a/companion/src/shared/autocombobox.cpp b/companion/src/shared/autocombobox.cpp index a14e08f2e..63494e4bf 100644 --- a/companion/src/shared/autocombobox.cpp +++ b/companion/src/shared/autocombobox.cpp @@ -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::of(&QComboBox::currentIndexChanged), this, &AutoComboBox::onCurrentIndexChanged); } AutoComboBox::~AutoComboBox() diff --git a/companion/src/shared/autodoublespinbox.cpp b/companion/src/shared/autodoublespinbox.cpp index a5590035f..7f6660b88 100644 --- a/companion/src/shared/autodoublespinbox.cpp +++ b/companion/src/shared/autodoublespinbox.cpp @@ -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::of(&QDoubleSpinBox::valueChanged), this, &AutoDoubleSpinBox::onValueChanged); } AutoDoubleSpinBox::~AutoDoubleSpinBox() diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 339a649ba..209f38517 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -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 diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index f9d92ecd7..b6e1e9024 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -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 diff --git a/companion/src/simulation/simulateduiwidgetNV14.cpp b/companion/src/simulation/simulateduiwidgetNV14.cpp new file mode 100644 index 000000000..caa36ab93 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetNV14.cpp @@ -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() << 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() << 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() << 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() << 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() << 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() << 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() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, + SIMU_STR_HLP_KEY_DN % "
" % 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() << 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() << 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; +} diff --git a/companion/src/simulation/simulateduiwidgetNV14.ui b/companion/src/simulation/simulateduiwidgetNV14.ui new file mode 100644 index 000000000..50f30f25b --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetNV14.ui @@ -0,0 +1,206 @@ + + + SimulatedUIWidgetNV14 + + + + 0 + 0 + 520 + 500 + + + + + 0 + 0 + + + + + 520 + 500 + + + + + 520 + 500 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 100 + 500 + + + + + 100 + 500 + + + + background:url(:/images/simulator/NV14/right.png) + + + + + + + + 0 + 0 + + + + + 320 + 480 + + + + + 320 + 480 + + + + + 5 + + + + + + + + + 0 + 0 + + + + + 100 + 500 + + + + + 100 + 500 + + + + true + + + background:url(:/images/simulator/NV14/left.png); + + + + + + + + 0 + 0 + + + + + 320 + 10 + + + + + 320 + 10 + + + + + 5 + + + + background:url(:/images/simulator/NV14/top.png) + + + + + + + + 0 + 0 + + + + + 320 + 10 + + + + + 320 + 10 + + + + + 5 + false + + + + background:url(:/images/simulator/NV14/bottom.png) + + + + + + + + LcdWidget + QWidget +
lcdwidget.h
+ 1 +
+ + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+
+ + +
diff --git a/companion/src/simulation/simulatorstartupdialog.cpp b/companion/src/simulation/simulatorstartupdialog.cpp index b7faf0261..e48a32afe 100644 --- a/companion/src/simulation/simulatorstartupdialog.cpp +++ b/companion/src/simulation/simulatorstartupdialog.cpp @@ -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() diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index b4160a579..1d1d3dbba 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -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; diff --git a/companion/src/storage/CMakeLists.txt b/companion/src/storage/CMakeLists.txt index ffbd39c58..8df237238 100644 --- a/companion/src/storage/CMakeLists.txt +++ b/companion/src/storage/CMakeLists.txt @@ -13,6 +13,7 @@ set(storage_NAMES categorized sdcard otx + yaml ) set(storage_SRCS diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index a17b9c1ce..0f3aa7c7e 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -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; diff --git a/companion/src/storage/storage.cpp b/companion/src/storage/storage.cpp index b23335606..63a5f429e 100644 --- a/companion/src/storage/storage.cpp +++ b/companion/src/storage/storage.cpp @@ -22,6 +22,7 @@ #include "eepe.h" #include "otx.h" #include "sdcard.h" +#include "yaml.h" #include "firmwareinterface.h" #include "eeprominterface.h" #include @@ -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("eepe")); registerStorageFactory(new DefaultStorageFactory("hex")); registerStorageFactory(new DefaultStorageFactory("otx")); + registerStorageFactory(new DefaultStorageFactory("yml")); registerStorageFactory(new SdcardStorageFactory()); } diff --git a/companion/src/storage/storage.h b/companion/src/storage/storage.h index 3837f6897..f9843650e 100644 --- a/companion/src/storage/storage.h +++ b/companion/src/storage/storage.h @@ -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); diff --git a/companion/src/storage/yaml.cpp b/companion/src/storage/yaml.cpp new file mode 100644 index 000000000..9be4b5ef0 --- /dev/null +++ b/companion/src/storage/yaml.cpp @@ -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 +#include + +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 +} diff --git a/companion/src/storage/yaml.h b/companion/src/storage/yaml.h new file mode 100644 index 000000000..9572536d2 --- /dev/null +++ b/companion/src/storage/yaml.h @@ -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 + +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); +}; diff --git a/companion/src/tests/CMakeLists.txt b/companion/src/tests/CMakeLists.txt index aecb746c3..02dd3dbf5 100644 --- a/companion/src/tests/CMakeLists.txt +++ b/companion/src/tests/CMakeLists.txt @@ -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") diff --git a/companion/src/tests/conversions.cpp b/companion/src/tests/conversions.cpp index 611c2a3f1..8317d6ef3 100644 --- a/companion/src/tests/conversions.cpp +++ b/companion/src/tests/conversions.cpp @@ -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(); diff --git a/companion/src/warnings.h b/companion/src/warnings.h index 598a911c0..6be8ee263 100644 --- a/companion/src/warnings.h +++ b/companion/src/warnings.h @@ -28,19 +28,19 @@ #include #define CPN_STR_MSG_WELCOME QCoreApplication::translate("Companion", \ - "

Welcome to OpenTX %1.

" \ + "

Welcome to EdgeTX %1.

" \ "

As the first step, please configure the initial Radio Profile by selecting your Radio Type, Menu Language, and Build Options.

" \ "

You may also want to take this time to review the other available options in the displayed Settings dialog.

" \ "

After saving your settings, we recommend you download the latest firmware for your radio by using the File -> Download menu option.

" \ - "

Please visit open-tx.org for latest news, updates and documentation. Thank you for choosing OpenTX!

" \ - "- The OpenTX Team.") + "

Please visit edge-tx.org for latest news, updates and documentation. Thank you for choosing EdgeTX!

" \ + "- The EdgeTX Team.") #define CPN_STR_MSG_UPGRADED QCoreApplication::translate("Companion", \ - "

Thank you for upgrading to OpenTX %1.

" \ + "

Thank you for upgrading to EdgeTX %1.

" \ "

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.

" \ - "

Please visit open-tx.org for release notes and other documentation.

" \ - "- The OpenTX Team.") + "

Please visit edge-tx.org for release notes and other documentation.

" \ + "- The EdgeTX Team.") #define CPN_STR_MSG_NO_RADIO_TYPE QCoreApplication::translate("Companion", \ "

The radio type in the selected profile does not exist. Using the default type instead.

Please update your profile settings!

") diff --git a/companion/src/wizarddata.cpp b/companion/src/wizarddata.cpp index 25adaee24..d3344c71c 100644 --- a/companion/src/wizarddata.cpp +++ b/companion/src/wizarddata.cpp @@ -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++; } diff --git a/radio/src/targets/simu/CMakeLists.txt b/radio/src/targets/simu/CMakeLists.txt index 9da980ba7..bc076d19d 100644 --- a/radio/src/targets/simu/CMakeLists.txt +++ b/radio/src/targets/simu/CMakeLists.txt @@ -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) diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index 8a1ec7e6e..49f58d380 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -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