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 05f3b8709..1f62cbf12 100644 Binary files a/companion/src/images/companion-title.png and b/companion/src/images/companion-title.png differ diff --git a/companion/src/images/simulator/NV14/bottom.png b/companion/src/images/simulator/NV14/bottom.png new file mode 100644 index 000000000..5fc10a49d Binary files /dev/null and b/companion/src/images/simulator/NV14/bottom.png differ diff --git a/companion/src/images/simulator/NV14/left.png b/companion/src/images/simulator/NV14/left.png new file mode 100644 index 000000000..6294e9922 Binary files /dev/null and b/companion/src/images/simulator/NV14/left.png differ diff --git a/companion/src/images/simulator/NV14/right.png b/companion/src/images/simulator/NV14/right.png new file mode 100644 index 000000000..6294e9922 Binary files /dev/null and b/companion/src/images/simulator/NV14/right.png differ diff --git a/companion/src/images/simulator/NV14/top.png b/companion/src/images/simulator/NV14/top.png new file mode 100644 index 000000000..5fc10a49d Binary files /dev/null and b/companion/src/images/simulator/NV14/top.png differ diff --git a/companion/src/images/splash.png b/companion/src/images/splash.png index 424d0c830..c61d211e9 100644 Binary files a/companion/src/images/splash.png and b/companion/src/images/splash.png differ diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 8352cd9bf..9e8ee3ae9 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -48,6 +48,7 @@ #include "dialogs/filesyncdialog.h" #include "profilechooser.h" +#include "constants.h" #include #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